STM32-串口接收、发送数据实验-程序代码分析
串口通信实验
Printf支持
printf向串口发送一些字符串数据。如果使用串口2,可以修改while((USART1->SR&0X40)==0);和USART1->DR = (u8) ch; 中的USART1为USART2.
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//解决HAL库使用时,某些情况可能报错的bug
int _ttywrch(int ch)
{ch=ch;return ch;
}
//标准库需要的支持函数
struct __FILE
{ int handle; /* Whatever you require here. If the only file you are using is */ /* standard output using printf() for debugging, no file handling */ /* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch;
}
#endif
实验现象
从电脑串口助手发送长度为200以内任意长度的字符串给STM32串口1(字符串以回车换行标识结束),STM32接收到字符串之后,一次性通过串口1把所有数据返回给电脑。
实现过程
把每个接收到的数据保存在一个程序定义的Buffer数组中(数组长度为200),同时把接收到的数据个数保存在定义的变量中。程序通过对接收到的每个数据进行结束判断(接收到回车0x0d之后再接收到换行0x0a),程序接收结束之后,设置相应的标记位,标记结束。。。外部 循环通过判断标志位来判断程序结束,然后一次性通过串口1发送出来。发送完成之后,所有标志位和数据量都清零
#define USART_REC_LEN 200 //定义最大接收字节数 200u8 USART_RX_BUF[USART_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符u16 USART_RX_STA; //接收状态标记
USART_RX_STA | ||
---|---|---|
bit15 | bit14 | bit13~0 |
接收完成标志 | 接收到0X0D标志 | 接收到的有效数据个数 |
程序要求,发送的字符是以回车换行结束(0x0D,0x0A),windows系统下,回车是由两个字符构成的,“0x0d”和“0x0a”。
ABCDEFGHI…….M(0x0D),(0x0A),每次接受一个数据,判断是不是0x0d。如果说接收完成,将接收到的数据发出去,同时清零标志位。
代码
main.c
在main里面有HAL_UART_Transmit函数,HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据,这个数据是一次性全部发送出去,USART_RX_BUF保存接收到的数据,len是此次接收到的数据长度。
发送完之后清零将USART_RX_STA设置为0
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
int main(void)
{u8 len; u16 times=0; HAL_Init(); //初始化HAL库 Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhzdelay_init(180); //初始化延时函数uart_init(115200); //初始化USARTLED_Init(); //初始化LED KEY_Init(); //初始化按键while(1){if(USART_RX_STA&0x8000){ len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度printf("\r\n您发送的消息为:\r\n");HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束printf("\r\n\r\n");//插入换行USART_RX_STA=0;}else{times++;if(times%5000==0){printf("aaaaaaaaaaaaaaa\r\n\r\n\r\n");}if(times%200==0)printf("请输入数据,以回车键结束\r\n"); if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.delay_ms(10); } }
}
usart.h
其中有一个extern,c语言中extern可置于变量或者函数之前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。extern声明变量可以多次,但定义只有一次。
比如usart.h头文件中,extern u8 aRxBuffer[RXBUFFERSIZE];//HAL库USART接收Buffer,aRxBuffer[RXBUFFERSIZE]已经在usart.c定义过了,这样的话,如果在main中引入usart.h头文件,相当于在main里面也用了extern,那么也就可以在main中用usart.c定义过的aRxBuffer[RXBUFFERSIZE]变量。
#ifndef _USART_H
#define _USART_H
#include "sys.h"
#include "stdio.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
extern UART_HandleTypeDef UART1_Handler; //UART句柄#define RXBUFFERSIZE 1 //缓存大小
extern u8 aRxBuffer[RXBUFFERSIZE];//HAL库USART接收Buffer//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);#endif
usart.c
以下文字描述配合代码去理解:
对于void uart_init(u32 bound)函数,里面调用了HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);这里面RXBUFFERSIZE为缓存大小,设置为1,希望在中断服务函数里,每接收到一个字符,都能够进入到回调函数里,方面在回调函数中对数据进行判断。
对于HAL_UART_MspInit函数,里面有一个#if EN_USART1_RX,如果开启接收的话,使能USART1中断通道,设置抢占和响应优先级,usart.h可以找到EN_USART1_RX,他是默认设置为1,也就是开启接收。
然后在USART1_IRQHandler中断服务函数里面,首先调用HAL库中断处理公用函数HAL_UART_IRQHandler(&UART1_Handler),由于设置RXBUFFERSIZE缓存大小为1,每接收到一个字符都将进入HAL_UART_RxCpltCallback回调函数里面。
在usart.c里面定义了两个重要的变量,u8 USART_RX_BUF[USART_REC_LEN];和u16 USART_RX_STA=0; USART_RX_BUF是一个长度为200的数组,在实验过程里面有做说明,它用来保存接收到的数据。USART_RX_STA是一个十六位的变量,为接收状态标记,bit15,接收完成标志;bit14,接收到0x0d;bit13~0,接收到的有效字节数目;
对于HAL_UART_RxCpltCallback接收完成回调函数,USART_RX_STA&0x8000即bit15位比较,若为1则接受完成。USART_RX_STA&0x4000即与第14位比较,若为1则说明,接收到了0x0d。aRxBuffer[0]指这一次接收到的数据,如果说接收到了0x0d之后又接收到了0x0a,说明程序接收结束,设置标记位最高位为1 。
如果说还没收到0X0D,先判断这次是不是0x0d,如果不是,就把这次接收到的数据保存在USART_RX_BUF里面,其中,USART_RX_STA&0x3fff,0x3fff即0011 1111 1111 1111,bit相同则为1否则为0,便得到已经接收的字符数量。
如果说接收数据量超过200,那么就重新开始处理。
#include "usart.h"
#include "delay.h"
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle;
}; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch;
}
#endif #if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记 u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound)
{ //UART 初始化设置UART1_Handler.Instance=USART1; //USART1UART1_Handler.Init.BaudRate=bound; //波特率UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量}//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{//GPIO端口设置GPIO_InitTypeDef GPIO_Initure;if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化{__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟GPIO_Initure.Pin=GPIO_PIN_9; //PA9GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9GPIO_Initure.Pin=GPIO_PIN_10; //PA10HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10#if EN_USART1_RXHAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif }}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance==USART1)//如果是串口1{if((USART_RX_STA&0x8000)==0)//接收未完成,最高位{if(USART_RX_STA&0x4000)//接收到了0x0d{if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始else USART_RX_STA|=0x8000; //接收完成了 }else //还没收到0X0D{ if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 } }}}
}//串口1中断服务程序
void USART1_IRQHandler(void)
{ u32 timeout=0;u32 maxDelay=0x1FFFF;
#if SYSTEM_SUPPORT_OS //使用OSOSIntEnter();
#endifHAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数timeout=0;while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪{timeout++;超时处理if(timeout>maxDelay) break; }timeout=0;while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1{timeout++; //超时处理if(timeout>maxDelay) break; }
#if SYSTEM_SUPPORT_OS //使用OSOSIntExit();
#endif
}
#endif
STM32-串口接收、发送数据实验-程序代码分析相关推荐
- 关于stm32串口接收发送数据不完整的相关问题
如果串口接收数据,数据传输的比较快,这个时候还要使用同一个串口发送数据. 如果是在主函数中使用HAL_UART_Receive();,这个时候不管使用HAL_UART_Transmit();还是使用H ...
- 89C52单片机 串口接收发送 数据
文章目录 1.简介 2. 功能实现 1.简介 通过串口对单片机发送数据,然后 数据 +1 返回串口 2. 功能实现 #include <reg52.h>#define uchar unsi ...
- STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085和串口只发送数据不能接收数据问题
STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085的问题讨论:http://www.rt-thr ...
- 笔记——STM32串口USART收发数据。
关于上次的串口内容只是知道怎么用,不晓得什么意思,这次就做个笔记详细的来描述一下.关于STM32有许多通信的协议,其中串口也是常用的通信协议,并且串口用来调试和查看信息非常重要的.包括用到阿里云的云智 ...
- C语言实现linux环境UDP协议接收发送数据
C语言实现linux环境UDP协议接收发送数据 说明 上代码 运行结果 说明 闲来无事,写了个C语言的UDP小程序,程序新建了两个线程,分别用来实现UDP数据到发送和接收.需要的直接拿去用. 上代码 ...
- 【STM32】串口收发主要程序代码分析
文章目录 数据发送与接收 串口状态 开启串口响应中断 获取相应中断状态 main.c usart.c usart.h 串口设置的一般步骤可以总结为如下几个步骤: 串口时钟使能,GPIO 时钟使能 串口 ...
- FPGA 串口中断_一个严谨的STM32串口DMA发送amp;接收(1.5Mbps波特率)机制
昨天分享的<嵌入式大杂烩读者福利:第一期>大家有去抽奖吗,没抽的可参与抽奖,碰碰运气.我最喜欢抽奖了,还记得前几个月疫情严重时期连抽中了3包口罩,真刺激,哈哈.之后多多安排抽奖,敬请期待. ...
- 一个严谨的STM32串口DMA发送接收(1.5Mbps波特率)机制
文章目录 1 前言 2 串口有必要使用DMA吗 3 实现方式 4 STM32串口使用DMA 5 串口DMA接收 5.1 基本流程 5.2 相关配置 5.3 接收处理 5.3 .1 接收数据大小 5.3 ...
- STM32串口接收不定长数据原理与源程序
**STM32串口接收不定长数据原理与源程序**CSDN上有很多关于STM32串口接收不定长数据的文章,但实际使用后发现照搬他们的代码,程序根本就不能正确接收数据,其中最关键的一句有问题.其余内容完全 ...
最新文章
- ubuntu 16 下安装 Ubuntu Make
- [实变函数]6 微分与不定积分
- xp系统无法创建宽带连接服务器地址,XP下无法建立宽带拨号连接修复一例(新建连接向导选项为灰色)...
- 推荐系统中使用ctr排序的f(x)的设计-传统模型篇
- Windows2008安装组件命令行工具ServerManagerCmd用法介绍
- 【华为大咖分享】6.华为专家揭秘研发效能提升之道(后附PPT下载地址)
- 直接插入排序、冒泡排序实验详解【数据结构实验报告】
- selenium中的window handle
- 游戏测试——1、主要测什么
- vtkContourFilter等值面(线)
- 数据结构c语言版陈越,数据结构 陈越
- 报错:SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.
- 如何防御ddos攻击?
- C语言写的学生管理系统
- 如何解决Vmware Ubuntu网络问题(适用于锐捷客户端用户)
- 阿里巴巴直播防控中的实人认证技术 1
- unity3d中隐藏/显示物体方法总结 – unity3d游戏开发
- Eclipse+Pydev搭建Python开发环境教程
- Linux命令之投影密码开启和关闭命令
- 如何使用Android原生接口,实现“应用双开”
热门文章
- elf文件格式实例解析
- 2 字符串求交集_PostGIS教程十八:维数扩展的9交集模型
- python csdn博客_GitHub - 1783955902/CSDNBlogBackup: Python实现CSDN博客的完整备份
- python 描述_python描述符
- linux java no x11_在linux下运行javaMail程序报No X11 DISPLAY variable was...
- Android代码设置角标,Android上的Badge,快速实现给应用添加角标
- python内置函数open的解释_在python的内置open()函数中缓冲的用途是什么?
- java 反射 父类的属性_用反射的方式获取父类中的所有属性和方法
- win7系统如何开启udma功能
- 谷歌Chrome浏览器正式上新Android版黑暗模式