(想要深入理解就把前面的看下,否则直接看使用总结即可)
usart文件夹
usart 文件夹内包含了 usart.c
和usart.h
两个文件。这两个文件用于串口的初始化和中断接收。代码只针对了串口1,如果要用其他串口需要对代码稍作修改。
usart.c
包含了2个函数,在下文分别进行讲解
void USART1_IRQHandler(void);
void uart_init(u32 bound);
另外,usart.c
中包含了对串口printf
的支持代码,若去掉会导致硬件无法启动,不要随意修改。
uart_init函数
逐行分析此函数
-
串口1对应GPIOA,使能相应的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1| RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟
-
初始化GPIO端口
根据串口的工作模式按下表进行配置
USART引脚 模式 GPIO配置 USARTx_TX 全双工模式 推挽复用输出 半双工同步模式 推挽复用输出 USARTx_RX 全双工模式 浮空输入或带上拉输入 半双工同步模式 未用,可作为通用I/O 所以需要把
TX(PA9)
设置为推挽复用输出模式,将RX(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; //PA.10 浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);
-
中断初始化
接下来需要对usart1进行中断初始化,设置抢占优先级和响应优先级的值
//Usart1 NVIC 中断配置 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 NVIC 寄存器
-
串口初始化
对串口进行初始化,形式与GPIO初始化类似
//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); //初始化串口1
-
开启串口中断以及使能串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断 USART_Cmd(USART1, ENABLE); //使能串口
至此,uart_init()函数讲解完毕,调用它即可完成串口的初始化,只需输入波特率即可。调用示例如下
uart_init(115200) //开启串口1 波特率:115200
USART1_IRQHandler函数
USART1_IRQHandler()
函数是串口 1
的中断响应函数,当串口 1 发生了相应的中 断后,就会跳到该函数执行。中断响应函数名不能自定义,在startup_stm32f10x_hd.s 文件中可以找到。
首先,通过以下函数判断是否接收到中断
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
若接收到中断,则读取串口接收到的数据
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
设计的接受协议如下:
USART_RX_STA
对接收到的有效数据进行计数,当收到回车(由两个字节组成:0X0D和0X0A
)的第一个字节0X0D时,计数器不再增加,等待0X0A的到来,若没有到来视为接受失败,重新开始下一次接收。若顺利接收到0X0A,则标记其第15位完成一次接收,等待该位被其他程序清除,以开始下一次接收。
若一直没有接收到0X0D,数据长度超过 USART_REC_LEN 时,丢弃数据,重新接收。
效果:若数据以回车作为结束在,则接收成功,保存在数组 USART_RX_BUF[]
中
中断相应函数如下:
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);//(USART1->DR); //读取接收到的数据
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_BUF[]进行数据的保存
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
}
EN_USART1_RX
和 USART_REC_LEN
都是在 usart.h
文件里面定义的,当需要使用串口接收的时候,我们只要在usart.h
里面设置 EN_USART1_RX
为 1 就可以了。不使用的时候,设置,EN_USART1_RX
为 0 即可,这样可以省出部分 sram 和 flash,我们默认是设置 EN_USART1_RX 为 1,也就是开启串口接收的。
SYSTEM_SUPPORT_OS
,则是用来判断是否使用 OS,如果使用了 OS,则调用 OSIntEnter 和 OSIntExit 函数,如果没有使用 ucos,则不调用这两个函数(这两个函数用于实现中断嵌套处理,这里我们先不理会)。
串口使用总结
串口初始化
在main()
中调用以下函数进行串口初始化,默认串口1
uart_init(115200); //波特率 115200
若使用其他串口,需要对uart.c
中的uart_init()
进行修改
当数据以回车结尾会被视为接收成功,保存在数组USART_RX_BUF[]
中
使用中会用到的寄存器、变量
-
USART_RX_STA
此寄存器定义如下表所示
bit13~0用于计数,故进行下面的操作可以得到此次接收到的数据长度,保存给变量len
len = USART_RX_STA&0x3fff
bit15用于判断接收是否完成,值为1才代表接收完成,故进行下面的操作判断是否数据接收完成
if(USART_RX_STA&0x8000) { ...... //对接收到的数据的操作都在这个if里进行 }
-
USART_RX_BUF[ ]
接收到的数据保存在此数组中,通过取其值得到数据
例如数据的第三个字节即为
USART_RX_BUF[2]
常用的函数
-
USART_SendData()
向串口发送数据,仅发送一个字节。思考下面的代码即可理解
for(t=0;t<len;t++) { USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); //等待发送结束 }
这段代码的含义是:将接收到的数据,重新发送给串口1
-
printf()
和stdio.h提供的printf()类似,直接发送信息给串口1
-
USART_GetFlagStatus(USARTx,USART_FLAG)
此函数用于读取串口状态信息
其中第一个参数填对应的串口名称,第二个参数填想要获取的状态
例如判断数据是否发送完成,调用方法如下
USART_GetFlagStatus(USART1,USART_FLAG_TC);
常用
while()
来等待发送完成,具体见上面那段USART_SendData()的代码
正点原子的串口使用源码示例——main函数
int main(void)
{
u16 t;
u16 len;
u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组 2:2
uart_init(9600); //串口初始化 波特率:9600
LED_Init(); //LED端口初始化
KEY_Init(); //按键的硬件接口初始化
BEEP_Init(); //蜂鸣器的硬件接口初始化
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
if(USART_RX_BUF!=0)
{
BEEP=!BEEP;
LED1=!LED1;
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\n战舰STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}