使用hal库函数通常会经常使用STM32cubeMX这个软件进行一个配置会相对较为简单,软件安装在我的另一篇博客里面有讲解,其中引脚的配置可以看一下这位大神写的:http://www.openedv.com/forum.php?mod=viewthread&tid=309468&highlight=hal%BF%E2
GPIO函数
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
功能: GPIO初始化
实例:HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
功能:在函数初始化之后的引脚恢复成默认的状态,即各个寄存器复位时的值实例:HAL_GPIO_Init(GPIOC, GPIO_PIN_4);
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:读取引脚的电平状态、函数返回值为0或1实例:HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
功能:引脚写0或1实例:HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4,0);
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
翻转引脚的电平状态实例:HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4); 常用在LED上
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:锁住引脚电平,比如说一个管脚的当前状态是1,当这个管脚电平变化时保持锁定时的值。实例:HAL_GPIO_LockPin(GPIOC, GPIO_PIN_4);
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
功能: 外部中断服务函数,清除中断标志位
实例:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
功能: 中断回调函数,可以理解为中断函数具体要响应的动作。实例:HAL_GPIO_EXTI_Callback(GPIO_PIN_4);
uart的发送接收函数:
UART结构体定义
UART_HandleTypeDef huart1;
UART的名称定义,这个结构体中存放了UART所有用到的功能,后面的别名就是我们所用的uart串口的别名,默认为huart1
1、串口发送/接收函数
HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收
这几个函数的参数基本都是一样的,我们挑两个讲解一下串口发送数据:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要发送的数据
Size 发送的字节数
Timeout 最大发送时间,发送数据超过该时间退出发送
举例: HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff); //串口发送三个字节数据,最大传输时间0xffff
中断接收数据:HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口中断接收,以中断方式接收指定长度数据。
大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)。如果使用该函数和回调函数则需要在串口初始化的时刻就进行一次调用开启中断参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 接收到的数据存放地址
Size 接收的字节数
举例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中断接收一个字符,存储到value中
2、串口中断函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数
串口接收中断回调函数:HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
举例: HAL_UART_RxCpltCallback(&huart1){ //用户设定的代码 }
串口中断处理函数HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用
如果接收数据,则会进行接收中断处理函数
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果发送数据,则会进行发送中断处理函数/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
3串口查询函数HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌
举例:
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX) //检测UART发送结束
重定义printf:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;}
重定义scanf:
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
原子的重定义printf:
int fputc (int ch,FILE *f)
{
while(USART2->SR(0X40) == 0);
USART2->DR = (uint8_t)ch;
return ch;
}
这是直接对寄存器操作。
定时器操作:
HAL_TIM_IRQHandler(&htim2);
定时器中断处理函数 在stm32f4xx_it.c的 TIM2_IRQHandler()定时器中断服务函数中这个函数的具体作用是判断中断是否正常,然后判断产生的是哪一类定时器中断(溢出中断/PWM中断.....),然后进入相应的中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
在HAL库中,每进行完一个中断,并不会立刻退出,而是会进入到中断回调函数中,这里我们是使用定时器溢出中断回调函数
void TIM3_IRQHandler(void) 首先进入中断函数
HAL_TIM_IRQHandler(&htim2);之后进入定时器中断处理函数
判断产生的是哪一类定时器中断(溢出中断/PWM中断.....) 和定时器通道
void HAL_TIM_PeriodElapsedCallback(&htim2); 进入相对应中断回调函数
在中断回调函数中添加用户代码
你也可以在在stm32f1xx_it.c中找到中断回调函数
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
pwm输出:
SMT32F1系列共有8个定时器:
高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。
SMT32F4系列共有15个定时器:
高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5、TIM9~TIM14);基本定时器(TIM6、TIM7)。
PWM频率:
Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)
- arr 是计数器值
- psc 是预分频值
占空比:
- duty circle = TIM3->CCR1 / arr(单位:%)
- TIM3->CCR1 用户设定值
arr=499,TIM3->CCR1=250 则pwm的占空比为50%
使能TIM3的PWM Channel1 输出。
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
修改占空比:
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwmVal);
ADC:
STM32一共有3个ADC ADC1,ADC2,ADC3
F4的ADC支持12位,10位,8位和6位精度,F1只支持12位
F1和F4都具有3个ADC,F1可提供21个输入通道,F4可以提供24个输入通道。
F1的ADC最大采样频率为1Msps,2路交替采样可到2Msps(F1不支持3路交替采样)。F4的ADC最大采样频率为2.4Msps,3路交替采样可到7.2Msps
在ADC初始化之后加上AD校准函数
MX_ADC1_Init(); HAL_ADCEx_Calibration_Start(&hadc1); //AD校准
在循环中:
HAL_ADC_Start(&hadc1); //启动ADC转换
HAL_ADC_PollForConversion(&hadc1, 50); //等待转换完成,50为最大等待时间,单位为ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
ADC_Value = HAL_ADC_GetValue(&hadc1); //获取AD值}中断读取:
如果使能了ADC转换结束中断,并且使能了定时器中断,可以这样写:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器中断回调
{
HAL_ADC_Start_IT(&hadc1); //定时器中断里面开启ADC中断转换,1ms开启一次采集
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //ADC转换完成回调
{
HAL_ADC_Stop_IT(&hadc1); //关闭ADC
HAL_TIM_Base_Stop_IT(&htim3); //关闭定时器
AD_Value=HAL_ADC_GetValue(&hadc1); //获取ADC转换的值printf("ADC1 Reading : %d \r\n",AD_Value);
printf("%.4f V\r\n",(AD_Value*3.3/4096)); //串口打印电压信息
HAL_TIM_Base_Start_IT(&htim3); //开启定时器
}开启ADC 3种模式 ( 轮询模式 中断模式 DMA模式 )
• HAL_ADC_Start(&hadcx); //轮询模式开启ADC
• HAL_ADC_Start_IT(&hadcx); //中断轮询模式开启ADC
• HAL_ADC_Start_DMA(&hadcx); //DMA模式开启ADC关闭ADC 3种模式 ( 轮询模式 中断模式 DMA模式 )
• HAL_ADC_Stop()
• HAL_ADC_Stop_IT()
• HAL_ADC_Stop_DMA()ADC校准函数 :
• HAL_ADCEx_Calibration_Start(&hadcx);
F4系列不支持
读取ADC转换值
• HAL_ADC_GetValue()
等待转换结束函数
• HAL_ADC_PollForConversion(&hadc1, 50);
第一个参数为那个ADC,第二个参数为最大等待时间
ADC中断回调函数
• HAL_ADC_ConvCpltCallback()转换完成后回调,DMA模式下DMA传输完成后调用
规则通道及看门狗配置
• HAL_ADC_ConfigChannel() 配置规则组通道
• HAL_ADC_AnalogWDGConfig()
DAC:
/* IO operation functions *****************************************************/
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel); //开启DAC输出
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef* hdac, uint32_t Channel); //关闭DAC输出
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment); //需要函数中不断开启 //开启DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel); //关闭DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data); //设置DAC输出值
uint32_t HAL_DAC_GetValue(DAC_HandleTypeDef* hdac, uint32_t Channel); //获取DAC输出值在main()主函数中设置DAC输出的数据为12位右对齐,DAC输出为2048,并使能DAC1输出通道
/* USER CODE BEGIN 2 */
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
/* USER CODE END 2 */HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);
功能:设置DAC的输出值
参数一: DAC结构体名
参数二: 设置DAC通道
参数三: 设置DAC对齐方式
参数四: 设置输出电压值 12位最大位4095HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
功能:开启DAC输出
参数一: DAC结构体名
参数二: DAC通道
DMA:
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。不经过cpu.
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
串口DMA发送数据:
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
1
功能:串口通过DMA发送指定长度的数据。参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要发送的数据
Size 发送的字节数
举例:HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff)); //串口发送Senbuff数组
1
串口DMA接收数据:HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
1
功能:串口通过DMA接受指定长度的数据。参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要存放接收数据的数组
Size 接受的字节数
举例:HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Recbuff, sizeof(Recbuff)); //串口发送Senbuff数组
1
串口DMA恢复函数HAL_UART_DMAResume(&huart1)
1
作用: 恢复DMA的传输返回值: 0 正在恢复 1 完成DMA恢复
IIC:
IIC一共有只有两个总线: 一条是双向的数据线SDA,一条是串行时钟线SCL
所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每一个设备都对应一个唯一的地址。
- 起始信号:SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。
- 停止信号:SCL保持高电平。SDA由低电平变为高电平。
IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。
也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,
应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
时序操作:MCU先发送一个开始信号(START)启动总线
接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
等待应答信号(ACK)
发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储>在哪个位置,此刻写的就是哪个地址。
发送要存储的数据第一字节、第二字节、…注意在写数据的过程中,E2PROM每个字节都会>回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。
发送结束信号(STOP)停止总线注意:
在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以IIC写函数
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
1
功能:IIC写数据
参数:*hi2c 设置使用的是那个IIC 例:&hi2c2
DevAddress 写入的地址 设置写入数据的地址 例 0xA0
*pData 需要写入的数据
Size 要发送的字节数
Timeout 最大传输时间,超过传输时间将自动退出传输函数
IIC读函数HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
1
功能:IIC读一个字节
参数:*hi2c: 设置使用的是那个IIC 例:&hi2c2
DevAddress: 写入的地址 设置写入数据的地址 例 0xA0
*pDat:a 存储读取到的数据
Size: 发送的字节数
Timeout: 最大读取时间,超过时间将自动退出读取函数
举例:
HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;
1
发送两个字节数据IIC写数据函数
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/* 第1个参数为I2C操作句柄
第2个参数为从机设备地址
第3个参数为从机寄存器地址
第4个参数为从机寄存器地址长度
第5个参数为发送的数据的起始地址
第6个参数为传输数据的大小
第7个参数为操作超时时间 */
功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址参数:
*hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
MemAddSize: 从机寄存器地址字节长度 8位或16位
写入数据的字节类型 8位还是16位
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
在stm32f1xx_hal_i2c.h中有定义*pData: 需要写入的的数据的起始地址
Size: 传输数据的大小 多少个字节
Timeout: 最大读取时间,超过时间将自动退出函数
使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。可以传输多个数据
void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)
{
uint8_t TxData[2] = {REG_Address,REG_data};
while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)
{
if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
}
在传输过程,寄存器地址和源数据地址是会自加的。
至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);