常用的串口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;
}

参考

STM32的串口通信UART/TTL的更多相关文章

  1. STM32串口通信UART使用

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

  2. STM32的串口通信

    本篇文章主要讲解一个在开发过程中经常使用到的一个外设---串口. 串口是绝大多数 MCU 中不可或缺的一个外设,同时也是我们开发中经常使用的一种调试手段,所以在STM32的学习中,串口的配置使用也是必 ...

  3. (三)stm32之串口通信DMA传输完成中断

    一.DMA功能简介 首先唠叨一下DMA的基本概念,DMA的出现大大减轻了CPU的工作量.在硬件系统中,主要由CPU(内核).外设.内存(SRAM).总线等结构组成,数据经常要在内存和外设之间,外设和外 ...

  4. STM32之串口通信

    一.RS232通信协议 1.概念 个人计算机上的通讯接口之一,由电子工业协会(Electronic Industries Association,EIA) 所制定的异步传输标准接口. 2.电气特性 逻 ...

  5. STM32 在串口通信时运用MODBUS协议

    最近一个项目用到了MODBUS协议,就学习了一下,这里做一下记录以免后续忘记. 要用到MODBUS肯定要先知道是MOBUS协议,这里呢我们就又要先理解协议的含义了. 所谓的协议是什么?就是互相之间的约 ...

  6. Stm32串口通信(USART)

    Stm32串口通信(UART) 串口通信的分类 串口通信三种传递方式 串口通信的通信方式 串行通信的方式: 异步通信:它用一个起始位表示字符的开始,用停止位表示字符的结束.其每帧的格式如下: 在一帧格 ...

  7. STM32学习笔记:【004】USART串口通信

    版本:STM32F429 Hal库v1.10 串口通信能够实现两块电路之间不同的通信,在开发中作为打印调试也是一门利器(printf重定向). 补充一点小知识: 1. weak修饰符修饰的函数,说明这 ...

  8. 嵌入式02 STM32 实验07 串口通信

    STM32串口通信(F1系列包含3个USART和2个UART) 一.单片机与PC机串行通信研究目的和意义: 单片机自诞生以来以其性能稳定,价格低廉.功能强大.在智能仪器.工业装备以及日用电子消费产品中 ...

  9. stm32中的串口通信你了解多少

    在基础实验成功的基础上,对串口的调试方法进行实践.硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中. b) 初始化函数定义: void USART_Confi ...

  10. stm32学习笔记之串口通信

    在基础实验成功的基础上,对串口的调试方法进行实践.硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中. b) 初始化函数定义: void USART_Confi ...

随机推荐

  1. Vue2.x项目整合ExceptionLess监控

    前言 一直以来我们都是用Sentry做项目监控,不过前段时间我们的Sentry坏掉了(我搞坏的) 但监控又是很有必要的,在sentry修好之前,我想先寻找一个临时的替代方案,同时发现网上关于Excep ...

  2. Nginx长连接学习之二

    Nginx长连接学习之二 背景 距离最开始学习Nginx的长连接已经一年半; 距离最开始学习Linux的TCP内核参数也已经过去了一年. 最近产品再次出现了TCP链接相关的问题. 因为一开始不知道部署 ...

  3. [转帖]使用 Dumpling 和 TiDB Lightning 备份与恢复

    本文档介绍如何使用 Dumpling 和 TiDB Lightning 进行全量备份与恢复. 在备份与恢复场景中,如果需要全量备份少量数据(例如小于 50 GB),且不要求备份速度,你可以使用 Dum ...

  4. [转帖]15分钟了解TiDB

    https://zhuanlan.zhihu.com/p/338947811 由于目前的项目把mysql换成了TiDb,所以特意来了解下tidb.其实也不能说换,由于tidb和mysql几乎完全兼容, ...

  5. 【转帖】mysql一个索引块有多少指针_深刻理解MySQL系列之索引

    索引 查找一条数据的过程 先看下InnoDB的逻辑存储结构:node 表空间:能够看作是InnoDB存储引擎逻辑结构的最高层,全部的数据都存放在表空间中.默认有个共享表空间ibdata1.若是启用in ...

  6. [转帖]自动化回归测试工具 —— AREX 上手实践

    https://my.oschina.net/arextest/blog/8589156   AREX 是一款开源的自动化测试工具平台,基于 Java Agent 技术与比对技术,通过流量录制回放能力 ...

  7. [转帖]kubelet 原理解析五: exec的背后

    https://segmentfault.com/a/1190000022163850 概述 线上排查pod 问题一般有两种方式,kubectl log或者kubectl exec调试.如果你的 lo ...

  8. zabbix 6.0 官方文档

    Choose your platform   ZABBIX VERSION 6.0 LTS 5.4 5.0 LTS 4.0 LTS OS DISTRIBUTION Red Hat Enterprise ...

  9. 解决Word等打开嵌入的文件提示 包含有害内容 无法打开的问题

    最近打开文件时提示: 从网上找了一下 最简单的解决办法是: 新建一个文件, 输入如下内容 导入注册表 每次打开时不进行 文件有效性的检查即可. 为了省事 我多加了几个版本的 如果是excel  将 w ...

  10. 使用Grafana 监控 SQLSERVER数据库

    使用Grafana 监控 SQLSERVER数据库 1.获取镜像信息以及启动镜像 docker pull awaragi/prometheus-mssql-exporter docker run -e ...