基础篇007. 串行通信

news2025/1/11 21:42:59

目录

1. 串行通信

1.1 串行通信概述

1.2 串行通信协议

2.  实验任务

3. 硬件原理

4. 利用STM32CubeMX创建MDK工程

5. 串行通信实验

5.1 UART串口printf,scanf函数串口重定向

5.2 UART串口printf输出实验

5.3串口控制LED实验

6.调试与验证

7.总结


串口调试助手: 下载地址     

1. 串行通信

1.1 串行通信概述

串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。

串口:可以算是一个泛称,一般指代的是串口时序标准。UART、RS232、RS485、TTL都遵循着类似的通信时序协议,因此都被通称为串口。

串行通信(Serial Communication)是指计算机主机与外设之间以及主机系统与主机系统之间数据的串行传送。使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。

串行通信按照发送时钟源和接收时钟源是否需要保持一致,又可分为同步通信和异步通信两种。

使用 RS-232 标准的串口设备间常见的通讯结构见下图:

078a77a6b5a642bca676530fe092290b.png

串口通讯结构图

根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准及 RS-232 标准:

通讯标准

电平标准 (发送端)

5V TTL

逻辑 1: 2.4V-5V

逻辑 0: 0~0.5V

 

RS-232

逻辑 1: -15V~-3V

逻辑 0: +3V~+15V

 

dce21a9cf5c1481489c60f3b299e6124.png

DB9 接口中的公头及母头的各个引脚的标准信号线接法见下图:

55437020d5244a77a4e26736e872a986.png

 

1.2 串行通信协议

串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成见下图:

 

2.  实验任务

利用STM32CubeMX,创建MDK工程,采用串口调试助手发送指令,实现对LED的控制。

1  ------    点亮LED0

2  ------    点亮LED1

3  ------    互斥点亮两个LED

4  ------    互斥点亮两个LED

ffbd9b0b2e0d4906b9ec48eb85ffaea3.png

3. 硬件原理

f29df763704246d4bcc183d027447d7f.png

 

048e8712a6fa42b49696d1762d96f910.png

4. 利用STM32CubeMX创建MDK工程

选择File下的New Project:

ebf5ad9e4a934b4dac8a42e76c16f4f7.png

选择芯片类型(本文为stm32f103RBt6),选择下边的item,然后Start Project:

a945c9c9159c4e4bb2c4b5cdb667f6ed.png

点击左侧的System Core下的SYS,将Debug设置为Serial Wire:

fb6e72175a4542b6aec602a7f98fac90.png

配置时钟:将RCC下的HSE设置为Crystal/Ceramic Resonator

6e1c25fd3b2e49dbb77ebb71fe59c0c5.png

结合开发版的硬件电路,进行GPIO设置

选择GPIO,依次将PA8、PD2设置为GPIO_Output:

7e046be4355544ee90eddb3aae4880ff.png

USART1参数配置:

在 Connectivity 中选择 USART1 设置,并选择 Asynchronous 异步通信。

波特率为 115200 Bits/s。传输数据长度为 8 Bit。奇偶检验 None,停止位 1 ,接收和发送都使能。

e01bf88e01084b5d8f835ed3e69131c1.png

 

配置NVIC

bd72e5cd93304ebea1f1f63faf166e10.png

检查是否已经使能串口中断:

1d2325dafaa348bd81994f915f882fba.png

结合开发版的硬件电路,选择Clock Configuration,做如下配置:

a6ae8978646c40e1ac8bc776765ad559.png

 

项目配置:

在Project Manager下的Project中设置工程名称和工程路径,并选择编译软件。取消勾选Use lastest available version,选择其他版本:

6e68900a80c346d8bb4a48f89ea411de.png

代码生成设置:

3147568144aa49599b13c5fea3a05bc8.png在Code Generate中选择第二个,然后Generate Code,即生成代码:0eea9653d96546efb2afb828ca4314aa.png

a3ee925a12ff4786b231ab2e8ccc5a49.png

可以打开MDK工程编辑了。以下将通过几个实验,说明串口的用法。

 

初始化程序说明。

串口设置的一般步骤可以总结为如下几个步骤:

  1. 串口时钟使能,GPIO 时钟使能
  2. 串口复位
  3. GPIO 端口模式设置
  4. 串口参数初始化
  5. 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
  6. 使能串口
  7. 编写中断处理函数

使用CubeMX配置,前面6个步骤已经自动生成了,我们只需要编写中断处理函数和主函数就可以了。

 

5. 串行通信实验

 

5.1 UART串口printf,scanf函数串口重定向

在学习C语言时我们经常使用C语言标准函数库输入输出函数,比如printf、scanf、getchar等。为让开发板也支持这些函数需要把USART发送和接收函数添加到这些函数的内部函数内。

在C语言HAL库中,fputc函数是printf函数内部的一个函数,功能是将字符ch写入到文件指针f所指向文件的当前写指针位置,简单理解就是把字符写入到特定文件中。

fgetc函数与fputc函数非常相似,实现字符读取功能。在使用scanf函数时需要注意字符输入格式。

注意:

在MDK中使用ARMCC即第5版本的编译器时,使用fput和fgetc函数达到重定向C语言HAL库输入输出函数必须在MDK的工程选项把“UseMicroLIB”勾选上,MicoroLIB是缺省C库的备选库,它对标准C库进行了高度优化使代码更少,占用更少资源。

为使用printf、scanf函数需要在文件中包含stdio.h头文件。

 

在usart.c文件的user code 0 区域内:

77b9bdf277cd4153b41c0b2c18c36949.png

输入如下内容:

4e828382974d4e71a1261d6251636148.png

#include "stdio.h"
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1		//方便调试,改为“#if 0”不要以下功能
	#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)  	//AC6编译器
		//加入以下代码,支持printf函数,而不需要选择use MicroLIB	
		__asm (".global __use_no_semihosting\n\t");  
		void _sys_exit(int x)
			{//定义_sys_exit()以避免使用半主机模式 
				x = x;
			}
			/* __use_no_semihosting was requested, but _ttywrch was */
		void _ttywrch(int ch)
			{
				ch = ch;
			}
			FILE __stdout;
	#elif   defined ( __CC_ARM )   	//AC5编译器
		//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)	
		#pragma import(__use_no_semihosting)             
		//标准库需要的支持函数                 
		struct __FILE 
		{ 
			int handle; 
		}; 
		FILE __stdout; 	
		void _sys_exit(int x)
		{
			x = x;
		}
	#endif
	重定义fputc函数 
	//int fputc(int ch, FILE *f)
	//{ 	
	//	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	//	USART1->DR = (u8) ch;      
	//	return ch;
	//}

	//重定向 c 库函数 printf 到串口 USARTx,重定向后可使用 printf 函数
  //重定向 c 库函数 scanf 到串口 USARTx,重写向后可使用 scanf、 getchar 等函数
  //使用 printf、 scanf 函数需要在文件中包含 stdio.h 头文件。
	#if defined ( __GNUC__ ) && !defined (__clang__) 
		#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
		#define GETCHAR_PROTOTYPE int __io_getchar(FILE *f)
	#else
		#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
		#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
	#endif 
	//改写fputc函数。【有多个串口是,需要修改下面的代码】
	PUTCHAR_PROTOTYPE
	{
	//发送一个字节数据到串口USARTx
		HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000); //发送数据到串口
		return ch;
	}
	
	GETCHAR_PROTOTYPE
	{
		uint8_t ch = 0;
		//等待串口输入数据
		//while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == RESET);
		HAL_UART_Receive(&huart1,&ch, 1, 0xffff);
		return ch;
	}
#endif 

5.2 UART串口printf输出实验

打开Keil文件后,点击Application,在 main.c 文件里的 while(1) 循环内的user code 3区域:

7cea5101f8874f0f9813ee4e6af6f3b9.png

输入如下代码:

80fb0ff834654140aaa505d9d23cdd33.png

		if (__ARMCC_VERSION > 6000000)
			printf("您编译工程时选择的是ARMCLANG(AC6)编译器\n");
		else
			printf("您编译工程时选择的是ARMCC(AC5)编译器\n");
		
		printf("串口输出测试\n\n");
		HAL_Delay(2000);

 

打开串口调试助手,实验结果如下:

选择AC5编译器时的调试结果:

5e1f5c11135d46709d72764537f42a83.png

选择AC6编译器时的调试结果:

d6755dddf47c4b539cebfea03d6e4849.png

 

 

5.3串口控制LED实验

打开Keil文件后,点击Application,在 main.c 文件里的 while(1) 循环内的

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

之间添加以下代码

edc86f2ed8c74f1f8b8b20d277209d5e.png

	char ch;
	GPIO_PinState ledxPinState;

在main.c的user code 3区域:

ac115a53b71244e58aadb5dc42250964.png

输入如下代码:

886a5075c6b94cab9da0314466bc37cf.png

		if (__ARMCC_VERSION > 6000000)
			printf("2您编译工程时选择的是ARMCLANG(AC6)编译器\n\n");
		else
			printf("您编译工程时选择的是ARMCC(AC5)编译器\n\n");
		
		printf("串口控制LED实验,请输入以下数字:\n");
		printf("1  ------    点亮LED0\n");
		printf("2  ------    点亮LED1\n");
		printf("3  ------    互斥点亮两个LED\n");
		printf("4  ------    互斥点亮两个LED\n");
		printf("您还可以重复输入上次输入的数字,观察灯效果. \n");
		ch=getchar();
		printf("已接收您刚才输入的字符: %c\n",ch);
		
		switch (ch) 
			{
			case '1':
				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
				if(HAL_GPIO_ReadPin(LED0_GPIO_Port,LED0_Pin))
					printf("\n已关闭LED0,请在开发板上观察效果\n\n\n\n");
				else
					printf("\n已点亮LED0,请在开发板上观察效果\n\n\n\n");
				break;
			case '2':
				HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);;
				if(HAL_GPIO_ReadPin(LED1_GPIO_Port,LED1_Pin))
					printf("\n已关闭LED1,请在开发板上观察效果\n\n\n\n");
				else
					printf("\n已点亮LED1,请在开发板上观察效果\n\n\n\n");
				break;
			case '3':
				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
				ledxPinState = HAL_GPIO_ReadPin(LED0_GPIO_Port,LED0_Pin);
				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,(GPIO_PinState)!ledxPinState);
				printf("\n两个LED互斥已点亮,请在开发板上观察效果\n\n\n\n");
				break;
			case '4':
				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
				ledxPinState = HAL_GPIO_ReadPin(LED0_GPIO_Port,LED0_Pin);
				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,ledxPinState);
				if(HAL_GPIO_ReadPin(LED0_GPIO_Port,LED0_Pin))
					printf("\n已同时关闭两个LED,请在开发板上观察效果\n\n\n\n");
				else
					printf("\r\n已同时点亮两个LED,请在开发板上观察效果\n\n\n\n");
				break;
			default:
			/* 如果不是指定指令字符,打印提示信息 */
			printf("\r\n请输入以下1~4之间的数字,然后观察点亮效果。\n\n\n");
			break;
			}

打开串口调试助手,实验结果如下:

选择AC6编译器时的调试结果:

 

121df57bb774405e90c4cf32eccf0e06.png

 

如果你需要AC5编译器,请参考如下博文安装设置:

Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载

Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载_笑春风oO的博客-CSDN博客

6.调试与验证

如果您需要虚拟仿真调试,请参考专栏如下博文的5.1节:

基础篇003. 使用STM32CubeMX创建MDK工程,实现流水灯的仿真与下载验证

https://blog.csdn.net/qcmyqcmy/article/details/129159801

如果您需要在Proteus中仿真调试,请参考本专栏的博文:

基础篇004. 采用Proteus + STM32CubeMX + MDK-ARM学习流水灯

https://blog.csdn.net/qcmyqcmy/article/details/129250108

将程序下载到开发板进行验证:

 

4b5d7ef29d5c4a868a9e648e63222b13.jpeg

 

7.总结

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/525456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis主从复制是怎么实现的

如果数据都是存储在一台服务器上,如果出事就完犊子了,比如: 如果服务器发生了宕机,由于数据恢复是需要点时间,那么这个期间是无法服务新的请求的;如果这台服务器的硬盘出现了故障,可能数据就都…

Java注解方式实现aop,切点切面实战

注解方式实现aop我们主要分为如下几个步骤(有更好的方法的话,欢迎交流): 1.在切面类(为切点服务的类)前用Aspect注释修饰,声明为一个切面类。 2.用Pointcut注释声明一个切点,目的是…

STM32 10个工程篇:1.IAP远程升级(三)

本想着周六去更新IAP远程升级(三),但是周三单位突然通知团建周六去爬水长城,晚上回来已经精疲力竭,打开电脑不由地点开网易云音乐听着听着感觉很乏,去床上躺了会可一觉醒来已经夜里三点,于是调整…

【人工智能】— 贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

【人工智能】— 贝叶斯网络 频率学派 vs. 贝叶斯学派贝叶斯学派Probability(概率):独立性/条件独立性:Probability Theory(概率论):Graphical models (概率图模型)什么是图模型(Grap…

深度学习之图像分类识别(一):AlexNet

本专栏介绍基于深度学习进行图像识别的经典和前沿模型,将持续更新,包括不仅限于:AlexNet, ZFNet,VGG,GoogLeNet,ResNet,DenseNet,SENet,MobileNet&#xff0c…

基于matlab使用麦克风阵列进行声波束成形

一、前言 此示例说明了麦克风阵列波束成形,以便在干扰为主的嘈杂环境中提取所需的语音信号。此类操作可用于增强语音信号质量以进行感知或进一步处理。例如,嘈杂的环境可以是交易室,麦克风阵列可以安装在交易计算机的显示器上。如果交易计算机…

js绘制的红心

看腻歪了粒子特效的红心&#xff0c;今天给各位整个线条的&#xff0c;效果图如下&#xff1a; 表白显圣神器&#xff0c;你值得拥有&#xff0c;代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…

必定收藏:国内免费可用 ChatGPT 网页版

ChatGPT是一个基于人工智能的聊天机器人&#xff0c;它可以与用户进行自然语言交互。ChatGPT使用了最新的自然语言处理技术&#xff0c;包括深度学习和神经网络&#xff0c;以便更好地理解用户的意图和回答用户的问题。 ChatGPT可以回答各种问题&#xff0c;包括但不限于常见问…

Cesium入门之七:Cesium加载地形数据

Cesium加载地形数据 一、什么是地形数据二、TerrainProvider类常用属性常用方法 三、TerrainProvider子类CesiumTerrainProvider类常用属性常用方法 CustomHeightmapTerrainProvider类ArcGISTiledElevationTerrainProvider类常用属性常用方法 EllipsoidTerrainProvider类常用属…

bash shell脚本常用代码记录

任何编程语言&#xff0c;常用的语法和代码结构其实不多的&#xff0c;如果为了快速的掌握入手一门编程语言&#xff0c;我认为只需要把该语言的常见语法和代码记下来&#xff0c;再结合实际需求去拼接成新的代码。这篇博客主要是记录bash shell的一些用法&#xff0c;便于日后…

机器学习指标: F1分数

动动发财的小手&#xff0c;点个赞吧&#xff01; F1 score 简介 在本文[1]中&#xff0c;您将了解 F1 分数。 F1 分数是一种机器学习指标&#xff0c;可用于分类模型。尽管分类模型存在许多指标&#xff0c;但通过本文&#xff0c;您将了解 F1 分数的计算方式以及何时使用它有…

腾讯云语音合成

用腾讯云 AI 语音合成打造有声书制作工具 代码开发 第一步&#xff1a;电子书文件解析 第二步&#xff1a;有声语音合成 第三步&#xff1a;完成有声书制作脚本 第四步&#xff1a;脚本可视化 产品体验 腾讯云 AI 语音合成服务已经非常成熟&#xff0c;基于开源工具整合 TTS P…

JAVA135-185

JAVA135-185 多线程多线程成员方法线程优先级eg&#xff0c;卖票&#xff08;线程的安全问题&#xff09;需要解决线程同时抢的问题 同步方法LOCK锁等待唤醒机制阻塞队列实现等待唤醒机制多线程的六种状态红包抽奖箱抽奖比较 线程池最大并行数网络编程InetAdress端口号协议练习…

Mutipart

含义&#xff1a; 多部分的、复合 场景&#xff1a; 位置&#xff1a;package org.springframework.boot.autoconfigure.web.servlet; Springboot中autoconfigration包下web包下servlet下DispatcherServletAutoConfiguration中&#xff1a; 有一个默认加载的Bean Bean …

stm32通用外部spi下载算法实现

参考硬汉嵌入式&#xff1a;【实战技能】任何支持SWD接口的单片机都可以方便移植的SPI Flash烧写算法制作_哔哩哔哩_bilibili 该up主提供的stm32H7的模板工程&#xff0c;目前需求是实现基于正点原子探索者stm32f407zet6W25Q128 下载算法实现步骤 使用cubemx建立一个LED工程…

postgresql walsender源码分析

专栏内容&#xff1a;postgresql内核源码分析个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 介绍 数据结构 WAL对端的状态 walsender进程 WALsender交互流程 在…

【连续介质力学】张量分量的变换定律

张量分量的变换定律 张量的分量是依赖于坐标系的&#xff0c;所以当坐标系发生旋转&#xff0c;张量分量也会发生改变&#xff0c;张量分量与坐标系通过分量变换规律互相关联起来的。 考虑在正交基 ( e ^ 1 , e ^ 2 , e ^ 3 ) (\hat e_1, \hat e_2, \hat e_3) (e^1​,e^2​,…

【51单片机】串口通信使用串口通信控制LED灯

&#x1f38a;专栏【51单片机】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【51单片机】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f354;串口通信 &#x1f60e;代码…

HTTP1.1(四)URI

一 URI的基本格式以及与URL的区别 ① URI直观感受 1&#xff09;URI是HTTP协议的重要组成部分2&#xff09;ABNF如何严格定义URI ② URL、URN、URI ③ URI细讲 ④ URI的组成 ⑤ ABNF关于URI定义 blob:http 地址的原理及生成方法 video 标签 src 带有blob:http data…

NetCore发布到Docker-单机

一.git安装 1.下载 https://github.com/git/git/releases/tag/v2.40.1 2.将本地的安装包上传到 linux 服务器上&#xff0c;我这里放在 /opt/git/ 目录下 3.解压压缩包&#xff0c;得到目录 git-2.40.1&#xff0c;位置在 /opt/git/git-2.40.1 tar -zxvf git-2.40.1.tar.gz 4.…