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("\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 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 ##\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 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++\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 commands
  Systick_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 set
  if (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;
}

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM