若该文为原创文章,转载请注明原文出处。
一直使用标准库开发STM32及其他单片机,刚好有个项目要求使用HAL库,以前有用过其他的,没有记录,趁此机会,记录下移植HAL库的全过程
简单的一个DEMO,使用的是STM32F103C8T6单片机,采用ESP-01SWIFI模块,连接Onenet通讯。
一、接口
OLED:
SCL -> PB5
SDA -> PB4
WIFI模块:
RX -> PA2
TX -> PA3
RST -> PA1
二、程序移植
没有采用CubeMX配置,在正点原子的例程上下载了个DEMO程序,直接修改
1、串口移植
串口移植主要是串口2配置,还有数据处理,这里直接附代码
#include "sys.h"
#include "usart.h"
#include "esp8266.h"
//C库
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
//////////////////////////////////////////////////////////////////////////////////
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用
#endif
uint8_t rx_data; // 接收缓冲区
//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄
UART_HandleTypeDef UART2_Handler; //UART句柄
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound)
{
//UART 初始化设置
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}
//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式!
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
#if EN_USART1_RX
HAL_NVIC_DisableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
else if(huart->Instance==USART2)//如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART2_CLK_ENABLE(); //使能USART1时钟
__HAL_RCC_AFIO_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_2; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_3; //PA10
GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式!
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
HAL_NVIC_EnableIRQ(USART2_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART2_IRQn,3,3); //抢占优先级3,子优先级3
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
rx_data = (uint8_t)(huart->Instance->DR);
HAL_UART_Receive_IT(&UART1_Handler, &rx_data, 1); // 再次开启中断接收
}
else if(huart->Instance==USART2)//如果是串口1
{
//HAL_UART_Transmit(&huart1, &rx_data, 1, HAL_MAX_DELAY); // 回传接收到的数据
rx_data = (uint8_t)(huart->Instance->DR);
Esp8266_Rx_IRQHandle(rx_data);
HAL_UART_Receive_IT(&UART2_Handler, &rx_data, 1); // 再次开启中断接收
}
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
}
#endif
//初始化IO 串口1
//bound:波特率
void Usart2_Init(u32 bound)
{
//UART 初始化设置
UART2_Handler.Instance=USART2; //USART1
UART2_Handler.Init.BaudRate=bound; //波特率
UART2_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART2_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART2_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART2_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART2_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART2_Handler); //HAL_UART_Init()会使能UART1
HAL_UART_Receive_IT(&UART2_Handler, (u8 *)aRxBuffer, 1);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
HAL_UART_Transmit(&UART2_Handler, (uint8_t*)str, len,1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART2_Handler,UART_FLAG_TC)!=SET); //等待发送结束
}
//串口1中断服务程序
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART2_Handler); //调用HAL库中断处理公用函数
}
void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
{
unsigned char UsartPrintfBuf[296];
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
va_end(ap);
HAL_UART_Transmit(&UART1_Handler, (uint8_t*)pStr, strlen(pStr),1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束
}
代码是在例程上直接添加,增加了初始化及传输和接收数据处理。
2、ESP8266接收处理
void Esp8266_Rx_IRQHandle(char data)
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++] = data;
}
替换原来的串口2中断函数
3、配置
OLED使有的是PB4和PB5没有关闭JTAT会不能显示
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_SWJ_NOJTAG(); // 禁用JTAG,保留SWD
4、堆栈
注意需要调节堆栈

5、程序架构

增加了Onenet底层,这个网上有,基本不用修改,修改的是串口部分,已经修改了
6、主程序
int main(void)
{
unsigned char *dataPtr = NULL;
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_SWJ_NOJTAG(); // 禁用JTAG,保留SWD
delay_init(72); //初始化延时函数
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
OLED_Init(); // OLED初始化
ESP8266_Init(); // ESP8266初始化
TIM3_Init(9999, 7199);
OLED_Clear();
OLED_ShowString(0,0,"Init Ok.",16, 1);
while(1)
{
/* 数据发送 */
if(time3scnt >= 20 || senflg == 1)
{
senflg = 0;
time3scnt = 0;
OneNet_SendData(); //发送数据到云平台
ESP8266_Clear(); //情况串口接收区缓存
}
dataPtr = ESP8266_GetIPD(0);//获取云平台下发的数据
if(dataPtr != NULL)//如果不为空,解析
{
OneNet_RevPro(dataPtr); //平台返回数据检测
}
}
}
测试是正常的,后续会增加传感器的HAL库使用。
如有侵权,或需要完整代码,请及时联系博主。