usart 文件
(Universal Synchronous/Asynchronous Receiver/Transmitter)
通用同步/异步串行接/收发送器
SYSTEM 文件夹下
包含 usart.c 和 usart.h
用于串口的初始化和中断接收
默认针对串口 1
使用其他串口时需要修改代码
usart.c
包含两个函数
-
void USART1_IRQHandler(void);
-
void uart_init(u32 bound);
一段代码
用于支持 printf 函数,删去不会报错,但STM32无法启动
uart_init 函数
串口1初始化函数 ,调用时 只需要比特率一个参数
但默认设置为 USART1 ,使用其他通用同步/异步串行接/收发送器 或修改波特率以外的设置时 ,要自己修改该文件
void uart_init(u32 bound){
//GPIO 端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA
|RCC_APB2Periph_AFIO, ENABLE); //使能 USART1,GPIOA 时钟
//以及复用功能时钟
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9 发送端
//USART1_RX PA.10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10 接收端
//Usart1 NVIC 中断配置 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //对应中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级配置
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl=
USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART1, ENABLE); //使能串口
}
1)在使用一个 内置外设 时,首先要使能相应的 GPIO 时钟,然后使能复用功能时钟和内置外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA
|RCC_APB2Periph_AFIO, ENABLE); //使能 USART1,GPIOA 时钟,以及复用功能时钟
2)之后要设置要使用的 GPIO 口
查询技术手册——《STM32 中文参考手册 V10》
可知当要设置全双工模式时
(允许同时在两个方向上传输数据
是两个单工通信方式的结合,要求发送和接收设备都有独立的接收和发送能力)
要把 PA9 设置为推挽输出 ,PA10 设置为浮空输入或带上拉输入
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
3)因为该实验要用到中断
所以需要初始化 usart1 的中断
设置抢占优先级和响应优先级
并在 main 中设置中断分组
//Usart1 NVIC 中断配置 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 VIC 寄存器
4)中断后要设置串口1 的初始化参数
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl=
USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发
USART_Init(USART1, &USART_InitStructure); //初始化串口
串口的初始化是通过调用 USART_Init() 实现
调用时需要的参数——结构体指针变量 USART_InitStructure
定义
typedef struct
{
uint32_t USART_BaudRate;
uint16_t USART_WordLength;
uint16_t USART_StopBits;
uint16_t USART_Parity;
uint16_t USART_Mode;
uint16_t USART_HardwareFlowControl;
} USART_InitTypeDef;
含有 6 个成员变量
- 串口波特率
- 字长
- 停止位
- 奇偶校验位
- 串口模式
- 是否支持硬件流控制
5)初始化完串口中断优先级和串口后
要开启中断和使能串口
中断的开启在初始化函数内完成
main 中的是设置分组
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART1, ENABLE); //使能串口
顺序总结
① 串口时钟使能,GPIO 时钟使能
② 串口复位
③ GPIO 端口模式设置
④ 串口参数初始化
⑤ 初始化 NVIC 并且开启中断
⑥ 使能串口
USART1_IRQHandler 函数
是初始化函数对应串口的 中断响应函数
当串口发生了相应的中断后,会跳到这个函数
中断相应函数的名字不可以随便定
要遵循MDK定义的函数名
从 startup_stm32f10x_hd.s 文件中找名字
函数体👇
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
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;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
#endif
函数通过
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
判断是否接收中断,如果串口接受中断,则读取串口接受到的数据
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
定义变量 Res 来储存接收的数据
之后定义了一个接收协议
数组——USART_RX_BUF[]
大小由 USART_REC_LEN 定义
该变量在 usare.c 中有定义
(BUF—— 缓存区)
接收状态寄存器(全局变量,但起到寄存器的作用)——USART_RX_STA
自定义的寄存器定义图👇
协议思路
-
接收到电脑发来的数据
把接收到的数据保存在 USART_RX_BUF -
在 USART_RX_STA 中计数接收到的有效数据个数
当接收到回车(回车的表示由 2 个字节组成:0X0D 和 0X0A)的第一个字节时,停止计数器的增加,等待0X0A -
如果 0X0A 没有到来,则认为这次接收失败,重新开始下一次接收
-
如果接收到 0X0A ,则标记 USART_RX_STA 的第 15 位,完成一次接收,并等待该位被清除、
-
如果一直没有收到 0X0D,当接收数据超过 USART_REC_LEN 的时候,则会丢弃前面的数据,重新接收
EN_USART1_RX 和 USART_REC_LEN 都定义在 usart.h 文件里,使用串口接收时要在 usart.h 里面设置 EN_USART1_RX 为 1
不使用用时设置为 0
其中默认设置 EN_USART1_RX 为 1
SYSTEM_SUPPORT_OS,则是用来判断是否使用 OS
如果使用了 OS,则调用OSIntEnter 和 OSIntExit 函数(用于实现中断嵌套处理),如果没有使用 ucos,则不调用这两个函数
main.c
#include "led.h"//需要使用LED灯
#include "delay.h"//要使用延时函数
#include "sys.h"
#include "usart.h"//需要使用串口
int main(void)
{
u8 t;
u8 len;
u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
/*👆初始化设置*/
while(1)
{
if(USART_RX_STA&0x8000)//判断最高位,是否接收完成
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n你发送的消息为:\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;//清零,以方便下一次接收
}else//没有接收的情况
{
times++;
if(times%5000==0)
{
printf("请输入数据,以回车键结束\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}