STM32单片机学习笔记——USART串口通信

2019-04-15 17:05发布

本人小白一个,最近对硬件很感兴趣,便买来一块秉火的STM32开发板,使用了STM32F103VET6芯片。 学习数天以后,获得了一点心得,现作一篇学习笔记,以作记录。 本人硬件小白一个,也是第一次写博客,文章多有纰漏和不当之处,还请大家多多关照。 下面正文开始。
首先,USART是什么呢? USART是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。(来自百度) 这是官方解释,而我对它的通俗解释是:这是一个用于和其他设备(如电脑、单片机等)通信(交换数据、信息等)的端口,就像手机数据线那样。 当然,这只是我的一种通俗看法,大家经过了深入的学习之后,一定会产生更为准确、成熟的看法。 我所学习的,就是通过这个模块来实现单片机和电脑之间的通信,并以此为基础,实现利用电脑来简单地控制单片机的目的。 那么,我们来简单地看一看我们这段代码由哪几部分组成: 1. 用于存储相关配置的结构体。 2. 变量Temp,用于存储从电脑接收到的信息。 3. 配置TX、RX、时钟、串口、LED灯。 4. 预先设置LED灯关闭的一段代码。 5. 主函数,实现接收数据、改变灯的亮灭状态。 好了,代码结构就是这样了,下面我们来具体地分析一下代码。
1. 结构体: GPIO_InitTypeDef GPIO_LED_InitStructure; GPIO_InitTypeDef GPIO_USART_TX_InitStructure; GPIO_InitTypeDef GPIO_USART_RX_InitStructure; USART_InitTypeDef USART_InitStructure;
我们先来看一下“GPIO_InitTypeDef”,这是定义在“stm32f10x_gpio.h”里面的结构体,用于存储相关的配置信息。 同理,“USART_InitTypeDef”也是这样,它们是存储两个不同寄存器配置的结构体。 然后,我们看右面的结构体名。这些名字没有什么特殊含义,定义成其他名字也不影响使用。我把它们定义成这个名字就是为了和它们对应的寄存器相匹配。 我一共定义了四个结构体,第一个是LED灯的配置信息,第二个是USART的TX寄存器的配置信息, 第三个是USART的RX寄存器的配置信息,第四个是USART的配置信息。 好了,结构体的说明就到此结束。
2. 变量Temp:
这个没有什么需要多说的,只需要注意它的变量类型,这决定了它能够存储什么样的信息。
3. 配置信息:
先上代码: // 配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 配置USART_TX GPIO_USART_TX_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_USART_TX_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_USART_TX_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_USART_TX_InitStructure); // 配置USART_RX GPIO_USART_RX_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_USART_RX_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_USART_RX_InitStructure); // 配置串口 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); //配置灯   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     GPIO_LED_InitStructure.GPIO_Pin = GPIO_Pin_0;   GPIO_LED_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   GPIO_LED_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     GPIO_Init(GPIOB, &GPIO_LED_InitStructure); 这就是完整的配置信息了。
配置的过程很简单,就是将配置信息赋值给相应的结构体成员,然后调用配置函数,由函数将配置信息写到单片机中。

先从时钟说起吧,“RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);”,这句就是来开启GPIOA时钟的。 当然,根据单片机的布线不同,相应的GPIO可能不是A,这个就灵活应变吧。 然后是“RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);”,这句是来开启USART1的时钟的。 同样,根据单片机的布线不同,相应的USART也有可能不是1。 这两个时钟是必需打开的,否则就不能使用相应的功能了。 这里有个比喻:“时钟就是心脏。”,这句话说的一点没错。即使不需要即使功能也要打开时钟。 (我有点觉得这个时钟其实不是用来计时的……)
然后来配置TX。 TX,就是输出单元,指的是单片机用来给电脑(或其他设备)发送数据的寄存器。 我的单片机上的TX位于Pin9,是Pin9的复用功能,所以就有了第七和第八行的代码。 然后速度设为50MHz,这个可以变更,但没有必要。速度快一点不是很好吗? 这三项配置好后,就可以调用GPIO_Init()来将其配置到单片机里面了。
然后是RX。 RX,相应的就是输入单元。 相似的,它位于Pin10,模式是浮动输入(当然了……)。 输入单元不用配置速度,因为它是被动输入,没有速度这种说法…… 同样,调用GPIO_Init()函数配置一下。
然后是串口。 串口的配置主要包括:波特率、字长、停止位、奇偶校验位、模式、硬件控制流等。 波特率就设为115200(原因我也不懂,大多这么设……自嘲一个……); 字长就设为8位,一个字节;停止位就设为1; 校验位现在不需要,就设为No(就一个字节的信息还校验什么啊……); 模式为TX和RX; 硬件控制流也不需要,设为None。 好了,现在调用USART_Init()来配置好。 这还不够,我们还需要“使能”USART,就是让它工作的意思。调用USART_Cmd()函数就行了。
最后是LED。 我把打开GPIOB时钟的代码写到这里了,好像有点不合理……和之前一样,只是把A改为了B。 我的单片机上的LED位于Pin0,引脚模式为输出,速度为50MHz。
至此,配置完毕。
4. LED预代码:
我起了一个高大上的名字,但其实就一段…… //关闭灯 GPIO_SetBits(GPIOB, GPIO_Pin_0); 我让灯先处于关闭的状态,当然,也可以让它先处于打开的状态,只要将“Set”改为“Reset”就行了。
5. 主函数:
这个主函数并不是main()函数的意思,是主要共嫩函数的意思。 while (1) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) { temp = USART_ReceiveData(USART1); USART_SendData(USART1,temp); GPIOB->ODR ^=GPIO_Pin_0; } } 将函数主体套在while死循环里,保证单片机一直在执行主要功能(保证它不罢工……)。 调用USART_GetFlagStatus()函数来检查单片机是否接收到来自电脑(或其他设备)的信息,如果是,则执行之后的代码。 将接收到的信息保存在Temp变量中,再将其发送给电脑(或其他设备)。 之后,改变LED的电平状态,实现由亮转灭和由灭转亮。
OK,到此为止,所有代码结束。最后附上完整版代码。 #include "stm32f10x.h" GPIO_InitTypeDef GPIO_LED_InitStructure; GPIO_InitTypeDef GPIO_USART_TX_InitStructure; GPIO_InitTypeDef GPIO_USART_RX_InitStructure; USART_InitTypeDef USART_InitStructure; int main() { unsigned short int temp; // 配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 配置USART_TX GPIO_USART_TX_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_USART_TX_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_USART_TX_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_USART_TX_InitStructure); // 配置USART_RX GPIO_USART_RX_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_USART_RX_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_USART_RX_InitStructure); // 配置串口 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); //配置灯 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_LED_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_LED_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_LED_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_LED_InitStructure); //关闭灯 GPIO_SetBits(GPIOB, GPIO_Pin_0); while (1) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) { temp = USART_ReceiveData(USART1); USART_SendData(USART1,temp); GPIOB->ODR ^=GPIO_Pin_0; } } }