stm32串口的使用过程:
1.使能串口时钟,同时使能串口对应的GPIO的时钟;
2.设置串口引脚的输入输出模式、速率,并初始化GPIO引脚;
3.对于需要接收数据的串口,配置其中断,并使能;
4.设置串口的波特率、数据位、停止位、奇偶校验位、流控方式、收发模式;
5.初始化串口;
6.开串口接收中断;
7.使能串口;
8.编写串口中断服务函数;
9.发送数据
补充:STM32中与串口相关的寄存器主要是状态寄存器SR和数据寄存器寄存器DR。
附图如下:
状态寄存器比较重要的两位是第六位RXNE和第七位TC,使用方法USARTx->SR。
TC:发送完成 (Transmission complete)
当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的
TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC
位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。
0:发送还未完成;
1:发送完成。
RXNE:读数据寄存器非空 (Read data register not empty)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果
USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清
零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
0:数据没有收到;
1:收到数据,可以读出。
所以常用SR的第六位RXNE来判断是否接收到数据,若接收到数据,就将数据从DR中读取出来,以便DR接收下个数据。
DR[8:0]:数据值 (Data value)
包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收
用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的
并行接口(参见图248)。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。
当使能校验位(USART_CR1中PCE位被置位)进行发送时,写到MSB的值(根据数据的长度不
同,MSB是第7位或者第8位)会被后来的校验位该取代。
当使能校验位进行接收时,读到的MSB位是接收到的校验位。
代码如下:
//定义初始化变量
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能串口2的时钟和GPIOA的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//PA2-TX,PA3-RX
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置串口对应引脚的收发模式
//USART2_RX GPIOA.3初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);/初始化GPIOA.3
//USART2_TX GPIOA.2初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2
//注意:此处TX和RX引脚都要配置,否则可能无法通信
//USART2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
//USART 初始化设置
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_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
//初始化USART2
USART_Init(USART2, &USART_InitStructure);
//开启USART2接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
//使能串口2
USART_Cmd(USART2, ENABLE);
//编写USART2的中断服务函数
void USART2_IRQHandler(void)
{
u8 Res;
USART_PRINTF_FLAG=2;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断,判断读寄存器是否为空
{
Res =USART_ReceiveData(USART2); //读取接收到的数据
if((USART2_RX_STA&0x8000)==0)//接收未完成
{
if(USART2_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)
USART2_RX_STA=0;//接收错误重新开始
else
{
if(crc_check(USART2_RX_BUF,17))
USART2_RX_STA|=0x8000; //接收完成
else
USART2_RX_STA=0;//接受错误,重新开始
}
}
else //未接收到0x0d
{
if(Res==0x0d)
USART2_RX_STA|=0x4000;
else
{
USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
USART2_RX_STA++;
if(USART2_RX_STA>(USART_REC_LEN-1))
USART2_RX_STA=0;
}
}
}
}
}
//注:此处参考了正点原子所提供的源码,根据自己的需要对接收完成的判断进行了修改
//发送数据
USART_SendData(UART4, USART2_TX_BUF[t]);//向UART4发送数据
while(USART_GetFlagStatus(UART4,USART_FLAG_TC)!=SET);//等待发送结束
//注while语句必须存在,否则可能出现接收字符不完整的情况,且USART_SendData一次只能发送一个数据,对于数组需要用循环的方式进行发送
//补充:如何用printf进行发送
//参照正点原子,重定义fputc函数
int fputc(int ch, FILE *f)
{
if(USART_PRINTF_FLAG==2)
{
while((USART2->SR&0X40)==0){};
USART2->DR = (u8) ch;
}
else
{
while((USART3->SR&0X40)==0){};
USART3->DR = (u8) ch;
}
return ch;
}
//注意:与正点原子不同的是补充了一个发送标志(全局变量),判断是用哪个串口进行发送,用于处理多串口收发