当前位置:网站首页>潘多拉 IOT 开发板学习(HAL 库)—— 实验9 PWM输出实验(学习笔记)
潘多拉 IOT 开发板学习(HAL 库)—— 实验9 PWM输出实验(学习笔记)
2022-07-06 03:40:00 【小辉_Super】
本文代码参考正点原子例程
文章目录
实验功能
例程源码:(main.c)
本实验实现的功能:用 PWM 波控制电机转动,通过控制 PWM 的占空比,从而控制电机的转速。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "pwm.h"
/********************************************************************************* ___ _ _____ _____ _ _ _____ _____ _ __ / _ \ | | |_ _|| ___|| \ | ||_ _|| ___|| | / / / /_\ \| | | | | |__ | \| | | | | |__ | |/ / | _ || | | | | __| | . ` | | | | __| | \ | | | || |_____| |_ | |___ | |\ | | | | |___ | |\ \ \_| |_/\_____/\___/ \____/ \_| \_/ \_/ \____/ \_| \_/ * ****************************************************************************** * 正点原子 Pandora STM32L475 IoT开发板 实验9 * PWM输出实验 HAL库版本 * 技术支持:www.openedv.com * 淘宝店铺:http://openedv.taobao.com * 关注微信公众平台微信号:"正点原子",免费获取STM32资料。 * 广州市星翼电子科技有限公司 * 作者:正点原子 @ALIENTEK * ******************************************************************************/
int main(void)
{
u8 time = 0;
u8 speed = 1; //速度控制 0:加速 1:减速
u8 dir = 1; //正反转标志 0:电机正转 1:电机反转
u16 pwmval = 500; //默认值
HAL_Init();
SystemClock_Config(); //初始化系统时钟为80M
delay_init(80); //初始化延时函数 80M系统时钟
uart_init(115200); //初始化串口,波特率为115200
LED_Init(); //初始化LED
PWM_Init(1000 - 1, 80 - 1); //TIM2时钟频率 80M/80=1M 计数频率1M/1000=1KHZ 默认占空比为50%
while(1)
{
if(speed) pwmval += 5;
else pwmval -= 5;
if(pwmval >= 1000) speed = 0;
if(pwmval <= 500)
{
speed = 1;
dir = dir ^ 0x01; //速度变为最小时改变电机方向
}
if(dir)
{
TIM_SetTIM2Compare1(pwmval);
TIM_SetTIM2Compare2(0);
}
else
{
TIM_SetTIM2Compare1(0);
TIM_SetTIM2Compare2(pwmval);
}
time++;
if( time%20 == 0 )
LED_B_TogglePin;
delay_ms(15);
}
}
代码剖析
HAL_Init()
HAL_Init()
定义如下:(具体实现的功能见注释)
HAL_StatusTypeDef HAL_Init(void)
{
HAL_StatusTypeDef status = HAL_OK;
/* 配置 Flash 预取,指令缓存,数据缓存 */
/* 默认配置为:预存取关闭 指令缓存和数据缓存开启 */
#if (INSTRUCTION_CACHE_ENABLE == 0) // Flash开启预存取配置,能加速CPU代码的执行
__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */
#if (DATA_CACHE_ENABLE == 0)
__HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */
#if (PREFETCH_ENABLE != 0)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); // 配置 NVIC 优先级分组
/* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) //初始化滴答定时器,时钟节拍设置为 1ms
{
status = HAL_ERROR;
}
else
{
/* Init the low level hardware */
HAL_MspInit(); // 低速的外设初始化,比如 GPIO、中断等的设置(使用 STM32CubeMx 生成代码时会将低速外设初始
// 代码当这类函数里,其他情况下可以忽略这个函数
}
/* Return function status */
return status;
}
HAL_InitTick()
滴答定时器时钟节拍初始化函数
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
HAL_StatusTypeDef status = HAL_OK;
/*Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U) // 系统时钟/1000,中断周期为 1ms
{
status = HAL_ERROR;
}
else
{
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0); // 将滴答定时器的中断优先级设置为最高
}
/* Return function status */
return status;
}
SystemClock_Config()
SystemClock_Config()
函数定义如下:(具体实现的功能见注释,仅供参考)
void SystemClock_Config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStruct; // 定义振荡器初始化结构体变量
RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义时钟初始化结构体变量
__HAL_RCC_PWR_CLK_ENABLE(); // 使能电源控制时钟
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 将 HSE(外部高速时钟)作为时钟源
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 开启 HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL(锁相环)
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 将 HSE 作为 PLL 的时钟源
RCC_OscInitStruct.PLL.PLLM = 1; // PLL-VCO 输入时钟分频系数,1 表示 2 分频(8 / 2 = 4M,本开发板外部晶振频率为 8MHz)
RCC_OscInitStruct.PLL.PLLN = 20; // PLL-VCO 输出时钟倍频系数,4 * 20 = 80M,即输出时钟频率为 80MHz
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI 时钟的分频系数
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC1, RNG 和 USB 的时钟分频系数
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主系统时钟的分频系数
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); //初始化时钟配置
if(ret != HAL_OK) while(1);
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; // 将所有时钟同时进行配置
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 将 PLL 作为系统时钟源
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1 不分频
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 不分频
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // 配置时钟初始结构体变量,
//使用 Flash 延迟4,等待状态(延迟)的数量需要根据CPU时钟(HCLK)的频率和内部电压范围来选择,具体怎么
//选需要参考芯片手册
if(ret != HAL_OK) while(1);
/*Configure the main internal regulator output voltage*/
ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //内部寄存器输出电压配置
// 下面是 HAL_PWREx_ControlVoltageScaling() 函数说明的部分内容:
//PWR_REGULATOR_VOLTAGE_SCALE1 Regulator voltage output range 1 mode, typical output voltage
// at 1.2 V, system frequency up to 80 MHz.
if(ret != HAL_OK) while(1);
}
delay_init()
滴答定时器已经在 HAL_Init()
中进行了初始化,下面这个函数实际上就是给 fac_us
赋了一个值(目前暂不涉及操作系统,其他代码暂时不去研究)。
static u32 fac_us = 0; //us延时倍乘数
/** * @brief 初始化延迟函数,SYSTICK的时钟固定为AHB时钟 * * @param SYSCLK 系统时钟频率 * * @return void */
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us = SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload = SYSCLK; //每秒钟的计数次数 单位为K
reload *= 1000000 / delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最大值:16777216,在80M下,约209.7ms左右
fac_ms = 1000 / delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD = reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
LED_Init()
/** * @brief LED IO初始化函数 * * @param void * * @return void */
void LED_Init(void)
{
/* LED-B PE9 LED-G PE8 LED-R PE7 */
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
}
PWM_Init()
本实验使用 TIM2 来输出 PWM,下面是 TIM2 的初始化函数。TIM2 的时钟频率为 80MHz,main() 函数中分频系数为 80 - 1,80M / 80 = 1MHz,对应的计时周期就是 1us;重载值为 1000 - 1,那么 PWM 的周期就是 1ms。
/** * @brief TIM2 PWM输出初始化函数 * * @param arr 自动重装值 * @param psc 时钟预分频数 * * @return void */
void PWM_Init(u16 arr, u16 psc)
{
TIM2_Handler.Instance = TIM2; //定时器2
TIM2_Handler.Init.Prescaler = psc; //定时器分频
TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
TIM2_Handler.Init.Period = arr; //自动重装载值
TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM2_Handler); //初始化PWM
TIM2_CHnHandler.OCMode = TIM_OCMODE_PWM1; //模式选择PWM1
TIM2_CHnHandler.Pulse = arr / 2; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
TIM2_CHnHandler.OCPolarity = TIM_OCPOLARITY_HIGH; //输出比较极性为低
HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CHnHandler, TIM_CHANNEL_1); //配置TIM2通道1
HAL_TIM_PWM_Start(&TIM2_Handler, TIM_CHANNEL_1); //开启PWM通道1
HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CHnHandler, TIM_CHANNEL_2); //配置TIM2通道2
HAL_TIM_PWM_Start(&TIM2_Handler, TIM_CHANNEL_2); //开启PWM通道2
}
HAL_TIM_Base_MspInit()
上面的 HAL_TIM_PWM_Init()
函数会调用定时器(PWM)的底层驱动初始化函数,该函数中配置了 PWM 通道对应的 GPIO。
/** * @brief 定时器底层驱动,时钟使能,引脚配置,此函数会被HAL_TIM_PWM_Init()调用 * * @param htim 定时器句柄 * * @return void */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM2_CLK_ENABLE(); //使能定时器2
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin = GPIO_PIN_0 | GPIO_PIN_1; //PA0.1
GPIO_Initure.Mode = GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate = GPIO_AF1_TIM2; //PA1复用为TIM2_CH1.CH2
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
}
TIM_SetTIM2Compare1()
TIM2 通道 1 占空比设置函数,直接向 CCR1 寄存器填值。
/** * @brief 设置TIM2通道1的占空比 * * @param compare 比较值 * * @return void */
void TIM_SetTIM2Compare1(u32 compare)
{
TIM2->CCR1 = compare;
}
通道 2 的占空比设置原理和通道 1 相同。
/** * @brief 设置TIM2通道2的占空比 * * @param compare 比较值 * * @return void */
void TIM_SetTIM2Compare2(u32 compare)
{
TIM2->CCR2 = compare;
}
HAL 库中也自带了占空比配置函数,同样是写 CCR 寄存器。
/** * @brief Set the TIM Capture Compare Register value on runtime without calling another time ConfigChannel function. * @param __HANDLE__ TIM handle. * @param __CHANNEL__ TIM Channels to be configured. * This parameter can be one of the following values: * @arg TIM_CHANNEL_1: TIM Channel 1 selected * @arg TIM_CHANNEL_2: TIM Channel 2 selected * @arg TIM_CHANNEL_3: TIM Channel 3 selected * @arg TIM_CHANNEL_4: TIM Channel 4 selected * @arg TIM_CHANNEL_5: TIM Channel 5 selected * @arg TIM_CHANNEL_6: TIM Channel 6 selected * @param __COMPARE__ specifies the Capture Compare register new value. * @retval None */
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \ (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\ ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\ ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\ ((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)) :\ ((__CHANNEL__) == TIM_CHANNEL_5) ? ((__HANDLE__)->Instance->CCR5 = (__COMPARE__)) :\ ((__HANDLE__)->Instance->CCR6 = (__COMPARE__)))
LED 操作函数
LED 的控制函数是宏函数,分别用到了 HAL_GPIO_WritePin()
和 HAL_GPIO_TogglePin()
两个库函数。
//RGB接口定义
#define LED_R(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_RESET))
#define LED_R_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_7) //LED_R电平翻转
#define LED_G(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_RESET))
#define LED_G_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_8) //LED_G电平翻转
#define LED_B(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET))
#define LED_B_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_9) //LED_B电平翻转
delay_ms()
delay_ms()
里运行的是 delay_us()
, delay_us()
通过滴答定时器实现延时。上面的 delay_init()
已经将 fac_us 设置为了 80,滴答定时器计数 80 次需要用 10-6 秒(系统时钟为 80MHz),即 1us。
/** * @brief 延时毫秒(ms)函数 * * @param nms 需要延时多少毫秒 * * @return void */
void delay_ms(u16 nms)
{
u32 i;
for(i = 0; i < nms; i++) delay_us(1000);
}
/** * @brief 延时微秒(us)函数 * * @remark nus:0~190887435(最大值即2^32/[email protected]_us=22.5) * * @param nus 需要延时多少微秒 * * @return void */
void delay_us(u32 nus)
{
u32 ticks;
u32 told, tnow, tcnt = 0;
u32 reload = SysTick->LOAD; //LOAD的值
ticks = nus * fac_us; //需要的节拍数
told = SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)tcnt += told - tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
边栏推荐
- Mapping between QoE and KQI
- The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
- mysql从一个连续时间段的表中读取缺少数据
- js凡客banner轮播图js特效
- 出现Permission denied的解决办法(750权限谨慎使用)
- Computer graduation project asp Net fitness management system VS development SQLSERVER database web structure c programming computer web page source code project
- MADDPG的pythorch实现——(1)OpenAI MADDPG环境配置
- Princeton University, Peking University & UIUC | offline reinforcement learning with realizability and single strategy concentration
- Shell 传递参数
- Tidb ecological tools (backup, migration, import / export) collation
猜你喜欢
SAP ALV cell level set color
pytorch加载数据
施努卡:视觉定位系统 视觉定位系统的工作原理
深入刨析的指针(题解)
[meisai] meisai thesis reference template
Research on cooperative control of industrial robots
Safety science to | travel, you must read a guide
阿里测试师用UI自动化测试实现元素定位
canvas切积木小游戏代码
An article will give you a comprehensive understanding of the internal and external components of "computer"
随机推荐
1.16 - 校验码
cookie,session,Token 这些你都知道吗?
Quick sort function in C language -- qsort
Quartz misfire missed and compensated execution
数据分析——seaborn可视化(笔记自用)
Cubemx transplantation punctual atom LCD display routine
A brief introduction to symbols and link libraries in C language
Esbuild & SWC: a new generation of construction tools
Multi project programming minimalist use case
Svg drag point crop image JS effect
Teach you to build your own simple BP neural network with pytoch (take iris data set as an example)
SWC介绍
An article will give you a comprehensive understanding of the internal and external components of "computer"
UDP reliable transport protocol (quic)
Blue Bridge Cup - day of week
three. JS page background animation liquid JS special effect
SAP ALV单元格级别设置颜色
阿里测试师用UI自动化测试实现元素定位
ASU & OSU | model based regularized off-line meta reinforcement learning
Computer graduation project asp Net fitness management system VS development SQLSERVER database web structure c programming computer web page source code project