【串口】初始串口-轮询模式

我们已经了解到了STM32的GPIO以及中断,本次介绍嵌入式领域最常用的通信方式------串口通信。

串口分为好多种类别,例如常见的RS-232、RS-485串口,甚至更常见的,日常使用的网线接口,也是名为RJ-45的串口。USB,全称为Universal Serial Bus 通用串行总线,也是串口。

不过这次我们要聊的是单片机中最常见的串口TTL串口。TTL串口仅需要两根数据线就可以完成两个设备的双向通信。一条是从A设备的发送IO口连接到B设备的接收IO口,另一条相反,是从B设备的发送IO口连接到A设备的接收IO口。两设备在数据线上按照约定好的频率切换高低电平,代表1和0,就可以完成通信。

用来发送的IO口我们称为TX(Transmit),用来接收的IO口称为RX(Receive)。所以要注意的是,使用串口时并不是两个设备的TX与TX连接,而是一台设备的TX接另一台设备的RX。

除了数据线,有时两设备之间还需要连接一根地线,这种行为我们称为共地。需要共地是因为我们平常所说的电压其实是一个相对值,就是说我们规定一个电势点为0V,则其他电压值其实是相对于此电势点的差值。由于通常我们会将大地的电势点作为0V参考点,所以即使设备并不真正接地,惯例上我们也常称这一0V参考点为"地(Ground)"。那么倘若两设备对0V参考点也就是"地"的定义不一样,则对高低电平的理解也就不会一样,一台设备的高低电平到了另一台设备可能会理解成低电平,也就无法进行通信,但是只要我们用导线将两设备的地连接到一起,就将设备的参考电势拉到了同一水平上,从而实现相互理解正常交流,共地是两设备正常通信的前提。

日常开发中,我们常使用串口与电脑进行通信,用电脑给STM32程序发送指令,或者接收STM32发送的数据,以此来调试STM32程序。本次学习我们就来一个简单的程序调试,实现电脑向STM32发送指令,分别控制开发板上红绿蓝三种颜色小灯的亮灭,指令的样式定长为两个字节。第一个字节用一个字母代表要控制的小灯,第二个字节用0或1代表关或开小灯,例如R0代表将红色小灯关闭,G1代表将绿色小灯打开,并且STM32收到指令后需要回复电脑当前收到的指令。

首先来Cube IDE新建一个工程

还是选择我们收藏好的STM32F103C8T6,名称就叫做serial,也就是串行的意思。

点开Connectivity,可以看到其中有三个USART(Universal Synchronous/Asynchronous Receiver&Transmitter),通用同步或异步接收器和发送器。TTL串口用的就是其中的Asynchronous异步通信,所以有时候我们也会见到UART这种缩写。

通过学习套件中附带的功能表我们可以了解到,学习板上调试串口使用的IO口是PA2、PA3。

点一下PA2就可以看到,原来它兼任USART2TX引脚,找到USART2,将它的模式改为TTL串口的模式,也就是异步模式(Asynchronous),可以看到PA2与PA3分别被设置为了USART2的TX与RX,

出现的参数中,我们需要注意的是这一行,baud Rate波特率,波特率是指每秒传送的码元数量,也就是每秒多少次高低电平信号。默认情况下,TTL串口每传递一个字节(Byte)也就是8bit数据,还需要添加一位起始位和一位停止位,也就是每传送1字节信息需要10bit,所以在115200bit/s的波特率下,1秒钟可以传递11520字节数据。听起来还是挺快的,除了115200,常见的波特率还有9600、19200、38400等等。通信的两设备需要使用相同的波特率才能正常通信,这里我们保持115200,下面的三个参数,分别是字节长度、校验位与停止位,我们平常保持默认即可。

Ctrl+s保存并生成代码,在生成的main函数中可以发现,出现了一个MX_USART2_UART_Init函数,就是这个函数帮我们初始化了串口2,设置比特率为115200。

我们先来尝试每隔一秒向电脑发送一次"Hello World"。首先定义一个字符数组叫message,里面存储着Hello World,然后我们在while死循环中写下串口发送函数HAL_UART_Transmit

复制代码
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

参数:
huart:指向一个UART_HandleTypeDef结构体的指针,该结构体包含了UART配置和状态信息。
pData:指向要发送的数据缓冲区的指针。
Size:要发送的数据的字节数。
Timeout:发送操作的超时时间(单位:毫秒)。如果设置为HAL_MAX_DELAY,则表示无限等待。

返回值:
HAL_StatusTypeDef:表示操作的状态,可以是HAL_OK(操作成功)、HAL_BUSY(UART正忙)、HAL_TIMEOUT(操作超时)等。

第一个参数是要操作的串口指针,这里要填huart2不过因为需要指针所以记得前面要有取地址符。第二个参数填的是需要发送的信息的指针,因为变量message是一个数组,所以此处直接写message就是这个数组的指针了,随后是要发送的内容的长度,我们可以用strlen(string length)函数取一下message数组字符串的长度。最后一个是超时时间,就是说多久之后如果还没发现发送完成的话,那就停止发送,可以填写100代表最多只愿意等待100ms。

而如果填写最大值HAL_MAX_DELAY(0xFFFFFFFF),则代表不设超过时间,可以无限等待到发送完成。HAL_Delay一秒钟,实现每秒发送一次的效果。

然后我们先不编译下载一气呵成,而是点击一下小锤子进行一次编译,编译完成会发现有3个warnings(警告),我们向上找找就可以找到,其中前两个其实说的是同一件事,strlen这个函数没被声明就使用了,当然,strlen作为一个内置函数,没声明就使用,也是可以通过编译的,只是编译器会警告我们,最好include一下string.h

那我们就来到main.c的上面#include <string.h>。注意要写在这个include BEGIN-END注释对中。

另外还有一个警告,说HAL_UART_Transmit的第二个参数类型不太对

这是因为我们写的message是一个char指针,但是按住Ctrl点进HAL_UART_Transmit后,我们会发现它第二个参数需要一个uint8_t的指针,实际上char类型与uint8_t类型是一样的,都是8位,所以我们回到main.c,将这里的message强转为uint8_t指针类型即可。

再次编译,0 errors,0 warnings ,然后我们将程序下载进开发板。

但是接下来我们好像遇到两个问题,第一个问题是,如何将STM32与电脑连接到一起进行通信?通常我们需要用到这种东西TTL串口转USB模块,这种模块的使用方式是将STM32的TX RX与模块的RX TX接在一起,当然也别忘了非常重要的共地,然后这个模块就可以充当TTL与USB协议的翻译器,让STM32可以与电脑进行通信。为了让大家更方便,已经在开发板上除了将一个串口的RX TX引出来,用于与蓝牙模块进行通信以外,还直接集成了一颗串口转USB芯片,连接到了我们刚刚使用的USART2上,并且引出了一个Type-C口。只要拿出一根Type-C的USB数据线,也就是平常用的手机数据线,就可以直接将开发板连接到电脑,一段插到开发板上,一段插到电脑USB上就可以了。

那么第二个问题,如何在电脑上看到stm32发来的信息呢?这时候我们需要一种叫做"串口调试助手"的软件,网上这类软件还是比较多的,可以随便下载一个,也可以在浏览器上打开serial.keysking.com就可以使用,整个串口助手是完全开源的,串口助手的使用非常简单,点击"选择串口",然后找到要连接的串口,使用开发板就可以选择这个"USB Single Serial",如果你不确定是哪一个,可以通过插拔数据线的方式,看哪个选项随着插拔出现,消失就可以确定了。

另外,如果没有找到自己的串口也不要着急,这是因为第一次使用串口模块要安装相应的串口驱动,在资料包中就可以找到串口的驱动

没有资料包的也可以直接在这个网页上找到常见的串口模块驱动

然后选择自己电脑的操作系统和串口模块用的芯片型号进行下载就好了,比如使用的开发板,就可以下载CH343的驱动

连接好串口后,可以发现已经能够接收到消息了,而且串口助手使用了非常独特的聊天气泡的方式显示通信消息,可以看到收到的消息确实是每秒钟一次,但好像有点奇怪,并不是我们要的Hello World。

首先,这是因为串口调试助手的波特率与STM32并不一致,我们将其改为115200就好了,现在收到的消息变成了一长串,但还不是Hello World,其实我们已经正常收到了Hello World,只是此时对于接收来的数据,软件默认的显示方式是HEX,也就是按16进制数进行显示,我们在此处点一下,将接收的显示模式改为ASCII,Hello World就展示在我们面前了。

ASCII码在学习C语言时便略有耳闻了,因为不管是计算机存储还是咱们的串口通信,在屋里层面上都只能是高低电平两种状态的组合,所以人们发明了使用8位二进制数来表示常见字符的方法,也就是ASCII码。例如16进制0x48就是大写的H,0x65是小写的e,点击串口助手的菜单,就可以找到ASCII码表平常需要的时候就可以进行查询。

另外串口助手还有一个非常实用的小功能,只要点击每个聊天气泡上方这里,就可以单独切换气泡的显示模式。

我们已经实现了STM32向电脑发送数据,那么STM32怎样接收电脑发送来的数据呢?回到Cube IDE将发送Hello World的代码先注释掉,我们来写接收串口数据的代码。

首先我们先定义一个用来接收数据的数组,这次就不定义char了,直接用uint8_t,数组的名字叫receiveData,我们之前说的指令是两个字节,所以数组长度就设置为2,然后使用HAL_UART_Receive函数来接收数据,其参数与HAL_UART_Transmit函数一模一样。

复制代码
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

参数:
huart:指向 UART_HandleTypeDef 结构体的指针,该结构体包含了 UART 设备的配置信息
pData:指向用于存储接收到的数据的缓冲区的指针 
Size:需要接收的数据长度 
Timeout:接收超时时间,单位为毫秒 
    
返回值:
HAL_OK:函数执行成功
HAL_TIMEOUT:接收数据超时
HAL_ERROR:函数执行出错

第一个参数是要用哪一个串口来接收,也就是huart2的地址。第二个参数是用来接收数据的指针,也就是receiveData,第三个参数是要接收数据的长度,也就是指令长度两个字节,最后一个参数是超时时间,我们设置为最大值HAL_MAX_DELAY,也就是没有超时时间,一直等待,然后我们先用HAL_UART_Transmit把接收到的数据发回电脑看一下,要发送的信息为receiveData,长度是2

复制代码
while(1)
{
    HAL_UART_Receive( &huart2 , receiveData , 2 , HAL_MAX_DELAY );
    HAL_UART_Transmit( &huart2 , receiveData , 2 , 100 );
}

编译下载,打开串口助手,我们把发送也调成ASCII,然后发送R0试试

从图中可以看到,STM32已经成功接收到了电脑发送的信息,并又给发送了回来,那么接下来就简单了,我们回到Cube IDE,首先将三个小灯的GPIO口给设置好,并设置用户标签,Ctrl+s保存并生成代码

然后定义一个GPIO_PinState类型的变量,名字就叫做stateGPIO_PinState是用于表示GPIO高低电平的变量类型,是HAL_GPIO_WritePin函数的最后一个参数,也是HAL_GPIO_ReadPin函数的返回值类型,我们让state的默认值为高电平GPIO_PIN_SET,然后判断一下receiveData的第1位数据是否是0,如果是就将state设置为低电平GPIO_PIN_RESET,然后再判断receiveData的第0位是不是R,如果是就用HAL_GPIO_WritePin控制红色小灯的IO口,将其设置为state变量的状态,同样的,如果是G就去控制绿色小灯,是B就去控制蓝色小灯。

编译下载一下,下载完成打开串口助手,发送R1,红色小灯亮起。发送B1,蓝色小灯亮起。发送B0,蓝色小灯熄灭。发送G1,绿色小灯亮起。

PS:本次学习使用了STM32串口的三大模式中最基本的模式------轮询模式,这种模式其实有一些缺点,比如必须要阻塞住程序的执行,直到完成发送或接收或者等待超时,再比如接收时,需要接收固定长度的数据

相关推荐
yong99902 小时前
基于 51 单片机配合霍尔传感器实现计数 + 转速测量
单片机·嵌入式硬件
崇山峻岭之间3 小时前
单片机时钟配置01
单片机·嵌入式硬件
LongRunning4 小时前
【BLE】STM32WB55_资料链接(八)
stm32
踏着七彩祥云的小丑4 小时前
嵌入式——面试题
单片机·嵌入式硬件
三万棵雪松4 小时前
【嵌入式刷题硬件设计基础(一)】
fpga开发·嵌入式·硬件基础
椰羊~王小美5 小时前
STM32加密
stm32·单片机·嵌入式硬件
Wave8455 小时前
STM32 启动模式与固件更新机制 (底层深度解析)
stm32·单片机·嵌入式硬件
EVERSPIN5 小时前
国产异步SRAM单片机外扩专用存储芯片
单片机·嵌入式硬件·sram·国产sram·异步sram·国产异步sram
CinzWS5 小时前
A53多核协同(下):一致性内存模型与内存屏障——ARM多核的“时间魔法“
arm开发·嵌入式·原型验证·a53