USART软件配置
具体步骤如下:(USART 相关库函数在 stm32f10x_usart.c 和 stm32f10x_usart.h 文件中)
(1)使能串口时钟及 GPIO 端口时钟
前面说过 STM32F103C8T6 芯片具有 3 个串口,对应不同的引脚,串口 1 挂接在 APB2 总线上,串口 2-串口 3 挂接在 APB1 总线上,根据自己所用串口使能总线时钟和端口时钟。
例如使用 USART1,其挂接在 APB2 总线上,并且 USART1 对应 STM32 芯片管脚的 PA9 和 PA10,因此使能时钟函数如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能 USART1 时钟
(2)GPIO 端口模式设置,设置串口对应的引脚为复用功能
因为使用引脚的串口功能,所以在配置 GPIO 时要将设置为复用功能,这里
把串口的 Tx 引脚配置为复用推挽输出, Rx 引脚为浮空输入,数据完全由外部
输入决定。如下:
GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //TX //串口输出 PA9 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入 IO */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //RX //串口输入 PA10 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入 GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化 GPIO */
(3)初始化串口参数,包含波特率、字长、奇偶校验等参数
要使用串口功能,必须对串口通信相关参数初始化,其库函数如下:
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
想必不用说,大家也知道第一个参数是什么意思,它是用来选择串口。
第二个参数是一个结构体指针变量,结构体类型是 USART_InitTypeDef,
其内包含了串口初始化的成员变量。下面我们就来看下这个结构体:
typedef struct { uint32_t USART_BaudRate; //波特率 uint16_t USART_WordLength; //字长 uint16_t USART_StopBits; //停止位 uint16_t USART_Parity; //校验位 uint16_t USART_Mode; //USART 模式 uint16_t USART_HardwareFlowControl; //硬件流控制 } USART_InitTypeDef;
USART_BaudRate:
波特率设置。常用的波特率为 4800、9600、115200 等。
标准库 函 数 会 根 据 设 定 值 计 算 得 到 USARTDIV 值 , 并 设 置USART_BRR 寄存器值。
USART_WordLength:
数据帧字长。可以选择为 8 位或者 9 位,
通过 USART_CR1 寄存器的 M 位的值决定。如果没有使能奇偶校验控制,一般使用 8 数据位;
如果使能了奇偶校验则一般设置为 9 数据位。
USART_StopBits:
停止位设置。可选 0.5 个、 1 个、 1.5 个和 2 个停止 位,
它设定 USART_CR2 寄存器的 STOP[1:0]位的值,一般我们选择 1 个停止位。
通常使用1
USART_Parity:
奇偶校验控制选择。可选 USART_Parity_No( 无 校 验 ) 、
USART_Parity_Even( 偶 校 验 ) 以 及 USART_Parity_Odd( 奇 校 验 ) ,
它设 定 USART_CR1 寄存器的 PCE 位和 PS 位的值。
USART_Mode:
USART 模式选择。
可以为 USART_Mode_Rx 和 USART_Mode_Tx, 允许使用逻辑或运算选择两个,
它设定 USART_CR1 寄存器的 RE 位和 TE 位。
USART_HardwareFlowControl:
硬件流控制选择。
只有在硬件流控制模式才 有效,
可以选择无硬件流 USART_HardwareFlowControl_None、
RTS 控制USART_HardwareFlowControl_RTS、
CTS 控制 USART_HardwareFlowControl_CTS、 RTS 和 CTS 控制 USART_HardwareFlowControl_RTS_CTS。 了解结构体成员功能后,就可以进行配置,例如我们配置 USART1,如下:
USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate =115200;//波特率设置 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_No ne;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口 1
(4)使能串口
配置好串口后,我们还需要使能它,使能串口库函数如下
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
例如我们要使能 USART1,如下:
USART_Cmd(USART1, ENABLE);//使能串口 1
(5)设置串口中断类型并使能
// 第五步:设置串口中断类型并使能 USART_ClearFlag(USART1, USART_FLAG_TC); // 清除中断 USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启接收中断
(6)设置串口中断优先级,使能串口中断通道
// 第六步:设置串口中断优先级,使能串口中断通道 NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; // 中断通道 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3; // 抢占 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3; // 响应 NVIC_Init(&NVIC_InitStruct);
(7)编写串口中断服务函数
//编写串口中断服务函数 void USART1_IRQHandler(void) { u8 r =0; if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) // 接受中断 不等于0 就是RESET { r = USART_ReceiveData(USART1); // 接收数据 if((USART1_RX_STA&0x8000)==0) { if((USART1_RX_STA&0x4000)==1) { if(r!=0x0a)USART1_RX_STA = 0; //接收错误,重新开始 else USART1_RX_STA |= 0x80000;//接收完成了 } else //还没收到0X0D { if(r==0x0d)USART1_RX_STA|=0x4000; // 是否为0d 次高位设置 else { USART_RX_BUF[USART1_RX_STA&0x3fff] = r; USART1_RX_STA++; if(USART1_RX_STA>(USART_REC_LEN-1))USART1_RX_STA=0; //接收数据错误,重新开始接收 } } } } }
为了确认 USART1 是否发生接收中断,调用了读取串口中断状态标志位函数
USART_GetITStatus,如果确实产生接收中断事件,那么就会执行 if 内的语句,
将串口接收到的数据保存在变量 r 内,然后有通过串口发送出去,通过
USART_GetFlagStatus 函数读取串口状态标志,如果数据发送完成,则退出 while
循环语句。
这里我们设计了一个小小的接收协议:通过这个函数,配合一个数组
USART1_RX_BUF[],一个接收状态变量 USART1_RX_STA 实现对串口数据的接收管
理。USART1_RX_BUF 的大小由 USART1_REC_LEN 定义,也就是一次接收的数据最
大不能超过 USART1_REC_LEN 个字节。USART1_RX_STA 是一个接收状态变量,其
各的定义如下图所示:
设计思路如下:
当接收到从电脑发过来的数据,把接收到的数据保存在 USART1_RX_BUF 中,同时在接收状态寄存器(USART1_RX_STA)中计数接收到的有效数据个数,
当收到回车(回车的表示由 2 个字节组成:0X0D 和 0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待 0X0A 的到来,而如果 0X0A 没有来到,
则认为这次接收失败,重新开始下一次接收。
如果顺利接收到 0X0A,则标记 USART1_RX_STA 的第 15 位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,
而如果迟迟没有收到 0X0D,那么在接收数据超过 USART1_REC_LEN 的时候,则会丢弃前面的数据,重新接收。
软件设计
usart.c
#include "usart.h"
u16 USART1_RX_STA =0;
u8 USART_RX_BUF[USART_REC_LEN];
void USART1_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 第一步 使能串口时钟及 GPIO 端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能 USART1 时钟
/* 配置GPIO的模式和IO口 */
// 第二步 GPIO 端口模式设置,设置串口对应的引脚为复用功能
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //TX //串口输出 PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入 IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //RX //串口输入 PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化 GPIO */
// 第三步:初始化串口参数,包含波特率、字长、奇偶校验等参数
USART_InitStruct.USART_BaudRate = bound; // 波特率
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 字长
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 停止位
USART_InitStruct.USART_Parity = USART_Parity_No; // 奇偶校验位
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 不使用硬件流
USART_InitStruct.USART_Mode =USART_Mode_Rx|USART_Mode_Tx; // //收发模式
USART_Init(USART1,&USART_InitStruct);
// 第四步:使能串口 可以放到最后
USART_Cmd(USART1,ENABLE);
// 第五步:设置串口中断类型并使能
USART_ClearFlag(USART1, USART_FLAG_TC); // 清除中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启接收中断
// 第六步:设置串口中断优先级,使能串口中断通道
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; // 中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3; // 抢占
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3; // 响应
NVIC_Init(&NVIC_InitStruct);
}
//编写串口中断服务函数
void USART1_IRQHandler(void)
{
u8 r =0;
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) // 接受中断 不等于0 就是RESET
{
r = USART_ReceiveData(USART1); // 接收数据
if((USART1_RX_STA&0x8000)==0)
{
if((USART1_RX_STA&0x4000)==1)
{
if(r!=0x0a)USART1_RX_STA = 0; //接收错误,重新开始
else USART1_RX_STA |= 0x80000;//接收完成了
}
else //还没收到0X0D
{
if(r==0x0d)USART1_RX_STA|=0x4000; // 是否为0d 次高位设置
else
{
USART_RX_BUF[USART1_RX_STA&0x3fff] = r;
USART1_RX_STA++;
if(USART1_RX_STA>(USART_REC_LEN-1))USART1_RX_STA=0; //接收数据错误,重新开始接收
}
}
}
}
}
usart.h
#ifndef _usart_H
#define _usart_H
#include "system.h"
#define USART_REC_LEN 200
extern u8 USART_RX_BUF[USART_REC_LEN];
extern u16 USART1_RX_STA;
void USART1_Init(u32 bound);
#endif
main.c
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
int main()
{
u8 i =0;
u16 len;
u16 t=0;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
USART1_Init(115200);
while(1)
{
if(USART1_RX_STA&0x8000)
{
len =USART1_RX_STA&0x3fff; //得到此次接收到的数据长度
for(t=0;t<len;t++)
{
USART_SendData(USART1,USART_RX_BUF[t]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET); // 获取标记 SET不等于1
}
USART1_RX_STA = 0;
}
i++;
if(i%10==0)
{
LED0=!LED0;
}
delay_ms(10);
}
}