USART驱动的工作原理
总结一下我们之前使用中断的方式来进行数据的发送和接收 如果收到数据数据在RDR寄存器中 RXNE标志位就从0到1触发中断 进入中断服务函数 把数据缓存在队列中 然后在到进程函数中断接收数据函数中进行出队处理
发送数据就是把中断关闭(标志位TXE的寄存器TDR为空的时候为1会误触发中断所以要关闭)要发送的数据进行入队然后打开中断 IDR寄存器中还是没数据
然后标志位TXE为1触发中断 进入中断服务函数把数据出队 逐个发送
USART驱动的具体使用方法
驱动概述
PAL库初始化USART
因为数据发送和数据的接收都是共同用一个中断源
所以只需要设置一个优先级分组和子优先级和抢占优先级
发送缓冲区和接收缓冲区
因为我们是使用中断的方式发送和接收数据的 PAL库中 所以要把数据缓存在队列中也就是缓冲区 但是这个队列不能太长因为队列占用的是RAM 芯片的RAM收到限制
PAL库函数的初始化
hUSART1.Init.USARTx = USART1;
hUSART1.Init.BaudRate = 9600;
hUSART1.Init.USART_WordLength = USART_WordLength_8b;
hUSART1.Init.USART_StopBits = USART_StopBits_1;
hUSART1.Init.USART_Parity = USART_Parity_No;
hUSART1.Init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
hUSART1.Init.USART_IRQ_PreemptionPriority = 0;
hUSART1.Init.USART_IRQ_SubPriority = 0;
hUSART1.Init.TxBufferSize = 128;//缓冲区的长度
hUSART1.Init.RxBufferSize = 128;//缓冲区的长度
hUSART1.Init.Advanced.Remap = 1;//启用复用功能 AFIO
hUSART1.Init.Advanced.LineSeparator = LineSeparator_CRLF; // \r\n
PAL_USART_Init(&hUSART1);
编写PAL库的中断服务函数
具体代码
void USART1_IRQHandler(void)
{
PAL_USART_IRQHandler(&hUSART1);
}
数据的发送
数据的接收
time out (超时值)如果在调用这个函数接口 对应传入的的TIME out值就如上图所示
数据的接收
PAL_USART_ReadLine 在使用这个函数接口之前先要设置行分隔符的方式 行分隔符就是\r \r\n 等多种分隔行 在初始化的时候调用高级参数
char strBuffer[64];设置为64即可
#include "stm32f10x.h"
#include "stm32f10x_pal.h"
#include "stm32f10x_pal_usart.h"
static PalUSART_HandleTypeDef hUSART1;//声明句柄
int main(void)
{
PAL_Init();
hUSART1.Init.USARTx = USART1;
hUSART1.Init.BaudRate = 9600;
hUSART1.Init.USART_WordLength = USART_WordLength_8b;
hUSART1.Init.USART_StopBits = USART_StopBits_1;
hUSART1.Init.USART_Parity = USART_Parity_No;
hUSART1.Init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
hUSART1.Init.USART_IRQ_PreemptionPriority = 0;
hUSART1.Init.USART_IRQ_SubPriority = 0;
hUSART1.Init.TxBufferSize = 128;//缓冲区的长度
hUSART1.Init.RxBufferSize = 128;//缓冲区的长度
hUSART1.Init.Advanced.Remap = 1;//启用复用功能 AFIO
hUSART1.Init.Advanced.LineSeparator = LineSeparator_CRLF; // \r\n
PAL_USART_Init(&hUSART1);
// // 1. 发送单个字节 0x5a
// PAL_USART_SendByte(&hUSART1, 0x5a);
// // 2. 发送字节数组 01 02 03 04 05
// const uint8_t a[] = {1,2,3,4,5};
//
// PAL_USART_SendBytes(&hUSART1, a, sizeof(a)/sizeof(uint8_t));
// // 3. 发送单个字符 H
// PAL_USART_PutChar(&hUSART1, 'H');
// // 4. 发送字符串Hello world\r\n
// PAL_USART_SendString(&hUSART1, "Hello world\r\n");
// // 5. 发送格式化字符串
// const char *name = "Tom";
// uint32_t age = 18;
// float height = 173.5;
//
// PAL_USART_Printf(&hUSART1, "\r\nName:%s\r\nAge:%d\r\nHeight:%.1fcm\r\n", name, age, height);
//
// // 1. 接收单个字节
// PAL_USART_SendString(&hUSART1, "Receive single byte...");
// uint8_t byteRcvd;
// byteRcvd = PAL_USART_ReceiveByte(&hUSART1, PAL_MAX_DELAY);
// PAL_USART_Printf(&hUSART1, "Byte received: 0x%02x\r\n", byteRcvd);
// // 2. 接收5个字节
// PAL_USART_SendString(&hUSART1, "Receive 5 bytes ...");
//
// uint8_t a[5];
// PAL_USART_ReceiveBytes(&hUSART1, a, 5, PAL_MAX_DELAY);
//
// PAL_USART_Printf(&hUSART1, "5 bytes received: %02x %02x %02x %02x %02x", a[0], a[1], a[2], a[3], a[4]);
// 3. 接收一行字符串
PAL_USART_SendString(&hUSART1, "Receive a line ...\r\n");
char strBuffer[64];
PAL_USART_ReadLine(&hUSART1, strBuffer, sizeof(strBuffer) / sizeof(char), PAL_MAX_DELAY);
PAL_USART_Printf(&hUSART1, "Line received: %s\r\n", strBuffer);
while(1)
{
}
}
void USART1_IRQHandler(void)
{
PAL_USART_IRQHandler(&hUSART1);
}
应用实例
使用PAL库函数来编写Echo实验
我们只需要在中断响应函数中初始化PAL库函数的中断函数就行 不需要过多的代码 如果使用readline收到了字符数据 (就是这个函数的返回值大于0)
就再把这个字符给发送回去
使用USART3进行编程
Echo实验
#include "stm32f10x.h"
#include "stm32f10x_pal.h"
#include "stm32f10x_pal_usart.h" //引用头文件
static void Echo_Proc_Init(void);//初始化函数
static void Echo_Proc(void); //进程函数
static PalUSART_HandleTypeDef hUSART3;//声明句柄
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
PAL_Init();
Echo_Proc_Init();
while(1)
{
Echo_Proc();
}
}
void USART3_IRQHandler(void)
{
PAL_USART_IRQHandler(&hUSART3);
}
void Echo_Proc_Init(void)
{
hUSART3.Init.USARTx = USART3;
hUSART3.Init.BaudRate =9600;
hUSART3.Init.USART_WordLength = USART_WordLength_8b;
hUSART3.Init.USART_Parity = USART_Parity_No;
hUSART3.Init.USART_StopBits = USART_StopBits_1;
hUSART3.Init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
hUSART3.Init.USART_IRQ_PreemptionPriority =0;
hUSART3.Init.USART_IRQ_SubPriority = 0;
hUSART3.Init.TxBufferSize = 128;//设置缓冲区大小
hUSART3.Init.RxBufferSize = 128;
hUSART3.Init.Advanced.LineSeparator = LineSeparator_CRLF;//设置高级参数
PAL_USART_Init(&hUSART3);
}
void Echo_Proc()
{
char strRcvd[64];
if(PAL_USART_ReadLine(&hUSART3,strRcvd,64,0) >0)
{
PAL_USART_SendString(&hUSART3,strRcvd);
}
}
串口打印日志
分为三个等级 第一个就是信息info 第二个就是警告 第三个就是错误
#include "stm32f10x.h"
#include "stm32f10x_pal.h"
#include "stm32f10x_pal_usart.h" //引用头文件
#include <string.h>
static void Echo_Proc_Init(void);//初始化函数
static void Echo_Proc(void); //进程函数
static PalUSART_HandleTypeDef hUSART3;//声明句柄
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
PAL_Init();
Echo_Proc_Init();
PAL_USART_Log(&hUSART3,WarningLevel_Info,"Initialize complele");
while(1)
{
Echo_Proc();
}
}
void USART3_IRQHandler(void)
{
PAL_USART_IRQHandler(&hUSART3);
}
void Echo_Proc_Init(void)
{
hUSART3.Init.USARTx = USART3;
hUSART3.Init.BaudRate =9600;
hUSART3.Init.USART_WordLength = USART_WordLength_8b;
hUSART3.Init.USART_Parity = USART_Parity_No;
hUSART3.Init.USART_StopBits = USART_StopBits_1;
hUSART3.Init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
hUSART3.Init.USART_IRQ_PreemptionPriority =0;
hUSART3.Init.USART_IRQ_SubPriority = 0;
hUSART3.Init.TxBufferSize = 128;//设置缓冲区大小
hUSART3.Init.RxBufferSize = 128;
hUSART3.Init.Advanced.LineSeparator = LineSeparator_CRLF;//设置高级参数
PAL_USART_Init(&hUSART3);
}
void Echo_Proc()
{
char strRcvd[64];
if(PAL_USART_ReadLine(&hUSART3,strRcvd,64,0) >0)//参数分别为句柄的指针 用来存数据的数组名称 strRcvd 数组大小 Timeout
{
PAL_USART_Log(&hUSART3,WarningLevel_Info,"string recvd, length:%d",strlen(strRcvd));
PAL_USART_SendString(&hUSART3,strRcvd);
}
}
串口波形监控
#include "stm32f10x.h"
#include "stm32f10x_pal.h"
#include "stm32f10x_pal_usart.h"
#include <math.h>
static PalUSART_HandleTypeDef hUSART3;
static void WaveformGenerato_Init(void);
static void WaveformGenerator_Proc(void);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
PAL_Init();
WaveformGenerato_Init();
while(1)
{
WaveformGenerator_Proc();
PAL_Delay(10);
}
}
void WaveformGenerato_Init(void)
{
hUSART3.Init.USARTx = USART3;
hUSART3.Init.BaudRate = 115200; //显示波形的软件中波特率为115200
hUSART3.Init.USART_WordLength = USART_WordLength_8b;
hUSART3.Init.USART_Parity = USART_Parity_No;
hUSART3.Init.USART_StopBits = USART_StopBits_1;
hUSART3.Init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
hUSART3.Init.USART_IRQ_PreemptionPriority =0;
hUSART3.Init.USART_IRQ_SubPriority = 0; //用到了中断就要设置中断优先级的分组 NVIC_PriorityGroupConfig
hUSART3.Init.TxBufferSize = 128;
hUSART3.Init.RxBufferSize = 128;
hUSART3.Init.Advanced.LineSeparator = LineSeparator_CRLF;//设置高级参数
PAL_USART_Init(&hUSART3);
}
void WaveformGenerator_Proc(void)
{
float t ,y1,y2;
t = PAL_GetTick()/1000.0 ; //使用这个函数就获得ms 需要除以1000.0获得秒 但是这里是float类型的需要除以1000.0
y1 = sin(10*t);//yi的纵坐标
y2 = cos(10*t);//y2的纵坐标
PAL_USART_Printf(&hUSART3,"$%.3f %.3f;" ,y1,y2);//输出的特定格式
}
void USART3_IRQHandler(void)
{
PAL_USART_IRQHandler(&hUSART3);
}
周期性任务的实现方法
例如我们想要一个进程每隔10ms进行一次 但是又不能在while语句中添加延时函数 所以需要解决这个问题
想象一下一个人从0开始跑步 比如他跑到了0这个地方碰到了棋子就把棋子扔向10ms的地方(跑过去需要10ms)然后跑向那个棋子 然后棋子到了10这个地方他从0跑到10经过了10ms然后又把棋子扔向20这个地方 跑过去有需要10ms 所以程序也是如此 第一行static uint64_t nextExecTime 就是下次程序要执行的时间 if语句的判断条件就是如果当前的时间等于下次的时间就执行一次任务 然后把这个任务丢到下次运行到需要10ms的地方 从而使得一个任务每个10ms执行一次。
{
float t ,y1,y2;
static uint64_t nextExecTime = 0;
if(PAL_GetTick() >= nextExecTime)
{
t = PAL_GetTick()/1000.0 ; //使用这个函数就获得ms 需要除以1000.0获得秒 但是这里是float类型的需要除以1000.0
y1 = sin(10*t);//yi的纵坐标
y2 = cos(10*t);//y2的纵坐标
PAL_USART_Printf(&hUSART3,"$%.3f %.3f;" ,y1,y2);//输出的特定格式
nextExecTime+=10;
}
如果以后想要一个进程函数每隔10ms执行一次
只需要调用PAL库函数中的宏
PAL_PERIODIC_PROC(10); //调用这个宏就可以使得进程函数周期性的调用10ms