联盛德 HLK-W801(四):W801的PWM实现

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(刹车)
2PB_26支持
3PB_25支持
4PB_24支持
8PB_20支持
9PB_19支持
18BOOTMODE/PA_00支持
19PA_1支持
20PA_2支持
21PA_3支持
22PA_4支持
23PA_5支持
25PA_7支持
26PA_8支持
28PA_10支持
30PA_11支持
31PA12支持
32PA_13支持
33PA_14支持
34PA_15支持
35PB_0支持
36PB_1支持
37PB_2支持
38PB_3支持
46PB_8支持
48PB_12支持
49PB_13支持
50PB_14支持
51PB_15支持
55PB_16支持
56PB_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 程序解释

  1. 独立模式下PWM其实我们重点关心三个参数:

    • 计数的时钟clk:决定计数加一需要的时间

    • PWM的周期:决定了PWM的周期

    • 计数比较值:决定占空比

      占空比= 计数比较值 / PWM的周期计数值

  2. 与其他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秒

  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"

注意事项和互补模式完全一样,这里不赘述。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈搭石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值