目录
- PWM 控制器
- 库函数
- 函数
- 参数
- 宏
- 测试程序
- 独立模式
- main.c
- wm_hal_msp.c
- wm_it.c
- 实验现象
- 多通道同步模式
- main.c
- wm_hal_msp.c
- wm_it.c
- 实验现象
Windows 10 20H2
HLK-W806-V1.0-KIT
WM_SDK_W806_v0.6.0
摘自《W806 芯片设计指导书 V1.0》、《W806 MCU 芯片规格书 V2.0》
PWM 控制器
5 通道 PWM 信号生成功能
2 通道输入信号捕获功能(PWM0 和 PWM4 两个通路)
频率范围:3Hz~160KHz
占空比最大精度:1/256,插入死区的计数器宽度:8bit
库函数
函数
打开wm_pwm.h,有如下的函数声明:
HAL_StatusTypeDef HAL_PWM_Init(PWM_HandleTypeDef *hpwm);
//初始化PWM通道
HAL_StatusTypeDef HAL_PWM_DeInit(PWM_HandleTypeDef *hpwm);
//将初始化之后的通道恢复成默认的状态–各个寄存器复位时的值
void HAL_PWM_MspInit(PWM_HandleTypeDef *hpwm);
//用于开启对应的时钟和复用对应引脚
void HAL_PWM_MspDeInit(PWM_HandleTypeDef *hpwm);
//将对应时钟和引脚恢复成默认的状态–各个寄存器复位时的值
HAL_StatusTypeDef HAL_PWM_Start(PWM_HandleTypeDef *hpwm, uint32_t Channel);
//开始输出波形
HAL_StatusTypeDef HAL_PWM_Stop(PWM_HandleTypeDef *hpwm, uint32_t Channel);
//停止输出波形
HAL_StatusTypeDef HAL_PWM_Duty_Set(PWM_HandleTypeDef *hpwm, uint32_t Channel, uint32_t Duty);
//设置输出波形的占空比
HAL_StatusTypeDef HAL_PWM_Freq_Set(PWM_HandleTypeDef *hpwm, uint32_t Channel, uint32_t Prescaler, uint32_t Period);
//设置输出波形的频率
参数
结构体和枚举类型
typedef struct
{uint32_t Prescaler; /* Specifies the prescaler value used to divide the PWM clock(40MHz).This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */uint32_t CounterMode; /* Specifies the counter mode.This parameter can be a value of @ref PWM_Counter_Mode */uint32_t Period; /* Specifies the period value to be loaded into the PERIODRegister at the next update event.This parameter can be a number between Min_Data = 0x00 and Max_Data = 0xFF. */uint32_t Pulse; /* Specifies the pulse value to be loaded into the Compare Register.This parameter can be a number between Min_Data = 0x00 and Max_Data = 0xFF */uint32_t AutoReloadPreload; /* Specifies the auto-reload preload.This parameter can be a value of @ref TIM_AutoReloadPreload */uint32_t OutMode; /* Specifies the output mode.This parameter can be a value of @ref PWM_Out_Mode*/uint32_t OutInverse; /* Specifies the output polarity.This parameter can be a value of @ref PWM_Out_Inverse */uint32_t Dtdiv; /* Specifies the prescaler value used to divide the dead zone clock(40MHz) when in complementary mode.This parameter can be a value of @ref PWM_DT_DIV */uint32_t Dtcnt; /* Specifies the number of dead time clocks when in complementary mode.This parameter can be a number between Min_Data = 0x00 and Max_Data = 0xFF */} PWM_InitTypeDef;typedef struct
{PWM_TypeDef *Instance;PWM_InitTypeDef Init;uint32_t Channel; /* This parameter can be a value of @ref PWM_Channel */} PWM_HandleTypeDef;
宏参数
#define PWM ((PWM_TypeDef *)PWM_BASE)// PWM_Channel
#define PWM_CHANNEL_0 0x00
#define PWM_CHANNEL_1 0x01
#define PWM_CHANNEL_2 0x02
#define PWM_CHANNEL_3 0x03
#define PWM_CHANNEL_4 0x04
#define PWM_CHANNEL_ALL 0x01F// PWM_Counter_Mode
#define PWM_COUNTERMODE_EDGEALIGNED_UP 0x0 // edge-aligned up mode for capture
#define PWM_COUNTERMODE_EDGEALIGNED_DOWN 0x1 // edge-aligned up mode for out
#define PWM_COUNTERMODE_CENTERALIGNED 0x2 // center-aligned mode for out// PWM_AutoReloadPreload
#define PWM_AUTORELOAD_PRELOAD_DISABLE 0x00 // TIMx_ARR register is not buffered
#define PWM_AUTORELOAD_PRELOAD_ENABLE 0x01 // TIMx_ARR register is buffered// PWM_Out_Mode
#define PWM_OUT_MODE_INDEPENDENT 0x00
#define PWM_OUT_MODE_2SYNC 0x01
#define PWM_OUT_MODE_2COMPLEMENTARY 0x02
#define PWM_OUT_MODE_5SYNC 0x03
#define PWM_OUT_MODE_BREAK 0x04// PWM_Out_Inverse
#define PWM_OUT_INVERSE_DISABLE 0x00
#define PWM_OUT_INVERSE_ENABLE 0x01// PWM_DT_DIV
#define PWM_DTDIV_NONE PWM_DTCR_DTDIV_1
#define PWM_DTDIV_2 PWM_DTCR_DTDIV_2
#define PWM_DTDIV_4 PWM_DTCR_DTDIV_4
#define PWM_DTDIV_8 PWM_DTCR_DTDIV_8
宏
#define IS_PWM_INSTANCE(INSTANCE) (((INSTANCE) == PWM))#define IS_PWM_CHANNELS(__CHANNEL__) (((__CHANNEL__) == PWM_CHANNEL_0) || \((__CHANNEL__) == PWM_CHANNEL_1) || \((__CHANNEL__) == PWM_CHANNEL_2) || \((__CHANNEL__) == PWM_CHANNEL_3) || \((__CHANNEL__) == PWM_CHANNEL_4) || \((__CHANNEL__) == PWM_CHANNEL_ALL))#define IS_PWM_PRESCALER(__PRESCALER__) (((__PRESCALER__) >= 0x0000) && ((__PRESCALER__) <= 0x0FFFF))#define IS_PWM_COUNTER_MODE(__MODE__) (((__MODE__) == PWM_COUNTERMODE_EDGEALIGNED_UP) || \((__MODE__) == PWM_COUNTERMODE_EDGEALIGNED_DOWN) || \((__MODE__) == PWM_COUNTERMODE_CENTERALIGNED))#define IS_PWM_PERIOD(__PERIOD__) (((__PERIOD__) >= 0x00) && ((__PERIOD__) <= 0x0FF))#define IS_PWM_PULSE(__PULSE__) (((__PULSE__) >= 0x00) && ((__PULSE__) <= 0x0FF))#define IS_PWM_AUTORELOADPRELOAD(__AUTORELOAD__) (((__AUTORELOAD__) == PWM_AUTORELOAD_PRELOAD_DISABLE) || \((__AUTORELOAD__) == PWM_AUTORELOAD_PRELOAD_ENABLE))#define IS_PWM_OUTMODE(__MODE__) (((__MODE__) == PWM_OUT_MODE_INDEPENDENT) || \((__MODE__) == PWM_OUT_MODE_2SYNC) || \((__MODE__) == PWM_OUT_MODE_2COMPLEMENTARY) || \((__MODE__) == PWM_OUT_MODE_5SYNC))#define IS_PWM_OUTINVERSE(__INVERSE__) (((__INVERSE__) == PWM_OUT_INVERSE_DISABLE) || \((__INVERSE__) == PWM_OUT_INVERSE_ENABLE))#define IS_PWM_DTDIV(__DIV__) (((__DIV__) == PWM_DTDIV_NONE) || \((__DIV__) == PWM_DTDIV_2) || \((__DIV__) == PWM_DTDIV_4) || \((__DIV__) == PWM_DTDIV_8))#define IS_PWM_DTCNT(__CNT__) (((__CNT__) >= 0) && ((__CNT__) <= 0x0FF))
测试程序
独立模式
main.c
#include <stdio.h>
#include "wm_hal.h"PWM_HandleTypeDef hpwm;static void PWM_Init(void);
void Error_Handler(void);uint32_t ch = PWM_CHANNEL_1;
int main(void)
{int i = 0;SystemClock_Config(CPU_CLK_160M);printf("enter main\r\n");PWM_Init();HAL_PWM_Start(&hpwm, ch);while (1){for (i = 0; i < 200; i++){HAL_PWM_Duty_Set(&hpwm, ch, i);HAL_Delay(20);}for (i = 200 - 1; i >= 0; i--){HAL_PWM_Duty_Set(&hpwm, ch, i);HAL_Delay(20);}}
}/* 输出波形的频率: f = 40MHz / Prescaler / (Period + 1);* 输出波形的占空比: * 沿对齐模式(递减):(Pulse + 1) / (Period + 1)* Pulse >= Period:PWM输出一直为高电平* Pulse < Period :PWM输出高电平宽度为(Pulse + 1),低电平宽度为(Period - Pulse)* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(Period)* * 中间对齐模式 :(2 * Pulse + 1) / (2 * (Period + 1))* Pulse > Period :PWM输出一直为高电平* Pulse <= Period:PWM输出高电平宽度为(2 * Pulse + 1),低电平宽度为(2 * (Period - Pulse) + 1)* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(2 * Period + 1)*/
static void PWM_Init(void)
{// 输出50Hz、占空比25%的波形hpwm.Instance = PWM;hpwm.Init.AutoReloadPreload = PWM_AUTORELOAD_PRELOAD_ENABLE;hpwm.Init.CounterMode = PWM_COUNTERMODE_EDGEALIGNED_DOWN;hpwm.Init.Prescaler = 4000; //0~65535hpwm.Init.Period = 200 - 1; //0~255 40MHz / 4000 / 200 = 50Hz (默认PWM时钟为40MHz)hpwm.Init.Pulse = 200 / 4 - 1; //0~255 25% DUTYhpwm.Init.OutMode = PWM_OUT_MODE_INDEPENDENT;hpwm.Channel = ch;HAL_PWM_Init(&hpwm);
}void Error_Handler(void)
{while (1){}
}void assert_failed(uint8_t *file, uint32_t line)
{printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
wm_hal_msp.c
配置引脚复用。
#include "wm_hal.h"void HAL_MspInit(void)
{}void HAL_PWM_MspInit(PWM_HandleTypeDef *hpwm)
{__HAL_RCC_PWM_CLK_ENABLE();__HAL_AFIO_REMAP_PWM1(GPIOB, GPIO_PIN_1);
}void HAL_PWM_MspDeInit(PWM_HandleTypeDef *hpwm)
{__HAL_RCC_PWM_CLK_DISABLE();HAL_GPIO_DeInit(GPIOB, GPIO_PIN_1);
}
wm_it.c
#include "wm_hal.h"#define readl(addr) ({unsigned int __v = (*(volatile unsigned int *) (addr)); __v;})
__attribute__((isr)) void CORET_IRQHandler(void)
{readl(0xE000E010);HAL_IncTick();
}
实验现象
可见输出的PWM信号为50Hz,占空比在0~100%间变化。
多通道同步模式
需要注意的是
配置占空比的函数是没有关于PWM_CHANNEL_ALL的实现的:
要用PWM_CHANNEL_0才能跑出来
main.c
#include <stdio.h>
#include "wm_hal.h"PWM_HandleTypeDef hpwm;static void PWM_Init(void);
static void GPIO_Init(void);
void Error_Handler(void);uint32_t ch = PWM_CHANNEL_0;int main(void)
{int i = 0;SystemClock_Config(CPU_CLK_160M);printf("enter main\r\n");GPIO_Init();PWM_Init();HAL_PWM_Start(&hpwm, ch);while (1){for (i = 0; i < 200; i++){HAL_PWM_Duty_Set(&hpwm, ch, i);HAL_Delay(20);}for (i = 200 - 1; i >= 0; i--){HAL_PWM_Duty_Set(&hpwm, ch, i);HAL_Delay(20);}}
}static void GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIO_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_16;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_16, GPIO_PIN_SET);
}/* 输出波形的频率: f = 40MHz / Prescaler / (Period + 1);* 输出波形的占空比: * 沿对齐模式(递减):(Pulse + 1) / (Period + 1)* Pulse >= Period:PWM输出一直为高电平* Pulse < Period :PWM输出高电平宽度为(Pulse + 1),低电平宽度为(Period - Pulse)* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(Period)* * 中间对齐模式 :(2 * Pulse + 1) / (2 * (Period + 1))* Pulse > Period :PWM输出一直为高电平* Pulse <= Period:PWM输出高电平宽度为(2 * Pulse + 1),低电平宽度为(2 * (Period - Pulse) + 1)* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(2 * Period + 1)*/
static void PWM_Init(void)
{// 输出50Hz、占空比25%的波形hpwm.Instance = PWM;hpwm.Init.AutoReloadPreload = PWM_AUTORELOAD_PRELOAD_ENABLE;hpwm.Init.CounterMode = PWM_COUNTERMODE_EDGEALIGNED_DOWN;hpwm.Init.Prescaler = 4000; //0~65535hpwm.Init.Period = 200 - 1; //0~255 40MHz / 4000 / 200 = 50Hz (默认PWM时钟为40MHz)hpwm.Init.Pulse = 200 / 4 - 1; //0~255 25% DUTYhpwm.Init.OutMode = PWM_OUT_MODE_5SYNC;hpwm.Channel = ch;HAL_PWM_Init(&hpwm);
}void Error_Handler(void)
{while (1){}
}void assert_failed(uint8_t *file, uint32_t line)
{printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
wm_hal_msp.c
#include "wm_hal.h"void HAL_MspInit(void)
{}void HAL_PWM_MspInit(PWM_HandleTypeDef *hpwm)
{__HAL_RCC_PWM_CLK_ENABLE();__HAL_AFIO_REMAP_PWM0(GPIOB, GPIO_PIN_0);__HAL_AFIO_REMAP_PWM1(GPIOB, GPIO_PIN_1);__HAL_AFIO_REMAP_PWM2(GPIOB, GPIO_PIN_2);__HAL_AFIO_REMAP_PWM3(GPIOB, GPIO_PIN_3);__HAL_AFIO_REMAP_PWM4(GPIOB, GPIO_PIN_16);
}void HAL_PWM_MspDeInit(PWM_HandleTypeDef *hpwm)
{__HAL_RCC_PWM_CLK_DISABLE();HAL_GPIO_DeInit(GPIOB, GPIO_PIN_0);HAL_GPIO_DeInit(GPIOB, GPIO_PIN_1);HAL_GPIO_DeInit(GPIOB, GPIO_PIN_2);HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3);HAL_GPIO_DeInit(GPIOB, GPIO_PIN_16);
}
wm_it.c
#include "wm_hal.h"#define readl(addr) ({unsigned int __v = (*(volatile unsigned int *) (addr)); __v;})
__attribute__((isr)) void CORET_IRQHandler(void)
{readl(0xE000E010);HAL_IncTick();
}
实验现象
可见3个LED都实现了类似呼吸灯的效果