搜索
bottom↓
回复: 10

茴字的5种写法——djyos应用程序实例,用5中方法实现PWM控制LED亮度的功能

[复制链接]

出0入0汤圆

发表于 2009-10-13 12:52:28 | 显示全部楼层 |阅读模式
刚刚发布的djyos v0.4.3中,在ekk8962板上提供了5种实现PWM控制led亮度的方法,从多个角度说明如何使用djyos编写应用程序,分别对应5个keil MDK工程文件,这5个程序的功能完全相同,即控制led渐亮渐暗循环不已。源代码:点击此处下载 ourdev_491103.zip(文件大小:1.42M) (原文件名:djysiV0.4.3.zip)

prj-ekk8962-1:PWM控制完全用事件,不用中断实现。
prj-ekk8962-2:PWM控制主要用事件,辅以最少量的中断代码实现。
prj-ekk8962-3:PWM控制主要用事件,辅以中等数量的中断代码实现。
prj-ekk8962-4:PWM控制全部用中断代码实现,但用事件触发执行PWM初始化函数。
prj-ekk8962-5:PWM控制全部用中断代码实现,完全不用事件。

下面1~5楼分别说明5个工程。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2009-10-13 13:00:41 | 显示全部楼层
prj-ekk8962-1工程:

main.c文件的内容是:
#include "inc_os.h"
#include "gpio.h"
#include "sysctl.h"
#include "pwm.h"

uint16_t pg_evtt_flash_led;

void flash_led(struct event_script *my_event)
{
    static s32 compare = 0;
    static bool_t up = true;
    bb_sysctl_rcgc2_gpiof = 1;                      //使能gpiof
    pg_gpio_regf->LOCK = 0x1ACCE551;                //解锁CR寄存器
    pg_gpio_regf->CR = 1;                           //允许修改AFSEL的PF0位
    pg_gpio_regf->AFSEL |= 0x1;                     //PF0设为PWM0输出
    pg_gpio_regf->LOCK = 0;                         //重新锁定CR寄存器
   
    pg_gpio_regf->DEN |= 1;                         //允许PF0
    pg_gpio_regf->IM &= 0xfe;                       //屏蔽掉PF0中断
    pg_gpio_regf->DR8R |= 1;                        //设置PF0=8mA驱动

    //配置PWM0
    pg_sysctl_reg->RCC &= ~bm_sysctl_rcc_pwmdiv;
    pg_sysctl_reg->RCC |= 5<<bo_sysctl_rcc_pwmdiv;  //置pwm时钟为系统时钟64分频
    bb_sysctl_rcc_enpwmdiv = 1;
    bb_sysctl_rcgc0_pwm = 1;                        //使能pwm
    pg_pwm_reg->PWM0CTL = 0;
    pg_pwm_reg->PWM0GENA =  (0<<bo_pwmx_act_zero)
                           +(0<<bo_pwmx_act_load)
                           +(3<<bo_pwmx_act_comp_au)
                           +(2<<bo_pwmx_act_comp_ad)
                           +(0<<bo_pwmx_act_comp_bu)
                           +(0<<bo_pwmx_act_comp_bd);
    pg_pwm_reg->PWM0LOAD = 3000;                    //pwm周期约7.7mS
    bb_pwm0_ctl_mode = 1;                           //增减循环模式
    bb_pwm0_ctl_enable = 1;                         //使能pwm0模块
    bb_pwm0_ctl_debug = 1;                          //使能pwm0输出
    bb_pwm_enable_pwm0en = 1;
    while(1)
    {
        if(up == true)
        {
            compare += 150;
            if(compare >= 3000 - 150)
                up = false;
        }else
        {
            compare -=150;
            if(compare <= 150)
                up = true;
        }
        pg_pwm_reg->PWM0CMPA = compare;
        djy_timer_sync(38);
    }
}

//本应命名为main的,但调试器总是默认这是整个程序的入口点,罢了
void djy_main(void)
{
    pg_evtt_flash_led = djy_evtt_regist(true,true,cn_prio_real,1,
                                        flash_led,10,NULL);
    djy_event_pop(pg_evtt_flash_led,0,0,0);
}

说明:djy_main函数是应用程序的入口点,位于工程目录的main目录下。
main函数中首先登记pg_evtt_flash_led,这里没有判断是否成功登记——如果一开始就不成功的话,还玩什么?登记事件类型时制定flash_led函数是本类型事件的处理函数。倒数第二个参数10是flash_led函数运行时需要的栈尺寸,注意djyos中,你为事件处理函数准备栈时,无需考虑系统服务函数所需要的栈。比如djy_timer_sync函数就需要16字节栈,但这里无需考虑。
然后弹出一条pg_evtt_flash_led类型的事件,这个动作会使一条pg_evtt_flash_led类型事件加入就绪事件队列,只要该事件的优先级在就绪队列中变得最高,就会处理该事件,flash_led函数就会被调用。
flash_led函数的功能很简单,初始化硬件后,就进入死循环,每38mS改变一下LED的亮度。注意了,djy_timer_sync相当于其他操作系统的task_delay函数,但事件单位是mS,而不是ticks数,这样可以使应用程序的可移植性更高,改变tick周期时,可以不改应用程序。

出0入0汤圆

 楼主| 发表于 2009-10-13 13:12:01 | 显示全部楼层
prj-ekk8962-2工程:

main.c的内容如下:
#include "inc_os.h"
#include "gpio.h"
#include "sysctl.h"
#include "pwm.h"

uint16_t pg_evtt_flash_led;
uint32_t pwm0_int(ufast_t int_line)
{
    pg_pwm_reg->PWM0ISC = 1<<bo_pwm0_intisc_intcntzero;  //清过零中断
    pg_pwm_reg->PWMISC = 1<<bo_pwm_intisc_pwm0;          //清PWM0中断
        return 0;
}

void flash_led(struct event_script *my_event)
{
    static s32 compare = 0;
    static bool_t up = true;
    bb_sysctl_rcgc2_gpiof = 1;                      //使能gpiof
    pg_gpio_regf->LOCK = 0x1ACCE551;                //解锁CR寄存器
    pg_gpio_regf->CR = 1;                           //允许修改AFSEL的PF0位
    pg_gpio_regf->AFSEL |= 0x1;                     //PF0设为PWM0输出
    pg_gpio_regf->LOCK = 0;                         //重新锁定CR寄存器
   
    pg_gpio_regf->DEN |= 1;                         //允许PF0
    pg_gpio_regf->IM &= 0xfe;                       //屏蔽掉PF0中断
    pg_gpio_regf->DR8R |= 1;                        //设置PF0=8mA驱动

    //配置PWM0
    pg_sysctl_reg->RCC &= ~bm_sysctl_rcc_pwmdiv;
    pg_sysctl_reg->RCC |= 5<<bo_sysctl_rcc_pwmdiv;  //置pwm时钟为系统时钟64分频
    bb_sysctl_rcc_enpwmdiv = 1;
    bb_sysctl_rcgc0_pwm = 1;                        //使能pwm
    pg_pwm_reg->PWM0CTL = 0;
    pg_pwm_reg->PWM0GENA =  (0<<bo_pwmx_act_zero)
                           +(0<<bo_pwmx_act_load)
                           +(3<<bo_pwmx_act_comp_au)
                           +(2<<bo_pwmx_act_comp_ad)
                           +(0<<bo_pwmx_act_comp_bu)
                           +(0<<bo_pwmx_act_comp_bd);
    pg_pwm_reg->PWM0LOAD = 3000;                    //pwm周期约7.7mS
    bb_pwm0_ctl_mode = 1;                           //增减循环模式
    bb_pwm0_ctl_enable = 1;                         //使能pwm0模块
    bb_pwm0_ctl_debug = 1;                          //使能pwm0输出
    bb_pwm_enable_pwm0en = 1;
    int_isr_connect(cn_int_line_PWM0,pwm0_int);     //中断连接
    int_setto_asyn_signal(cn_int_line_PWM0);
    int_restore_line(cn_int_line_PWM0);
    bb_pwm0_inten_intcntzero = 1;                   //使能PWM0的过零中断
    bb_pwm_inten_pwm0 = 1;                          //使能PWM0中断
    while(1)
    {
        if(up == true)
        {
            compare += 150;
            if(compare >= 3000 - 150)
                up = false;
        }else
        {
            compare -=150;
            if(compare <= 150)
                up = true;
        }
        pg_pwm_reg->PWM0CMPA = compare;
        djy_evtt_pop_sync(my_event->evtt_id,5,cn_timeout_forever);
    }
}

//本应命名为main的,但调试器总是默认这是整个程序的入口点,罢了
void djy_main(void)
{
    pg_evtt_flash_led = djy_evtt_regist(true,true,cn_prio_real,1,
                                        flash_led,10,NULL);
    djy_event_pop(pg_evtt_flash_led,0,0,0);
    int_evtt_connect(cn_int_line_PWM0,pg_evtt_flash_led);
}

说明:这个工程其实跟第一个非常相似,不同的是:
1、main函数中增加了函数调用:int_evtt_connect(cn_int_line_PWM0,pg_evtt_flash_led);含义是,当发生PWM0中断时,自动弹出pg_evtt_flash_led类型的事件。
2、增加了中断响应函数pwm0_int,但该函数只清中断标志,什么都不干,如果中断硬件能自动清中断的话,该函数根本没有必要存在。djyos中,你使用了中断功能,却可以无须编写中断处理函数。
3、flash_led函数中增加了初始化PWM中断的部分。
4、flash_led函数中djy_timer_sync调用换成了djy_evtt_pop_sync系统调用,作用是:当程序运行至此就进入阻塞,直到类型号为my_event->evtt_id的事件发生5(第二个参数)次,然后唤醒,cn_timeout_forever的意思是,如果事件一直不发生,则无限等待。
这样,每次PWM中断,都由djyos的中断系统弹出pg_evtt_flash_led类型的事件,每5次中断,将唤醒事件,flash_led函数继续执行。PWM周期时7.7mS,5次刚好大约38mS,这就是第一个工程中38mS的来由。

出0入0汤圆

 楼主| 发表于 2009-10-13 13:21:36 | 显示全部楼层
prj-ekk8962-3工程:

main.c的内容如下:
#include "inc_os.h"
#include "gpio.h"
#include "sysctl.h"
#include "pwm.h"

uint16_t pg_evtt_flash_led;
uint32_t pwm0_int(ufast_t int_line)
{
    pg_pwm_reg->PWM0ISC = 1<<bo_pwm0_intisc_intcntzero;  //清过零中断
    pg_pwm_reg->PWMISC = 1<<bo_pwm_intisc_pwm0;          //清PWM0中断
    djy_event_pop(pg_evtt_flash_led,0,0,0);
        return 0;
}

void flash_led(struct event_script *my_event)
{
    static s32 compare = 0;
    static bool_t up = true;
    bb_sysctl_rcgc2_gpiof = 1;                      //使能gpiof
    pg_gpio_regf->LOCK = 0x1ACCE551;                //解锁CR寄存器
    pg_gpio_regf->CR = 1;                           //允许修改AFSEL的PF0位
    pg_gpio_regf->AFSEL |= 0x1;                     //PF0设为PWM0输出
    pg_gpio_regf->LOCK = 0;                         //重新锁定CR寄存器
   
    pg_gpio_regf->DEN |= 1;                         //允许PF0
    pg_gpio_regf->IM &= 0xfe;                       //屏蔽掉PF0中断
    pg_gpio_regf->DR8R |= 1;                        //设置PF0=8mA驱动

    //配置PWM0
    pg_sysctl_reg->RCC &= ~bm_sysctl_rcc_pwmdiv;
    pg_sysctl_reg->RCC |= 5<<bo_sysctl_rcc_pwmdiv;  //置pwm时钟为系统时钟64分频
    bb_sysctl_rcc_enpwmdiv = 1;
    bb_sysctl_rcgc0_pwm = 1;                        //使能pwm
    pg_pwm_reg->PWM0CTL = 0;
    pg_pwm_reg->PWM0GENA =  (0<<bo_pwmx_act_zero)
                           +(0<<bo_pwmx_act_load)
                           +(3<<bo_pwmx_act_comp_au)
                           +(2<<bo_pwmx_act_comp_ad)
                           +(0<<bo_pwmx_act_comp_bu)
                           +(0<<bo_pwmx_act_comp_bd);
    pg_pwm_reg->PWM0LOAD = 3000;                    //pwm周期约7.7mS
    bb_pwm0_ctl_mode = 1;                           //增减循环模式
    bb_pwm0_ctl_enable = 1;                         //使能pwm0模块
    bb_pwm0_ctl_debug = 1;                          //使能pwm0输出
    bb_pwm_enable_pwm0en = 1;
    int_isr_connect(cn_int_line_PWM0,pwm0_int);     //中断连接
    int_setto_asyn_signal(cn_int_line_PWM0);
    int_restore_line(cn_int_line_PWM0);
    bb_pwm0_inten_intcntzero = 1;                   //使能PWM0的过零中断
    bb_pwm_inten_pwm0 = 1;                          //使能PWM0中断
    while(1)
    {
        if(up == true)
        {
            compare += 150;
            if(compare >= 3000 - 150)
                up = false;
        }else
        {
            compare -=150;
            if(compare <= 150)
                up = true;
        }
        pg_pwm_reg->PWM0CMPA = compare;
        djy_evtt_pop_sync(my_event->evtt_id,5,cn_timeout_forever);
    }
}

//本应命名为main的,但调试器总是默认这是整个程序的入口点,罢了
void djy_main(void)
{
    pg_evtt_flash_led = djy_evtt_regist(true,true,cn_prio_real,1,
                                        flash_led,10,NULL);
    djy_event_pop(pg_evtt_flash_led,0,0,0);
}

说明:这个工程和prj-ekk8962-2工程只有很小的差别:
1、main函数中不再调用 int_evtt_connect 函数连接中断和事件。
2、在中断响应函数pwm0_int中调用 djy_event_pop(pg_evtt_flash_led,0,0,0); 弹出事件。
本工程的执行过程和prj-ekk8962-2也非常相似,唯一的差别是,弹出事件由系统执行还是由应用程序执行。系统执行则灵活性差,只能弹出一条事件,且难于灵活控制事件参数,应用程序执行则可以弹出多条事件,以及灵活控制事件参数。

出0入0汤圆

发表于 2009-10-13 14:23:08 | 显示全部楼层
沙发,帮定

出0入0汤圆

 楼主| 发表于 2009-10-13 14:30:20 | 显示全部楼层
prj-ekk8962-4工程的main.c的内容如下:

#include "inc_os.h"
#include "gpio.h"
#include "sysctl.h"
#include "pwm.h"

uint32_t pwm0_int(ufast_t int_line)
{
    static s32 compare = 0,delay = 0;
    static bool_t up = true;
    pg_pwm_reg->PWM0ISC = 1<<bo_pwm0_intisc_intcntzero;  //清过零中断
    pg_pwm_reg->PWMISC = 1<<bo_pwm_intisc_pwm0;         //清PWM0中断
    if(delay < 5)
    {
        delay++;
        return 0;
    }else
        delay = 0;
    if(up == true)
    {
        compare += 150;
        if(compare >= 3000 - 150)
            up = false;
    }else
    {
        compare -=150;
        if(compare <= 150)
            up = true;
    }
    pg_pwm_reg->PWM0CMPA = compare;
        return 0;
}

void flash_led(struct event_script *my_event)
{
    bb_sysctl_rcgc2_gpiof = 1;                      //使能gpiof
    pg_gpio_regf->LOCK = 0x1ACCE551;                //解锁CR寄存器
    pg_gpio_regf->CR = 1;                           //允许修改AFSEL的PF0位
    pg_gpio_regf->AFSEL |= 0x1;                     //PF0设为PWM0输出
    pg_gpio_regf->LOCK = 0;                         //重新锁定CR寄存器

    pg_gpio_regf->DEN |= 1;                         //允许PF0
    pg_gpio_regf->IM &= 0xfe;                       //屏蔽掉PF0中断
    pg_gpio_regf->DR8R |= 1;                        //设置PF0=8mA驱动

    //配置PWM0
    pg_sysctl_reg->RCC &= ~bm_sysctl_rcc_pwmdiv;
    pg_sysctl_reg->RCC |= 5<<bo_sysctl_rcc_pwmdiv;  //置pwm时钟为系统时钟64分频
    bb_sysctl_rcc_enpwmdiv = 1;
    bb_sysctl_rcgc0_pwm = 1;                        //使能pwm
    pg_pwm_reg->PWM0CTL = 0;
    pg_pwm_reg->PWM0GENA =  (0<<bo_pwmx_act_zero)
                           +(0<<bo_pwmx_act_load)
                           +(3<<bo_pwmx_act_comp_au)
                           +(2<<bo_pwmx_act_comp_ad)
                           +(0<<bo_pwmx_act_comp_bu)
                           +(0<<bo_pwmx_act_comp_bd);
    pg_pwm_reg->PWM0LOAD = 3000;                    //pwm周期约7.7mS
    bb_pwm0_ctl_mode = 1;                           //增减循环模式
    bb_pwm0_ctl_enable = 1;                         //使能pwm0模块
    bb_pwm0_ctl_debug = 1;                          //使能pwm0输出
    bb_pwm_enable_pwm0en = 1;
    int_isr_connect(cn_int_line_PWM0,pwm0_int);     //中断连接
    int_setto_asyn_signal(cn_int_line_PWM0);
    int_restore_line(cn_int_line_PWM0);
    bb_pwm0_inten_intcntzero = 1;                   //使能PWM0的过零中断
    bb_pwm_inten_pwm0 = 1;                          //使能PWM0中断
    djy_evtt_unregist(my_event->evtt_id);
}

//本应命名为main的,但调试器总是默认这是整个程序的入口点,罢了
void djy_main(void)
{
    uint16_t evtt_flash_led;
    evtt_flash_led = djy_evtt_regist(true,true,cn_prio_real,1,
                                        flash_led,10,NULL);
    djy_event_pop(evtt_flash_led,0,0,0);
}

说明:这个工程和前面三个工程相比,有明显的不同,前三个工程中,要么没使用中断,即使使用了,中断处理函数也属于跑龙套的角色。在这个工程中却要担起主角的重任了,看官,事件处理函数flash_led其实啥也不干,初始化完硬件就卸磨————函数返回了,返回前还把事件类型注销掉,连磨盘都给砸了。控制led亮度的工作,完全落在中断处理函数上。
    由于evtt_flash_led类型事件正在使用,djy_evtt_unregist函数实际上只是在系统中标记了evtt_flash_led事件类型即将注销,真正的注销行为在flash_led返回以后执行的。evtt_flash_led类型注销后,就可以分配给别人用了,不拉屎就不占茅坑,很自觉。

出0入0汤圆

 楼主| 发表于 2009-10-13 14:30:35 | 显示全部楼层
prj-ekk8962-5工程的main.c的内容如下:

#include "inc_os.h"
#include "gpio.h"
#include "sysctl.h"
#include "pwm.h"

uint32_t pwm0_int(ufast_t int_line)
{
    static s32 compare = 0,delay = 0;
    static bool_t up = true;
    pg_pwm_reg->PWM0ISC = 1<<bo_pwm0_intisc_intcntzero;  //清过零中断
    pg_pwm_reg->PWMISC = 1<<bo_pwm_intisc_pwm0;         //清PWM0中断
    if(delay < 5)
    {
        delay++;
        return 0;
    }else
        delay = 0;
    if(up == true)
    {
        compare += 150;
        if(compare >= 3000 - 150)
            up = false;
    }else
    {
        compare -=150;
        if(compare <= 150)
            up = true;
    }
    pg_pwm_reg->PWM0CMPA = compare;
        return 0;
}

void set_led(void)
{
    bb_sysctl_rcgc2_gpiof = 1;                      //使能gpiof
    pg_gpio_regf->LOCK = 0x1ACCE551;                //解锁CR寄存器
    pg_gpio_regf->CR = 1;                           //允许修改AFSEL的PF0位
    pg_gpio_regf->AFSEL |= 0x1;                     //PF0设为PWM0输出
    pg_gpio_regf->LOCK = 0;                         //重新锁定CR寄存器

    pg_gpio_regf->DEN |= 1;                         //允许PF0
    pg_gpio_regf->IM &= 0xfe;                       //屏蔽掉PF0中断
    pg_gpio_regf->DR8R |= 1;                        //设置PF0=8mA驱动

    //配置PWM0
    pg_sysctl_reg->RCC &= ~bm_sysctl_rcc_pwmdiv;
    pg_sysctl_reg->RCC |= 5<<bo_sysctl_rcc_pwmdiv;  //置pwm时钟为系统时钟64分频
    bb_sysctl_rcc_enpwmdiv = 1;
    bb_sysctl_rcgc0_pwm = 1;                        //使能pwm
    pg_pwm_reg->PWM0CTL = 0;
    pg_pwm_reg->PWM0GENA =  (0<<bo_pwmx_act_zero)
                           +(0<<bo_pwmx_act_load)
                           +(3<<bo_pwmx_act_comp_au)
                           +(2<<bo_pwmx_act_comp_ad)
                           +(0<<bo_pwmx_act_comp_bu)
                           +(0<<bo_pwmx_act_comp_bd);
    pg_pwm_reg->PWM0LOAD = 3000;                    //pwm周期约7.7mS
    bb_pwm0_ctl_mode = 1;                           //增减循环模式
    bb_pwm0_ctl_enable = 1;                         //使能pwm0模块
    bb_pwm0_ctl_debug = 1;                          //使能pwm0输出
    bb_pwm_enable_pwm0en = 1;
    int_isr_connect(cn_int_line_PWM0,pwm0_int);     //中断连接
    int_setto_asyn_signal(cn_int_line_PWM0);
    int_restore_line(cn_int_line_PWM0);
    bb_pwm0_inten_intcntzero = 1;                   //使能PWM0的过零中断
    bb_pwm_inten_pwm0 = 1;                          //使能PWM0中断
}

//本应命名为main的,但调试器总是默认这是整个程序的入口点,罢了
void djy_main(void)
{
    set_led();
}

说明:prj-ekk8962-4中,用事件触发flash_led函数执行,只是为了演示事件类型的登记和注销过程,实战中,完全是画蛇添足之举!在main函数中直接调用函数set_led就行了。

出0入0汤圆

发表于 2009-10-13 15:01:19 | 显示全部楼层
您的系统能用到8位机吗?最好有个KEIL的例子啊

出0入0汤圆

发表于 2009-10-13 15:56:46 | 显示全部楼层
帮顶了...

出0入0汤圆

发表于 2009-10-13 16:09:22 | 显示全部楼层
支持,搞个51的应用看看,即使是点灯的,51估计大家都有,但是你的板估计很少人有吧

出0入0汤圆

 楼主| 发表于 2009-10-13 16:12:26 | 显示全部楼层
回9楼:51估计很难运行djyos,晚些时候会发布一个用stm32实现的相似功能例程,也用多种方法对比实现。
回7楼:高端的8位机应该可以,但目前没有移植计划。现在stm32很普及了,晚些出stm32+keil的。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 17:59

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表