PWM principle and buzzer driver example development in ARM Linux

Publisher:SereneSoul55Latest update time:2016-06-15 Source: eefocusKeywords:PWM Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
1. What is PWM?

   PWM (Pulse Width Modulation) is simply a frequency conversion technology that controls the output voltage by changing the pulse width and controls the output frequency by changing the cycle. If it is still not clear, let's take a look at some examples in real life. Why does the speed of our electric fan change when we turn the button? Why does the volume of the radio change when we adjust the volume button? And the buzzer we will talk about later will also emit different frequencies according to different input values, etc.! ! These are all applications of PWM, and they are all controlled by the frequency signal output by PWM.

2. PWM in ARM Linux

   According to the manual of S3C2440, S3C2440A has five 16-bit timers inside. Timers 0, 1, 2, and 3 all have pulse width modulation (PWM) function. Timer 4 is an internal timer without output pins. Timer 0 has a dead zone generator for high current devices. See the figure below for explanation!!
PWM principle and buzzer driver example development in ARM Linux


Based on the technical manual of S3C2440 and the structure diagram above, let's summarize the characteristics of the internal timer module of 2440:
 
1) There are 5 16-bit timers in total, and timers 0, 1, 2, and 3 all have pulse width modulation function (PWM);
2) Each timer has a comparison buffer register (TCMPB) and a count buffer register (TCNTB);
3) Timers 0 and 1 share an 8-bit prescaler (prescaler), and timers 2, 3, and 4 share another 8-bit prescaler (prescaler), and its value range is 0~255;
4) Timers 0 and 1 share a clock divider, and timers 2, 3, and 4 share another clock divider. Both clock dividers can generate 5 different divided signal values ​​(i.e.: 1/2, 1/4, 1/8, 1/16 and TCLK);
5) The two 8-bit prescalers are programmable and divide PCLK according to the loaded value. The values ​​of the prescaler and clock divider are stored in the timer configuration registers TCFG0 and TCFG1 respectively;
6) There is a TCON control register that controls the properties and status of all timers. Bits 0 to 7 of TCON control timer 0, bits 8 to 11 control timer 1, bits 12 to 15 control timer 2, bits 16 to 19 control timer 3, and bits 20 to 22 control timer 4.
 
Still according to the description of the S3C2440 manual and the structure of the figure above, the steps to start a PWM timer function are as follows (assuming that the first timer is used):
 
1) Set the prescaler value and clock division value of timer 0 respectively for the comparison buffer register and count buffer register of timer 0;
2) Set the initial value of the comparison buffer register TCMPB0 and the count buffer register TCNTB0 (that is, the output clock frequency of timer 0);
3) Turn off the dead zone generator of timer 0 (set the 4th bit of TCON);
4) Turn on the automatic reload of timer 0 (set the 3rd bit of TCON);
5) Turn off the inverter of timer 0 (set the 2nd bit of TCON);
6) Turn on the manual update TCNTB0&TCMPB0 function of timer 0 (set the 1st bit of TCON);
7) Start timer 0 (set the 0th bit of TCON);
8) Clear the manual update TCNTB0&TCMPB0 function of timer 0 (set the 1st bit of TCON).
 
From this, we can see that the output frequency of PWM is related to the values ​​of the comparison buffer register and the count buffer register, and the values ​​of the comparison buffer register and the count buffer register are related to the values ​​of the prescaler and the clock divider; to use the PWM function is actually to operate the relevant registers of the timer. There is also a formula in the manual: Timer output frequency = PCLK / {prescaler value + 1} / clock divider value. Let's use a buzzer example to illustrate the use of PWM function.

3. Buzzer driving example
 
1. Types and working principles of buzzers
    
   Buzzers are mainly divided into two types: piezoelectric buzzers and electromagnetic buzzers.
 
   Piezoelectric buzzers are mainly composed of multivibrators, piezoelectric buzzers, impedance matchers, resonance boxes, shells, etc. Some piezoelectric buzzers are also equipped with light-emitting diodes on their shells. Multivibrators are composed of transistors or integrated circuits. When the power is turned on (1.5~15V DC working voltage), the multivibrator starts to oscillate and outputs an audio signal of 1.5~2.5kHZ, and the impedance matcher drives the piezoelectric buzzer to sound.
 
   Electromagnetic buzzers are composed of oscillators, electromagnetic coils, magnets, vibrating diaphragms, and shells. After the power is turned on, the audio signal current generated by the oscillator passes through the electromagnetic coil, causing the electromagnetic coil to generate a magnetic field. Under the interaction between the electromagnetic coil and the magnet, the vibrating diaphragm vibrates periodically and sounds.
 
   The difference between active buzzers and passive buzzers: the word "source" does not refer to the power supply, but to the oscillation source, that is, the active buzzer has an oscillation source while the passive buzzer does not. The one with an oscillation source can make a sound when powered on, while the one without an oscillation source needs a pulse signal to drive it.

   Additional knowledge: How to make a simple buzzer
   1) Prepare the electromagnet M: Wind 100 turns of wire around an iron bolt about 6 cm long, leaving 5 cm at the end of the wire as a lead, tape the coil with transparent tape to prevent it from loosening, and then tape it to a box with tape, and the electromagnet is ready;
   2) Prepare the shrapnel P: Cut a 2 cm wide iron sheet from the tin can, bend it into a right angle, connect a lead of the electromagnet to the shrapnel, and then use tape to stick the shrapnel tightly to the wooden board;
   3) Use a paper clip as contact Q, raise the paper clip with a book, stick it with tape, lead out a wire, and connect the circuit as shown in the figure;
   4) Adjust the distance between M and P (by moving the box) so that the electromagnet can attract the shrapnel, and adjust the distance between the contact and the shrapnel so that they can just touch. You can hear a buzzing sound after power is turned on.

2. Analysis of the buzzer schematic on the development board
 PWM principle and buzzer driver example development in ARM Linux

From the schematic diagram, we can know that the buzzer is driven by the PWM signal through the GPB0 IO port, and the GPB0 port is a multiplexed IO port. To use it, you must first set it to TOUT0 PWM output mode.
 
3. Write a buzzer driver suitable for the development board, file name: my2440_pwm.c
 

/*
 ================================================
 Name: my2440_pwm.c
 Author: Huang Gang
 Date: 25/11/09
 Copyright: GPL
 Description: my2440 pwm driver
 ========================== ======================
 */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define PWM_MAJOR 0 //Main device number
#define PWM_NAME "my2440_pwm" //Device name

 

static int device_major = PWM_MAJOR; //The main device number dynamically generated by the system

//Open the device
static int pwm_open(struct inode *inode, struct file *file)
{
    //Set the multiplexing function of the GPB0 multiplexing port to TOUT0 PWM output
    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);

    return 0;
}

//Close the device
static int pwm_close(struct inode *inode, struct file *file)
{
    return 0;
}

//Control the device
static int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    if(cmd <= 0)//If the input parameter is less than or equal to 0, stop the buzzer
    {
        //Here we restore the GPB0 port to IO port output function. From the schematic diagram, we can see that giving a low level directly can stop the buzzer from working
        s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);
        s3c2410_gpio_setpin(S3C2410_GPB0, 0);
    }
    else //If the input parameter is greater than 0, the buzzer will start working. Different parameters will result in different buzzer frequencies
    {
        //Define some local variables
        unsigned long tcon;
        unsigned long tcnt;
        unsigned long tcfg1;
        unsigned long tcfg0;

        struct clk *clk_p;
        unsigned long pclk;

        //The following operations on each register are easier to understand by combining the steps of starting a PWM timer mentioned above and the PWM register operation section of the 2440 manual
        tcfg1 = __raw_readl(S3C2410_TCFG1); //Read the value of timer configuration register 1
        tcfg0 = __raw_readl(S3C2410_TCFG0); //Read the value of timer configuration register 0

        tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; 
        tcfg0 |= (50 - 1); //Set the value of tcfg0 to 49

        tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
        tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; //Set the value of tcfg1 to 0x0011, i.e.: 1/16

        __raw_writel(tcfg1, S3C2410_TCFG1); //Write the value tcfg1 to timer configuration register 1
        __raw_writel(tcfg0, S3C2410_TCFG0); //Write the value tcfg0 into the timer configuration register 0

        clk_p = clk_get(NULL, "pclk"); 
        pclk = clk_get_rate(clk_p); //Get the clock frequency of pclk from the system platform clock queue, defined in include/linux/clk.h
        tcnt = (pclk/50/16)/cmd; //Calculate the output clock frequency of timer 0 (pclk/{prescaler0 + 1}/divider value)

        __raw_writel(tcnt, S3C2410_TCNTB(0)); //Set the value of the timer 0 count buffer register
        __raw_writel(tcnt/2, S3C2410_TCMPB(0)); //Set the value of the timer 0 compare buffer register

        tcon = __raw_readl(S3C2410_TCON); //Read the value of the timer control register
                   
        tcon &= ~0x1f;
        tcon |= 0xb; //Turn off dead zone, auto-reload, turn off inverter, update TCNTB0&TCMPB0, start timer 0
        __raw_writel(tcon, S3C2410_TCON); //Set bits 0-4 of the timer control register, i.e. control timer 0
        
        tcon &= ~2;
        __raw_writel(tcon, S3C2410_TCON); //Clear the manual update bit of timer 0
    }

    return 0;
}

//Device operation structure
static struct file_operations pwm_fops = 
{
    .owner = THIS_MODULE,
    .open = pwm_open,
    .release = pwm_close,
    .ioctl = pwm_ioctl,
};

//Define a device class
static struct class *pwm_class;

static int __init pwm_init(void)
{
    //Register as a character device, the major device number is 0 for the system to automatically assign, the device name is my2440_pwm, and the registration is successful to return the dynamically generated major device number
    device_major = register_chrdev(PWM_MAJOR, PWM_NAME, &pwm_fops);

    if(device_major < 0)
    {
        printk(PWM_NAME " register falid!/n");
        return device_major;
    }

    //Register a device class so that mdev can automatically create a device node in the /dev/ directory
    pwm_class = class_create(THIS_MODULE, PWM_NAME);

    if(IS_ERR(pwm_class))
    {
        printk(PWM_NAME " register class falid!/n");
        return -1;
    }

    //Create a device node, the device name is PWM_NAME, that is: my2440_pwm
    device_create(pwm_class, NULL, MKDEV(device_major, 0), NULL, PWM_NAME);

    return 0;
}

static void __exit pwm_exit(void)
{
    //Unregister device
    unregister_chrdev(device_major, PWM_NAME);

    //Delete device node
    device_destroy(pwm_class, MKDEV(device_major, 0));

    //Unregister device class
    class_destroy(pwm_class);
}

module_init(pwm_init);
module_exit(pwm_exit);

MODULE_LICENSE("PGL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("my2440 pwm driver");

 

4. Deploy the PWM buzzer driver code into the kernel.
 

#cp -f my2440_pwm.c /linux-2.6.30.4/drivers/char //Put the driver source code into the character device of the kernel driver

 

#gedit /linux-2.6.30.4/drivers/char/Kconfig //Add PWM buzzer device configuration

config MY2440_PWM_BEEP
    tristate "My2440 PWM Beep Device"
    depends on ARCH_S3C2440
    default y
    ---help---
      My2440 PWM Beep

 

#gedit /linux-2.6.30.4/drivers/char/Makefile //Add PWM buzzer device configuration

obj-$(CONFIG_MY2440_PWM_BEEP) += my2440_pwm.o


5. Configure the kernel and select the PWM buzzer device option

#make menuconfig

Device Drivers --->
    Character devices ---> 
        <*> My2440 PWM Beep Device (NEW)


6. Compile the kernel and download it to the development board. Note that we don't need to manually create device nodes on the development board now, because we are now using mdev for management (see: Device file system analysis and use for usage), and the driver has also added support for class device interfaces. Some of the drivers mentioned before do not have this, and we will use this method in the future. Now you can see the my2440_pwm device node that is automatically created in the /dev directory, and you can use it directly.

7. Write a test program for the PWM buzzer driver. File name: pwm_test.c

/*
 ==============================================
 Name: pwm_test.c
 Author : Huang Gang
 Date : 25/11/2009
 Copyright : GPL
 Description : my2440 pwm driver test
 =========================== ===================
 */

#include
#include
#include
#include

int main(int argc, char **argv)
{
    int tmp;
    int fd;
    int i;

    //Open the buzzer device
    fd = open("/dev/my2440_pwm", O_RDWR);

    if(fd < 0)
    {
        printf("Open PWM Device Faild!/n");
        exit(1);
    }

    //Prompt the user to enter a parameter to adjust the frequency of the buzzer. 0 means stop working
    printf("please enter the times number(0 is stop):/n");

    while(1)
    {
        //Input parameters
        scanf("%d", &tmp);
        printf("times = %d/n", tmp);
        
        //IO Control
        ioctl(fd, tmp);

        if(tmp <= 0)
        {
            break;
        }
    }

    //Close the device
    close(fd);

    return 0;
}


8. Cross-compile the test application on the development host and put it in the /usr/sbin directory of the file system, then recompile the file system and download it to the development board.

#arm-linux-gcc -o pwm_test pwm_test.c


9. Run the test program on the development board. You can see that the buzzer will make different frequencies of sound according to the size of the input parameters. Enter 0 to stop the buzzer.

Keywords:PWM Reference address:PWM principle and buzzer driver example development in ARM Linux

Previous article:uboot porting process on ARM s3c2410
Next article:zc301 camera driver and using serfox and spcaview in S3C2410

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号