打印
[其他ST产品]

STM32的串口通信UART/TTL

[复制链接]
563|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
常用的串口pin
STM32的串口是基础通信方式, 每个型号都带多组串口, 一般都使用默认的组, 可以参考芯片的datasheet, 去看pinout and pin definitions,

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


使用特权

评论回复
沙发
王派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

使用特权

评论回复
地板
王派oo|  楼主 | 2023-9-22 11:11 | 只看该作者
可以同时使用三组UART: USART1(PA9, PA10)或(PB6, PB7), USART2(PA2, PA3), USART6(PA11, PA12)

使用特权

评论回复
5
王派oo|  楼主 | 2023-9-22 11:13 | 只看该作者
串口相关的中断
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.

[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.

  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);

使用特权

评论回复
6
王派oo|  楼主 | 2023-9-22 11:13 | 只看该作者
pending bits是一种防止中断丢失的机制, 当Mask bits为1的时候中断请求并不会发出, 而是将pending bits置为1, 当mask bits变为0时中断会发出, 然后pending位被清0.

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

使用特权

评论回复
7
王派oo|  楼主 | 2023-9-22 11:13 | 只看该作者
串口通信的常见问题
理想的通信方式是发送->等待响应->返回响应, TX之后等待RX响应, 而且响应是完整发送的, 但是实际使用中, RX接收有多种特殊情况
响应太长将缓冲区写满
缓冲区一般会设置为u8[128], 或者u8[256], 对于大部分消息是够的, 对于更大的返回, 如果会造成缓冲溢出的, 建议

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

使用特权

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

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

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

使用特权

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

使用特权

评论回复
10
王派oo|  楼主 | 2023-9-22 11:32 | 只看该作者
串口通信常见实现方式
一般通过以下的步骤实现串口通信

使用特权

评论回复
11
王派oo|  楼主 | 2023-9-22 11:33 | 只看该作者
实现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");
}

使用特权

评论回复
12
王派oo|  楼主 | 2023-9-22 11:33 | 只看该作者
初始化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");
}

使用特权

评论回复
13
王派oo|  楼主 | 2023-9-22 11:33 | 只看该作者
实现中断处理方法接收消息
一个是串口的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);
}

使用特权

评论回复
14
王派oo|  楼主 | 2023-9-22 11:33 | 只看该作者
实现消息发送
下面这个例子, 在收到消息后, 调用 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;
}

使用特权

评论回复
15
王派oo|  楼主 | 2023-9-22 11:34 | 只看该作者
下面这个例子, 直接在参数中指定期望的响应结果, 只需要返回对比的结果
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;
}

使用特权

评论回复
16
万图| | 2024-4-14 13:05 | 只看该作者

当异常过压消失,恢复至高阻态

使用特权

评论回复
17
Clyde011| | 2024-8-11 08:32 | 只看该作者

并且考虑到TVS相同的尺寸

使用特权

评论回复
18
公羊子丹| | 2024-8-11 09:25 | 只看该作者

板与板(或空板)之间用邮票孔连接

使用特权

评论回复
19
Uriah| | 2024-8-11 11:31 | 只看该作者

微控制器、数字信号控制器和处理器都具有内部的ESD钳位二极管

使用特权

评论回复
20
帛灿灿| | 2024-8-11 13:27 | 只看该作者

ESD电压一般超过导通电压

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

306

帖子

0

粉丝