混雜裝置驅動--輸出兩路PWM,混雜--兩路pwm

來源:互聯網
上載者:User

混雜裝置驅動--輸出兩路PWM,混雜--兩路pwm
嘗試用2440的TOUT0和TOUT1輸出PWM驅動兩個電機,電機的硬體驅動電路是使用L298N。
先單獨測試TOUT0的PWM輸出:
(1)驅動程式:使用misc混雜裝置驅動模型,當然也可以使用基本的字元裝置模型。
使用misc裝置驅動模型步驟:
①初始化一個struct miscdevice結構體:主要是file_operation結構體成員和name
②使用misc_register和misc_deregister註冊和登出這個結構體
程式碼範例:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/gpio.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <plat/regs-timer.h>#include <mach/regs-irq.h>#include <linux/clk.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/miscdevice.h>#define DEVICE_NAME "pwm" //裝置名稱#define IOCTL_SET_PWM_DUTY 1 #define IOCTL_PWM_STOP 2/* 定義訊號量 lock用於互斥,因此,改驅動程式只能同時有一個進程使用 */static struct semaphore lock;static void pwm_set_duty(unsigned long duty){    unsigned long tcon;    unsigned long tcnt0,tcmp0;    unsigned long tcfg1;    unsigned long tcfg0;    tcfg0 = __raw_readl(S3C2410_TCFG0);    tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;    tcfg0 |= (50 - 1);         tcfg1 = __raw_readl(S3C2410_TCFG1);    tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;     tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;        __raw_writel(tcfg0, S3C2410_TCFG0);    __raw_writel(tcfg1, S3C2410_TCFG1);         tcnt0 = 1000;//設定PWM頻率    tcmp0 = duty;//設定PWM占空比    __raw_writel(tcnt0, S3C2410_TCNTB(0));    __raw_writel(tcmp0, S3C2410_TCMPB(0));     tcon = __raw_readl(S3C2410_TCON);    tcon |= 0xf; //設定輸出電平反轉,start pwm output    __raw_writel(tcon, S3C2410_TCON);     tcon &= ~2;   /* clear manual update bit */    __raw_writel(tcon, S3C2410_TCON);}static void pwm_stop(void){unsigned long tcon;tcon = __raw_readl(S3C2410_TCON);    tcon &= ~(1<<3);//Turn off the auto reload bit    __raw_writel(tcon, S3C2410_TCON); // }static int my_pwm_open(struct inode *inode, struct file *file){    if (!down_trylock(&lock))//不會導致進程休眠    {    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);        return 0;    }    else        return -EBUSY; }static int my_pwm_close(struct inode *inode, struct file *file){    pwm_stop();    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPIO_INPUT);    up(&lock);    return 0;}static int my_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){    switch (cmd) {        case IOCTL_SET_PWM_DUTY:             if (arg == 0)                 return -EINVAL;            pwm_set_duty(arg);            break;        case IOCTL_PWM_STOP:            pwm_stop();            break;        default:        printk(KERN_INFO"cmd error!\n");        break;    }    return 0;}static struct file_operations dev_fops = {    .owner = THIS_MODULE,    .open = my_pwm_open,    .release = my_pwm_close,    .ioctl = my_pwm_ioctl,};//混雜裝置驅動static struct miscdevice misc = {    .minor = MISC_DYNAMIC_MINOR,    .name = DEVICE_NAME,    .fops = &dev_fops,};static int __init dev_init(void){int ret;init_MUTEX(&lock); /* 初始化一個互斥鎖 */ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}static void __exit dev_exit(void){    misc_deregister(&misc); }module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("CLBIAO");MODULE_DESCRIPTION("S3C2440 Pwm Driver");
(2)測試app程式:
#include <stdlib.h>#include <stdio.h>  #include <sys/types.h>  #include <sys/stat.h>  #include <fcntl.h>  #include <unistd.h>#include <string.h> #define IOCTL_SET_PWM_DUTY 1 #define IOCTL_PWM_STOP 2int main(int argc, char **argv){int fd,i;unsigned long duty;fd = open("/dev/pwm",O_RDWR);for(i=0;i<4;i++){    ioctl(fd,IOCTL_SET_PWM_DUTY,duty);//duty=80%    duty += 100;     sleep(3);}close(fd);exit(0);}
實驗現象是:
用外用表電壓檔測gpb0引腳會出現從大到小的變化,說明pwm實現了占空比可調,萬用表測到的是引腳的平均電壓。
對tout1的單獨測試也是一樣。
當我把兩個單獨可以工作的驅動代碼合并到一起的時候就出現了奇怪的問題:只要我先設定其中一個timer輸出之後另外一個就輸出高電平,之後對他進行的配置工作不起效,相當於不受程式控制。
分析問題:在後面才配置的timer在進行操作寄存器之前是否對他有另外的操作?有,將gpb1配置為定時器1輸出。
測試方案①:只設定了timer0寄存器,沒有配置timer1的,同時gpb0和gpb1設定為定時器輸出,使用printk函數列印寄存器的值
測試記錄:
tout0實際電平輸出:
0.27,0.82,1.67,->0.06
tout1實際電平輸出:
3.31,3.31,3.31,->0.47
串口列印:
before set:tcfg0=512,tcfg1=0,tcon=5242894 - 10100000000000000001110 -> timer0/1未開始輸出
 after set:tcfg0=561,tcfg1=3,tcon=5242893 - 10100000000000000001101 -> timer0開始輸出,timer1未開始輸出
before set:tcfg0=561,tcfg1=3,tcon=5242893
 after set:tcfg0=561,tcfg1=3,tcon=5242893
before set:tcfg0=561,tcfg1=3,tcon=5242893
 after set:tcfg0=561,tcfg1=3,tcon=5242893
資料分析:
(512)->1000000000 => bit8-bit0:00000000 -> 00
(561)->1000110001 => bit8-bit0:00110001 -> 49
初步結論:
timer0處於正常工作狀態,從讀取的寄存器來看timer1並沒有開始工作,之所以會在timer0工作時輸出高電平猜測是在調用open函數時:執行完s3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPB1_TOUT1);之後timer1的輸出引指令碼身就是高電平,之所以會這樣的原因進一步猜測是程式中只對timer0進行配置:關鍵是Timer 0 output inverter on,而此時從讀取的timer1來看 Timer 1 output inverter off 的,晶片手冊上說關閉反轉功能時引腳的初始狀態是表現為高電平的。之所以timer0最後會輸出0,而不是1,是因為在設定占空比過程中開啟了位反轉功能和最後引腳配置為INPUT,timer1最後也輸出0同樣是因為引腳配置為INPUT。
進一步驗證:
測試方案②:在配置gpb0/1為timer輸出時就進行設定位反轉功能
測試資料:
tout0實際電平輸出:
0.27,0.82,1.67,->0.06
tout1實際電平輸出:
0.06,0.06,0.06,->0.62
串口列印:
before set:tcfg0=512,tcfg1=0,tcon=5243918 - 101000000000100 00001110
 after set:tcfg0=561,tcfg1=3,tcon=5243917 - 101000000000100 00001101
before set:tcfg0=561,tcfg1=3,tcon=5243917
 after set:tcfg0=561,tcfg1=3,tcon=5243917
before set:tcfg0=561,tcfg1=3,tcon=5243917
 after set:tcfg0=561,tcfg1=3,tcon=5243917
和上面的猜測完全吻合。
附上自己對s3c2440英文pdf手冊第319頁的翻譯--即關於輸出電平反轉功能和輸出電平狀態的那部分:

The following procedure describes how to maintain TOUT as high or low (assume the inverter is off):
1. Turn off the auto reload bit. And then, TOUTn goes to high level and the timer is stopped after the TCNTn reaches
0 (recommended).
2. Stop the timer by clearing the timer start/stop bit to 0. 
If TCNTn<=TCMPn, the output level is high. 
If TCNTn>TCMPn, the output level is low.
3. The TOUTn can be inverted by the inverter on/off bit in TCON. The inverter removes the additional circuit to
adjust the output level.

下面的步驟是描述如何維持TOUT為高電平或低電平(假設電平反轉功能關閉):
1.關掉自動重新載入位,然後TOUTn引腳變為高電平,當TCNTn自減到0時,定時器停止工作
2.通過清除定時器的start/stop位來停止定時器
①如果清除停止位時:TCNTn<=TCMPn,那麼輸出維持為高電平
②如果清除停止位時:TCNTn>TCMPn,那麼輸出維持為低電平
3.TOUTn引腳的電平反轉可以通過TCON寄存器的“inverter on/off”位來進行設定,反轉器移除附加的迴路來調整輸出電平
小結:
也就是說,如果我需要在停止pwm之後引腳的電平保持為確定的電平,就只能考慮第一點,通過關掉自動重新載入位來
停止pwm,然後再來清除start/stop位
---------------------------------------------------------------------------------------------------------------------------------------------------------
之後引腳串連到電機驅動電路之後又遇到類似調不了速的奇葩問題,得到一個結論就是tcon寄存器裡的控制各種功能的位不要一次性寫進去,有些位寫完之後還要稍加延時才能實現對兩路PWM占空比的獨立控制。
最終可以使用的驅動代碼:2440_pwm.c
#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/interrupt.h>#include <linux/gpio.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <plat/regs-timer.h>#include <mach/regs-irq.h>#include <asm/mach/time.h>#include <linux/clk.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/miscdevice.h>#define DEVICE_NAME "pwm"#define IOCTL_CONFIG_PWM0 1#define IOCTL_CONFIG_PWM1 2#define IOCTL_PWM0_STOP3#define IOCTL_PWM1_STOP 4#define TIMER05#define TIMER16#define AUTO_RELOAD_ON 7#define AUTO_RELOAD_OFF 8#define INVER_ON9#define INVER_OFF   (10)static struct semaphore lock;static void set_input_clock(void){unsigned int tcfg0,tcfg1;tcfg0 = __raw_readl(S3C2410_TCFG0);tcfg1 = __raw_readl(S3C2410_TCFG1);tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;    tcfg0 |= (50- 1);     tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;     tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;    __raw_writel(tcfg0, S3C2410_TCFG0);    __raw_writel(tcfg1, S3C2410_TCFG1);}static void set_pwm_duty(int select,unsigned int duty){unsigned int tcon;    tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:    __raw_writel(1000, S3C2410_TCNTB(0));    __raw_writel(duty, S3C2410_TCMPB(0));    break;        case TIMER1:    __raw_writel(1000, S3C2410_TCNTB(1));    __raw_writel(duty, S3C2410_TCMPB(1));    break;}}static void auto_reload_on_off(int select,int status){unsigned int tcon;tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:if(status==7){    tcon |= (1<<3);    __raw_writel(tcon,S3C2410_TCON);    mdelay(1);    }    else if(status==8)    {    tcon &= ~(1<<3);    __raw_writel(tcon,S3C2410_TCON);    mdelay(1);    }    break;    case TIMER1:    if(status==7){    tcon |= (1<<11);    __raw_writel(tcon,S3C2410_TCON);    mdelay(1);    }    else if(status==8)    {    tcon &= ~(1<<11);    __raw_writel(tcon,S3C2410_TCON);    mdelay(1);    }    break;    }}static void start_pwm_out(int select){unsigned int tcon;tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:    tcon |= 1;    __raw_writel(tcon, S3C2410_TCON);    break;    case TIMER1:    tcon |= (1<<8);    __raw_writel(tcon, S3C2410_TCON);    break;}}static void stop_pwm_out(int select){unsigned int tcon;tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:auto_reload_on_off(TIMER0,AUTO_RELOAD_OFF);mdelay(5);    tcon &= ~(1<<0);    __raw_writel(tcon,S3C2410_TCON);    break;    case TIMER1:auto_reload_on_off(TIMER1,AUTO_RELOAD_OFF);    mdelay(1);    tcon &= ~(1<<8);    __raw_writel(tcon,S3C2410_TCON);    break;    }}static void manual_update_tcont_tcmp(int select){unsigned int tcon;    tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:    tcon |= (1<<1);//Update TCNTB0 & TCMPB0    __raw_writel(tcon, S3C2410_TCON);    break;        case TIMER1:    tcon |= (1<<9);//Update TCNTB1 & TCMPB1    __raw_writel(tcon, S3C2410_TCON);    break;}}static void clear_manual_bit(int select){unsigned int tcon;tcon = __raw_readl(S3C2410_TCON);switch(select){case TIMER0:    tcon &= ~(1<<1);    __raw_writel(tcon, S3C2410_TCON);    break;    case TIMER1:    tcon &= ~(1<<9);    __raw_writel(tcon, S3C2410_TCON);    break;}}static void output_inverte_on_off(int status){unsigned long tcon;switch(status){case INVER_ON:tcon = __raw_readl(S3C2410_TCON);    tcon |= ((1<<2)|(1<<10));    __raw_writel(tcon, S3C2410_TCON);    break;case INVER_OFF:tcon = __raw_readl(S3C2410_TCON);    tcon &= ~((1<<2)|(1<<10));    __raw_writel(tcon, S3C2410_TCON);    break;}}static void print_tcon(int status){unsigned int tcon;tcon = __raw_readl(S3C2410_TCON);switch(status){case 0:printk(KERN_INFO"beford set tcon:tcon=%d\n",tcon);break;case 1:printk(KERN_INFO" after set tcon:tcon=%d\n",tcon);break;}}static int s3c2440_pwm_open(struct inode *inode, struct file *file){    if (!down_trylock(&lock))    {    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);//靠外邊的一個TOUT0    s3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPB1_TOUT1);//    s3c2410_gpio_setpin(S3C2410_GPB6,1);output_inverte_on_off(INVER_ON);stop_pwm_out(TIMER0);stop_pwm_out(TIMER1);set_input_clock();        return 0;    }    else    {    printk(KERN_INFO"/dev/pwm opened!\n");        return -EBUSY;     }}static int s3c2440_pwm_close(struct inode *inode, struct file *file){auto_reload_on_off(TIMER0,AUTO_RELOAD_OFF);    auto_reload_on_off(TIMER1,AUTO_RELOAD_OFF);stop_pwm_out(TIMER0);stop_pwm_out(TIMER1);output_inverte_on_off(INVER_OFF);    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPIO_INPUT);    s3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPIO_INPUT);    s3c2410_gpio_setpin(S3C2410_GPB6,0);    up(&lock);    return 0;}static void config_pwm0(unsigned long duty){stop_pwm_out(TIMER0);set_pwm_duty(TIMER0,duty);manual_update_tcont_tcmp(TIMER0);auto_reload_on_off(TIMER0,AUTO_RELOAD_ON);start_pwm_out(TIMER0);clear_manual_bit(TIMER0);}static void config_pwm1(unsigned long duty){stop_pwm_out(TIMER1);set_pwm_duty(TIMER1,duty);manual_update_tcont_tcmp(TIMER1);auto_reload_on_off(TIMER1,AUTO_RELOAD_ON);start_pwm_out(TIMER1);clear_manual_bit(TIMER1);}static int s3c2440_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){    switch (cmd) {        case IOCTL_CONFIG_PWM0:        printk(KERN_INFO"config timer0:\n");        print_tcon(0);            config_pwm0(arg);            print_tcon(1);            break;        case IOCTL_CONFIG_PWM1:        printk(KERN_INFO"config timer1:\n");        print_tcon(0);            config_pwm1(arg);            print_tcon(1);            break;        case IOCTL_PWM0_STOP:        printk(KERN_INFO"stop timer0:\n");        print_tcon(0);            stop_pwm_out(TIMER0);            print_tcon(1);            break;        case IOCTL_PWM1_STOP:        printk(KERN_INFO"stop timer1:\n");        print_tcon(0);            stop_pwm_out(TIMER1);            print_tcon(1);            break;        default:        printk(KERN_INFO"ioctl cmd error!\n");        break;    }    return 0;}static struct file_operations dev_fops = {    .owner   = THIS_MODULE,    .open    = s3c2440_pwm_open,    .release = s3c2440_pwm_close,    .ioctl   = s3c2440_pwm_ioctl,};static struct miscdevice misc = {    .minor = MISC_DYNAMIC_MINOR,    .name = DEVICE_NAME,    .fops = &dev_fops,};static int __init dev_init(void){int ret;init_MUTEX(&lock); /* 初始化一個互斥鎖 */ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}static void __exit dev_exit(void){    misc_deregister(&misc); }module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("CLBIAO");MODULE_DESCRIPTION("S3C2440 Pwm Driver");
測試APP:
#include <stdlib.h>#include <stdio.h>  #include <sys/types.h>  #include <sys/stat.h>  #include <fcntl.h>  #include <unistd.h>#include <string.h> #define IOCTL_CONFIG_PWM0 1#define IOCTL_CONFIG_PWM1 2#define IOCTL_PWM0_STOP3#define IOCTL_PWM1_STOP 4int main(int argc, char **argv){int fd;fd = open("/dev/pwm",O_RDWR);sleep(12);ioctl(fd,IOCTL_CONFIG_PWM0,900);ioctl(fd,IOCTL_CONFIG_PWM1,100);sleep(6);ioctl(fd,IOCTL_CONFIG_PWM0,700);ioctl(fd,IOCTL_CONFIG_PWM1,300);sleep(6);ioctl(fd,IOCTL_PWM0_STOP);ioctl(fd,IOCTL_PWM1_STOP);sleep(6);ioctl(fd,IOCTL_CONFIG_PWM0,500);ioctl(fd,IOCTL_CONFIG_PWM1,500);sleep(6);close(fd);exit(0);}
感想:s3c2440晶片手冊上並沒有說到兩路timer同時輸出時應該注意哪些方面,我想這就是這次調試過程中遇到最蛋疼的問題了,只能靠自己通過實驗現象一點一點去摸索,醉了醉了.............




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.