STM32串口寄存器库函数配置方法
一. STM32常用寄存器和库函数
二. 串口配置一般步骤(串口实例)
1.1 常用的串口相关寄存器:
USART_SR状态寄存器
USART_DR数据寄存器
USART_BRR波特率寄存器
USART_CR1控制寄存器
状态寄存器适用于检测串口此时所处的状态。
主要关注两个位:RXNE和TC(第5、6两位)。
RXNE(读数据寄存器非空):当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)。这时候要做的就是尽快读取USART_DR,从而将该位清零,也可以向该位写0,直接清除。
TC(发送完成):当该位被置1的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:读USART_SR,写USART_DR;直接向该位写0。
USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内;当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。
串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的;发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。
波特率寄存器包括定义了两个部分:DIV_Mantissa(整数部分)和DIV_Fraction(小数部分)。
控制寄存器主要是设置USART使能、检验控制使能、校验选择(奇校验偶校验)、PE中断使能、发送缓冲区空中断使能、发送完成中断使能、接收缓冲区非空使能、发送使能、接受使能、字长等等。
1.2 波特率的计算
十进制小数直接换算成十六进制需要×16,与整数部分÷16正好相反。
1.3 串口操作相关库函数:
1个初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
作用:串口初始化函数:波特率,数据字长,奇偶校验,硬件流控以及收发使能
2个使能函数
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
作用:第一个使能串口,第二个使能串口相关中断
2个数据收发函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
作用:前者发送数据到串口,后者从串口接收数据。
4个状态位函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。
串口配置的一般步骤
2.1 步骤
1 GPIO时钟使能,串口时钟使能。调用函数:RCC_APB2PeriphClockCmd()的部分内容);
2 串口复位(这一步不是必须的)。调用函数:USART_DeInit();
3 GPIO外设功能下的端口模式设置。调用函数:GPIO_Init();
4 串口参数初始化。调用函数:USART_Init();
5 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)。
调用函数:NVIC_Init();USART_ITConfig();
6 使能串口。调用函数:USART_Cmd();
7 编写中断处理函数。调用函数:USARTx_IRQHandler();
8 串口数据收发。调用函数:USART_SendData();USART_ReceiveData();
9 串口传输状态获取。调用函数:USART_GetFlagStatus();USART_ClearITPendingBit();
2.2 代码:
void My_USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrue;
USART_InitTypeDef USART_InitStrue;
NVIC_InitTypeDef NVIC_InitStrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO端口使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口端口使能
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
USART_InitStrue.USART_BaudRate=115200;
USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStrue.USART_Parity=USART_Parity_No;
USART_InitStrue.USART_StopBits=USART_StopBits_1;
USART_InitStrue.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStrue);//
USART_Cmd(USART1,ENABLE);//使能串口1
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启接收中断,如果不开启,即不用写。
NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn; //(通道)
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE; //(设置开启)
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;//(优先级)
NVIC_Init(&NVIC_InitStrue);
}
void USART1_IRQHandler(void)
{
u8 res; //(定义一个变量)
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) //(判断是否要执行中断)
{
res= USART_ReceiveData(USART1); //接受数据
USART_SendData(USART1,res); //发送数据
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
My_USART1_Init();
while(1);
}
附
对于串口中断函数(自定义数据接收协议)的理解
正点原子的例程中通过语句USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)开启相关中断,当读数据寄存器非空,即单片机一接收到数据时,便会触发串口1的中断函数。
改协议的核心是定义了一个16位的变量USART_RX_STA,该变量的0-13位用于存储接收到的数据,最后的14、15两位作用在于,当14、15位依次接收到da0x0d和0x0a时,依次将这两位置1,作为判断数据是否接收完的标志位。
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res; //定义unsigned char型字符Res
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
//接收中断(接收到的数据必须是0x0d 0x0a结尾)
//这里判断发送接收完成的依据就是串口数据0x0d 0x0a,
//0x0d是CR(carriage return)回车的意思,光标回到最左边,
//0x0a是LF(line feed)换行的意思,光标到达下一行,
//但是在PC上回车和换行是在一起的就是按下回车按键
//当然可以更改程序使用其他进行判断例如使用0x2a也就是*进行结束判断
{
Res =USART_ReceiveData(USART1);//(USART1->DR);
//读取接收到的数据,存放到变量Res中
if((USART_RX_STA&0x8000)==0)
//判断接收是否未完成
//接收完成未清除标志位,还是会不断进入到接收中断,所以使用标志进行判断,
//当接收完成便不会跳入到判断,从而不执行任何指令,空等待
//使用条件判断是否已经接收完数据,这里判断接收完的依据就是收到了0x0a;
//具体判断在后面
{
if(USART_RX_STA&0x4000)
//如果接收到了0x0d,那么再进一步执行是否接收到0x0a的判断
{
if(Res!=0x0a)USART_RX_STA=0;
//没有接收到0x0a那么说明,数据未正确传输或者接收错误,重新开始判断,
//但是这里没有将接收到的数据进行清空,也没有退出接收中断,此程序只是从头开始执行接收判断
else USART_RX_STA|=0x8000;
//接收完成了,收到了0x0a那么标志位USART_RX_STA将会变成0x8000,将不再进行数据检测与存储
}
else
//还没收到0X0D,说明数据还未发送结束继续进行数据的检测与存储
{
if(Res==0x0d)USART_RX_STA|=0x4000;
//收到了数据0x0d,标志位USART_RX_STA变成0x4000
else
{
//如果没有接收到数据0x0d,执行判断是否存储数组已满,已满则重新开始接收
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
//将接收到的数据写进数组,标志位USART_RX_STA与上0X3FFF清除前两位以防止标志位与8000和4000冲突
USART_RX_STA++;
//数组地址加一,向后排
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
//接收数据错误,超出数组大小,又开始接收向数组重新写
}
}
}
}
}
对于main函数的解释:
int main(void)
{
u8 t;
u8 len;
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //延时初始化
uart_init(115200); //串口初始化波特率为115200
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%300==0)
{
printf("\r\nALIENTEK 探索者STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}