做哪类网站,营销外包团队有哪些,做公众号主页面的有哪些网站,手机网站设计理念选择HAL库还是标准库呢#xff1f;HAL库是趋势#xff0c;标准库不再升级了#xff0c;转HAL库是大势所趋。HAL库有优点#xff0c;也有自身的不足#xff0c;建议初学者还是从标准库入手。 
标准库是单片机开发的基本库#xff0c;它把“用寄存器实现的功能”写成一个函…选择HAL库还是标准库呢HAL库是趋势标准库不再升级了转HAL库是大势所趋。HAL库有优点也有自身的不足建议初学者还是从标准库入手。 
标准库是单片机开发的基本库它把“用寄存器实现的功能”写成一个函数或定义成宏供用户使用。 
HAL库把“有关系的功能”封装成一个函数供用户调用其目的是想隔绝用户直接干预寄存器操作想法很好但实际是行不通的大部分是好的另外提供了两个用户接口函数MspInit和Callback。 1、MspInit函数 
HAL库写了很多个Init函数供用户调用前提是必须要写好MspInit函数。同种功能硬件资源共用一个MspInit函数所以所以这个MspInit函数必然就有许多分支降低了程序的执行效率。下面举例说明 
//函数功能:使能TIMx时钟,设置中断优先级,使能TIMx中断 
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) 
{ if(htim-Instance  TIM1)//初始化TIM1 { __HAL_RCC_TIM1_CLK_ENABLE();                      //使能TIM1时钟 HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0, 0); //设置中断优先级 
HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); 
//使能TIM1刹车事件更新事件触发事件COM事件中断 } if(htim-Instance  TIM3)//初始化TIM3 { __HAL_RCC_TIM3_CLK_ENABLE();     //使能TIM3时钟 HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0); //设置中断优先级 HAL_NVIC_EnableIRQ(TIM3_IRQn);     //使能TIM3中断 } 
} 
用户通过增删MspInit函数中的内容好像也很容易。在初始化时设备用到的时钟引脚复用中断优先级管理等都需要写在这里。 2、Callback函数 
HAL库写了很多个IRQHandler函数供用户调用前提是必须要写好Callback函数。同种功能硬件资源共用一个Callback函数所以所以这个Callback函数必然就有许多分支降低了程序的执行效率。下面举例说明 
//函数功能:在定时器更新中断时,HAL_TIM_IRQHandler()会调用函数,处理用户程序 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 
{ if(htim-InstanceTIM1)//处理定时器1 { TIM1_LED_Toggle(); } if(htim-InstanceTIM3)//处理定时器3 { TIM3_LED_Toggle(); } 
} //函数功能:TIM1中断服务程序 
void TIM1_BRK_UP_TRG_COM_IRQHandler(void) 
{ 
TIM_HandleTypeDef TIM1_HandleStructure; TIM1_HandleStructure.InstanceTIM1; HAL_TIM_IRQHandler(TIM1_HandleStructure); 
} //函数功能:TIM3中断服务程序 
void TIM3_IRQHandler(void) 
{ 
TIM_HandleTypeDef TIM3_HandleStructure; TIM3_HandleStructure.InstanceTIM3; HAL_TIM_IRQHandler(TIM3_HandleStructure); 
} 
通过上面的举例好像Callback函数也不像想象中的那么难写。但我们发现一个问题这个IRQHandler函数传递是一个结构指针所以必须有一个结构变量与之对应所以在中断服务程序中申请一个局部结构变量因为申请为全局变量确实有点浪费内存。官方给的例子基本都是全局变量容易把人带入歧途。 
3、HAL库的缺点 
HAL库中的某些功能性函数有可能不能满足需求需要自己动手写也是常有的事情。例如串口的函数库有些函数脱离实际如 
1)、串口接收函数必须先知道接收多少个数据,才可以调用。 
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 
2、串口发送函数需要把UART_HandleTypeDef结构类型的变量定义为全局变量才可以使用。这么设计很消耗内存。 
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 
如果你不按照HAL库的套路干就需要对某些函数和宏定义进行重构。如果我们不了解底层就只能按照它的意思干。 
还是举例说明   
#define _HAL_UART_SendByte(__INSTANCE__, __DATA__) (__INSTANCE__)-DR  ( (__DATA__)  (uint16_t)0x01FF );
//将(__DATA__)写入串口发送缓冲区#define _HAL_UART_ReceiveByte(__INSTANCE__) ( (uint16_t)( (__INSTANCE__)-DR  (uint16_t)0x01FF) )
//读串口发送缓冲区#define _HAL_UART_GET_FLAG(__INSTANCE__, __FLAG__) ( ( (__INSTANCE__)-SR  (__FLAG__) )  (__FLAG__) )
//读串口中断标志位
//(__FLAG__)UART_IT_RXNE,读串口接收寄存器为非空时产生的中断标志位
//(__FLAG__)UART_IT_PE,读串口奇偶校验错误产生的中断标志位
//(__FLAG__)UART_IT_ERR,读帧错误、噪音错误和溢出错误时产生的中断标志位
//(__FLAG__)UART_IT_TXE时,读串口发送寄存器为空产生的中断标志位
//(__FLAG__)UART_IT_TC时,读发送完成产生的中断标志位#define _HAL_UART_CLEAR_FLAG(__INSTANCE__, __FLAG__) ( (__INSTANCE__)-SR  ~(__FLAG__) )
//清除串口中断标志位
//(__FLAG__)UART_IT_RXNE,清除串口接收寄存器为非空时产生的中断标志位
//(__FLAG__)UART_IT_PE,清除串口奇偶校验错误产生的中断标志位
//(__FLAG__)UART_IT_ERR,清除帧错误、噪音错误和溢出错误时产生的中断标志位
//(__FLAG__)UART_IT_TXE时,清除串口发送寄存器为空产生的中断标志位
//(__FLAG__)UART_IT_TC时,清除发送完成产生的中断标志位#define _HAL_UART_ENABLE_IT(__INSTANCE__, __INTERRUPT__)   ((((__INTERRUPT__)  28U)  UART_CR1_REG_INDEX)? ((__INSTANCE__)-CR1 | ((__INTERRUPT__)  UART_IT_MASK)): \(((__INTERRUPT__)  28U)  UART_CR2_REG_INDEX)? ((__INSTANCE__)-CR2 | ((__INTERRUPT__)  UART_IT_MASK)): \((__INSTANCE__)-CR3 | ((__INTERRUPT__)  UART_IT_MASK)))
//设置串口中断使能位
//(__INTERRUPT__)UART_IT_RXNE,设置串口接收寄存器为非空时,使其产生中断
//(__INTERRUPT__)UART_IT_PE,设置串口奇偶校验错误时,使其产生中断
//(__INTERRUPT__)UART_IT_ERR,设置帧错误、噪音错误和溢出错误时,使其产生中断
//(__INTERRUPT__)UART_IT_TXE时,设置串口发送寄存器为空时,使其产生中断
//(__INTERRUPT__)UART_IT_TC时,设置串口发送寄存器发送完成时,使其产生中断#define _HAL_UART_DISABLE_IT(__INSTANCE__, __INTERRUPT__)  ((((__INTERRUPT__)  28U)  UART_CR1_REG_INDEX)? ((__INSTANCE__)-CR1  ~((__INTERRUPT__)  UART_IT_MASK)): \(((__INTERRUPT__)  28U)  UART_CR2_REG_INDEX)? ((__INSTANCE__)-CR2  ~((__INTERRUPT__)  UART_IT_MASK)): \((__INSTANCE__)-CR3  ~ ((__INTERRUPT__)  UART_IT_MASK)))
//设置串口中断不使能
//(__INTERRUPT__)UART_IT_RXNE,设置串口接收寄存器为非空时,不使其产生中断
//(__INTERRUPT__)UART_IT_PE,设置串口奇偶校验错误时,不使其产生中断
//(__INTERRUPT__)UART_IT_ERR,设置帧错误、噪音错误和溢出错误时,不使其产生中断
//(__INTERRUPT__)UART_IT_TXE时,设置串口发送寄存器为空时,不使其产生中断
//(__INTERRUPT__)UART_IT_TC时,设置串口发送寄存器发送完成时,不使其产生中断 //重定义fputc函数 //函数功能:发送ch的值给USART2串口 int fputc(int ch, FILE *f) {   _HAL_UART_SendByte(USART2, (unsigned char) ch);   while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)! SET); 
//等待发送完成标志位被置1         return ch; } 
//函数功能:串口2发送一个字节 void USART2_SendByte(  unsigned char ch ) {   _HAL_UART_SendByte(USART2, (unsigned char) ch);   while( _HAL_UART_GET_FLAG(USART2,USART_SR_TC)! SET); 
//等待发送完成标志位被置1 } 
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{GPIO_InitTypeDef  GPIO_InitStructureure;if(huart-InstanceUSART2){__HAL_RCC_USART2_CLK_ENABLE();//使能USART2外设时钟__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟//串口引脚映射开始/GPIO_InitStructureure.Pin  GPIO_PIN_0;     //选择第0脚,PA0是为USART2_TXGPIO_InitStructureure.Mode  GPIO_MODE_AF_PP;            //复用功能推挽模式GPIO_InitStructureure.Pull  GPIO_PULLUP;                //引脚上拉被激活GPIO_InitStructureure.Speed  GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速GPIO_InitStructureure.Alternate  GPIO_AF9_USART2;       //将引脚复用为USART2HAL_GPIO_Init(GPIOA, GPIO_InitStructureure);//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器//将PA0初始化为USART2_TXGPIO_InitStructureure.Pin  GPIO_PIN_1;     //选择第1脚,PA1是USART2_RXGPIO_InitStructureure.Mode  GPIO_MODE_AF_PP;            //复用功能推挽模式GPIO_InitStructureure.Pull  GPIO_PULLUP;                //引脚上拉被激活GPIO_InitStructureure.Speed  GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速GPIO_InitStructureure.Alternate  GPIO_AF9_USART2;       //将引脚复用为USART2HAL_GPIO_Init(GPIOA, GPIO_InitStructureure);//根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器//将PA1初始化为USART2_RX
//串口引脚映射结束/HAL_NVIC_SetPriority(USART2_IRQn, 0x01, 0);//设置串口2中断优先级为0x01,0无意义}
}//函数功能:
//PA0是为USART2_TX,PA1是USART2_RX
//中断优先级为0x01
//波特率为115200,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,只允许接收中断,并使能串口
void USART2_Init(uint32_t baudrate)
{UART_HandleTypeDef UART_HandleStructureure;UART_HandleStructureure.Instance           USART2;              //接口为USART2UART_HandleStructureure.Init.BaudRate      baudrate;            //波特率为115200bpsUART_HandleStructureure.Init.WordLength    UART_WORDLENGTH_8B;  //串口字长度为8UART_HandleStructureure.Init.StopBits      UART_STOPBITS_1;     //串口停止位为1位UART_HandleStructureure.Init.Parity        UART_PARITY_NONE;    //串口无需奇偶校验UART_HandleStructureure.Init.HwFlowCtl     UART_HWCONTROL_NONE; //串口无硬件流程控制UART_HandleStructureure.Init.Mode          UART_MODE_TX_RX;     //串口工作模式为发送和接收模式UART_HandleStructureure.AdvancedInit.AdvFeatureInitUART_ADVFEATURE_NO_INIT;//不使用自动波特率
//	UART_HandleStructureure.AdvancedInit.AdvFeatureInitUART_ADVFEATURE_AUTOBAUDRATE_INIT;//使用自动波特率配置
//	UART_HandleStructureure.AdvancedInit.AutoBaudRateEnableUART_ADVFEATURE_AUTOBAUDRATE_ENABLE;//自动波特率使能
//	UART_HandleStructureure.AdvancedInit.AutoBaudRateModeUART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT;//自动波特率模式HAL_UART_Init(UART_HandleStructureure);//根据UART_HandleStructureure型结构初始化USART2__HAL_UART_ENABLE_IT(UART_HandleStructureure, UART_IT_RXNE);开启串口接收中断//串口接收数据时,使能接收数据寄存器不为空则产生中断(位RXNE1)//Enable the UART Data Register not empty Interrupt__HAL_UART_DISABLE_IT(UART_HandleStructureure, UART_IT_TXE);//串口发送数据时,不使能串口发送数据寄存器为空产生中断(位TXE0)//Disable the UART Transmit Complete Interrupt__HAL_UART_DISABLE_IT(UART_HandleStructureure,UART_IT_TC);//串口发送数据时,不使能串口发送完成产生中断(位TC1)HAL_NVIC_EnableIRQ(USART2_IRQn);//使能串口2中断//USART2_IRQn表示中断源为串口2
} 放弃了HAL_UART_Receive_IT()之后HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)也就废了。直接用自己的不用Callback函数,如下 
//函数功能:串口2中断服务程序 void USART2_IRQHandler(void) {     uint8_t RX_temp; (void)RX_temp;//防止RX_temp不使用而产生警告 if( _HAL_UART_GET_FLAG(USART2,USART_SR_RXNE) )     {//在串口状态寄存器中,发现RXNE1,且串口控制寄存器1允许接收数据         RX_temp _HAL_UART_ReceiveByte(USART2);//读串口数据 _HAL_UART_CLEAR_FLAG(USART2,USART_SR_RXNE);         if(RX_temp1  USART2_RX_Time_Count0)    USART2_RX_Time_Count  1; 
//如果接收到帧头为变频器地址为0x1,则启动USART2接收时间计数器         if(USART2_RX_Time_Count  0)         {             USART2_RX_Time_Count  1;//设置USART2接收时间计数器为1;             USART2_RX_Buffer[USART2_RX_Buffer_Load_Index]  RX_temp;//保存接收到的新数据             USART2_RX_Buffer_Load_Index; if(USART2_RX_Buffer_Load_IndexUSART2_RX_Buffer_Size) USART2_RX_Buffer_Load_Index1;//防止USART2_RX_Buffer[]溢出         } //软件先读串口状态寄存器(USART_SR),然后再读串口数据寄存器USART_DR,就可以将ORE位(Overrun错误标志)清零;         //软件先读串口状态寄存器(USART_SR),然后再读串口数据寄存器USART_DR,就可以将NE位(噪声错误标志)清零;         //软件先读串口状态寄存器(USART_SR),然后再读串口数据寄存器USART_DR,就可以将FE位(帧错误标志)清零;         //软件先读串口状态寄存器(USART_SR),然后再读串口数据寄存器USART_DR,就可以将PE位(奇偶校验值错误)清零;         //软件读串口数据寄存器USART_DR,就可以将RXNE位清零   } } 5、学习HAL库的方法 
1、要了解了HAL库对程序的管理方法这个只能靠自己去看库中的函数了。 
2、熟悉结构变量中各个成员的意义。 
3、熟悉HAL库的函数。 
4、多测试多看高手的程序。如果刚入门可能会被半桶水的人带偏了。例如官方的程序他们的思路可能不符合实际使用但是用来测试还是可以的我们需要把它修改实际需要的那种代码。 
不建议初学者直接学习HAL库还是从标准库学习比较好。只要你有兴趣学习都是一样的。 
有时没办法不能按照HAL库思路干还是需要涉及到寄存器操作。 6、学习总结 标准库转HAL库HAL里面的初始化函数需要重点掌握虽然申请的结构变量占很多空间但是初始化完后就可以释放了但串口有点特殊最好自己写。 1、因为内存容量有限不建议将初始化结构参数申请为全局变量。由于没有将结构参数设为全局变量有些函数或宏需要重写以满足实际需求。如: 读中断标志位 清除中断标志位 使能中断 不使能能中断 串口发送 串口接收 等相关的函数需要重构。 2、带MspInit的函数建议写在需要初始化的函数中。HAL采用同一管理这个MspInit的函数只有一个所以很庞大而且还要做判断效率极低。 3、带CallBack的函数只有一个由于没有食使用全局结构变量所以不能用。而申请为全局结构变量又很浪费内存。 4、为了方便移植中断不再使用同一个CallBack函数了。 7、HAL库有自己的优点: 1、把所有的硬件驱动都写好了我们要从中扣出有价值的函数使编程更加灵活。 2、初始化函数只有一个。 不管是用什么库里面的结构参数知道怎么赋值干啥用的基本就上路了。 
这是我个人的体会觉得这么做可以提高程序的执行效率其次是节省内存更主要的是功能模块化易读易移植易交流。程序短小。 
程序短小精悍是每个程序员的追求。 芯片内存不是问题HAL库的程序管理方法可以值得借鉴除了那些不切合实际的库函数外。经得起实践检验的方法就是最好的方法不必死板硬套。