W801的PWM使用
文章目录
首先感谢我评论区的a7243563用户,我开始的时候写错了,是他指出了我的错误,后来我修改了我的这篇文章。
若有人发现我的错误,希望能够指正,让我在分享中得到进步。谢谢。
1. 开发环境
- 系统:win10
- 开发板:联盛德 HLK-W801开发板
- SDK版本:wm_sdk_w80x_20211115
- IDE:cdk-windows-V2.12.1
2. PWM资源
2.1 w801的PWM控制器功能
根据官方提供的《W801芯片规格书V1.0.pdf》内容可知pwm资源情况如下:
2.2 引脚对应的PWM通道(我对照手册抄的,可能有个别引脚忘写了)
编号 | 引脚 | PWM_0(通道0) | PWM_1(通道1) | PWM_2(通道2) | PWM_3(通道3) | PWM_4(通道4) | PWM_BRAKE(刹车) |
---|---|---|---|---|---|---|---|
2 | PB_26 | 支持 | |||||
3 | PB_25 | 支持 | |||||
4 | PB_24 | 支持 | |||||
8 | PB_20 | 支持 | |||||
9 | PB_19 | 支持 | |||||
18 | BOOTMODE/PA_00 | 支持 | |||||
19 | PA_1 | 支持 | |||||
20 | PA_2 | 支持 | |||||
21 | PA_3 | 支持 | |||||
22 | PA_4 | 支持 | |||||
23 | PA_5 | 支持 | |||||
25 | PA_7 | 支持 | |||||
26 | PA_8 | 支持 | |||||
28 | PA_10 | 支持 | |||||
30 | PA_11 | 支持 | |||||
31 | PA12 | 支持 | |||||
32 | PA_13 | 支持 | |||||
33 | PA_14 | 支持 | |||||
34 | PA_15 | 支持 | |||||
35 | PB_0 | 支持 | |||||
36 | PB_1 | 支持 | |||||
37 | PB_2 | 支持 | |||||
38 | PB_3 | 支持 | |||||
46 | PB_8 | 支持 | |||||
48 | PB_12 | 支持 | |||||
49 | PB_13 | 支持 | |||||
50 | PB_14 | 支持 | |||||
51 | PB_15 | 支持 | |||||
55 | PB_16 | 支持 | |||||
56 | PB_17 | 支持 |
2.3 PWM的工作模式
根据SDK中的《wm_pwm.h》文件代码:
/** pwm work mode for signal generate */
enum tls_pwm_out_mode
{
WM_PWM_OUT_MODE_BRAKE = 0, /**< brake mode */
WM_PWM_OUT_MODE_ALLSYC, /**< all synchronous mode */
WM_PWM_OUT_MODE_2SYC, /**< two channel synchronous mode */
WM_PWM_OUT_MODE_MC, /**< complementary mode */
WM_PWM_OUT_MODE_INDPT /**< independent mode */
};
可知w801支持的PWM模式有:
- 刹车模式
- 全同步模式
- 双通道同步方式
- 互补模式:
- 独立模式:
3. 独立模式(WM_PWM_OUT_MODE_INDPT)
3.1 没有什么注意事项,配置过程比较常规
这是我通过通道3举例:
#include "wm_include.h"
#include "wm_config.h"
#include "wm_pwm.h"
#include <stdint.h>
//task资源
#define S_PWM_TASK_SIZE 256
static OS_STK s_pwm_task_stk[S_PWM_TASK_SIZE];
//pwm参数设置
#define S_CHANNEL 3 //pwm的通道
#define S_CHANNEL_PIN WM_IO_PB_15 //通道映射的引脚
#define _S_PWM_CHANNEL_CONFIG(channel, pin) wm_pwm##channel##_config(pin) //替代wm_pwm3_config(WM_IO_PB_15)函数
#define S_PWM_CHANNEL_CONFIG(channel, pin) _S_PWM_CHANNEL_CONFIG(channel, pin)
#define S_FREQ 50 //设置pwm的频率,单位是hz,设置pwm频率为50hz
#define S_DUTY 19 //占空比,占空比 = S_DUTY/255,其中255由pwm_param.period写死
#define S_NUM 0 //波形周期数(0表示一直运行)
//pwm的初始化
int steering_pwm_init()
{
int ret = -1;
//停止S_CHANNEL通道pwm
tls_pwm_stop(S_CHANNEL);
//使用S_CHANNEL通道pwm,并将S_CHANNEL通道pwm映射到引脚S_CHANNEL_PIN上
/*S_PWM_CHANNEL_CONFIG宏用来替换wm_pwm3_config(WM_IO_PB_15);这样可以使pwm的初始化函数具有通用性*/
S_PWM_CHANNEL_CONFIG(S_CHANNEL, S_CHANNEL_PIN);
//模式采用独立模式函数设置pwm的参数
ret = tls_pwm_init(S_CHANNEL, S_FREQ, S_DUTY, S_NUM);
if(ret != WM_SUCCESS)
return ret;
//启动pwm输出
tls_pwm_start(S_CHANNEL);
return WM_SUCCESS;
}
//设置占空比duty范围[0,255]
void set_steering_duty(uint8_t duty)
{
tls_pwm_duty_set(S_CHANNEL, duty);
}
//设置pwm的周期,freq from 3 to 156250
void set_steering_freq(uint32_t freq)
{
//停止S_CHANNEL通道pwm
tls_pwm_stop(S_CHANNEL);
tls_pwm_freq_set(S_CHANNEL, freq);
//启动pwm输出
tls_pwm_start(S_CHANNEL);
}
//pwm线程
void s_pwm_task(void *sdata)
{
//初始化pwm
steering_pwm_init();
for(;;)
{
tls_os_time_delay(20);
}
}
//创建pwm线程
void create_s_pwm_task(void)
{
tls_os_task_create(NULL, NULL,
s_pwm_task,
NULL,
(void *)s_pwm_task_stk, /* task's stack start address */
S_PWM_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
32,
0);
}
3.2 程序解释
-
独立模式下PWM其实我们重点关心三个参数:
-
计数的时钟clk:决定计数加一需要的时间
-
PWM的周期:决定了PWM的周期
-
计数比较值:决定占空比
占空比= 计数比较值 / PWM的周期计数值
-
-
与其他MCU不同的PWM的计算周期方式
w801在初始化pwm的时候,直接在
tls_pwm_init()
写死了pwm计数周期pwm_param.period = 255
,既pwm的周期默认就是计数255次(所以pwm的分辨率只有1/255,也就是占空比范围[ 1/255, 256/255] ),那就有了疑惑就是怎样改周期呢,它采用了一种神奇的做法,那就是通过变量freq改PWM计数的时钟clk的分频系数,来改变pwm的周期。在初始化pwm的tls_pwm_init函数内部有这样一段代码:
int tls_pwm_init(u8 channel,u32 freq, u8 duty, u8 pnum) { pwm_init_param pwm_param; int ret=-1; tls_sys_clk sysclk; tls_sys_clk_get(&sysclk); memset(&pwm_param, 0, sizeof(pwm_init_param)); pwm_param.period = 255; //写死了pwm周期的计数 pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT; pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP; pwm_param.mode = WM_PWM_OUT_MODE_INDPT; pwm_param.inverse_en = DISABLE; pwm_param.pnum = pnum; pwm_param.pnum_int = DISABLE; pwm_param.duty = duty; pwm_param.channel = channel; pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq; //计算pwm的计数时钟的分频值,下面会重点讲 //printf("clkdiv:%d\n", pwm_param.clkdiv); printf("apbclk:%d\n", sysclk.apbclk); //自己添加 printf("cpuclk:%d\n", sysclk.cpuclk); //自己添加 printf("wlanclk:%d\n", sysclk.wlanclk); //自己添加 ret = tls_pwm_out_init(&pwm_param); // tls_pwm_start(channel); return ret; }
在tls_pwm_init函数通过添加打印信息知道时钟频率
//上面程序的printf输出结果,以兆为单位 apbclk:40 cpuclk:240 wlanclk:160
我在启动时候设置的cpu时钟为240MHZ,sysclk.apbclk经过时钟树变成了 40MHZ。
通过上面程序这句话
pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq; //通过改变时钟的分频系数,来改变pwm计数时钟频率
UNIT_MHZ为SDK定义的宏,目的是将40M转换为40,000,000HZ这个数,如下:
#define UNIT_MHZ (1000000)
同时我们注意到
pwm_param.clkdiv
这个变量的类型为U16
,这也就说明了sysclk.apbclk*UNIT_MHZ/256/freq
要小于65536.因此可以确定freq的最小值。freq不能小于40000000/256/65536 = 2.38,因此freq最小为3HZ.
同时pwm_param.clkdiv不能小于1,因此可以确定freq的最大值,40000000/256/1 = 156250,因此freq最大为156250HZ.
在sdk中找到了一个关于设置freq的函数,注释如下:
/** ... * @param[in] freq frequency, range from 1 to 156250 ... */ void tls_pwm_freq_set(u8 channel, u32 freq) { ... }
这个注释说freq范围是[1, 156250],但是根据刚才的推算,这个应该不准确,实际范围应该是**[3, 156250]**。
同时确定了pwm的最大的周期时间为1/3秒。
-
占空比的设置
通过设置duty值来设置占空比,由于占空比 = duty/ 255; 因此duty不会大于255,正因如此,您会发现 在sdk设置比较值的类型为u8 duty。如下:
void tls_pwm_duty_set(u8 channel, u8 duty)
到此,独立模式就可以运行起来了。
4. 额外的惊喜
通过第二节引脚对应的PWM通道的对照表可知,每个PWM通道可以对应好几个引脚,让人惊喜的是,这些引脚可以同时配置输出,测试程序如下:
//task资源
#define LED_TASK_SIZE 256
static OS_STK led_task_stk[LED_TASK_SIZE];
//pwm参数设置
#define L_CHANNEL 0 //LED的pwm的通道
#define L_CHANNEL_PIN_1 WM_IO_PB_19 //通道映射的引脚1
#define L_CHANNEL_PIN_2 WM_IO_PA_02 //通道映射的引脚2
#define _L_PWM_CHANNEL_CONFIG(channel, pin) wm_pwm##channel##_config(pin)
#define L_PWM_CHANNEL_CONFIG(channel, pin) _L_PWM_CHANNEL_CONFIG(channel, pin)
#define L_FREQ 50 //pwm的频率
#define L_DUTY 19 //占空比,占空比 = S_DUTY/255,其中255由pwm_param.period写死
#define L_NUM 0 //波形周期数(0表示一直运行)
//LED的pwm的初始化
int led_pwm_init()
{
int ret = -1;
tls_pwm_stop(L_CHANNEL);
//使用pwm0,并将两个引脚都映射到一个pwm通道上
L_PWM_CHANNEL_CONFIG(L_CHANNEL, L_CHANNEL_PIN_1);
L_PWM_CHANNEL_CONFIG(L_CHANNEL, L_CHANNEL_PIN_2);
//模式采用独立模式函数设置pwm的参数
ret = tls_pwm_init(L_CHANNEL, L_FREQ, L_DUTY, L_NUM);
if(ret != WM_SUCCESS)
return ret;
//启动pwm输出
tls_pwm_start(L_CHANNEL);
return WM_SUCCESS;
}
//设置PWM占空比duty范围[0-255]
void set_led_duty(uint8_t duty)
{
tls_pwm_duty_set(L_CHANNEL, duty);
}
//led线程
void led_task(void *sdata)
{
//初始化LDE的pwm
led_pwm_init();
for(;;)
{
tls_os_time_delay(100);
}
}
//创建led线程
void create_led_task(void)
{
tls_os_task_create(NULL, NULL,
led_task,
NULL,
(void *)led_task_stk, /* task's stack start address */
LED_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
32,
0);
}
这样您就可以通过pwm0,来得到一样的多路pwm波形,我大概看了一下,应该是对齐的,等有需要的要测一下,看看是不是所有pwm波形都同步。
5. W800手册关于PWM其他模式说明
没有w801的数据手册是个不小的遗憾,庆幸的是w801和w806和w800都采用一样的内核,并且w801是在w800的基础上开发的。所以可以参考w800的寄存器手册《WM_W800_寄存器手册_V2.1.pdf》,有如下相关描述:
关于pwm的其他模式描述
关于捕获的描述
6. 互补模式(WM_PWM_OUT_MODE_MC)
因为w801的手册能提取的信息实在是太少了,于是通过查看w800的寄存器手册,在手册中可以知道,pwm0和pwm1可以配置成互补模式,pwm参数由pwm0控制。pwm2和pwm3可以配置成互补模式,参数由pwm2控制。
6.1 举例用通道0和通道1配置互补模式:
#include "wm_include.h"
#include "wm_config.h"
#include "wm_pwm.h"
#include <stdint.h>
//task资源
#define LED_TASK_SIZE 256
static OS_STK led_task_stk[LED_TASK_SIZE];
//pwm参数设置
#define L_CHANNEL_1 0 //pwm的通道0
#define L_CHANNEL_2 1 //pwm的通道1
#define L_CHANNEL_PIN_1 WM_IO_PA_02 //pwm的通道0映射的引脚
#define L_CHANNEL_PIN_2 WM_IO_PA_03 //pwm的通道1映射的引脚
#define _L_PWM_CHANNEL_CONFIG(channel, pin) wm_pwm##channel##_config(pin)
#define L_PWM_CHANNEL_CONFIG(channel, pin) _L_PWM_CHANNEL_CONFIG(channel, pin)
#define L_FREQ 50 //设置pwm的频率,单位是hz,设置pwm频率为50hz
#define L_DUTY 19 //占空比,占空比 = S_DUTY/255,其中255由pwm_param.period写死
#define L_NUM 0 //波形周期数(0表示一直运行)
//LED的pwm的初始化
int led_pwm_init()
{
int ret = -1;
//停止pwm
tls_pwm_stop(L_CHANNEL_1);
tls_pwm_stop(L_CHANNEL_2);
//并将引脚映射到pwm通道上
L_PWM_CHANNEL_CONFIG(L_CHANNEL_1, L_CHANNEL_PIN_1);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_2, L_CHANNEL_PIN_2);
//模式采用互补模式函数设置pwm的参数,这个函数在下面有写
ret = pwm_mc_mode(L_CHANNEL_1, L_FREQ, L_DUTY, L_NUM);//由通道0来控制pwm的参数
if(ret != WM_SUCCESS)
return ret;
#if 0
//这个注释千万别开,下面会说,这个地方有坑
ret = pwm_mc_mode(L_CHANNEL_2, L_FREQ, L_DUTY, L_NUM);
if(ret != WM_SUCCESS)
return ret;
#endif
//启动pwm输出
tls_pwm_start(L_CHANNEL_1);
tls_pwm_start(L_CHANNEL_2);
return WM_SUCCESS;
}
//设置PWM占空比duty范围[0-255]
void set_led_duty(uint8_t duty)
{
tls_pwm_duty_set(L_CHANNEL_1, duty);//由通道0来控制pwm的参数
}
//led线程
void led_task(void *sdata)
{
//初始化LDE的pwm
led_pwm_init();
for(;;)
{
tls_os_time_delay(100);
}
}
//创建led线程
void create_led_task(void)
{
tls_os_task_create(NULL, NULL,
led_task,
NULL,
(void *)led_task_stk, /* task's stack start address */
LED_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
32,
0);
}
再编写一个互补模式函数设置pwm的参数设置函数
其实就是copy的demo的pwm_demo_mc_mode(channel_used, freq, duty, num)函数,如下:
int pwm_mc_mode(u8 channel,u32 freq, u8 duty, u8 num)
{
pwm_init_param pwm_param;
int ret=-1;
tls_sys_clk sysclk;
tls_sys_clk_get(&sysclk);
memset(&pwm_param, 0, sizeof(pwm_init_param));
pwm_param.period = 255;
pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT;
pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP;
pwm_param.mode = WM_PWM_OUT_MODE_MC;
pwm_param.inverse_en = DISABLE;
pwm_param.pnum = num;
pwm_param.pnum_int = DISABLE;
pwm_param.duty = duty;
pwm_param.channel = channel;
pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq;
pwm_param.dten = DISABLE; //启用死区互补模式
pwm_param.dtclkdiv = 3; //dead zone clock divided value (0~3)
pwm_param.dtcnt = 255; //period number of dead zone time (0~255)
ret = tls_pwm_out_init(&pwm_param);
return ret;
}
同时在有函数pwm_mc_mode
的这个文件中添加
#include "wm_cpu.h"
6.2 程序解释
互补模式指的是两个通道互补,由于pwm0和pwm1可以配置成互补模式,pwm参数由pwm0控制。pwm2和pwm3可以配置成互补模式,参数由pwm2控制。
上文中配置pwm参数的时候注销掉的代码如下:
...
#if 0
//这个注释千万别开,下面会说,这个地方有坑
ret = pwm_mc_mode(L_CHANNEL_2, L_FREQ, L_DUTY, L_NUM);
if(ret != WM_SUCCESS)
return ret;
#endif
...
由于pwm的参数由宏L_CHANNEL_1代表的pwm0控制,所以配置L_CHANNEL_2的pwm参数是多余的,但是您可能想上面那段代码加上也无所谓,大不了没效果,结果就傻眼了。因为在pwm_mc_mode函数会调取sdk下的tls_pwm_out_mode_config
函数(在wm_pwm.c
中定义的),这个函数里面有这样的一段代码,如下:
...
else if (WM_PWM_OUT_MODE_MC == mode)
{
if (channel != 0 && channel != 2)
return WM_FAILED;
tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) & ~(0x1800<<channel));
tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & ~BIT(6));
tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & ~BIT(14 + channel / 2));
tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(0 + channel / 2));
}
...
这个代码说明当WM_PWM_OUT_MODE_MC模式下设置pwm通道pwm1或pwm3的时候,将返回FAILED。这将导致上面那个注释掉的程序返回值ret返回FAILED。结果led_pwm_init()函数的 tls_pwm_start(L_CHANNEL_1)和tls_pwm_start(L_CHANNEL_2)没执行,结果就是pwm没输出。
互补模式还有一个重要的功能就是配置死区,在pwm_mc_mode函数可以配置死区。具体请参考W800的寄存器手册。
7. 双通道同步方式(WM_PWM_OUT_MODE_2SYC)
双通道同步方式,即一个通道的pwm波形和另外一个通道的完全一致。
pwm0和pwm1可以配置成同步方式,pwm参数由pwm0控制。pwm2和pwm3可以配置成同步方式,参数由pwm2控制。
#include "wm_include.h"
#include "wm_config.h"
#include "wm_pwm.h"
#include <stdint.h>
//task资源
#define LED_TASK_SIZE 256
static OS_STK led_task_stk[LED_TASK_SIZE];
//pwm参数设置
#define L_CHANNEL_1 2 //pwm的通道2
#define L_CHANNEL_2 3 //pwm的通道3
#define L_CHANNEL_PIN_1 WM_IO_PB_24 //pwm的通道2映射的引脚
#define L_CHANNEL_PIN_2 WM_IO_PB_25 //pwm的通道3映射的引脚
#define _L_PWM_CHANNEL_CONFIG(channel, pin) wm_pwm##channel##_config(pin)
#define L_PWM_CHANNEL_CONFIG(channel, pin) _L_PWM_CHANNEL_CONFIG(channel, pin)
#define L_FREQ 50 //设置pwm的频率,单位是hz,设置pwm频率为50hz
#define L_DUTY 19 //占空比,占空比 = S_DUTY/255,其中255由pwm_param.period写死
#define L_NUM 0 //波形周期数(0表示一直运行)
//LED的pwm的初始化
int led_pwm_init()
{
int ret = -1;
//停止pwm
tls_pwm_stop(L_CHANNEL_1);
tls_pwm_stop(L_CHANNEL_2);
//并将引脚映射到pwm通道上
L_PWM_CHANNEL_CONFIG(L_CHANNEL_1, L_CHANNEL_PIN_1);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_2, L_CHANNEL_PIN_2);
//模式采用双通道同步方式设置pwm的参数,这个函数在下面有写
ret = pwm_2syc_mode(L_CHANNEL_1, L_FREQ, L_DUTY, L_NUM);
if(ret != WM_SUCCESS)
return ret;
//启动pwm输出
tls_pwm_start(L_CHANNEL_1);
tls_pwm_start(L_CHANNEL_2);
return WM_SUCCESS;
}
//设置PWM占空比duty范围[0-255]
void set_led_duty(uint8_t duty)
{
tls_pwm_duty_set(L_CHANNEL_1, duty);
}
//led线程
void led_task(void *sdata)
{
//初始化LDE的pwm
led_pwm_init();
for(;;)
{
tls_os_time_delay(100);
}
}
//创建led线程
void create_led_task(void)
{
tls_os_task_create(NULL, NULL,
led_task,
NULL,
(void *)led_task_stk, /* task's stack start address */
LED_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
32,
0);
}
再编写一个双通道同步方式函数设置pwm的参数函数
其实就是copy的demo的pwm_demo_2syc_mode(channel_used, freq, duty, num)函数,如下:
static int pwm_2syc_mode(u8 channel,u32 freq, u8 duty, u8 num)
{
pwm_init_param pwm_param;
int ret=-1;
tls_sys_clk sysclk;
tls_sys_clk_get(&sysclk);
memset(&pwm_param, 0, sizeof(pwm_init_param));
pwm_param.period = 255;
pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT;
pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP;
pwm_param.mode = WM_PWM_OUT_MODE_2SYC;
pwm_param.inverse_en = DISABLE;
pwm_param.pnum = num;
pwm_param.pnum_int = DISABLE;
pwm_param.duty = duty;
pwm_param.channel = channel;
pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq;
ret = tls_pwm_out_init(&pwm_param);
return ret;
}
同时在有函数pwm_mc_mode
的这个文件中添加
#include "wm_cpu.h"
注意事项和互补模式完全一样,这里不赘述。
8. 全同步模式(WM_PWM_OUT_MODE_ALLSYC)
指的是pwm的5个通道输出波形完全一致,pwm的参数由pwm0来控制
下面给出程序,但是未验证,因为跟上面的原理差不多,我就懒得调了,若使用有问题,请留言。
#include "wm_include.h"
#include "wm_config.h"
#include "wm_pwm.h"
#include <stdint.h>
//task资源
#define LED_TASK_SIZE 256
static OS_STK led_task_stk[LED_TASK_SIZE];
//pwm参数设置
#define L_CHANNEL_1 0 //pwm的通道0
#define L_CHANNEL_2 1 //pwm的通道1
#define L_CHANNEL_3 2 //pwm的通道2
#define L_CHANNEL_4 3 //pwm的通道3
#define L_CHANNEL_4 4 //pwm的通道4
#define L_CHANNEL_PIN_1 WM_IO_PB_19 //pwm的通道0映射的引脚
#define L_CHANNEL_PIN_2 WM_IO_PB_20 //pwm的通道1映射的引脚
#define L_CHANNEL_PIN_1 WM_IO_PB_24 //pwm的通道2映射的引脚
#define L_CHANNEL_PIN_2 WM_IO_PB_25 //pwm的通道3映射的引脚
#define L_CHANNEL_PIN_2 WM_IO_PB_26 //pwm的通道4映射的引脚
#define _L_PWM_CHANNEL_CONFIG(channel, pin) wm_pwm##channel##_config(pin)
#define L_PWM_CHANNEL_CONFIG(channel, pin) _L_PWM_CHANNEL_CONFIG(channel, pin)
#define L_FREQ 50 //设置pwm的频率,单位是hz,设置pwm频率为50hz
#define L_DUTY 19 //占空比,占空比 = S_DUTY/255,其中255由pwm_param.period写死
#define L_NUM 0 //波形周期数(0表示一直运行)
//LED的pwm的初始化
int led_pwm_init()
{
int ret = -1;
//停止pwm
tls_pwm_stop(L_CHANNEL_1);
tls_pwm_stop(L_CHANNEL_2);
tls_pwm_stop(L_CHANNEL_3);
tls_pwm_stop(L_CHANNEL_4);
tls_pwm_stop(L_CHANNEL_5);
//并将引脚映射到pwm通道上
L_PWM_CHANNEL_CONFIG(L_CHANNEL_1, L_CHANNEL_PIN_1);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_2, L_CHANNEL_PIN_2);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_3, L_CHANNEL_PIN_3);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_4, L_CHANNEL_PIN_4);
L_PWM_CHANNEL_CONFIG(L_CHANNEL_5, L_CHANNEL_PIN_5);
//模式采用全同步模式函数设置pwm的参数,这个函数在下面有写
ret = pwm_allsyc_mode(L_CHANNEL_1, L_FREQ, L_DUTY, L_NUM);
if(ret != WM_SUCCESS)
return ret;
//启动pwm输出
tls_pwm_start(L_CHANNEL_1);
tls_pwm_start(L_CHANNEL_2);
tls_pwm_start(L_CHANNEL_3);
tls_pwm_start(L_CHANNEL_4);
tls_pwm_start(L_CHANNEL_5);
return WM_SUCCESS;
}
//设置PWM占空比duty范围[0-255]
void set_led_duty(uint8_t duty)
{
tls_pwm_duty_set(L_CHANNEL_1, duty);
}
//led线程
void led_task(void *sdata)
{
//初始化LDE的pwm
led_pwm_init();
for(;;)
{
tls_os_time_delay(100);
}
}
//创建led线程
void create_led_task(void)
{
tls_os_task_create(NULL, NULL,
led_task,
NULL,
(void *)led_task_stk, /* task's stack start address */
LED_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
32,
0);
}
再编写一个全同步模式函数设置pwm的参数函数
其实就是copy的demo的pwm_demo_allsyc_mode(channel_used, freq, duty, num)函数,如下:
static int pwm_allsyc_mode(u8 channel,u32 freq, u8 duty, u8 num)
{
pwm_init_param pwm_param;
int ret=-1;
tls_sys_clk sysclk;
tls_sys_clk_get(&sysclk);
memset(&pwm_param, 0, sizeof(pwm_init_param));
pwm_param.period = 255;
pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT;
pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP;
pwm_param.mode = WM_PWM_OUT_MODE_ALLSYC;
pwm_param.inverse_en = DISABLE;
pwm_param.pnum = num;
pwm_param.pnum_int = DISABLE;
pwm_param.duty = duty;
pwm_param.channel = channel;
pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq;
ret = tls_pwm_out_init(&pwm_param);
return ret;
}
同时在有函数pwm_mc_mode
的这个文件中添加
#include "wm_cpu.h"
注意事项和互补模式完全一样,这里不赘述。