STM32标准库和HAL库的比较
使用STM32芯片的时候呢,大家可能都喜欢用库,什么标准库啦,HAL库啦,拿到了就一股脑的用,也没有深究其区别,这样出BUG的时候就只能两手一摊,程序瘫痪了,所以今天咱们来看看标准库和HAL库有啥区别,各个库它的优点在哪里。
串口通信
标准库初始化串口
void uart_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化设置
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
USART_Cmd(USART1, ENABLE); //使能串口1
}
HAL库初始化串口
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
区别
标准库的UART和USART的初始化的核心代码是一样的(USART和UART的区别在于USART在UART异步通信的基础上增加了同步通信的功能,而同步通信在通信网络中使用的比较多),普通的设备中,异步通信即可满足需求,所以USART的用法和UART基本差不多。 回到标准库和HAL库的对比,在标准库中,初始化函数内部先对GPIO进行了初始化,然后再对串口的停止位,引脚等进行配置,好处就是每个增加的部分都是互相独立得,代码可拓展性更强一些,坏处就是逻辑结构不是很明确。
在HAL库中直接对串口进行了配置,并且在串口初始化错误的时候会死循环这对于嵌入式开发来说是很重要的,因为芯片不同于操作系统,芯片不能把每个错误都以人类看得懂的方式显示出来哦~所以死循环可以在Debug的时候帮助我们快速定位问题。但是在HAL库中在哪里初始化串口GPIO口的配置?仔细一看,原来HAL库里面有SystemClock_Config();对各个部分所需要的时钟线都进行了配置并且做了封装,使得代码的逻辑性更加好了,但是如果要新增加模块,就很有可能出现问题。
总结
以串口通信的初始化为例可以看出,HAL库对于代码的封装性做的更好,管理得也是十分到位,但是如果对于HAL库内部结构不熟悉的话又贸然增加新的模块可能会有莫名的BUG出现哦,但是如果搭配CubeMX这款软件,HAL库的使用又变的很简单了呢,标准库的话各个结构分立比较明确,增加代码的难度比HAL库略低一点。综合以上考虑,本人觉得HAL库更适合嵌入式开发有一些经验的人员,而标准库更适合刚接触嵌入式开发的新手,帮助新手多了解一些底层库实现和外设的应用。
HAL库下串口接收不定长数据时轮询、中断方式比较
中断
大家应该都有遇到过这样的需求,外设会发一串长度不定长的字符过来,需要我们用串口接收下来,那么串口有3种工作方式:轮询、中断和DMA,究竟用哪一种好呢?之前的我也是一知半解,这次我尝试了其中2个,轮询和中断,发现了一些个中区别,特此分享。 我用的芯片是STMF103(后简称为103),底层库用的是HAL库,有一说一HAL库用起来比标准库方便很多了呢,真香~~ 先贴一段芯片初始化的代码:
int main(){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
while(1){
......
}
}
但是呢还是遇到了一个问题,就是外设持续以较低速度(以103的晶振频率一定能接收到的速度,1s10个字节的数据)发送数据给103的时候呢,刚开始103同学还是好好的工作者着,把自己收到的数据都老老实实通过串口传输到了电脑上,但是都是三分钟热度,过一会儿,串口就接收不到数据了,这可把我搞懵了。不知所措,明明有数据过来,103同学为什么接收不到了呢?
刚开始我们以为是数据接收缓存区爆了,所以吭哧吭哧去把代码改了,每次接收到数据之后把数据放到一个数组里,结果还是一样,这个bug就把我们劝退了,改用轮询方式接收数据了。 后来我回过头查资料的时候发现CSDN上早已有大佬遇到过这个问题并且把他完美的解决了,讲的超级详细,在CSDN上搜索:STM32 串口中断卡死,一片片的答案。其实就是在串口接收中断里面的库函数里面一个标志位的问题。 所以呢,这个教训告诉我们调库一时爽,BUG火葬场哦。以后在调库的时候可得要好好看看里面的实现,还有技术手册也得经常看,虽然很大概率是全英文的,但是看多了,专业名词就那么几个,很快就很熟练啦。
轮询
串口中断方式把我们劝退了之后我们就选了轮询方案,就是在while(1){}里程序不断的问串口,现在你接到数据了嘛?然后如果串口回答:是!那么就进行相应数据的处理,如果回答:否!那就只能让他哪凉快哪呆着去啦。 贴一些轮询处理的代码(这部分的思路是我和我的队员一起讨论出来的,实现是他实现的,写的还不错)
if(Uart3RXBuffer == 'U'){
HAL_UART_Receive(&huart3,(uint8_t*)&Uart3RXBuffer,1,0xffff);
if(Uart3RXBuffer == '1'){
for(i=0;i<12;i++){
HAL_UART_Receive(&huart3,(uint8_t*)&Uart3RXBuffer,1,0xffff);
Uart3RX_Data[cnt++] = Uart3RXBuffer;
}
}
这个就是在轮询过程中处理不定长数据的核心部分,我们的解决思路是先一个字符一个字符接收到数据,当且仅当接收到数据帧头的时候,我们会将接收到下一个帧头前接收到的数据存储到一个固定长度的数组里面,然后通过HAL_UART_Transmit这个函数将数据一次性发送出去,而对于定长的,而且发送数据较慢的情况来说,直接一次性把固定长度的数据接收下来即可,因为数据发送速度不高,所以很少出现丢包的情况。
总结
今天使用轮询方式解决串口接收不定长数据的方案有些简单,但是对于第一次接触串口的朋友们来说不失为一次深入了解串口的机会哦~钻到库里面看看他是怎么样实现的,再看看技术手册,一定会获益匪浅!
(づ ̄3 ̄)づ╭❤~一键三连,这次一定(๑•̀ㅂ•́)و✧