Hybrid device driver-output two-way PWM, hybrid-two-way pwm

Source: Internet
Author: User
Tags pdf manual

Hybrid device driver-output two-way PWM, hybrid-two-way pwm
Try to use 2440 TOUT0 and TOUT1 output PWM to drive two motors. The hardware drive circuit of the motor is L298N.
Test the PWM output of TOUT0 separately:
(1) DRIVER: Use the misc Hybrid device driver model. Of course, you can also use the basic character device model.
To use the misc device to drive the model:
① Initialize a struct miscdevice struct: mainly the file_operation struct member and name
② Use misc_register and misc_deregister to register and deregister this struct.
Sample Code:

# 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" // device name # defi Ne IOCTL_SET_PWM_DUTY 1 # define IOCTL_PWM_STOP 2/* defines the semaphore lock for mutual exclusion. Therefore, only one process can use */static struct semaphore lock to change the driver; static void forward (unsigned long duty) {unsigned long tcon; unsigned long tcnt0, tcmp0; unsigned long tcfg1; unsigned long tcfg0; tcfg0 = _ raw_readl (forward); tcfg0 & = ~ S3c2410_t1__prescaler0_mask; t1_0 | = (50-1); tcfg1 = _ raw_readl (S3C2410_TCFG1); tcfg1 & = ~ Expires; tcfg1 | = expires; _ raw_writel (t1_0, s3c2410_t1_0); _ raw_writel (tcfg1, S3C2410_TCFG1); tcnt0 = 1000; // set the PWM frequency tcmp0 = duty; // set the PWM duty cycle _ raw_writel (tcnt0, S3C2410_TCNTB (0); _ raw_writel (tcmp0, S3C2410_TCMPB (0); tcon = _ raw_readl (S3C2410_TCON ); tcon | = 0xf; // sets the output level reversal, 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) // does not cause process sleep {s3c2410_gpio_cfgpin (S3C2410_GPB0, timeout); 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,}; // Hybrid device driver 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 ); /* initialize a 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) Test the 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);}
The experimental phenomenon is:
The gpb0 pin changes from large to small by using the voltage file of the External table, indicating that the pwm achieves adjustable duty cycle and the multimeter detects the average voltage of the pin.
The same is true for a separate test of tout1.
When I merge two Driver codes that can work independently, a strange problem occurs: As long as I set one timer to output, and the other to output a high level, the subsequent configuration cannot work, which is equivalent to not being controlled by the program.
Analysis Problem: Does the timer configured later perform other operations on it before performing the operation register? Yes. Set gpb1 to timer 1 for output.
Test Scheme ①: Only timer0 registers are set, timer1 is not configured, gpb0 and gpb1 are set to timer output at the same time, and the register value is printed using the printk function.
Test records:
Tout0 actual level output:
0.27, 0.82, 1.67,-> 0.06
Tout1 actual level output:
3.31, 3.31, 3.31,-> 0.47
Serial Port printing:
Before set: t00000 = 512, tcfg1 = 0, tcon = 5242894-10100000000000000001110-> timer0/1 not started output
After set: t00000 = 561, tcfg1 = 3, tcon = 5242893-10100000000000000001101-> timer0 starts to output, timer1 does not start to output
Before set: t00000 = 561, tcfg1 = 3, tcon = 5242893
After set: t00000 = 561, tcfg1 = 3, tcon = 5242893
Before set: t00000 = 561, tcfg1 = 3, tcon = 5242893
After set: t00000 = 561, tcfg1 = 3, tcon = 5242893
Data analysis:
(512)-> 1000000000 => bit8-bit0: 00000000-> 00
(561)-> 1000110001 => bit8-bit0: 00110001-> 49
Preliminary conclusion:
Timer0 is in normal working state. From the read registers, timer1 does not start to work. The reason why timer0 outputs a high level of speculation is that when the open function is called: When the s3c2410_gpio_cfgpin (S3C2410_GPB1, s3C2410_GPB1_TOUT1); then, the output pin of timer1 is itself a high level. The reason for this is that only timer0 is configured in the program. The key is Timer 0 output inverter on, at this time, we can see from the read timer1 Timer 1 output inverter off. In the chip manual, the initial state of the pin is displayed as high when the reverse function is disabled. Timer0 finally outputs 0, not 1, because the bitwise reversal function is enabled during the duty cycle setting process and the final pin is configured as INPUT. timer1 finally outputs 0, also because the PIN is configured as INPUT.
Further verification:
Test Solution ②: When gpb0/1 is configured as the timer output, the bitwise inversion function is configured.
Test data:
Tout0 actual level output:
0.27, 0.82, 1.67,-> 0.06
Tout1 actual level output:
0.06, 0.06, 0.06,-> 0.62
Serial Port printing:
Before set: t00000 = 512, tcfg1 = 0, tcon = 5243918-101000000000100 00001110
After set: t00000 = 561, tcfg1 = 3, tcon = 5243917-101000000000100 00001101
Before set: t00000 = 561, tcfg1 = 3, tcon = 5243917
After set: t00000 = 561, tcfg1 = 3, tcon = 5243917
Before set: t00000 = 561, tcfg1 = 3, tcon = 5243917
After set: t00000 = 561, tcfg1 = 3, tcon = 5243917
This is exactly the same as the above guess.
Attach your own translation of page 319th of the s3c2440 English pdf manual-the part about the output level reversal function and output level status:

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
Adjust the output level.

The following steps describe how to maintain TOUT as high or low (assuming the Level Reversal function is disabled ):
1. Turn off the automatic reload bit, And the TOUTn pin changes to a high level. When the TCNTn is reduced to 0, the timer stops working.
2. stop the timer by clearing the start/stop bit of the timer.
① If the stop position is cleared: TCNTn <= TCMPn, the output remains high.
② If the stop position is cleared: TCNTn> TCMPn, the output is maintained as low.
3. The TOUTn pin Level Reversal can be set through the "inverter on/off" bit of the TCON register, and the invert removes the additional loop to adjust the output level.
Summary:
That is to say, if I need to maintain the pin level to a fixed level after stopping the pwm, I can only consider the first point by turning off the auto reload bit.
Stop pwm, and then clear start/stop bit
Certificate ---------------------------------------------------------------------------------------------------------------------------------------------------------
After the PIN is connected to the drive circuit of the motor, it encounters a strange problem that the speed cannot be adjusted. The conclusion is that the bit of various functions in the tcon register should not be written in at one time, after some digits are written, a slight delay is required to achieve independent control of the two-way PWM duty cycle.
The final driver code: 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 <lin Ux/clk. h> # include <linux/cdev. h> # include <linux/device. h> # include <linux/miscdevice. h> # define DEVICE_NAME "pwm" # define interval 1 # define interval 2 # define interval 4 # define TIMER05 # define TIMER16 # define AUTO_RELOAD_ON 7 # define AUTO_RELOAD_OFF 8 # define inveron9 # define INVER_OFF (10) static struct semaphore lock; static void set_input_clock (void) {unsig Ned int t1_0, tcfg1; t1_0 = _ raw_readl (s3c2410_t1_0); tcfg1 = _ raw_readl (S3C2410_TCFG1); t1_0 & = ~ S3c2410_t1__prescaler0_mask; t1_0 | = (50-1); tcfg1 & = ~ Bytes; tcfg1 | = bytes; _ raw_writel (t1_0, s3c2410_t1_0); _ 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 (se Lect) {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) {unsig Ned 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) {timeout (S3C2410_GPB0, timeout); // A TOUT0 timeout (S3C2410_GPB1, timeout) on the outside; // 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 open! \ N "); return-EBUSY ;}} static int s3c2440_pwm_close (struct inode * inode, struct file * file) {partition (TIMER0, AUTO_RELOAD_OFF); partition (TIMER1, AUTO_RELOAD_OFF ); stop_pwm_out (TIMER0); stop_pwm_out (TIMER1); partial (INVER_OFF); Round (S3C2410_GPB0, partial); Round (S3C2410_GPB1, partial); Round (S3C2410_GPB6, 0 ); Up (& lock); return 0;} static void config_pwm0 (unsigned long duty) {stop_pwm_out (TIMER0); set_pwm_duty (TIMER0, duty); merge (TIMER0); merge (TIMER0, struct); start_pwm_out (TIMER0); reverse (TIMER0);} static void config_pwm1 (unsigned long duty) {stop_pwm_out (TIMER1); reverse (TIMER1, duty); reverse (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); bre Ak; 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 ); /* initialize a 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 ");
Test 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);}
Feelings: The s3c2440 chip Manual does not mention the two-way timer output at the same time should pay attention to what aspects, I think this is the most painful problem encountered in this debugging process, you can only find yourself through the experiment at. You are drunk .............




Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.