27.串口通信实验源码讲解

2023-05-16

串口通信实验源码讲解

笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除

在工程文件中,位置:工程文件-SYSTEM-usart.c文件下定义好了串口通讯的库文件,可以直接用printf函数调用

在这里插入图片描述

一、printf函数

//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting) 		
//标准库需要的支持函数
struct __FILE 
{ int handle; 
}; 
FILE __stdout; 		
//定义_sys_exit()以避免使用半主机模式
_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

下面是摘抄百度百科的关于C语言中printf函数的知识
printf()函数的调用格式为:
printf("<格式化字符串>", <参量表>);
其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以"%“开始, 后跟一个或几个规定字符,用来确定输出内容格式。参量表是需要输出的一系列参数, 其个数必须与格式化字符串所说明的输出参数个数一样多, 各参数之间用”,"分开, 且顺序一一对应, 否则将会出现意想不到的错误。

  • 规定符
    %d 十进制有符号整数
    %u 十进制无符号整数
    %f 浮点数
    %s 字符串
    %c 单个字符
    %p 指针的值
    %e 指数形式的浮点数
    %x, %X 无符号以十六进制表示的整数
    %o 无符号以八进制表示的整数
    %g 把输出的值按照%e或者%f类型中输出长度较小的方式输出
    %p 输出地址符
    %lu 32位无符号整数
    %llu 64位无符号整数
  • 说明
    (1).可以在“%”和字母之间插进数字表示最大场宽。例如:%3d表示输出3位整型数,不够3位右对齐。%9.2f表示输出场宽为9的浮点数,其中小数位为2,整数位为6,小数点占一位,不够9位右对齐。%8s表示输出8个字符的字符串,不够8个字符右对齐。如果字符串的长度、或整型数位数超过说明的场宽,将按其实际长度输出。但对浮点数,若整数部分位数超过了说明的整数位宽度,将按实际整数位输出;若小数部分位数超过了说明的小数位宽度,则按说明的宽度以四舍五入输出。另外,若想在输出值前加一些0,就应在场宽项前加个0。
    例如:%04d表示在输出一个小于4位的数值时,将在前面补0使其总宽度为4位。如果用非浮点数表示字符或整型量的输出格式,小数点后的数字代表最大宽度,小数点前的数字代表最小宽度。
    例如:%6.9s表示显示一个长度不小于6且不大于9的字符串。若大于9,则第9个字符以后的内容将被删除。
    (2).可以在“%”和字母之间加小写字母l,表示输出的是长型数。
    例如:%ld表示输出long整数%lf表示输出double浮点数
    (3).可以控制输出左对齐或右对齐,即在"%“和字母之间加入一个”-"号可说明输出为左对齐, 否则为右对齐。
    例如:%-7d 表示输出7位整数左对齐%-10s 表示输出10个字符左对齐
  • 一些特殊规定字符作用
    \n 换行
    \f 清屏并换页
    \r 回车
    \t Tab符
    \xhh 表示一个ASCII码用16进表示,
    其中hh是1到2个16进制数

二、实验例子

1.实验现象

从电脑串口助手发送长度为200以内任意长度的字符串给STM32串口1(字符串以回车换行标识结束),STM32接收到字符串之后,一次性通过串口1把所有数据返回给电脑。

2.实验过程

把每个接收到的数据保存在一个程序定义的Buffer数组中(数组长度为200),同时把接收到的数据个数保存在定义的变量中。程序通过对接收到的每个数据进行结束判断(接收到回车0x0d之后再接收到换行0x0a),程序接收结束之后,设置相应的标记位,标记结束。。。外部 循环通过判断标志位来判断程序结束,然后一次性通过串口1发送出来。发送完成之后,所有标志位和数据量都清零。

#define USART_REC_LEN 200 		//定义最大接收字节数 200
u8 USART_RX_BUF[USART_REC_LEN];		//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
u16 USART_RX_STA; 				//接收状态标记

在这里插入图片描述

注意: 回车的ASCII码为0X0D,即系统判断当收到回车时表示这一次串口数据输入完毕,即将发送,在接收完成时,系统返回0X0A

这一讲建议看视频,网址: https://www.bilibili.com/video/BV1Wx411d7wT?p=36

三、usart.c文件源码

#include "usart.h"
#include "delay.h"
// 	 
//如果使用os,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//os 使用	  
#endif

// 	  
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)	
#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;					    //USART1
	UART1_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()会使能UART1
	
	HAL_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;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FAST;		//高速
		GPIO_Initure.Alternate=GPIO_AF7_USART1;	//复用为USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
#if EN_USART1_RX
		HAL_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	 	//使用OS
	OSIntEnter();    
#endif
	
	HAL_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	 	//使用OS
	OSIntExit();  											 
#endif
} 
#endif	

/*下面代码我们直接把中断控制逻辑写在中断服务函数内部。
 说明:采用HAL库处理逻辑,效率不高。*/
/*
//串口1中断服务程序
void USART1_IRQHandler(void)                	
{ 
	u8 Res;
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntEnter();    
#endif
	if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
        HAL_UART_Receive(&UART1_Handler,&Res,1,1000); 
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	}
	HAL_UART_IRQHandler(&UART1_Handler);	
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntExit();  											 
#endif
} 
#endif	
*/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

27.串口通信实验源码讲解 的相关文章

随机推荐

  • kali Linux使用蓝牙

    lsusb 运行hciconfig可以看到 xff1a hci0 Type BR EDR Bus USB BD Address 28 B2 BD 4F 6A 63 ACL MTU 8192 128 SCO MTU 64 128 UP RUN
  • 解决请在微信客服端打开链接问题

    现象 xff1a 在浏览器打开链接 xff0c 显示如图 现象原因 xff1a 调用了微信的接口 解决 xff1a 不调用微信的接口即可 应用场景 xff1a 如果需要在浏览器调试微信公众号网页 xff0c 可以模拟调用必要微信接口的信息或
  • vue+axios上传文件的几种方式及步骤(以上传图片为例)

    1 用js的formData对象上传 xff08 服务器返回url地址 xff09 lt input class 61 34 file 34 name 61 34 file 34 type 61 34 file 34 accept 61 3
  • 解决浏览器缩放时导致的页面布局的变化

    现象 xff1a 正常展示 xff1a 缩放展示 xff1a 原因 xff1a 在网页中 xff0c 如果一个元素没有设置最小宽度 min width xff0c 这时当浏览器缩小到一定程度时 xff0c 元素中的布局可能会发生变化 缩放实
  • 机器学习各大算法简单介绍

    这两天把机器学习的一些基础算法的 简单介绍 整理了一下 xff0c 分别都有概念 xff08 基本思想 xff09 优点 缺点 基本上都是在网络上各个文章里摘录的 xff0c 所以内容也不算我原创 xff0c 但是我把它们筛选整理在了一起
  • Github for windows 使用教程(二)

    转载请注明出处 GitHub for windows使用教程 xff08 二 xff09 分支的使用 创建分支 我们创建第一个分支取名为 new masterh 点击Create new branch创建第一个分支 我们发现此时的分支已经切
  • 「leetcode」C++题解:239. 滑动窗口最大值,单调队列的经典题目

    更多精彩文章持续更新 xff0c 微信搜索 代码随想录 第一时间围观 xff0c 本文https github com youngyangyang04 TechCPP 已经收录 xff0c 里面有更多干货等着你 xff0c 欢迎Star x
  • BAT程序员总结的力扣刷题指南,已经在Github了!!刷题顺序,优质题解一网打尽!

    相信很多小伙伴刷题的时候面对力扣上近两千道题目 xff0c 感觉无从下手 xff01 我花费半年时间整理了Github学习项目 力扣刷题攻略 xff1a https github com youngyangyang04 leetcode m
  • 扩展卡尔曼滤波EKF与多传感器融合

    Extended Kalman Filter xff08 扩展卡尔曼滤波 xff09 是卡尔曼滤波的非线性版本 在状态转移方程确定的情况下 xff0c EKF已经成为了非线性系统状态估计的事实标准 本文将简要介绍EKF xff0c 并介绍其
  • 我把年终总结写成了年度回忆录(1)

    写在前面 这大概是我第一次正儿八经的写个年终总结 xff0c 其实 xff0c 更像是一篇很有意思的回忆录 去年元旦的情景我已经记不起来了 但这一年里 xff0c 却是有很多事情让我难以忘记 从去年寒假自己在出租屋里苦学的时光 xff0c
  • .mat文件后缀名消失

    情况说明 xff1a 下载了 mat文件后 xff0c 打开文件发现文件的后缀名缺失了 xff0c 并且文件类型变为Microsoft Access Table Shortcut类型 具体原因 xff1a 这是由于MATLAB和Access
  • lsusb命令

    在 Linux 中我们可以使用 lsusb 来列出 USB 设备和它的属性 xff0c lsusb 会显示驱动和内部连接到你系统的设备 直接在控制台输入 lsusb 即可 如果无法运行 lsusb xff0c 使用以下命令安装 xff08
  • 现代控制理论基础——卡尔曼滤波(kalman filtering)

    现代控制理论基础 卡尔曼滤波 xff08 kalman filtering xff09 什么是卡尔曼滤波 xff1f 在任何含有不确定信息的动态系统中使用卡尔曼滤波 xff0c 对系统下一步的走向做出有根据的预测 xff0c 对系统状态进行
  • C/C++中的'\0'

    在C C 43 43 语言中 xff0c 字符是按其所对应的ASCII码来存储的 xff0c 一个字符占一个字节 xff0c 而 0 就是ASCII码表中的第一个字符 xff0c ASCII码为00000000 xff0c 它被称为空字符
  • OpenCV 创建图像时,CV_8UC1,CV_32FC3,CV_32S等参数的含义

    形式 xff1a CV lt bit depth gt S U F C lt number of channels gt bit depth xff1a 比特数 代表8bite 16bites 32bites 64bites 举个例子吧 比
  • 解决apt-get update更新错误

    sudo apt get update出现解析错误 xff0c 如下 fkuner 64 data3 span class token function sudo span span class token function apt get
  • C++初阶:vector类

    vector 0 vector的介绍 vector是用数组实现的 可变长度的顺序容器 xff0c 本质是一种类模板 span class token keyword template span span class token operat
  • Git之分支创建策略

    分支类型 git上始终保持两个分支 xff0c master分支 develop分支 master分支主要用于发布时使用 xff0c 而develop分支主要用于开发使用 除了以上两个常驻分支外 xff0c 我们还可以适当分支出三种分支 x
  • ubuntu 设置pip源

    前言 在Ubuntu下我们一般使用pip工具去管理我们的Python包 但是在使用pip命令操作的时候一般都是使用的默认设置 xff0c 使用的是国外的镜像 xff0c 这就导致了我们在国内下载安装包的时候很慢 xff08 乌龟慢慢爬 xf
  • 27.串口通信实验源码讲解

    串口通信实验源码讲解 笔记基于正点原子官方视频 视频连接https www bilibili com video BV1Wx411d7wT p 61 71 amp spm id from 61 333 1007 top right bar