STM32与串口(Usart)的通俗介绍与使用(例子:串口控制小灯)
一、说明
写本文目的是从 对串口知识了解不清或刚接触的朋友 的角度出发,帮助此类朋友了解掌握串口的配置与使用。
前面介绍三组串口的具体配置,文章最后举一个例子:串口控制小灯。
二、串口的简单介绍
什么是串口?
简单来说就是实现printf(""),getchar()等函数,即实现字符或字符串的接收发送,从而实现通讯。
其中串口又分为:USART(同步异步收发器)——全双工数据交换 和 UART(异步收发器)——只有异步传输功能,本文只介绍三个USART的配置与使用。
三、串口中断功能的流程图(从右到左看)
四、有关管脚的电路原理图介绍
如上图所示:该板子具有三组串口接口(TX为发送引脚,RX为接收引脚),分别对应:
RX1/TX1 ——> PA10/PA9 ——> USART1
RX2/TX2 ——> PA3/PA2 ——> USART2
RX3/TX3 ——> PB11/PB10 ——> USART3
引脚 | USART1 | USART2 | USART3 |
---|---|---|---|
TX | PA9 | PA2 | PB10 |
RX | PA10 | PA3 | PB11 |
时钟线 | APB2 | APB1 | APB1 |
中断函数名 | USART1_IRQHandler(void) | USART2_IRQHandler(void) | USART3_IRQHandler(void) |
五、代码配置
1.USART1
(1)发送、接收管脚配置和串口配置
void USART1_Config(void)
{
GPIO_InitTypeDef x;
USART_InitTypeDef y;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启管脚PA9,PA10对应的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //开启串口USART1的时钟,注意:**APB2**
x.GPIO_Pin=GPIO_Pin_9;
x.GPIO_Mode=GPIO_Mode_AF_PP;
x.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&x); //配置发送引脚——选择管脚9,模式为复用推挽输出(因为是发送引脚),频率为50MHz,初始化到GPIOA
x.GPIO_Pin=GPIO_Pin_10;
x.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&x); //配置接收引脚——选择管脚10,模式为浮空输入(因为是接收引脚),输入模式不用配置频率,初始化到GPIOA
y.USART_BaudRate=9600; //配置波特率9600/115200等,具体看你的硬件配置要求
y.USART_WordLength=USART_WordLength_8b; //配置数据位8位
y.USART_StopBits=USART_StopBits_1; //配置停止位为1
y.USART_Parity=USART_Parity_No; //奇偶校验位为无校验
y.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //配置硬件控制流选择为无
y.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //配置串口模式为接收和发送
USART_Init(USART1,&y); //初始化到USART1
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启窗口接收中断(即当发生接收数据或发送数据时将触发对应的中断处理函数)
USART_Cmd(USART1,ENABLE); //使能USART1,也就是打开USART1功能
NVIC_Configuration(); //中断优先级配置
}
(2)配置中断优先级
优先级分组 | 主优先级 | 子优先级 |
---|---|---|
NVIC_ PriorityGroup0(0位用于抢占优先级 4位用于子优先级) | 0 | 0~15 |
NVIC_ PriorityGroup1(1位用于抢占优先级 3位用于子优先级) | 0~1 | 0~7 |
NVIC_ PriorityGroup2(2位用于抢占优先级 2位用于子优先级) | 0~3 | 0~3 |
NVIC_ PriorityGroup3(3位用于抢占优先级 1位用于子优先级) | 0~7 | 0~1 |
NVIC_ PriorityGroup4(4位用于抢占优先级 0位用于子优先级) | 0~15 | 0 |
void NVIC_Configuration()
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置分组为组2
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //配置中断源为USART1
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //配置主优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //配置子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_Init(&NVIC_InitStruct); //初始化NVIC
}
(3)中断处理函数
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) //若当前USART1的状态为接收
{
res= USART_ReceiveData(USART1); //读取此时接收的数据
USART_SendData(USART1,res); //将数据重新发回去,用于告诉它接收成功
}
}
(4)重定向收发函数(在 keil 的 C 标准函数中类似 printf 等函数并没有实现其底层,程序开发者需要重定向其中的 fgets 与 fputs 函数后可使用 printf、scanf、gets、puts 等标准函数,记得加#include "stdio.h"哦)
#include "stdio.h"
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t)ch); //发送一个字符
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //直到发送完毕
return (ch);
}
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET); //直到接收完毕
return (int)USART_ReceiveData(USART1);
}
(5)最后主函数
int main(void)
{
USART1_Config();
while(1);
}
这样一个完整的串口配置使用就实现了哦!!!
接下来同样的步骤,来用其他两组串口也实现该功能吧
2.USART2
(1)发送、接收管脚配置和串口配置
void USART2_Config(void)
{
GPIO_InitTypeDef x;
USART_InitTypeDef y;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //注意**APB1**,**USART2**
x.GPIO_Pin=GPIO_Pin_2;
x.GPIO_Mode=GPIO_Mode_AF_PP;
x.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&x);
x.GPIO_Pin=GPIO_Pin_3;
x.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&x);
y.USART_BaudRate=9600;
y.USART_WordLength=USART_WordLength_8b;
y.USART_StopBits=USART_StopBits_1;
y.USART_Parity=USART_Parity_No;
y.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
y.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART2,&y);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
USART_Cmd(USART2,ENABLE);
NVIC_Configuration();
}
(2)配置中断优先级
void NVIC_Configuration()
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; //注意:**USART2_IRQn**
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
(3)中断处理函数
void USART2_IRQHandler(void) //注意:**USART2_IRQHandler**
{
u8 res;
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
{
res= USART_ReceiveData(USART2);
USART_SendData(USART2,res);
}
}
(4)重定向收发函数(记得加#include "stdio.h"哦)
#include "stdio.h"
int fputc(int ch,FILE *f)
{
USART_SendData(USART2,(uint8_t)ch);
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
return (ch);
}
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE)==RESET);
return (int)USART_ReceiveData(USART2);
}
(5)最后主函数
int main(void)
{
USART2_Config();
while(1);
}
2.USART3
(1)发送、接收管脚配置和串口配置
void USART3_Config(void)
{
GPIO_InitTypeDef x;
USART_InitTypeDef y;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //注意:**GPIOB**
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //注意:**APB1**,**USART3**
x.GPIO_Pin=GPIO_Pin_10;
x.GPIO_Mode=GPIO_Mode_AF_PP;
x.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&x); //注意:**GPIOB**
x.GPIO_Pin=GPIO_Pin_11;
x.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB,&x); //注意:**GPIOB**
y.USART_BaudRate=9600;
y.USART_WordLength=USART_WordLength_8b;
y.USART_StopBits=USART_StopBits_1;
y.USART_Parity=USART_Parity_No;
y.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
y.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART3,&y);
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
USART_Cmd(USART3,ENABLE);
NVIC_Configuration();
}
(2)配置中断优先级
void NVIC_Configuration()
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn; //注意:**USART3_IRQn**
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
}
(3)中断处理函数
void USART3_IRQHandler(void) //注意:**USART3_IRQHandler**
{
u8 res;
if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)
{
res= USART_ReceiveData(USART3);
USART_SendData(USART3,res);
}
}
(4)重定向收发函数(记得加#include "stdio.h"哦)
#include "stdio.h"
int fputc(int ch,FILE *f)
{
USART_SendData(USART3,(uint8_t)ch);
while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);
return (ch);
}
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART3,USART_FLAG_RXNE)==RESET);
return (int)USART_ReceiveData(USART3);
}
(5)最后主函数
int main(void)
{
USART3_Config();
while(1);
}
六、最后来个串口控制小灯例子
(1)配置串口(上面三组随便选一组,这里以组1-PA9,PA10为例)
(2)初始化小灯(我的STM32控制LED灯的管脚为PC13)
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
}
(3)小灯控制函数
void LED_G(uint8_t n) //LED_G(0): 灯亮 LED_G(1):灯灭
{
if(n)
GPIO_SetBits(GPIOC, GPIO_Pin_13);
else
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}
(4)修改主函数
#include "stm32f10x.h"
int main(void)
{
USART_Config();
LED_GPIO_Config();
while(1);
}
(5)修改串口中断函数
void USART1_IRQHandler(void) //如果串口发送‘0’则灯亮,(若其他数据)否则灯灭
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
{
res= USART_ReceiveData(USART1);
USART_SendData(USART1,res);
if(res=='0')
{
LED_G(0);
}
else
{
LED_G(1);
}
}
}
七、完整工程资源共享
链接:https://pan.baidu.com/s/1UvxMlFSzKVpQfpDA3zxnBA
提取码:o8c7