本文只要涉及STM32的串口相关内容比如串口通信的分类,以及串口发送数据的原理,同时包括对USART1的配置代码
计算机与外设的通信方式包括:
并行通信
串行通信
并行通信是将传输数据的各比特(位)同时进行传送。以单字节(8比特)
数据为例,单片机通过并行接口与外设进行并行通信的示意图如下所示
串行通信是将传输数据的各比特(位)按先后顺序逐位进行传送。单片机通过串行接口与外设进行串行通信的示意图如图所示。
1、按照数据传送方向,分为:
单工
:数据传输只支持数据在一个方向上传输
半双工
:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
全双工
:允许数据同时在两个方向上传输,因此,全双工通信是两个
单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
同步通信:带时钟同步信号传输。
异步通信:不带时钟同步信号。
异步串行通信是指串口的发送与接收设备使用各自的时钟控制数据的发送和接收过程,以字符为数据传输单位,字符之间可以有间隔。为使双方的收发协调,要求发送和接收设备的串口工作时钟频率要尽可能一致。
同步串行通信,带有时钟同步信号,例如I2C接口,SPI接口。
UART:通用异步收发器
USART:通用同步异步收发器
STM32F4XX目前最多支持8个UART,STM32F407一般是6个。具体可以对照选型手册和数据手册来看。
STM32F103目前最多支持5个UART
串口设置的一般步骤可以总结为如下几个步骤:
所以要在数据手册中查看,如图:
可以知道只有USART1和USART6是挂载在APB2时钟上的,其他的串口是挂载在APB1上的
然后可以在开发板的原理图上进行USART的搜索,可以看到相应的引脚位置
这里我们以USART1为例进行使用所以可以知道首先时钟应该是要使能APB2.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能 USART1 时钟
初始化函数:
void usart1_init(uint32_t bound)
{
static GPIO_InitTypeDef GPIO_InitStructure;
static USART_InitTypeDef USART_InitStructure;
static NVIC_InitTypeDef NVIC_InitStructure;
//打开硬件时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//打开串口1硬件时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//配置PA9和PA10为复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
//将PA9和PA10引脚连接到串口1的硬件
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
//配置串口1相关参数:波特率、无校验位、8位数位、1位停止位
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
//配置串口1的中断触发方法 接收一个字节触发中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//配置串口1的中断优先级
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); //根据指定的参数初始化VIC寄存器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
//使能串口1工作
USART_Cmd(USART1,ENABLE);
}
//串口发送字符串
void Usart_SendString(USART_TypeDef* USARTx,uint8_t *data)
{
uint32_t i;
for(i = 0;data[i] != '\0';i ++)
{
USART_SendData(USARTx,data[i]);//发送数据
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);//等待发送完成
}
}
// 串口接收--注意这个是可以接收字符串的,如果需要收集大于50个字符的,请你修改USART_ReceiveString[]数组的大小
char USART_ReceiveString[50]; //接收PC端发送过来的字符
int Receive_Flag = 0; //接收消息标志位
int Receive_sum = 0; //数组下标
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_RXNE) == 1) //USART_FLAG_RXNE判断数据,== 1则有数据
{
if(Receive_sum > 49) //数组能存放50个字节的数据
{
USART_ReceiveString[49] = '\0'; //数据字节超过50位时,将最后一位设置为\0
Receive_Flag = 1; //接收标志位置1,停止接收数据
Receive_sum = 0; //数组下标置0
}
if(Receive_Flag == 0) //接收标志位等于0,开始接收数据
{
USART_ReceiveString[Receive_sum++] = USART_ReceiveData(USART1); //通过USART1串口接收字符
// Receive_sum++; //数组下标++
}
if(Receive_sum >= 2) //数组下标大于2
{
// printf("%s,%d\r\n",USART_ReceiveString,Receive_sum);
if(USART_ReceiveString[Receive_sum-2] == '\r' && USART_ReceiveString[Receive_sum-1] == '\n' )//判断是否有换行
{
USART_ReceiveString[Receive_sum-1] = '\0';
USART_ReceiveString[Receive_sum-2] = '\0';
Receive_Flag = 1; //接收标志位置1,停止接收数据
Receive_sum = 0; //数组下标置0
printf("%s\r\n",USART_ReceiveString);
if(strcmp(USART_ReceiveString,"hello") == 0)
{
LED1=!LED1;
Receive_Flag = 0;
}
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //接收后先清空标志位
}
// 下面的服务函数是接收一个字符的,如果你只在上位机中发送一个字符来控制代码的逻辑,可以使用下面的函数
/*
/*
void USART1_IRQHandler(void)
{
//char d;
//检查标志位
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断,判断到底是否是串口发送完成中断
{
rec_data=USART_ReceiveData(USART1);
//printf("Receive Succsed: %2x \r\n", rec_data);
//清空标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
*/
基于stm32F4的工程,主要实现在串口接收数字1 LED闪烁,接收数字2 蜂鸣器响,接收数字3风扇转动(这里还没有使用PWM控制)
实现代码的主逻辑为
if (rec_data==1)
{
delay_ms(100);
//GPIO_ResetBits(GPIOE,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10);//输出为0,点亮LED
GPIO_ToggleBits(GPIOE,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10);
}
else if (rec_data == 2)
{
GPIO_SetBits(GPIOB,GPIO_Pin_10); //输出为1 蜂鸣器
}
else if(rec_data ==3)
{
GPIO_SetBits(GPIOB,GPIO_Pin_15); //输出为1
}
关于上面的工程,大家可以在我的gitee上面去拉,在master分支下的单片机课设工程中03-串口收发控制,工程下载地址