• STM32的串口通信UART/TTL


    常用的串口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 register
         2. USART_IT_RXNE : to indicate the status of the receive buffer register
         3. USART_IT_TC :   to indicate the status of the transmit operation
         4. USART_IT_IDLE : to indicate the status of the Idle Line
         5. USART_IT_CTS :  to indicate the status of the nCTS input
         6. USART_IT_LBD :  to indicate the status of the LIN break detection
         7. USART_IT_NE :   to indicate if a noise error occur
         8. USART_IT_FE :   to indicate if a frame error occur
         9. USART_IT_PE :   to indicate if a parity error occur
        10. USART_IT_ORE :  to indicate if an Overrun error occur
    
      Interrupt 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("
    ");
    }
    
    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("
    ");
    }
    
    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("
    ");
    }
    
    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("
    ");
    }
    
    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("
    ");
    }
    
    

    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 TX
      GPIO_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 RX
      GPIO_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); 
      // NVIC
      NVIC_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);
      // USART
      USART_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 ##
    ");
    }
    

    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 it
        TIM_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++
    ");
        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
    ");
    }
    
    bool RFID_Send_Cmd(const u8* cmd, u16 length)
    {
      RFID_Send_String(cmd, length);
      // Delay 50ms to avoid being joinned by other commands
      Systick_Delay_ms(50);
    
      u8 waittime = 10;
      while (waittime--) {
        if(RFID_RX_STATE > 0) {
          printf("RFID_RX_STATE %d
    ", 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 set
      if (waittime < 10) waittime = 10;
    
      while (waittime--) {
        if(ESP_RX_STATE > 0) {
          printf("ESP_RX_STATE %d
    ", 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
    
    ");
              return ACK_SUCCESS;
            }
            if (strlen(ack2) > 0) {
              if(strstr((char *)(ESP_RX_CLIP.data), ack2) != NULL) {
                printf("return success
    
    ");
                return ACK_SUCCESS;
              }
            }
          }
        }
        Systick_Delay_ms(20);
      }
      printf("return defeat
    
    ");
      return ACK_DEFEAT;
    }
    

    参考

  • 相关阅读:
    jenkins 配置杂项优化
    Ansible终极速度优化之--- Mitogen Plugin
    jenkins基于Ansible自动发布/回滚/管理
    Jenkins Pipeline 参数详解
    jenkins pipeline 复杂的发布流程
    jenkins 更新插件使用代理
    pipeline 多个参数如何传入
    k8s-jenkins x CI/CD 动态创建slave---01
    k8s集群-node节点设置不可调度或者删除node节点
    Kubelet 证书如何自动续期
  • 原文地址:https://www.cnblogs.com/milton/p/14714164.html
Copyright © 2020-2023  润新知