本人小白一个,最近对硬件很感兴趣,便买来一块秉火的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;
}
}
}