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 register2. USART_IT_RXNE : to indicate the status of the receive buffer register3. USART_IT_TC : to indicate the status of the transmit operation4. USART_IT_IDLE : to indicate the status of the Idle Line5. USART_IT_CTS : to indicate the status of the nCTS input6. USART_IT_LBD : to indicate the status of the LIN break detection7. USART_IT_NE : to indicate if a noise error occur8. USART_IT_FE : to indicate if a frame error occur9. USART_IT_PE : to indicate if a parity error occur10. USART_IT_ORE : to indicate if an Overrun error occurInterrupt 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], 对于大部分消息是够的, 对于更大的返回, 如果会造成缓冲溢出的, 建议
- 如果是累计的多个返回, 最好改进接收响应完成状态的判断机制, 尽量分段处理
- 对于无法分段的特殊情况, 需要保留最新的内容, 将缓冲设计成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 TXGPIO_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 RXGPIO_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); // NVICNVIC_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);// USARTUSART_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 itTIM_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 commandsSystick_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 setif (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;
}
参考
- https://geekilyinteresting.wordpress.com/tag/usart1_irqhandler/
- http://www.disca.upv.es/aperles/arm_cortex_m3/curset/STM32F4xx_DSP_StdPeriph_Lib_V1.0.1/html/group___u_s_a_r_t___group9.html
- https://www.mikrocontroller.net/attachment/274908/Code.txt
- https://github.com/PX4/PX4-Flow/blob/master/src/modules/flow/usart.c
- https://hackaday.com/2020/10/22/stm32-clones-the-good-the-bad-and-the-ugly/
- https://hackaday.com/2021/01/20/blue-pill-vs-black-pill-transitioning-from-stm32f103-to-stm32f411/
STM32的串口通信UART/TTL相关推荐
- linux串口命令UART,STM32串口通信UART使用
STM32串口通信UART使用 uart使用的过程为: 1. 使能GPIO口和UART对应的总线时钟 2. 配置GPIO口的输出模式 3. 配置uart口相关的基本信息 4. 使能uart口的相关的中 ...
- 嵌入式STM32入门之STM32基础串口通信
STM32基础串口通信 一.前言 二.串口协议 (1)浅谈设备通信方式 (2)具体串口协议 (3)STM32的串口通信 (4)RS-232通信协议 (5)USB To TTL(CH340) 5.1 C ...
- 【星曈科技】OpenMv笔记——利用OpenMV与STM32进行串口通信
利用OpenMV与STM32进行串口通信 OpenMV端的程序 # Untitled - By: dell - 周一 7月 19 2021# Blob Detection and uart trans ...
- STM32 —— STM32 的串口通信
STM32 -- STM32 的串口通信 STM32的串口通信接口有两种,分别是:UART(通用异步收发器).USART(通用同步异步收发器).而对于大容量 STM32F10x 系列芯片,分别有 3 ...
- K210学习笔记(三) K210与STM32进行串口通信,K210发STM收
文章目录 前言 一.以/r/n为结尾 二.K210端代码 三.STM32端 3.1 main函数 四.测试结果 4.1当收到1时 4.2当收到2时 4.3 测试视频 总结 前言 前面我们讲了STM32 ...
- Ardunio下实现STM32的串口通信及其与Stduino的区别
摘要 本文介绍了在Ardunio下配置STM32并进行STM32的串口通信:之后我将介绍一下国人MCU开发平台Stduino与现在所使用的Arduino进行比较. 文章目录 摘要 :man_stude ...
- 单片机与PC机的交流———基于STM32的串口通信
STM32之串口通信程序 前言: 一.串口通信要求 二.串口通信准备 1.硬件准备 2.软件准备 三.下载程序进入STM32 1.下载之前相关配置 2.下载 四.最终效果展示 总结: 前言: 本文主要 ...
- 英飞凌TC264学习(四)串口通信UART
英飞凌TC264学习(四)串口通信UART 串口部分的函数在LQ_UART.c中 TC264有四路UART中断,需要中断可以来配置中断,与外部中断一样,中断服务函数,中断号,优先级,不需要中断的话就不 ...
- RS232串口通信(UART的接收部分)
RS232串口通信(UART的接收部分) 置顶 新人博主,创作不易,波形图手绘,请给个点赞关注吧,非常感谢! 串口简介 串口作为常用的三大低速总线(UART.SPI.IIC)之一,在设计众多通信接 ...
最新文章
- 投影与三维视觉——本征矩阵和基础矩阵
- matlab中sign函数用法
- 计算机计算能力共享,服务器计算能力计算器
- 视频来了!Visual Studio Online 东半球首秀 @ .NET Conf 2019 中国峰会
- JAVA补充-抽象类
- 乐山市计算机学校谭娟,乐山市计算机学校重视新闻写作培训
- 读取word 图片_Word中快速输入复杂公式
- 数模美赛LATEX傻瓜入门+快速上手+常见问题(不定期更新)
- 转:施炜:铁军组织是怎样炼成的?高能组织=人×管理体系×数字标准
- 报文解析_101规约报文格式定义解析
- Python实现中英互译
- Unity Application Block 1.0系列(5): 使用BuildUp让已存在对象实例也支持依赖注入
- 如何在功能、特点、价格和性能方面选择PDF编辑器?
- HTML简单动画制作
- C++ Primer读书笔记(从后向前看)
- ng-alain php,在angular中基于ng-alain如何定义自己的select组件?
- Rabbit MQ的基本使用
- Swift语法学习--数组
- html需要电脑什么配置,买电脑主要看什么配置和参数
- 短线看盘比较有效的方法
热门文章
- Eclipse导入java项目后不能修改(read only)
- 游戏SDK应用内悬浮窗的实现(四)
- 被美国三辆警车追是一种什么体验?
- 98% after emitting CopyPlugin vueCli 卡住错误解决
- a1a2b1b2c1c2语言级别区分,在留学前你的意大利语必须要达到什么等级,A1A2B1B2C1C2等级说明...
- 弟中弟的Leetcode总结——数组类(七)
- 【rotors】多旋翼无人机仿真(三)——SE3控制
- 只能进入grub,无法启动windows
- 【渝粤题库】广东开放大学 PLC技术与应用 形成性考核
- Qt:出现错误 QWindowsWindow::setGeometry: Unable to set geometry 401x48+759+477 on QWidgetWindow/‘Message