[其他ST产品] STM32的串口通信UART/TTL

[复制链接]
1148|16
 楼主| 王派oo 发表于 2023-9-22 11:09 | 显示全部楼层 |阅读模式
常用的串口pin
STM32的串口是基础通信方式, 每个型号都带多组串口, 一般都使用默认的组, 可以参考芯片的datasheet, 去看pinout and pin definitions,

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

 楼主| 王派oo 发表于 2023-9-22 11:10 | 显示全部楼层
可以同时使用三组UART: USART1(PA9, PA10), USART2(PA2, PA3), USART3(PB10, PB11)
 楼主| 王派oo 发表于 2023-9-22 11:10 | 显示全部楼层
stm32f401ccu6
USART1和USART6是APB2, USART2是APB1
48082650d05b8db20a.png
 楼主| 王派oo 发表于 2023-9-22 11:11 | 显示全部楼层
可以同时使用三组UART: USART1(PA9, PA10)或(PB6, PB7), USART2(PA2, PA3), USART6(PA11, PA12)
 楼主| 王派oo 发表于 2023-9-22 11:13 | 显示全部楼层
串口相关的中断
  1. Interrupt Mode
  2.   ===============
  3.   In Interrupt Mode, the USART communication can be managed by 8 interrupt sources and 10 pending bits:

  4.   Pending Bits:
  5.   -------------
  6.      1. USART_IT_TXE :  to indicate the status of the transmit buffer register
  7.      2. USART_IT_RXNE : to indicate the status of the receive buffer register
  8.      3. USART_IT_TC :   to indicate the status of the transmit operation
  9.      4. USART_IT_IDLE : to indicate the status of the Idle Line
  10.      5. USART_IT_CTS :  to indicate the status of the nCTS input
  11.      6. USART_IT_LBD :  to indicate the status of the LIN break detection
  12.      7. USART_IT_NE :   to indicate if a noise error occur
  13.      8. USART_IT_FE :   to indicate if a frame error occur
  14.      9. USART_IT_PE :   to indicate if a parity error occur
  15.     10. USART_IT_ORE :  to indicate if an Overrun error occur

  16.   Interrupt Source:
  17.   -----------------
  18.      1. USART_IT_TXE :  specifies the interrupt source for the Tx buffer empty interrupt.
  19.      2. USART_IT_RXNE : specifies the interrupt source for the Rx buffer not empty interrupt.
  20.      3. USART_IT_TC :   specifies the interrupt source for the Transmit complete interrupt.
  21.      4. USART_IT_IDLE : specifies the interrupt source for the Idle Line interrupt.            
  22.      5. USART_IT_CTS :  specifies the interrupt source for the CTS interrupt.
  23.      6. USART_IT_LBD :  specifies the interrupt source for the LIN break detection interrupt.
  24.      7. USART_IT_PE :   specifies the interrupt source for the parity error interrupt.
  25.      8. USART_IT_ERR :  specifies the interrupt source for the errors interrupt.

  26. [url=home.php?mod=space&uid=536309]@NOTE[/url] Some parameters are coded in order to use them as interrupt source or as pending bits.

  27.   In this Mode it is advised to use the following functions:
  28.      - void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
  29.      - ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
  30.      - void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
 楼主| 王派oo 发表于 2023-9-22 11:13 | 显示全部楼层
pending bits是一种防止中断丢失的机制, 当Mask bits为1的时候中断请求并不会发出, 而是将pending bits置为1, 当mask bits变为0时中断会发出, 然后pending位被清0.

串口通信编程
串口通信就是TX发送和RX接收. 其中TX在绝大多数场合可以直接按字节发送, 需要额外处理的是RX.
 楼主| 王派oo 发表于 2023-9-22 11:13 | 显示全部楼层
串口通信的常见问题
理想的通信方式是发送->等待响应->返回响应, TX之后等待RX响应, 而且响应是完整发送的, 但是实际使用中, RX接收有多种特殊情况
响应太长将缓冲区写满
缓冲区一般会设置为u8[128], 或者u8[256], 对于大部分消息是够的, 对于更大的返回, 如果会造成缓冲溢出的, 建议

如果是累计的多个返回, 最好改进接收响应完成状态的判断机制, 尽量分段处理
对于无法分段的特殊情况, 需要保留最新的内容, 将缓冲设计成FIFO的环形结构, 后接收的消息可以覆盖掉最早的内容.
 楼主| 王派oo 发表于 2023-9-22 11:24 | 显示全部楼层
非请求产生的响应
有两种情况, 一种是在设备开机阶段自检和初始化产生的内容, 这些内容可以通过在上位机设置足够长的delay, 把这些内容忽略掉; 第二种就是在正常工作中, 随时出现的通知信息, 这种情况就不能使用发送->等待的处理方式了, 因为RX和TX完全异步. 体现在代码中, 就是TX之后不等待RX的结果. 在TX之后只需要必要的delay, 例如20ms, 以避免和下一条TX连在一起.

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

如果每次响应到达时基本都是完整的(中间的间隔不会超过1个字节的传输耗时), 就可以使用IDLE中断, 这样实现最简单
如果不能满足上一条的条件, 使用IDLE中断就会有问题, 一个响应可能会被拆成好几份, 导致后期处理难度增大. 这时候可以用一个单独的定时器做延时, 判断响应是否接收完整. 延时设置到10ms - 40ms, 对于大部分串口设备的返回都可以完整收集, 又不至于累积太长的响应. 在定时器中断时将缓冲中的整个响应取出处理.
 楼主| 王派oo 发表于 2023-9-22 11:24 | 显示全部楼层
因请求产生的响应, 响应等待时间可能较长(几十毫秒到几百毫秒)
首先, 如果串口设备带回显, 要将回显先关闭. 回显是为了方便手工调试, 但是在程序中, 会引起不必要的麻烦. 因为命令通过TX输出之后RX就会立即收到回显, 但是真正的响应要过一阵子回来, 在程序处理中很可能就把回显当成是响应, 而把真正的响应丢了. 虽然可以将等待响应的定时器设置得长一点, 但是中间的空档期长, 出错的概率也越大. 对响应的接收是通过RXNE中断将接收到的字节写入缓冲实现的, 和前面的处理方式一样, 可以通过定时器延时, 在请求发送后, 设置一个超时时间然后阻塞进程, 在进程中循环判断响应的接收情况. 在串口的中断处理中, 每次收到RXNE中断后都重置并启用定时器延时20ms, 直至超过这个间隔无响应, 在定时器中断中将响应完整状态置位, 在进程中收集到响应.
 楼主| 王派oo 发表于 2023-9-22 11:32 | 显示全部楼层
串口通信常见实现方式
一般通过以下的步骤实现串口通信
 楼主| 王派oo 发表于 2023-9-22 11:33 | 显示全部楼层
实现Buffer工具方法
  1. #ifndef __BUFFER_H_
  2. #define __BUFFER_H_

  3. #include "stm32f10x.h"

  4. typedef struct
  5. {
  6.   u8* buf;
  7.   u16 size;
  8.   u16 front;
  9.   u16 rear;
  10. } BufferTypeDef;

  11. typedef struct
  12. {
  13.   u8 size;
  14.   u8 length;
  15.   u8* data;
  16. } BufferClip;

  17. void Buffer_Reset(BufferTypeDef* buff);
  18. u16  Buffer_Length(BufferTypeDef* buff);
  19. u8   Buffer_Push(BufferTypeDef* buff, u8 data);
  20. u8   Buffer_Pop(BufferTypeDef* buff, u8* data);
  21. u8   Buffer_Pop_All(BufferTypeDef* buff, BufferClip* clip);
  22. void Buffer_Print(BufferTypeDef* buff);
  23. void Buffer_Print_Hex(BufferTypeDef* buff);
  24. void Buffer_Print_All(BufferTypeDef* buff);

  25. void Buffer_Clip_Print(BufferClip* clip);
  26. void Buffer_Clip_Print_Hex(BufferClip* clip);

  27. #endif




  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include "buffer.h"

  32. void Buffer_Reset(BufferTypeDef* buff)
  33. {
  34.   buff->front = 0;
  35.   buff->rear = 0;
  36. }

  37. u16 Buffer_Length(BufferTypeDef* buff)
  38. {
  39.   if (buff->rear >= buff->front) {
  40.     return buff->rear - buff->front;
  41.   } else {
  42.     return (buff->size - buff->front) + (buff->rear - 0);
  43.   }
  44. }

  45. u8 Buffer_Push(BufferTypeDef* buff, u8 data)
  46. {
  47.   buff->buf[buff->rear] = data;
  48.   buff->rear++;
  49.   if (buff->rear >= buff->size) {
  50.     buff->rear = 0;
  51.   }
  52.   if (buff->front == buff->rear) {
  53.     buff->front = (buff->front + 1) % buff->size;
  54.     return NULL;
  55.   } else {
  56.     return !NULL;
  57.   }
  58. }

  59. u8 Buffer_Pop(BufferTypeDef* buff, u8* data)
  60. {
  61.   if (buff->front == buff->rear) return NULL;

  62.   *data = buff->buf[buff->front];
  63.   buff->front = (buff->front + 1) % buff->size;
  64.   return !NULL;
  65. }

  66. u8 Buffer_Pop_All(BufferTypeDef* buff, BufferClip* clip)
  67. {
  68.   if (buff->front == buff->rear) return NULL;
  69.   
  70.   memset(clip->data, 0x00, clip->size * sizeof(u8));
  71.   clip->length = 0;
  72.   if (buff->front > buff->rear) {
  73.     while (buff->front < buff->size && clip->length <= clip->size) {
  74.       *(clip->data + clip->length++) = buff->buf[buff->front++];
  75.     }
  76.     if (buff->front == buff->size) {
  77.       buff->front = 0;
  78.     }
  79.   }
  80.   while (buff->front < buff->rear && clip->length <= clip->size) {
  81.     *(clip->data + clip->length++) = buff->buf[buff->front++];
  82.   }
  83.   return !NULL;
  84. }

  85. void Buffer_Print(BufferTypeDef* buff)
  86. {
  87.   printf("BUFF:[%03d,%03d)",buff->front, buff->rear);
  88.   if (buff->front == buff->rear) {
  89.     // print nothing;
  90.   } else if (buff->front < buff->rear) {
  91.     for(int i=buff->front; i < buff->rear; i++) {
  92.       printf("%c", buff->buf[i]);
  93.     }
  94.   } else {
  95.     for(int i = buff->front; i < buff->size; i++) {
  96.       printf("%c", buff->buf[i]);
  97.     }
  98.     for(int i = 0; i < buff->rear; i++) {
  99.       printf("%c", buff->buf[i]);
  100.     }
  101.   }
  102.   printf("\r\n");
  103. }

  104. void Buffer_Print_Hex(BufferTypeDef* buff)
  105. {
  106.   printf("BUFF:[%03d,%03d)",buff->front, buff->rear);
  107.   if (buff->front == buff->rear) {
  108.     // print nothing;
  109.   } else if (buff->front < buff->rear) {
  110.     for(int i=buff->front; i<buff->rear; i++) {
  111.       printf("%02X ", buff->buf[i]);
  112.     }
  113.   } else {
  114.     for(int i=buff->front; i < buff->size; i++) {
  115.       printf("%02X ", buff->buf[i]);
  116.     }
  117.     for(int i=0; i<buff->rear; i++) {
  118.       printf("%02X ", buff->buf[i]);
  119.     }
  120.   }
  121.   printf("\r\n");
  122. }

  123. void Buffer_Print_All(BufferTypeDef* buff)
  124. {
  125.   printf("BUFF:[%d,%d)",buff->front, buff->rear);
  126.   for(int i=0; i < buff->size; i++) {
  127.     printf("%c", buff->buf[i]);
  128.   }
  129.   printf("\r\n");
  130. }

  131. void Buffer_Clip_Print(BufferClip* clip)
  132. {
  133.   printf("CLIP:[%03d]", clip->length);
  134.   for(int i = 0; i < clip->length; i++) {
  135.     printf("%c", clip->data[i]);
  136.   }
  137.   printf("\r\n");
  138. }

  139. void Buffer_Clip_Print_Hex(BufferClip* clip)
  140. {
  141.   printf("CLIP:[%03d]", clip->length);
  142.   for(int i = 0; i < clip->length; i++) {
  143.     printf("%02X ", clip->data[i]);
  144.   }
  145.   printf("\r\n");
  146. }
 楼主| 王派oo 发表于 2023-9-22 11:33 | 显示全部楼层
初始化UART端口: 使能GPIO, UART, NVIC
  1. BufferTypeDef RFID_RX_BUF;
  2. u8 RFID_RX_BUF_BUFF[RFID_BUF_SIZE] = {0x00};

  3. BufferClip RFID_RX_CLIP;
  4. u8 RFID_RX_CLIP_DATA[UINT8_MAX] = {0x00};

  5. u8 RFID_RX_STATE = 0;

  6. void RFID_Init(void)
  7. {
  8.   RFID_RX_BUF.buf = RFID_RX_BUF_BUFF;
  9.   RFID_RX_BUF.size = RFID_BUF_SIZE;
  10.   RFID_RX_CLIP.data = RFID_RX_CLIP_DATA;
  11.   RFID_RX_CLIP.size = UINT8_MAX;

  12.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  13.   RCC_APB1PeriphClockCmd(RFID_RCC, ENABLE);
  14.   // GPIO for TX
  15.   GPIO_InitTypeDef  GPIO_InitStructure;
  16.   GPIO_InitStructure.GPIO_Pin = RFID_TX_PIN;
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  18.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  19.   GPIO_Init(RFID_TX_GPIO, &GPIO_InitStructure);
  20.   // GPIO for RX
  21.   GPIO_InitStructure.GPIO_Pin = RFID_RX_PIN;
  22.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  23.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  24.   GPIO_Init(RFID_RX_GPIO, &GPIO_InitStructure);
  25.   // NVIC
  26.   NVIC_InitTypeDef NVIC_InitStructure;
  27.   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  28.   NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
  29.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;
  30.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  31.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  32.   NVIC_Init(&NVIC_InitStructure);
  33.   // USART
  34.   USART_DeInit(RFID_USART);
  35.   USART_InitTypeDef USART_InitStructure;
  36.   USART_InitStructure.USART_BaudRate = 115200;
  37.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  38.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
  39.   USART_InitStructure.USART_Parity = USART_Parity_No;
  40.   USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
  41.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  42.   USART_Init(RFID_USART, &USART_InitStructure);
  43.   USART_ClearFlag(RFID_USART, USART_FLAG_CTS);
  44.   USART_Cmd(RFID_USART, ENABLE);

  45.   USART_ITConfig(RFID_USART, USART_IT_RXNE, ENABLE);
  46.   printf("## RFID Initialized ##\r\n");
  47. }
 楼主| 王派oo 发表于 2023-9-22 11:33 | 显示全部楼层
实现中断处理方法接收消息
一个是串口的RXNE中断, 用于接收每个字节; 另一个是TIMx的计时中断, 用于标记响应接收完成
  1. void USART3_IRQHandler(void)
  2. {
  3.   u8 rev_byte;
  4.   u32 clear;
  5.   if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  {
  6.     rev_byte = USART_ReceiveData(USART3);
  7.     Buffer_Push(&RFID_RX_BUF, rev_byte);
  8.     // Reset the TIM2 counter and enable it
  9.     TIM_SetCounter(TIM2, 0);
  10.     TIM_Cmd(TIM2, ENABLE);
  11.     USART_ClearITPendingBit(USART3, USART_IT_RXNE);
  12.   }
  13. }

  14. void TIM2_IRQHandler(void)
  15. {
  16.   if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
  17.     printf("RFID_RX_STATE++\r\n");
  18.     RFID_RX_STATE++;
  19.   }
  20.   TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  21.   TIM_Cmd(TIM2, DISABLE);
  22. }
 楼主| 王派oo 发表于 2023-9-22 11:33 | 显示全部楼层
实现消息发送
下面这个例子, 在收到消息后, 调用 RFID_Handle_Message()处理响应
  1. void RFID_Send_String(const u8* data, u16 length)
  2. {
  3.   printf("RFID CMD: ");
  4.   for (u16 i = 0; i < length; i++) {
  5.     printf("%02X ", *(data + i));
  6.     USART_SendData(RFID_USART, *(data + i));
  7.     while(USART_GetFlagStatus(RFID_USART, USART_FLAG_TXE) == RESET) { // Wait till sent
  8.       ;// Do nothing
  9.     }
  10.   }
  11.   printf(">> Sent\r\n");
  12. }

  13. bool RFID_Send_Cmd(const u8* cmd, u16 length)
  14. {
  15.   RFID_Send_String(cmd, length);
  16.   // Delay 50ms to avoid being joinned by other commands
  17.   Systick_Delay_ms(50);

  18.   u8 waittime = 10;
  19.   while (waittime--) {
  20.     if(RFID_RX_STATE > 0) {
  21.       printf("RFID_RX_STATE %d\r\n", RFID_RX_STATE);
  22.       if (Buffer_Pop_All(&RFID_RX_BUF, &RFID_RX_CLIP) != NULL) {
  23.         Buffer_Clip_Print_Hex(&RFID_RX_CLIP);
  24.         RFID_Handle_Message();
  25.       }
  26.       RFID_RX_STATE--;
  27.     }
  28.     Systick_Delay_ms(50);
  29.   }
  30.   return true;
  31. }
 楼主| 王派oo 发表于 2023-9-22 11:34 | 显示全部楼层
下面这个例子, 直接在参数中指定期望的响应结果, 只需要返回对比的结果
  1. u8 ESP8266_Send_Cmd2(char *cmd, char *ack, char *ack2, u16 waittime)
  2. {
  3.   ESP8266_Send_String((u8 *)cmd);
  4.   Systick_Delay_ms(50);
  5.   // Make sure waittime is set
  6.   if (waittime < 10) waittime = 10;

  7.   while (waittime--) {
  8.     if(ESP_RX_STATE > 0) {
  9.       printf("ESP_RX_STATE %d\r\n", ESP_RX_STATE);
  10.       ESP_RX_STATE--;
  11.       if (Buffer_Pop_All(&ESP_RX_BUF, &ESP_RX_CLIP) != NULL) {
  12.         Buffer_Clip_Print(&ESP_RX_CLIP);
  13.         if(strstr((char *)(ESP_RX_CLIP.data), ack) != NULL) {
  14.           printf("return success\r\n\n");
  15.           return ACK_SUCCESS;
  16.         }
  17.         if (strlen(ack2) > 0) {
  18.           if(strstr((char *)(ESP_RX_CLIP.data), ack2) != NULL) {
  19.             printf("return success\r\n\n");
  20.             return ACK_SUCCESS;
  21.           }
  22.         }
  23.       }
  24.     }
  25.     Systick_Delay_ms(20);
  26.   }
  27.   printf("return defeat\r\n\n");
  28.   return ACK_DEFEAT;
  29. }
万图 发表于 2024-4-14 13:05 | 显示全部楼层

当异常过压消失,恢复至高阻态
Clyde011 发表于 2024-8-11 08:32 | 显示全部楼层

并且考虑到TVS相同的尺寸
公羊子丹 发表于 2024-8-11 09:25 | 显示全部楼层

板与板(或空板)之间用邮票孔连接
Uriah 发表于 2024-8-11 11:31 | 显示全部楼层

微控制器、数字信号控制器和处理器都具有内部的ESD钳位二极管
帛灿灿 发表于 2024-8-11 13:27 | 显示全部楼层

ESD电压一般超过导通电压
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

308

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部