一、USART基本简述
1,USART,一般不使用它的同步传输功能,只使用异步传输模式。即没有时钟参与,和UART在使用时相似;
2,数据传递的基本构成,起始位+数据位+校验位(可选)+停止位(可选)
起始位:1bit的低电平
数据位:一般为8bit的数据
校验位:根据校验方式,奇校验、偶校验、无校验,根据具体的校验方式确定
停止位:1bit的高电平
3,USART模块框图解析
①外部接口:其中一般只使用TX ,RX
②数据接受、发送寄存器模块(USART操作最重要的模块),后面库函数的对于数据的读取、发送都是通过这些寄存器实现的
③控制模块
④波特率发生器:主要根据配置的时钟,选择对应波特率,由此处发送时钟信号,从而按照波特率的具体值,发送对应频率的数据;
二、代码控制应用模块
1,GPIO初始化
USART只是内部模块,与外部模块配合传递数据,是需要GPIO进行数据中转的;
ps:1-所有通过内部模块发送数据,必须配置成复用模块,具体是复用推挽还是复用开漏,根据模块的需求配置;
关键点:①TX:配置成复用推挽输出,RX:浮空输入
②时钟配置:PA时钟开启,USART时钟一并开启(在配置所有模块时,要先开启时钟)
③对应脚位初始化
//时钟开启GPIO USART
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1,ENABLE);
//配置GPIO PA9 TX 复用推挽输出 PA10 RX 浮空输入
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStruct);
2,USART初始化
关键点:USART的初始化,关键是通过结构体在初始化函数中,配置对应寄存器
(本质上所有模块的配置逻辑都是这样的)
①波特率;②传输模式;③数据位长度;④停止位;⑤校验位;⑥硬件流
//初始化USART
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate=115200;
USART_InitStruct.USART_Mode=USART_Mode_Rx |USART_Mode_Tx ;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_Parity=USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_Init( USART1, & USART_InitStruct);
3,USART---NVIC模块初始化
关键点:USART---NVIC的初始化
①优先级分组配置;②中断通道选择(NVIC负责管理所有的中断管理,主要分为两大块,GPIO中断、内部模块中断);③抢占优先级、次优先级(根据优先级分组中状态进行分配)
static void bsp_usart_NVIC_config(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
}
4,USART 中断使能、模块使能开启
关键点:
①调用NVIC初始化函数
② 调用 USART_ITConfig( USART1, USART_IT_RXNE, ENABLE);
其中,我们只在外部接受到中断时刻,进入中断服务函数 ,进行数据处理
③开启USART
//配置NVIC
bsp_usart_NVIC_config();
//开启usart中断
USART_ITConfig( USART1, USART_IT_RXNE, ENABLE);
//使能打开
USART_Cmd( USART1, ENABLE);
以上,已经配置好USART初始化模块
5,发送数据
关键点:
①由于内部寄存器大小的等原因,数据必须是8bit,如果是其他类型的数据,则分成多个8bit传递即可
②调用USART_SendData,等待数据寄存器为空,则说明发送完毕;
// 发送数据
void bsp_usart_senddata(USART_TypeDef* USARTx,uint16_t Data)
{
USART_SendData( USARTx, Data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==RESET);
}
6,发送双字节、发送数据、发送字符串的常用模块
发送双字节:数据拆分为高位和地位
//发送16位数据
void bsp_usart_halfword(USART_TypeDef* USARTx,uint16_t Data)
{
uint8_t mes_h,mes_l;
mes_h=Data>>8;
mes_l=Data;
bsp_usart_senddata( USARTx,mes_h);
bsp_usart_senddata( USARTx,mes_l);
}
发送数据:按照数组的大小,重复调用数据
//发送数组
void bsp_usart_arr(USART_TypeDef* USARTx,uint8_t *arr,uint8_t num)
{
uint8_t i=0;
for(i=0;i<num;i++)
{
bsp_usart_senddata(USARTx,arr[i]);
}
}
发送字符串:与发送数组类似,主要是结束标志位为‘\0’
//发送字符串
void bsp_usart_string(USART_TypeDef* USARTx,uint8_t *str)
{
uint8_t i=0;
while(str[i]!='\0')
{
bsp_usart_senddata(USARTx,str[i]);
i++;
}
}
7,重定向printf、重定向scanf(复写 fputc、复写fgetc),记住即可(要包含stdio的头文件)
//重定向printf
int fputc(int ch, FILE *f)
{
bsp_usart_senddata(USART1,(uint16_t)ch);
return (ch);
}
//重定向scanf
int fgetc(FILE *f)
{
while(USART_GetFlagStatus( USART1, USART_FLAG_RXNE)==RESET);
return (int)USART_ReceiveData(USART1);
}
8,中断服务函数
关键点:在中断服务函数,首先检测标志位(数据位非空,规定动作),然后转数据接受给临时变量TEMP
//中断服务函数
void USART1_IRQHandler(void)
{
//检测
if(USART_GetFlagStatus( USART1, USART_FLAG_RXNE)==SET)
{
temp=USART_ReceiveData(USART1);
bsp_usart_senddata(USART1,temp);
}
}
三、示范程序
电脑串口发送数据,控制led点亮
整体逻辑:
1,初始化LED,并配置各种点灯函数
2,初始化usart,按照上述配置即可,关键是在usart头文件中,要将中断函数的temp,用extern引用出来,在主函数中接受,并判断
3,在主函数中,通过switch函数
1,初始化LED,并配置各种点灯函数
#include "led.h"
void LED_config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_5;// |GPIO_Pin_5;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStruct);
}
void led_red(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_SetBits(GPIOB,GPIO_Pin_1);
}
void led_green(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOB,GPIO_Pin_1);
}
void led_blue(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
}
void led_off(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_SetBits(GPIOB,GPIO_Pin_1);
}
2,初始化usart,按照上述配置即可,关键是在usart头文件中,要将中断函数的temp,用extern引用出来,在主函数中接受,并判断
c文件的配置,和上述相同,H文件如下
#ifndef __BSP_UASRT_H
#define __BSP_UASRT_H
#include "stm32f10x.h" // Device header
#include <stdio.h>
extern uint8_t temp;
void bsp_usart_config(void);
void bsp_usart_senddata(USART_TypeDef* USARTx,uint16_t Data);
void bsp_usart_halfword(USART_TypeDef* USARTx,uint16_t Data);
void bsp_usart_arr(USART_TypeDef* USARTx,uint8_t *arr,uint8_t num);
void bsp_usart_string(USART_TypeDef* USARTx,uint8_t *str);
#endif
3,在主函数中,通过switch函数处理
#include "stm32f10x.h" // Device header
#include "LED.H"
#include "BSP_USART.H"
uint8_t arr[10]={1,2,3,4,5,6,7,8,9,12};
int main(void)
{
LED_config();
bsp_usart_config();
// bsp_usart_senddata( USART1,0x66);
// bsp_usart_halfword(USART1,0xff31);
// bsp_usart_arr(USART1,arr,10);
bsp_usart_string( USART1,"欢迎来到新世界\n");
printf("welcome!\n");
while(1)
{
switch(temp)
{
case 1: led_red();break;
case 2: led_green();break;
case 3: led_blue();break;
default: led_off();break;
}
}
}