一、STM32串口硬件接收与发送框图
0.补充
串口是以ascii码字符(8位二进制数据)来通讯的串口的物理线 数据是一位一位传输的1.串口发送
数据写入发送数据寄存器TDR数据从发送数据寄存器TDR逐步转入发送移位寄存器()-------前:TXE=0-----------数据全部转入发送移位寄存器--------后:TXE=1--------------发送移位寄存器将每一帧数据逐步发送到引脚上
-------前:TC=0-----------最后一帧数据发送完 --------后:TC=1--------------串口发送完成中断按照设置是否执行
2.串口接收
数据从引脚到接收移位寄存器数据从接收移位寄存器到接收数据寄存器RDR-------前:RXNE=0-----------数据全部转入接收数据寄存器RDR--------后:RXNE=1--------------数据可以被读取,也可以产生接收中断
二、printf串口重定义(串口发送)
1.将syscall.c
文件在工程编译中排除
(因为此文件和我们即将要添加的文件相冲突)
2.添加两个文件到工程中
添加retarget.h
文件到Core >> Inc中添加retarget.c
文件到Core >> Src中/** retarget.c** Created on: 10月20日*Author: Administrator*/#include <_ansi.h>#include <_syslist.h>#include <errno.h>#include <sys/time.h>#include <sys/times.h>#include <limits.h>#include <signal.h>#include <../Inc/retarget.h>#include <stdint.h>#include <stdio.h>#if !defined(OS_USE_SEMIHOSTING)#define STDIN_FILENO 0#define STDOUT_FILENO 1#define STDERR_FILENO 2UART_HandleTypeDef *gHuart;void RetargetInit(UART_HandleTypeDef *huart) {gHuart = huart;/* Disable I/O buffering for STDOUT stream, so that* chars are sent out as soon as they are printed. */setvbuf(stdout, NULL, _IONBF, 0);}int _isatty(int fd) {if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)return 1;errno = EBADF;return 0;}int _write(int fd, char* ptr, int len) {HAL_StatusTypeDef hstatus;if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);if (hstatus == HAL_OK)return len;elsereturn EIO;}errno = EBADF;return -1;}int _close(int fd) {if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)return 0;errno = EBADF;return -1;}int _lseek(int fd, int ptr, int dir) {(void) fd;(void) ptr;(void) dir;errno = EBADF;return -1;}int _read(int fd, char* ptr, int len) {HAL_StatusTypeDef hstatus;if (fd == STDIN_FILENO) {hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);if (hstatus == HAL_OK)return 1;elsereturn EIO;}errno = EBADF;return -1;}int _fstat(int fd, struct stat* st) {if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {st->st_mode = S_IFCHR;return 0;}errno = EBADF;return 0;}#endif //#if !defined(OS_USE_SEMIHOSTING)
/** retarget.h** Created on: 10月20日*Author: Administrator*/#ifndef INC_RETARGET_H_#define INC_RETARGET_H_#include "stm32f1xx_hal.h"#include "stdio.h"//用于printf函数串口重映射#include <sys/stat.h>void RetargetInit(UART_HandleTypeDef *huart);int _isatty(int fd);int _write(int fd, char* ptr, int len);int _close(int fd);int _lseek(int fd, int ptr, int dir);int _read(int fd, char* ptr, int len);int _fstat(int fd, struct stat* st);#endif /* INC_RETARGET_H_ */
3.刷新工程 使新添加的文件显示
4.在main.c
中添加retarget.c文件路径
5.将printf()
函数映射到USART1上
6.在while(1){}
中添加以下代码并验证printf()
到USART1的映射
if(KEY_1() == 1){LED_1_Contrary();HAL_UART_Transmit(&huart1, (uint8_t*)&"KEY1\r\n", 6, 0xffff);}if(KEY_2() == 1){LED_2_Contrary();printf("KEY2\r\n");}
三、串口接收中断
0.HAL库执行逻辑
一次中断回调函数的执行时间 < USART外设接收一个8位的数据时间
1.打开串口接收中断
2.添加串口驱动程序文件到icode中
usart.c
#include "usart.h"uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d(回车),bit13~0:接收到的有效字节数目uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数{if(huart == &huart1)//判断中断来源(串口1:USB转串口){//最好是将下一行代码注释,否则会影响长串串口数据的传输,会导致数据丢失。//printf("%c",USART1_NewData); //把本次收到的数据以 a符号变量 发送回电脑if((USART1_RX_STA&0x8000)==0){//接收未完成if(USART1_RX_STA&0x4000){//上次接收到了0x0d(回车)if(USART1_NewData!=0x0a) //确保此次最后一个字符是0x0a(换行)USART1_RX_STA=0; //接收错误,重新开始else USART1_RX_STA|=0x8000; //接收完成了}else{//上次还没收到0X0D(回车)if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; //此次是否接收到0X0D(回车)else{USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组USART1_RX_STA++; //数据长度计数加1if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收}}}HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断}}
备注:HAL库中有串口中断回调的弱函数定义,不需理会
HAL库中的串口回调函数(弱函数)= 替补球员
我们手动添加的串口回调函数 = 正式球员
3. 添加串口文件路径
4.开启串口接收中断
5.在while(1){}
里面添加以下代码验证功能
//串口接收完成标志0x8000,可以尝试使用0xC000实验是否会出现问题if(USART1_RX_STA&0x8000){//串口1判断中断接收标志位if(USART1_RX_BUF[0]=='1'){LED_1(1);//LED1灯控制(1点亮,0熄灭)LED_2(1);//LED2灯控制(1点亮,0熄灭)}if(USART1_RX_BUF[0]=='0'){LED_1(0);//LED1灯控制(1点亮,0熄灭)LED_2(0);//LED2灯控制(1点亮,0熄灭)}USART1_RX_STA=0;//串口接收标志清0,即开启下一轮接收}
6.usart.c
源码对应分析
串口每接收一个8位的数据,中断就会执行一次,中断回调也会执行一次。(若没有回车+换行)将多次中断获得的数据不断累积会一直超出缓冲区的大小,报错并且重新开始。(若有回车+换行)数据接收完毕,串口接收完成标志位(自定义) 就可以在CPU中读取到,并做数据处理。备注:
结束标志必须是回车+换行,\n\r上述代码中的标志位、缓冲区,都是软件中的自定义接收完成标志(USART1_RX_STA的bit15) 指的是全部串口数据接收完成的标志接收完成标志(USART1_RX_STA的bit15)会在CPU中 接收的数据处理完后 置0
四、printf()函数向串口重定义方法二
1.在main.c
中添加下列代码
#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *f)#endifPUTCHAR_PROTOTYPE{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);return ch;};
这样基本上就可以了,但是好像会有警告
如果觉得《06_STM32Cubeide开发_串口通讯》对你有帮助,请点赞、收藏,并留下你的观点哦!