HAL库代码基础介绍
PWM
预分频值与自动重装值ARR均要减1
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
//设置占空比
for(int i=0;i<100;i++)
{
_HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,i);
HAL_Delay(10);
}
for(int i=0;i<100;i++)
{
_HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,100-i);
HAL_Delay(10);
}
设置CCR的值
_HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,CCR值);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PWM中断
比较中断:当CNT等于CCR设定值就中断
1.在NVIC Settings 中,勾选 TIMx global interrupt
2.启动中断的函数:HAL_TIM_PWM_Start_IT(&htimX, TIM_CHANNEL_Y);
3.编写回调函数:
/* USER CODE BEGIN 4 */
* @param htim 定时器句柄指针
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
// 1. 先判断是哪个定时器触发的(例如TIM2)
if (htim->Instance == TIM2)
{
// 2. 判断是哪个通道(例如通道2)
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
// 3.中断时真正想干的事
}
}
}
/* USER CODE END 4 */
PWM更新中断:CNT值等于自动重装值ARR值(即周期值)时,归零瞬间触发中断
1.在NVIC Settings 中,勾选 TIMx global interrupt
2.启动中断的函数:HAL_TIM_Base_Start_IT(&htimX); //与比较中断不一样
3.编写回调函数:
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是哪个定时器
if (htim->Instance == TIM2)
{
// 中断时真正想干的事
}
}
/* USER CODE END 4 */
///////////////////////////////////////////////////////////////
捕获中断
1.TIM的Slave Mode: Reset Mode
Trigger Source : TI1FP1
定时器通道的模式选为 “Input Capture direct mode”(输入捕获直接模式)。
设置极性(Rising Edge/ Falling Edge)
在 NVIC Settings 中勾选 TIMx global interrupt
2.启动中断函数:HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
3.编写回调函数:
/* USER CODE BEGIN 4 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
// 1. 判断定时器
if (htim->Instance == TIM2)
{
// 2. 判断通道(如果只用1个通道可不加判断,但建议加上)
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
// 3. 把刚锁存到CCR里的时间戳读取出来,也就是外部信号跳变时的CNT值
uint32_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)+1;
// 4. 编写逻辑,比如计算频率、切换边沿极性等
freq = fc/capture
}
}
}
/* USER CODE END 4 */
PWMI模式
在上面的基础上加一个通道,设置为: Input Capture indirect mode
Polarity Selection : Falling Edge
两个通道都启动:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
int32_t freq
uint32_t duty
uint32_t capture
/* USER CODE BEGIN 4 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
//上升沿触发中断
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)+1;
freq = fc/capture
}
//下降沿触发中断
{
uint32_t capture2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2)+1;
duty = capture2 *100/capture //*100是因为是百分比
}
}
}
/* USER CODE END 4 */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ADC
//使用前校准,放在初始化处
HAL_ADCEx_Calibration_Start(&hadc1);
//开启ADC
HAL_ADC_Start(&hadc1);
//等待转换
HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);
HAL_MAX_DELAY是等待的最大时间
//获取结果
int16_t value=0;
float voltage=0.0;
value = HAL_ADC_GetValue(&hadc1);
voltage=(float)value/4095.0*3.3;
//显示小数的方法
OLED_ShowNum(2,9,(uint32_t)voltage,1);
//在2行9列显示一位整数
OLED_ShowNum(2, 11,((uint16_t)(voltage*100))%100, 2);
//在2行11列显示小数点后的两位
ADC多通道
//数组,用于存放ADC数据
uint16_t values[4]={0};
//放于while处
for(uint8_t i=0; i<4;i++)
{
HAL_ADC_Start(&hadc1);
if(HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY)==HAL_OK)
{
values[i]=HAL_ADC_GetValue(&hadc1);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DMA配置步骤
启用DMA时钟、初始化DMA通道、配置源地址和目的地址、设置传输方向和数据大小、配置传输模式(循环/正常)、设置优先级、启用DMA中断(看是否需要)、启用DMA通道
//初始化
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
//启动DMA
HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t*)&DataA,(uint32_t*)&DataB,4);
(通道、源地址、目标地址)
//等待传输完成
HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1,HAL_DMA_FULL_TRANSFER,HAL_MAX_DELAY);
HAL_MAX_DELAY是设置的最长等待时间
//等待传输完成
HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel1,HAL_DMA_FULL_TRANSFER,HAL_MAX_DELAY);
循环模式时
//初始化
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
hdma_memtomem_dma1_channel1.init.Mode=DMA_CIRCULAR;
HAL_DMA_Init(&hdma_memtomem_dma1_channel1);
HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t*)&DataA,(uint32_t*)&DataB,4);
4代表要传输的数据单元个数
///////////////////////////////////////////////////////////////
DMA+多通道ADC
方法1:
在CODE BEGIN0中
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc==&hadc1)
{
//ADC转换完成时想执行的操作
OLED_ShowNum(1,1,value[0],5);
}
}
while中:
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)values,4); 数据单元个数为4
HAL_Delay(1000);
方法二:ADC连续+DMA循环
设置ADC Continuous Conversion Mode 为 Enabled
设置DMA Mode为Circular
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)values,4);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
USART
Mode为Asynchronous
发送:
HAL_UART_Transmit(huart1,&byteNumber,1,HAL_MAX_DELAY);
(句柄,要发送的数据地址,要发送的字节数,最大延迟时间)
该函数发送成功会返回一个值:
HAL_OK 表示发送成功
HAL_BUSY 表示正常发送数据,无法立即发送
HAL_TIMEOUT 表示发送超时
HAL_ERROR 表示发生错误
接受:
在PV处:
uint8_t receiveData[5];
在while处:
HAL_UART_Receive(&huart1,receiveData,5,HAL_MAX_DELAY);
receiveData是接收数据的数组
USART中断模式
在nvic中打开usart的中断
在begin中:
HAL_UART_Receive_IT(&huart1,receiveData,sizeof(receiveData)); //接收,启动中断的函数
添加中断函数
void HAL_USART_RxCpltCallback
{
HAL_UART_Transmit_IT(huart1,receiveData,sizeof(receiveData));
HAL_UART_Receive_IT(&huart1,receiveData,sizeof(receiveData)); //重新开始中断
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IIC
///////////////////////////////////////////////////////////////
软件IIC
//引脚配置
#define W_SCL(x) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,x)
#define W_SDA(x) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,x)
//引脚初始化
void MyI2C_Init(void)
{
MX_GPIO_Init();
W_SCL(1);
W_SDA(1);
}
uint8_t MyI2C_R_SDA (void)
{
uint8_t BitValue;
BitValue = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_11);
return BitValue ;
}
void MyI2C_Start(void)
{
W_SDA(1);
W_SCL(1);
W_SDA(0);
W_SCL(0);
}
void MyI2C_Stop(void)
{
W_SDA(0);
W_SCL(1);
W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for ( i = 0; i <8; i++)
{
W_SDA(!!( Byte & (0x80 >> i)));
W_SCL(1);
W_SCL(0);
}
W_SCL(1); //额外的一个时钟,不处理应答信号
W_SCL(0);
}
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00 ;
W_SDA(1);
for ( i = 0; i <8; i++)
{
W_SCL(1);
if (MyI2C_R_SDA()){Byte |= (0x80 >> i);}
W_SCL(0);
}
return Byte
}
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit ;
W_SDA(1);
W_SCL(1);
AckBit =MyI2C_R _SDA();
W_SCL(0);
return AckBit;
}
///////////////////////////////////////////////////////////////
硬件IIC
对于有内部寄存器的设备:
HAL_I2C_Mem_Write(&hi2c1, 设备地址,目标寄存器地址,寄存器地址长度(比如1,代表1字节),要写入的数据值,数据字节长度,超时时间);
HAL_I2C_Mem_Read(&hi2c1, 设备地址,寄存器起始地址,寄存器地址长度(比如1,代表1字节),存放读取数据的数组,读取的字节数,超时时间);
对于没有内部寄存器,通过命令字节或者纯数据流的设备:
用: HAL_I2C_Master_Transmit 和 HAL_I2C_Master_Receive
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SPI
软件SPI
PA4: CS PA5:CLK PA6:MISO PA7:MOSI
void MySPI_W_SS(uint8_t BitValue)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, BitValue);
}
void MySPI_W_SCK(uint8_t BitValue)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, BitValue);
}
void MySPI_W_MOSI(uint8_t BitValue)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, BitValue);
}
uint8_t MySPI_R_MISO(void)
{
return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6);
}
void MySPI_Init(void)
{
MX_GPIO_Init(); //4引脚初始默认高电平,其余5和7为低电平 457为推挽输出,6为浮空输入或上拉输入
MySPI_W_SS(1);
MySPI_W_SCK(0);
}
void MySPI_Start(void)
{
MySPI_W_SS(0);
}
void MySPI_Stop(void)
{
MySPI_W_SS(1);
}
//使用SPI模式0来交换传输一个字节
void MySPI_SwapByte(uint8_t ByteSend)
{
uint8_t i , ByteReceive = 0x00 ;
for ( i = 0; i <8; i++)
{
MySPI_W_MOSI(!!(ByteSend & (0x80 >>i)));
MySPI_W_SCK(1);
if (MySPI_R_MISO()) {ByteReceive |= (0x80 >> i) ; }
MySPI_W_SCK(0);
}
return ByteReceive;
}
///////////////////////////////////////////////////////////////
硬件SPI
HAL_SPI_Transmit(&hspi1, &cmd, 1,1000); (句柄,命令,字节,等待时间)
HAL_SPI_Transmit(&hspi1, response, 1,1000); (句柄,数组名,数组字节数,等待时间)
void WriteEnable(void)
{
CS_LOW();
HAL_SPI_Transmit(&hspi1,WRITE_ENABLE, 1, 1000 );
CS_HIGH();
}
//等待忙
void WaitBusy(void)
{
uint32_t Timeout = 100000;
uint8_t status;
CS_LOW();
HAL_SPI_Transmit(&hspi1, READ_STATUS_REGISTER_1, 1,1000);
HAL_SPI_Transmit(&hspi1, &status, 1,1000);
while ((status & 0x01) == 0x01) //表示正在忙
{
Timeout --;
if(Timeout == 0)
{
break; //等待超时
}
}
HAL_SPI_Transmit(&hspi1,WRITE_ENABLE, 1, 1000 );
CS_HIGH();
}
//页编程
void PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
uint8_t cmd[4] = { PAGE_PROGRAM, (uint8_t)((Address >> 16) & 0xFF),(uint8_t)((Address >> 8 ) &0xFF),(uint8_t)(Address & 0xFF)} ;
WriteEnable();
CS_LOW();
HAL_SPI_Transmit(&hspi1,cmd, 4, 1000 );
HAL_SPI_Transmit(&hspi1,DataArray, Count, 1000 );
CS_HIGH();
WaitBusy();
}
//读数据
void ReadData(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
uint8_t cmd[4] = { READ_DATA, (uint8_t)((Address >> 16) & 0xFF),(uint8_t)((Address >> 8 ) &0xFF),(uint8_t)(Address & 0xFF)} ;
CS_LOW();
HAL_SPI_Transmit(&hspi1,cmd, 4, 1000 );
HAL_SPI_Receive(&hspi1,DataArray, Count, 1000 );
CS_HIGH();
}