目录
一、STM32串口
串口通信是一种全双工的异步串行通信方式,通常用于设备与设备、设备和电脑间的通信。由TX和RX两根线传输数据:
TX:发送数据
RX:接收数据
两设备的接线方式交叉相连(TX接RX,RX接TX)。
STM32的USART框图如下:
精简后的串口通信只用到TX和RX两条线。数据的发送和接收由发送器和接收器来完成,我们只需将数据写入发送数据寄存器(TDR)中即可,在接收数据寄存器(RDR)中读取接收到的数据,TDR和RDR是同一个数据寄存器寄存器(DR),该寄存器只有8位有效,因此一次只能传输一个字节(8bit)。
波特率:每秒传输的bit位个数,通常使用115200、9600。两个设备必须设置相同的波特率才能正常通信,否则发送的数据错乱。串口通信没有时钟线,发送的每一bit位有一个固定的时长,接收器依据这个时长来读取数据,这个时长就是由波特率决定的,因此发送设备和接收设备的波特率设置需一致。
串口每次发送的数据包包含起始位、数据位(8位)、校验位、停止位(默认1位)。
校验位:(可不使用)
奇校验:数据位和校验位中“1”的个数为奇数,例如数据为1100 1001,含有4个“1”,则校验位为1;
偶校验:数据位和校验位中“1”的个数为偶数,例如数据为1100 1001,含有4个“1”,则校验位为0;
二、可配置的中断
三、使用的相关寄存器
1、状态寄存器(USART_SR)
常用到的有位4、5、6、7。
2、数据寄存器(USART_DR)
3、控制寄存器1(USART_CR1)
该寄存器用来设置中断使能及其他配置。
位5置1时,当接收寄存器不为空时将产生中断,即接收中断;
位6置1时,当检测到空闲时产生中断。
四、相关库函数
1、初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
2、使能函数
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
3、发送数据函数
//将一个字节(8bit)数据写入发送数据寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
4、接收数据函数
//在就收数据寄存器读取一个字节(8bit)数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
5、使能中断函数
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
6、获取标志函数
//读取状态寄存器的某个位
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
五、程序设计
步骤:
1、使能引脚和串口时钟;
2、引脚和串口配置;
3、中断配置及优先级设置;
4、使能串口开启串口;
发送接收数据思路:
发送:使用USART_SendData()函数发送,一个字节一个字节的发送。
接收:使用中断接收,需使能接收数据寄存器不为空中断和空闲中断。每接收到一个字节(8bit)的数据发生一个接收数据寄存器不为空中断,在中断中完成数据的读取并保存。当接收数据结束会产生一个空闲中断,此时标记接收数据结束即可。
1、库函数
uart1.h中:
#ifndef __UART1_H
#define __UART1_H
#include "stm32f4xx.h"
//结构体声明
typedef struct
{
char RXBUFF[255]; //用于保存数据
int end_len_flag; //结束标志和数据下标 最高位为接收结束标志位
}Uart1_Data;
void Uart1_Init(u32 bound);
void Uart1_SendString(char *string);
char *GetResiveData(void);
#endif
uart1.c中:
#include "uart1.h"
Uart1_Data Uart1_ReData; //定义结构体变量
//初始化串口1
void Uart1_Init(u32 bound)
{
/*定义相关结构体变量*/
GPIO_InitTypeDef GPIO_Uart1;
USART_InitTypeDef USART_Uart1;
NVIC_InitTypeDef NVIC_Uart1;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能引脚时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能串口1时钟
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 ,GPIO_AF_USART1); //复用
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10,GPIO_AF_USART1); //复用
/*GPIO引脚配置*/
GPIO_Uart1.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Uart1.GPIO_Mode = GPIO_Mode_AF;
GPIO_Uart1.GPIO_OType = GPIO_OType_PP;
GPIO_Uart1.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Uart1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Uart1);
/*串口配置*/
USART_Uart1.USART_BaudRate = bound; //波特率
USART_Uart1.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //模式:发送和接收
USART_Uart1.USART_Parity = USART_Parity_No; //校验位无
USART_Uart1.USART_StopBits = USART_StopBits_1; //停止位1
USART_Uart1.USART_WordLength = USART_WordLength_8b; //数据长度8位
USART_Uart1.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流不使用
USART_Init(USART1,&USART_Uart1);
USART_Cmd(USART1,ENABLE); //使能串口
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //使能串口接收中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //使能空闲中断
/*串口中断配置*/
NVIC_Uart1.NVIC_IRQChannel = USART1_IRQn;
NVIC_Uart1.NVIC_IRQChannelCmd = ENABLE;
NVIC_Uart1.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Uart1.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_Uart1);
}
//串口1发送字符串
void Uart1_SendString(char *string)
{
u8 i=0;
while(*(string + i) != '\0') //未到字符串的结尾
{
USART_SendData(USART1,*(string + i)); //发送一个字符
/*等待发送数据寄存器为空 读取状态寄存器(SR)的位7 TEX
0:数据未传输到移位寄存器
1:数据传输到移位寄存器
*/
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
i++;
}
/*等待发送完成 读取状态寄存器(SR)的位6 TC
0:传送未完成
1:传送已完成
*/
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
//获取接收到的数据
char *GetResiveData(void)
{
Uart1_ReData.end_len_flag= 0; //清标志位
return Uart1_ReData.RXBUFF; //返回数据地址
}
//串口接收中断函数
void USART1_IRQHandler()
{
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) //读取数据寄存器不为空中断
{
if( (Uart1_ReData.end_len_flag & 0x7fff ) < 255) //接收缓存区未满
{
Uart1_ReData.RXBUFF [ Uart1_ReData.end_len_flag & 0x7fff ] = USART_ReceiveData(USART1);
// USART_SendData(USART1,Uart1_ReData.RXBUFF[ Uart1_ReData.end_len_flag & 0x7fff ]);
Uart1_ReData.end_len_flag++; //下标后移
}
// else
// Uart1_ReData.end_len_flag = 0; //接收到的数据过长
}
if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) != RESET) //触发空闲中断
{
Uart1_ReData.RXBUFF[ Uart1_ReData.end_len_flag & 0x7fff ] = '\0'; //添加结束符
Uart1_ReData.end_len_flag |= 0x8000; //标记接收结束,最高位置1
USART_ReceiveData(USART1); //读取DR会对SR清零
}
}
main函数中:
#include "stm32f4xx.h"
#include "uart1.h"
extern Uart1_Data Uart1_ReData;
extern void * memset(void *,int,unsigned int);
int main(void)
{
Uart1_Init(115200); //初始化串口1
Uart1_SendString("串口通信练习\r\n");
for(;;)
{
if(Uart1_ReData.end_len_flag & 0x8000 ) //接收结束
{
Uart1_SendString("发送的数据为:\r\n");
Uart1_SendString(GetResiveData()); //向串口发送接收到的数据
memset(Uart1_ReData.RXBUFF,0,255); //清空接收缓存区
}
}
return 0;
}
2、寄存器
#include "usart.h"
#define Fck 100000000 //串口时钟频率
char RX_BUFF[50]={0};
u8 Usart1_flag = 0;
//A9 TX A10 RX
//PA9 推挽输出 速度25MHZ
//PA10上拉输入
void Usart1_Init(u32 bound)
{
RCC->APB2ENR |= 1<<4; //使能串口1时钟
RCC->AHB1ENR |= 1<<0; //使能A口时钟
/*引脚配置*/
GPIOA->MODER &= ~(0xf <<9*2 ); //清0
GPIOA->MODER |= (0xa <<9*2 ); //复用 1010
GPIOA->OTYPER &=~( 1<<9); //A9推挽
GPIOA->OSPEEDR &= ~(3 << 9*2);
GPIOA->OSPEEDR |=(1 << 9*2); //A9 25MHZ
GPIOA->PUPDR &= ~( 3<<2*10);
GPIOA->PUPDR |=( 1<<2*10); //A10上拉 01
GPIOA->AFR[1] |= (0x77 << 4); //0111 0111高位 复用为AF7 usart1
USART1->BRR = Fck/bound; //波特率
USART1->CR1 |= 3 << 2; //使能发送接收功能
USART1->CR1 |= (1 << 13) |(1 << 5); //使能 开启接收中断
// NVIC_SetPriorityGrouping(7-3);//抢占占3位(0~7),响应1位(0~1)//优先级分组设置,放在主函数中
NVIC_EnableIRQ((IRQn_Type)37); //使能串口中断
NVIC_SetPriority((IRQn_Type)37, NVIC_EncodePriority (7-2, 0, 1));//配置抢占0和响应优先级1
}
//发送一个字节数据
void Usart1_SendData(char ch)
{
while( !(USART1->SR & (1<<6))); //等待数据发送完成
USART1->DR = ch;
}
//发送字符串
void Usart1_Send_Srting(char *str)
{
char i=0;
while( *(str + i)!= '\0')
{
Usart1_SendData(*(str + i));
i++;
}
}
//重映射fputc函数
//可以直接使用C库printf函数
//需要添加<stdio.h>头文件
//并勾选MDK中Use MicroLIB选项(魔术棒中打开)
int fputc(int ch,FILE *stream)
{
Usart1_SendData(ch);
return 0;
}
//接收一个字节数据
char Usart1_ReceiveData(void)
{
char ch=0;
if( USART1->SR & (1<<5)) //接收到数据
{
ch = USART1->DR;
return ch;
}
return 0;
}
//中断接收函数
void USART1_IRQHandler(void)
{
char ch = 0;
static u8 count = 0;
if( USART1->SR & (1<<5)) //接收到数据
{
ch = USART1->DR;
if( (ch != '\r') && (ch != '\n') ) //不是回车符(未结束),发送时需要添加回车
{
RX_BUFF[count++] = ch;
}
else
{
RX_BUFF[count] = '\0'; //添加结束符
Usart1_flag = 1; //标记接收结束
count = 0;
}
}
}