HAL 库函数的使用

关于 HAL 库,我们通过 cubeMX 可以完成初始化等代码生成,极大的提高了 stm32 的使用效率.但是重要的函数,还是需要我们自己掌握,这里首先学习 hal 库函数的使用.之后抽空对 cubeMX 进行细致的了解.

GPIO

gpio 是 stm32 里基本的功能,其中经常使用的函数如下:

翻转电平

HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)用来 控 制 LED 的亮灭。

读取引脚电平状态

HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)该函数可以返回状态值 0 或 1.

写入引脚电平状态

HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)功能与读取相反

UART

串口发送数据

HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

用来将数据发送给其他设备,所需要的参数包括串口位置、字符串位置,个数,以及最大传输字节。

串口中断模式接收函数

HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

参数包括串口位置、字符串存放位置、个数。在读入指定个数的数据之后串口中断,触发 IRQHandler 函数.

由于这是接收中断,故 IRQHandler 函数处理后调用串口接收中断回调函数(此函数需要自己配置)

串口接收中断回调函数

HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

在此附上例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/

if(Uart2_Rx_Cnt >= 255) //溢出判断
{
Uart2_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer));
HAL_UART_Transmit(&huart2, (uint8_t *)"you are sb!", 10,0xFFFF);

}
else
{
RxBuffer[Uart2_Rx_Cnt++] = aRxBuffer; //接收数据转存

if((RxBuffer[Uart2_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart2_Rx_Cnt-2] == 0x0D)) //判断结束位
{
// RxBuffer[Uart2_Rx_Cnt - 2] = '\0';
if (strcmp(RxBuffer, "lovebuaa\r\n") == 0) {HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_2);}


HAL_UART_Transmit(&huart2, (uint8_t *)&RxBuffer, Uart2_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
Uart2_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
}
}

HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}

一种小技巧:重写 fget 和 fput 函数

需要注意,在 keil5 中,点开魔法棒,将 use MicroLIB 打勾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//方法一

//在 stm32f4xx_hal.c 中重写fget和fput函数
//stm32g4的单片机与pc通信的是huart2!
//缺点是每次MX生成代码后都要重新配置
#include "stdio.h"

int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart2, &ch, 1, 0xffff);
return ch;
}

//方法二

//直接在main.c中重写
//目前只改写了printf
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__*/


PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch,1,0xFFFF);
return ch;
}


修改之后,我们就可以直接使用 scanf 和 printf 来实现 pc 与 stm32 的通信.

TIM

这里的配置一般都是系统配置,需要到 MX 中进行修改~

DAC

DAC 是数模转换,可用于制作 DDS 信号发生器,以及将数字信号转换为模拟信号。

触发方式分为三种,分别是定时器触发,外部引脚触发,软件控制.

结合 DMA 可以做到生成任意波形,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	HAL_TIM_Base_Start(&htim6);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)sine,256, DAC_ALIGN_12B_R);
char message[100];
double f = 100000.0/255;
sprintf(message,"the flent is %.2lf Hz \n",f);
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
HAL_UART_Transmit(&huart2,(uint8_t*)message,strlen(message),HAL_MAX_DELAY);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}

此代码中定义了一个名叫 sine 的数组,将数组通过 DMA 传递,从而转化为模拟信号.

1
2
3
//正弦函数的波形数据
const uint16_t sine[256] ={1100,1124,1149,1173,1198,1222,1246,1270,1295,1319,1342,1366,1390,1413,1436,1459,1482,1505,1527,1549,1571,1592,1614,1634,1655,1675,1695,1715,1734,1753,1771,1789,1807,1824,1840,1857,1873,1888,1903,1917,1931,1944,1957,1970,1981,1993,2003,2014,2023,2032,2041,2049,2056,2063,2070,2075,2080,2085,2089,2092,2095,2097,2098,2099,2100,2099,2098,2097,2095,2092,2089,2085,2080,2075,2070,2063,2056,2049,2041,2032,2023,2014,2003,1993,1981,1970,1957,1944,1931,1917,1903,1888,1873,1857,1840,1824,1807,1789,1771,1753,1734,1715,1695,1675,1655,1634,1614,1592,1571,1549,1527,1505,1482,1459,1436,1413,1390,1366,1342,1319,1295,1270,1246,1222,1198,1173,1149,1124,1100,1075,1050,1026,1001,977,953,929,904,880,857,833,809,786,763,740,717,694,672,650,628,607,585,565,544,524,504,484,465,446,428,410,392,375,359,342,326,311,296,282,268,255,242,229,218,206,196,185,176,167,158,150,143,136,129,124,119,114,110,107,104,102,101,100,100,100,101,102,104,107,110,114,119,124,129,136,143,150,158,167,176,185,196,206,218,229,242,255,268,282,296,311,326,342,359,375,392,410,428,446,465,484,504,524,544,565,585,607,628,650,672,694,717,740,763,786,809,833,857,880,904,929,953,977,1001,1026,1050,1075};

教程可见视频: STM32_DAC

ADC

模数转换,通常用于采样波形进行分析.

使用 DMA 的相关代码:HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuffer, SAMPLES);

对于 ADC 来说,最重要的是学会配置 MX.

特别是时钟的配置比较重要,事关采样频率的计算.

注意选择时钟触发,这样才能确保采样的准确度.

详见:STM32HAL ADC+TIM+DMA 采集交流信号 基于 cubemx_tim+adc+dma-CSDN 博客

DSP

首先需要在 cubeMX 中寻找 DSP_library 下载,其次还需要进入 keil 后将包含<arm_math.h>的目录添加到路径中.添加有关的宏文件,还要注意将<arm_cortexM4lf_math.lib>也添加到项目中,这样就能正常使用 DSP 的相关库函数了.

可以参考:STM32 DSP 库的添加 - 上帝的绵羊 - 博客园 (cnblogs.com)

WDG

全称 watchdog(看门狗),用于检测系统运行状态并及时做出反馈.共分为两种.

IWDG(独立看门狗)

独立看门狗采用内部时钟,独立于系统时钟,按照分频和窗口值得出间隔的时间.

在间隔时间内不执行喂狗函数,就会自动 reset

1
2
//喂狗函数
HAL_IWDG_Refresh(&hiwdg);

WWDG(窗口看门狗)

窗口看门狗规定必须要在指定的时间内喂狗,过早或者过晚都会导致 reset.

如果要使用,需要把中断(NVIC SETTINGS)打开 Enabled

原理是在计数器快要溢出时,看门狗触发一次中断,在中断回调函数里进行喂狗即可.

因此添加如下函数:

1
2
3
4
5
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(hwwdg);
}