In a twinkling of an eye, we have arrived at the last class-PWM-provided by the mini2440 manufacturer. The driver uses the timer 0 to generate the PWM pulse signal, and the gpb0 pin output is connected to the buzzer. The Code is as follows:
# 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" // device name
# Define pwm_ioctl_set_freq 1 // macro variable, used for the CMD variable of IOCTL
# Define pwm_ioctl_stop 0
Static struct semaphore lock; // defines the semaphore lock
/* Freq: pclk/50/16/65536 ~ Pclk/50/16
* If pclk = 50 MHz, freq is 1Hz to 62500Hz
* Human ear: 20Hz ~ 20000Hz
*/
Static void pwm_set_freq (unsigned long freq)
{
Unsigned long tcon;
Unsigned long tcnt;
Unsigned long tcfg1;
Unsigned long t00000;
Struct CLK * clk_p;
Unsigned long pclk;
S3c2410_gpio_cfgpin (s3c2410_gpb (0), s3c2410_gpb0_tout0 );
// Set gpb0 to PWM output mode
Tcon = _ raw_readl (s3c2410_tcon); // read the register tcon
Tcfg1 = _ raw_readl (s3c2410_tcfg1); // read and access tcfg1
T1_0 = _ raw_readl (s3c2410_t1_0); // read the t1_0 register.
T00000 & = ~ S3c2410_t1__prescaler0_mask; // pre-division mask: t00000 [0: 7]
T00000 | = (50-1); // sets the 50-bit frequency.
Tcfg1 & = ~ S3c2410_t00000000mux0_mask; // split value mask: tcfg1 [0: 3]
Tcfg1 | = s3c2410_t00000000mux0_div16; // The timer 0 performs 16 segmentation.
_ Raw_writel (tcfg1, s3c2410_tcfg1); // Save the set data to tcfg1 and t1_0
_ Raw_writel (t1_0, s3c2410_t1_0 );
Clk_p = clk_get (null, "pclk ");
Pclk = clk_get_rate (clk_p); // obtain the pclk clock.
Tcnt = (pclk/50/16)/freq; // obtain the timer initialization value.
_ Raw_writel (tcnt, s3c2410_tcntb (0); // write tcnt to the timer count register
_ Raw_writel (tcnt/2, s3c2410_tcmpb (0); // duty cycle 50%
Tcon & = ~ 0x1f; // disable deadzone, auto-Reload, inv-off,
Tcon | = 0xb; // update tcntb0 & tcmpb0, start timer 0
_ Raw_writel (tcon, s3c2410_tcon );
Tcon & = ~ 2; // clear manual update bit
_ Raw_writel (tcon, s3c2410_tcon );
}
Static void pwm_stop (void)
{// Set gpb0 to the output mode, and output the low level to stop PWM.
S3c2410_gpio_cfgpin (s3c2410_gpb (0), s3c2410_gpio_output );
S3c2410_gpio_setpin (s3c2410_gpb (0), 0 );
}
Static int s3c24xx_pwm_open (struct inode * inode, struct file * file)
{
If (! Down_trylock (& lock) // semaphores-1. If it succeeds, down_trylock (& lock) returns 0.
Return 0;
Else
Return-ebusy;
}
Static int s3c24xx_pwm_close (struct inode * inode, struct file * file)
{
Pwm_stop (); // call the stop function to disable PWM.
Up (& lock); // release the semaphore
Return 0;
}
Static int s3c24xx_pwm_ioctl (struct inode * inode, struct file * file, unsigned int cmd, unsigned long Arg)
{// Select an operation based on the cmd
Switch (CMD)
{
Case pwm_ioctl_set_freq: If (ARG = 0)
Return-einval;
Pwm_set_freq (ARG );
Break;
Case pwm_ioctl_stop: pwm_stop ();
Break;
}
Return 0;
}
Static struct file_operations dev_fops =
{// Funtion provided by the driver to the upper layer
. Owner = this_module,
. Open = s3c24xx_pwm_open,
. Release = s3c24xx_pwm_close,
. IOCTL = s3c24xx_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 the mutex lock
Ret = misc_register (& MISC); // register the mise Device
Printk (device_name "/tinitialized/N ");
Return ret;
}
Static void _ exit dev_exit (void)
{
Misc_deregister (& MISC); // cancel the mise Device
}
Module_init (dev_init );
Module_exit (dev_exit );
Module_license ("GPL ");
Module_author ("friendlyarm Inc .");
Module_description ("S3C2410/S3C2440 PWM driver ");
Conclusion:
Through the study and testing of these drivers, I have a deeper understanding of the linxu driver. The example may be simple and superficial, but it must be more suitable for beginners, it provides a good starting point for the majority of beginners. There are many shortcomings in this series of articles. The most bad thing for me is that I did not post the upper-layer applications to share with you, let's see how the upper-layer applications call the functions provided by the underlying driver. (Because you are a little lazy ~~) If you need to view the application code, you can contact your colleagues at any time.
Because this series of driving learning is followed by one course, in the program annotation, the subsequent courses have not repeated the previous ones, if you have any questions, you can refer to your blog post on the same topic. If you have not found a satisfactory answer yet, contact me and learn and make progress together!