常用的串口pin

STM32的串口是基础通信方式, 每个型号都带多组串口, 一般都使用默认的组, 可以参考芯片的datasheet, 去看pinout and pin definitions,

stm32f103c8t6

这是48pin的芯片, 提供3组串口, 注意USART1是APB2, USART2和3都是PBA1. 各组串口的pin脚为

USART2 USART1 USART3
总线 APB1 APB2 APB1
TX PA2 PA9 PB10
RX PA3 PA10 PB11
CTS PA0 PA11 PB13
RTS PA1 PA12 PB14
CK PA4 PA8 PB12

可以同时使用三组UART: USART1(PA9, PA10), USART2(PA2, PA3), USART3(PB10, PB11)

stm32f401ccu6

USART1和USART6是APB2, USART2是APB1

USART2 USART1 USART6
总线 APB1 APB2 APB2
TX PA2 PA9 / PB6 PA11
RX PA3 PA10 / PB7 PA12
CTS PA0 PA11 PB13
RTS PA1 PA12 PB14
CK PA4 PA8 PC8

可以同时使用三组UART: USART1(PA9, PA10)或(PB6, PB7), USART2(PA2, PA3), USART6(PA11, PA12)

串口相关的中断

Interrupt Mode===============In Interrupt Mode, the USART communication can be managed by 8 interrupt sources and 10 pending bits: Pending Bits:------------- 1. USART_IT_TXE :  to indicate the status of the transmit buffer register2. USART_IT_RXNE : to indicate the status of the receive buffer register3. USART_IT_TC :   to indicate the status of the transmit operation4. USART_IT_IDLE : to indicate the status of the Idle Line5. USART_IT_CTS :  to indicate the status of the nCTS input6. USART_IT_LBD :  to indicate the status of the LIN break detection7. USART_IT_NE :   to indicate if a noise error occur8. USART_IT_FE :   to indicate if a frame error occur9. USART_IT_PE :   to indicate if a parity error occur10. USART_IT_ORE :  to indicate if an Overrun error occurInterrupt Source:-----------------1. USART_IT_TXE :  specifies the interrupt source for the Tx buffer empty interrupt. 2. USART_IT_RXNE : specifies the interrupt source for the Rx buffer not empty interrupt.3. USART_IT_TC :   specifies the interrupt source for the Transmit complete interrupt. 4. USART_IT_IDLE : specifies the interrupt source for the Idle Line interrupt.             5. USART_IT_CTS :  specifies the interrupt source for the CTS interrupt. 6. USART_IT_LBD :  specifies the interrupt source for the LIN break detection interrupt. 7. USART_IT_PE :   specifies the interrupt source for the parity error interrupt. 8. USART_IT_ERR :  specifies the interrupt source for the errors interrupt.@note Some parameters are coded in order to use them as interrupt source or as pending bits.In this Mode it is advised to use the following functions:- void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);- ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);- void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

pending bits是一种防止中断丢失的机制, 当Mask bits为1的时候中断请求并不会发出, 而是将pending bits置为1, 当mask bits变为0时中断会发出, 然后pending位被清0.

串口通信编程

串口通信就是TX发送和RX接收. 其中TX在绝大多数场合可以直接按字节发送, 需要额外处理的是RX.

串口通信的常见问题

理想的通信方式是发送->等待响应->返回响应, TX之后等待RX响应, 而且响应是完整发送的, 但是实际使用中, RX接收有多种特殊情况

响应太长将缓冲区写满

缓冲区一般会设置为u8[128], 或者u8[256], 对于大部分消息是够的, 对于更大的返回, 如果会造成缓冲溢出的, 建议

  1. 如果是累计的多个返回, 最好改进接收响应完成状态的判断机制, 尽量分段处理
  2. 对于无法分段的特殊情况, 需要保留最新的内容, 将缓冲设计成FIFO的环形结构, 后接收的消息可以覆盖掉最早的内容.

非请求产生的响应

有两种情况, 一种是在设备开机阶段自检和初始化产生的内容, 这些内容可以通过在上位机设置足够长的delay, 把这些内容忽略掉; 第二种就是在正常工作中, 随时出现的通知信息, 这种情况就不能使用发送->等待的处理方式了, 因为RX和TX完全异步. 体现在代码中, 就是TX之后不等待RX的结果. 在TX之后只需要必要的delay, 例如20ms, 以避免和下一条TX连在一起.

在处理接收时, 可以使用IDLE中断, 也可以使用定时器判断.

  • 如果每次响应到达时基本都是完整的(中间的间隔不会超过1个字节的传输耗时), 就可以使用IDLE中断, 这样实现最简单
  • 如果不能满足上一条的条件, 使用IDLE中断就会有问题, 一个响应可能会被拆成好几份, 导致后期处理难度增大. 这时候可以用一个单独的定时器做延时, 判断响应是否接收完整. 延时设置到10ms - 40ms, 对于大部分串口设备的返回都可以完整收集, 又不至于累积太长的响应. 在定时器中断时将缓冲中的整个响应取出处理.

因请求产生的响应, 响应等待时间可能较长(几十毫秒到几百毫秒)

首先, 如果串口设备带回显, 要将回显先关闭. 回显是为了方便手工调试, 但是在程序中, 会引起不必要的麻烦. 因为命令通过TX输出之后RX就会立即收到回显, 但是真正的响应要过一阵子回来, 在程序处理中很可能就把回显当成是响应, 而把真正的响应丢了. 虽然可以将等待响应的定时器设置得长一点, 但是中间的空档期长, 出错的概率也越大. 对响应的接收是通过RXNE中断将接收到的字节写入缓冲实现的, 和前面的处理方式一样, 可以通过定时器延时, 在请求发送后, 设置一个超时时间然后阻塞进程, 在进程中循环判断响应的接收情况. 在串口的中断处理中, 每次收到RXNE中断后都重置并启用定时器延时20ms, 直至超过这个间隔无响应, 在定时器中断中将响应完整状态置位, 在进程中收集到响应.

串口通信常见实现方式

一般通过以下的步骤实现串口通信

1. 实现Buffer工具方法

#ifndef __BUFFER_H_
#define __BUFFER_H_#include "stm32f10x.h"typedef struct
{u8* buf;u16 size;u16 front;u16 rear;
} BufferTypeDef;typedef struct
{u8 size;u8 length;u8* data;
} BufferClip;void Buffer_Reset(BufferTypeDef* buff);
u16  Buffer_Length(BufferTypeDef* buff);
u8   Buffer_Push(BufferTypeDef* buff, u8 data);
u8   Buffer_Pop(BufferTypeDef* buff, u8* data);
u8   Buffer_Pop_All(BufferTypeDef* buff, BufferClip* clip);
void Buffer_Print(BufferTypeDef* buff);
void Buffer_Print_Hex(BufferTypeDef* buff);
void Buffer_Print_All(BufferTypeDef* buff);void Buffer_Clip_Print(BufferClip* clip);
void Buffer_Clip_Print_Hex(BufferClip* clip);#endif#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "buffer.h"void Buffer_Reset(BufferTypeDef* buff)
{buff->front = 0;buff->rear = 0;
}u16 Buffer_Length(BufferTypeDef* buff)
{if (buff->rear >= buff->front) {return buff->rear - buff->front;} else {return (buff->size - buff->front) + (buff->rear - 0);}
}u8 Buffer_Push(BufferTypeDef* buff, u8 data)
{buff->buf[buff->rear] = data;buff->rear++;if (buff->rear >= buff->size) {buff->rear = 0;}if (buff->front == buff->rear) {buff->front = (buff->front + 1) % buff->size;return NULL;} else {return !NULL;}
}u8 Buffer_Pop(BufferTypeDef* buff, u8* data)
{if (buff->front == buff->rear) return NULL;*data = buff->buf[buff->front];buff->front = (buff->front + 1) % buff->size;return !NULL;
}u8 Buffer_Pop_All(BufferTypeDef* buff, BufferClip* clip)
{if (buff->front == buff->rear) return NULL;memset(clip->data, 0x00, clip->size * sizeof(u8));clip->length = 0;if (buff->front > buff->rear) {while (buff->front < buff->size && clip->length <= clip->size) {*(clip->data + clip->length++) = buff->buf[buff->front++];}if (buff->front == buff->size) {buff->front = 0;}}while (buff->front < buff->rear && clip->length <= clip->size) {*(clip->data + clip->length++) = buff->buf[buff->front++];}return !NULL;
}void Buffer_Print(BufferTypeDef* buff)
{printf("BUFF:[%03d,%03d)",buff->front, buff->rear);if (buff->front == buff->rear) {// print nothing;} else if (buff->front < buff->rear) {for(int i=buff->front; i < buff->rear; i++) {printf("%c", buff->buf[i]);}} else {for(int i = buff->front; i < buff->size; i++) {printf("%c", buff->buf[i]);}for(int i = 0; i < buff->rear; i++) {printf("%c", buff->buf[i]);}}printf("\r\n");
}void Buffer_Print_Hex(BufferTypeDef* buff)
{printf("BUFF:[%03d,%03d)",buff->front, buff->rear);if (buff->front == buff->rear) {// print nothing;} else if (buff->front < buff->rear) {for(int i=buff->front; i<buff->rear; i++) {printf("%02X ", buff->buf[i]);}} else {for(int i=buff->front; i < buff->size; i++) {printf("%02X ", buff->buf[i]);}for(int i=0; i<buff->rear; i++) {printf("%02X ", buff->buf[i]);}}printf("\r\n");
}void Buffer_Print_All(BufferTypeDef* buff)
{printf("BUFF:[%d,%d)",buff->front, buff->rear);for(int i=0; i < buff->size; i++) {printf("%c", buff->buf[i]);}printf("\r\n");
}void Buffer_Clip_Print(BufferClip* clip)
{printf("CLIP:[%03d]", clip->length);for(int i = 0; i < clip->length; i++) {printf("%c", clip->data[i]);}printf("\r\n");
}void Buffer_Clip_Print_Hex(BufferClip* clip)
{printf("CLIP:[%03d]", clip->length);for(int i = 0; i < clip->length; i++) {printf("%02X ", clip->data[i]);}printf("\r\n");
}

2. 初始化UART端口: 使能GPIO, UART, NVIC

BufferTypeDef RFID_RX_BUF;
u8 RFID_RX_BUF_BUFF[RFID_BUF_SIZE] = {0x00};BufferClip RFID_RX_CLIP;
u8 RFID_RX_CLIP_DATA[UINT8_MAX] = {0x00};u8 RFID_RX_STATE = 0;void RFID_Init(void)
{RFID_RX_BUF.buf = RFID_RX_BUF_BUFF;RFID_RX_BUF.size = RFID_BUF_SIZE;RFID_RX_CLIP.data = RFID_RX_CLIP_DATA;RFID_RX_CLIP.size = UINT8_MAX;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RFID_RCC, ENABLE);// GPIO for TXGPIO_InitTypeDef  GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = RFID_TX_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(RFID_TX_GPIO, &GPIO_InitStructure);// GPIO for RXGPIO_InitStructure.GPIO_Pin = RFID_RX_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(RFID_RX_GPIO, &GPIO_InitStructure); // NVICNVIC_InitTypeDef NVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// USARTUSART_DeInit(RFID_USART);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_Init(RFID_USART, &USART_InitStructure);USART_ClearFlag(RFID_USART, USART_FLAG_CTS);USART_Cmd(RFID_USART, ENABLE);USART_ITConfig(RFID_USART, USART_IT_RXNE, ENABLE);printf("## RFID Initialized ##\r\n");
}

3. 实现中断处理方法接收消息

一个是串口的RXNE中断, 用于接收每个字节; 另一个是TIMx的计时中断, 用于标记响应接收完成

void USART3_IRQHandler(void)
{u8 rev_byte;u32 clear;if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  {rev_byte = USART_ReceiveData(USART3);Buffer_Push(&RFID_RX_BUF, rev_byte);// Reset the TIM2 counter and enable itTIM_SetCounter(TIM2, 0);TIM_Cmd(TIM2, ENABLE);USART_ClearITPendingBit(USART3, USART_IT_RXNE);}
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {printf("RFID_RX_STATE++\r\n");RFID_RX_STATE++;}TIM_ClearITPendingBit(TIM2, TIM_IT_Update);TIM_Cmd(TIM2, DISABLE);
}

4. 实现消息发送

下面这个例子, 在收到消息后, 调用 RFID_Handle_Message()处理响应

void RFID_Send_String(const u8* data, u16 length)
{printf("RFID CMD: ");for (u16 i = 0; i < length; i++) {printf("%02X ", *(data + i));USART_SendData(RFID_USART, *(data + i));while(USART_GetFlagStatus(RFID_USART, USART_FLAG_TXE) == RESET) { // Wait till sent;// Do nothing}}printf(">> Sent\r\n");
}bool RFID_Send_Cmd(const u8* cmd, u16 length)
{RFID_Send_String(cmd, length);// Delay 50ms to avoid being joinned by other commandsSystick_Delay_ms(50);u8 waittime = 10;while (waittime--) {if(RFID_RX_STATE > 0) {printf("RFID_RX_STATE %d\r\n", RFID_RX_STATE);if (Buffer_Pop_All(&RFID_RX_BUF, &RFID_RX_CLIP) != NULL) {Buffer_Clip_Print_Hex(&RFID_RX_CLIP);RFID_Handle_Message();}RFID_RX_STATE--;}Systick_Delay_ms(50);}return true;
}

下面这个例子, 直接在参数中指定期望的响应结果, 只需要返回对比的结果

u8 ESP8266_Send_Cmd2(char *cmd, char *ack, char *ack2, u16 waittime)
{ESP8266_Send_String((u8 *)cmd);Systick_Delay_ms(50);// Make sure waittime is setif (waittime < 10) waittime = 10;while (waittime--) {if(ESP_RX_STATE > 0) {printf("ESP_RX_STATE %d\r\n", ESP_RX_STATE);ESP_RX_STATE--;if (Buffer_Pop_All(&ESP_RX_BUF, &ESP_RX_CLIP) != NULL) {Buffer_Clip_Print(&ESP_RX_CLIP);if(strstr((char *)(ESP_RX_CLIP.data), ack) != NULL) {printf("return success\r\n\n");return ACK_SUCCESS;}if (strlen(ack2) > 0) {if(strstr((char *)(ESP_RX_CLIP.data), ack2) != NULL) {printf("return success\r\n\n");return ACK_SUCCESS;}}}}Systick_Delay_ms(20);}printf("return defeat\r\n\n");return ACK_DEFEAT;
}

参考

  • https://geekilyinteresting.wordpress.com/tag/usart1_irqhandler/
  • http://www.disca.upv.es/aperles/arm_cortex_m3/curset/STM32F4xx_DSP_StdPeriph_Lib_V1.0.1/html/group___u_s_a_r_t___group9.html
  • https://www.mikrocontroller.net/attachment/274908/Code.txt
  • https://github.com/PX4/PX4-Flow/blob/master/src/modules/flow/usart.c
  • https://hackaday.com/2020/10/22/stm32-clones-the-good-the-bad-and-the-ugly/
  • https://hackaday.com/2021/01/20/blue-pill-vs-black-pill-transitioning-from-stm32f103-to-stm32f411/

STM32的串口通信UART/TTL相关推荐

  1. linux串口命令UART,STM32串口通信UART使用

    STM32串口通信UART使用 uart使用的过程为: 1. 使能GPIO口和UART对应的总线时钟 2. 配置GPIO口的输出模式 3. 配置uart口相关的基本信息 4. 使能uart口的相关的中 ...

  2. 嵌入式STM32入门之STM32基础串口通信

    STM32基础串口通信 一.前言 二.串口协议 (1)浅谈设备通信方式 (2)具体串口协议 (3)STM32的串口通信 (4)RS-232通信协议 (5)USB To TTL(CH340) 5.1 C ...

  3. 【星曈科技】OpenMv笔记——利用OpenMV与STM32进行串口通信

    利用OpenMV与STM32进行串口通信 OpenMV端的程序 # Untitled - By: dell - 周一 7月 19 2021# Blob Detection and uart trans ...

  4. STM32 —— STM32 的串口通信

    STM32 -- STM32 的串口通信 STM32的串口通信接口有两种,分别是:UART(通用异步收发器).USART(通用同步异步收发器).而对于大容量 STM32F10x 系列芯片,分别有 3 ...

  5. K210学习笔记(三) K210与STM32进行串口通信,K210发STM收

    文章目录 前言 一.以/r/n为结尾 二.K210端代码 三.STM32端 3.1 main函数 四.测试结果 4.1当收到1时 4.2当收到2时 4.3 测试视频 总结 前言 前面我们讲了STM32 ...

  6. Ardunio下实现STM32的串口通信及其与Stduino的区别

    摘要 本文介绍了在Ardunio下配置STM32并进行STM32的串口通信:之后我将介绍一下国人MCU开发平台Stduino与现在所使用的Arduino进行比较. 文章目录 摘要 :man_stude ...

  7. 单片机与PC机的交流———基于STM32的串口通信

    STM32之串口通信程序 前言: 一.串口通信要求 二.串口通信准备 1.硬件准备 2.软件准备 三.下载程序进入STM32 1.下载之前相关配置 2.下载 四.最终效果展示 总结: 前言: 本文主要 ...

  8. 英飞凌TC264学习(四)串口通信UART

    英飞凌TC264学习(四)串口通信UART 串口部分的函数在LQ_UART.c中 TC264有四路UART中断,需要中断可以来配置中断,与外部中断一样,中断服务函数,中断号,优先级,不需要中断的话就不 ...

  9. RS232串口通信(UART的接收部分)

    RS232串口通信(UART的接收部分) 置顶 新人博主,创作不易,波形图手绘,请给个点赞关注吧,非常感谢! 串口简介 ​ 串口作为常用的三大低速总线(UART.SPI.IIC)之一,在设计众多通信接 ...

最新文章

  1. 投影与三维视觉——本征矩阵和基础矩阵
  2. matlab中sign函数用法
  3. 计算机计算能力共享,服务器计算能力计算器
  4. 视频来了!Visual Studio Online 东半球首秀 @ .NET Conf 2019 中国峰会
  5. JAVA补充-抽象类
  6. 乐山市计算机学校谭娟,乐山市计算机学校重视新闻写作培训
  7. 读取word 图片_Word中快速输入复杂公式
  8. 数模美赛LATEX傻瓜入门+快速上手+常见问题(不定期更新)
  9. 转:施炜:铁军组织是怎样炼成的?高能组织=人×管理体系×数字标准
  10. 报文解析_101规约报文格式定义解析
  11. Python实现中英互译
  12. Unity Application Block 1.0系列(5): 使用BuildUp让已存在对象实例也支持依赖注入
  13. 如何在功能、特点、价格和性能方面选择PDF编辑器?
  14. HTML简单动画制作
  15. C++ Primer读书笔记(从后向前看)
  16. ng-alain php,在angular中基于ng-alain如何定义自己的select组件?
  17. Rabbit MQ的基本使用
  18. Swift语法学习--数组
  19. html需要电脑什么配置,买电脑主要看什么配置和参数
  20. 短线看盘比较有效的方法

热门文章

  1. Eclipse导入java项目后不能修改(read only)
  2. 游戏SDK应用内悬浮窗的实现(四)
  3. 被美国三辆警车追是一种什么体验?
  4. 98% after emitting CopyPlugin vueCli 卡住错误解决
  5. a1a2b1b2c1c2语言级别区分,在留学前你的意大利语必须要达到什么等级,A1A2B1B2C1C2等级说明...
  6. 弟中弟的Leetcode总结——数组类(七)
  7. 【rotors】多旋翼无人机仿真(三)——SE3控制
  8. 只能进入grub,无法启动windows
  9. 【渝粤题库】广东开放大学 PLC技术与应用 形成性考核
  10. Qt:出现错误 QWindowsWindow::setGeometry: Unable to set geometry 401x48+759+477 on QWidgetWindow/‘Message