笔者将讲解STM32与外部设备通过串口通信的方式。
所谓串口通信,其实是一个类似于计算机网络的概念,它有物理层,比如规定用什么线通信,几伏特算高电平,几伏特算低电平。传输层,通信前要发RTS,CTS。每一层都有不同的协议所约束。在STM32中采用的USART就是其中之一。
USART模块由GPIO_InitTypeDef结构体控制,它的定义如下
typedef struct {
u32 USART_BaudRate; // 波特率
u16 USART_WordLength; // 字长
u16 USART_StopBits; // 停止位
u16 USART_Parity; // 校验位
u16 USART_Mode; // USART 模式
u16 USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
外部设备的输入输出分别使用GPIOA的9号和10号管脚,因此我们要先把管脚输入输出模式配置好
GPIO_InitTypeDef GPIO_USART_INIT;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_9;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz; //最高输出速率 50MHz
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_USART_INIT);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_10;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz; //最高输出速率 50MHz
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA,&GPIO_USART_INIT);
然后我们设置USART,将波特率设置为9600,禁用硬件流控制模式,工作模式允许发送和接收,不设置校验位,数据位为8比特,在帧尾传输1个停止位。
USART_InitTypeDef USART_INIT;
USART_INIT.USART_BaudRate = 9600;
USART_INIT.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制失能
USART_INIT.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //接收/发送使能
USART_INIT.USART_Parity = USART_Parity_No; //奇偶失能
USART_INIT.USART_StopBits = USART_StopBits_1; //在帧结尾传输 1 个停止位
USART_INIT.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_INIT);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
当然,处理器不可能时时刻刻检查串口有没有信息传来,因此我们刚刚学习的中断就派上了用场,如果串口传输了信息,那么则请求中断并进行处理。
NVIC_InitTypeDef NVIC_USART_INIT;
NVIC_USART_INIT.NVIC_IRQChannel = USART1_IRQn;
NVIC_USART_INIT.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_USART_INIT.NVIC_IRQChannelSubPriority = 1;
NVIC_USART_INIT.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_USART_INIT);
USART_Cmd(USART1,ENABLE);
中断回调函数,负责接收信息
u8 RXData;
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
RXData = USART_ReceiveData(USART1);
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE|USART_IT_TXE);
}
依然采取防御式编程,只有确信是被中断调用时才进行消息接收,比较坑的一点在于,回调函数的返回值被写死成void类型了,所以必须定义一个外部变量,进行接收值的传输。
输出函数,注意需要等待,直到信息发送完成的flag被设置。
void USART1_SData(u8 Data)
{
USART_SendData(USART1,Data);
while(!USART_GetFlagStatus(USART1,USART_FLAG_TC));
USART_ClearFlag(USART1,USART_FLAG_TC);
}
#ifndef _USART_H
#define _USART_H
#include
void usart_configer();
void USART1_SData(u8 Data);
void USART1_IRQHandler();
extern u8 RXData;
#endif
//usart.h
#include
void usart_configer()
{
GPIO_InitTypeDef GPIO_USART_INIT;
USART_InitTypeDef USART_INIT;
NVIC_InitTypeDef NVIC_USART_INIT;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_9;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_USART_INIT);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_10;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_USART_INIT);
//有些参数和串口助手上设置的比较
USART_INIT.USART_BaudRate = 9600;
USART_INIT.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_INIT.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_INIT.USART_Parity = USART_Parity_No;//奇偶模式,不设置奇偶
USART_INIT.USART_StopBits = USART_StopBits_1;//停止位
USART_INIT.USART_WordLength = USART_WordLength_8b;//数据位
USART_Init(USART1,&USART_INIT);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收中断
NVIC_USART_INIT.NVIC_IRQChannel = USART1_IRQn;
NVIC_USART_INIT.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_USART_INIT.NVIC_IRQChannelSubPriority = 1;
NVIC_USART_INIT.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_USART_INIT);
USART_Cmd(USART1,ENABLE);
}
void USART1_SData(u8 Data)
{
USART_SendData(USART1,Data);
//USART_SendData(USART1,(u8)USART_GetFlagStatus(USART1,USART_IT_TC));
while(!USART_GetFlagStatus(USART1,USART_FLAG_TC));//等待发送完毕
USART_ClearFlag(USART1,USART_FLAG_TC);//清除标志位
}
u8 RXData;
void USART1_IRQHandler()//接收
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)//判断是否真的是接收中断 每次接收1byte
{
RXData = USART_ReceiveData(USART1);
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE|USART_IT_TXE);//清空中断源(接收中断和发送中断)
}
//usart.c
下面我们进行几个串口通信的实际应用。
实验一:发信实验,让开发板通过串口向电脑发送信息:
#include
#include
#include
#include
#include
#include
#include
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led_configer();
button_configer();
usart_configer();
while(1)
{
USART1_SData(0x66);
}
}
//main.c
实验二:电脑向串口发送0x55时,开灯,电脑向串口发送0x66时,关灯。
#include
#include
#include
#include
#include
#include
#include
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led_configer();
button_configer();
usart_configer();
while(1)
{
if(RXData==0x55)
lightup(GPIO_Pin_1);
else if(RXData==0x66)
shutdown(GPIO_Pin_1);
}
}
//main.c
笔者将讲解STM32与外部设备通过串口通信的方式。
所谓串口通信,其实是一个类似于计算机网络的概念,它有物理层,比如规定用什么线通信,几伏特算高电平,几伏特算低电平。传输层,通信前要发RTS,CTS。每一层都有不同的协议所约束。在STM32中采用的USART就是其中之一。
USART模块由GPIO_InitTypeDef结构体控制,它的定义如下
typedef struct {
u32 USART_BaudRate; // 波特率
u16 USART_WordLength; // 字长
u16 USART_StopBits; // 停止位
u16 USART_Parity; // 校验位
u16 USART_Mode; // USART 模式
u16 USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
外部设备的输入输出分别使用GPIOA的9号和10号管脚,因此我们要先把管脚输入输出模式配置好
GPIO_InitTypeDef GPIO_USART_INIT;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_9;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz; //最高输出速率 50MHz
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_USART_INIT);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_10;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz; //最高输出速率 50MHz
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA,&GPIO_USART_INIT);
然后我们设置USART,将波特率设置为9600,禁用硬件流控制模式,工作模式允许发送和接收,不设置校验位,数据位为8比特,在帧尾传输1个停止位。
USART_InitTypeDef USART_INIT;
USART_INIT.USART_BaudRate = 9600;
USART_INIT.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制失能
USART_INIT.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //接收/发送使能
USART_INIT.USART_Parity = USART_Parity_No; //奇偶失能
USART_INIT.USART_StopBits = USART_StopBits_1; //在帧结尾传输 1 个停止位
USART_INIT.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_INIT);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
当然,处理器不可能时时刻刻检查串口有没有信息传来,因此我们刚刚学习的中断就派上了用场,如果串口传输了信息,那么则请求中断并进行处理。
NVIC_InitTypeDef NVIC_USART_INIT;
NVIC_USART_INIT.NVIC_IRQChannel = USART1_IRQn;
NVIC_USART_INIT.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_USART_INIT.NVIC_IRQChannelSubPriority = 1;
NVIC_USART_INIT.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_USART_INIT);
USART_Cmd(USART1,ENABLE);
中断回调函数,负责接收信息
u8 RXData;
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
RXData = USART_ReceiveData(USART1);
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE|USART_IT_TXE);
}
依然采取防御式编程,只有确信是被中断调用时才进行消息接收,比较坑的一点在于,回调函数的返回值被写死成void类型了,所以必须定义一个外部变量,进行接收值的传输。
输出函数,注意需要等待,直到信息发送完成的flag被设置。
void USART1_SData(u8 Data)
{
USART_SendData(USART1,Data);
while(!USART_GetFlagStatus(USART1,USART_FLAG_TC));
USART_ClearFlag(USART1,USART_FLAG_TC);
}
#ifndef _USART_H
#define _USART_H
#include
void usart_configer();
void USART1_SData(u8 Data);
void USART1_IRQHandler();
extern u8 RXData;
#endif
//usart.h
#include
void usart_configer()
{
GPIO_InitTypeDef GPIO_USART_INIT;
USART_InitTypeDef USART_INIT;
NVIC_InitTypeDef NVIC_USART_INIT;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_9;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_USART_INIT);
GPIO_USART_INIT.GPIO_Pin = GPIO_Pin_10;
GPIO_USART_INIT.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_USART_INIT.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_USART_INIT);
//有些参数和串口助手上设置的比较
USART_INIT.USART_BaudRate = 9600;
USART_INIT.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_INIT.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_INIT.USART_Parity = USART_Parity_No;//奇偶模式,不设置奇偶
USART_INIT.USART_StopBits = USART_StopBits_1;//停止位
USART_INIT.USART_WordLength = USART_WordLength_8b;//数据位
USART_Init(USART1,&USART_INIT);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收中断
NVIC_USART_INIT.NVIC_IRQChannel = USART1_IRQn;
NVIC_USART_INIT.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_USART_INIT.NVIC_IRQChannelSubPriority = 1;
NVIC_USART_INIT.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_USART_INIT);
USART_Cmd(USART1,ENABLE);
}
void USART1_SData(u8 Data)
{
USART_SendData(USART1,Data);
//USART_SendData(USART1,(u8)USART_GetFlagStatus(USART1,USART_IT_TC));
while(!USART_GetFlagStatus(USART1,USART_FLAG_TC));//等待发送完毕
USART_ClearFlag(USART1,USART_FLAG_TC);//清除标志位
}
u8 RXData;
void USART1_IRQHandler()//接收
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)//判断是否真的是接收中断 每次接收1byte
{
RXData = USART_ReceiveData(USART1);
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE|USART_IT_TXE);//清空中断源(接收中断和发送中断)
}
//usart.c
下面我们进行几个串口通信的实际应用。
实验一:发信实验,让开发板通过串口向电脑发送信息:
#include
#include
#include
#include
#include
#include
#include
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led_configer();
button_configer();
usart_configer();
while(1)
{
USART1_SData(0x66);
}
}
//main.c
实验二:电脑向串口发送0x55时,开灯,电脑向串口发送0x66时,关灯。
#include
#include
#include
#include
#include
#include
#include
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led_configer();
button_configer();
usart_configer();
while(1)
{
if(RXData==0x55)
lightup(GPIO_Pin_1);
else if(RXData==0x66)
shutdown(GPIO_Pin_1);
}
}
//main.c
举报