Drive to implement LED,PWM and interrupt basics

Source: Internet
Author: User

2015.4.8
Sunny Wednesday

What the teacher said today is the kernel to write led and PWM driver, to achieve the function of pattern lights and singing. Understand the application and the driver of the docking, and finally realized in the song at the time according to
The rhythm of the song Lights out a small lamp, should be two independent drivers have been written, want to combine is actually very simple, as long as in the master function inside open two drive device node,
operate separately and combine organically together. Finally, the teacher reviewed some of the basic knowledge of the interruption, summed up:

Exception handling:
When the exception is sent:
NAND flash copy to SDRAM run, which is one of the differences from NOR flash

1. Copy CPSR to SPSR
2. Set the appropriate CPSR bit
2.1 Changing the processor state into the arm state
2.2 Changing the processor mode into the appropriate exception mode
2.3 Set interrupt disable bit to prohibit corresponding interrupt
3. Save return address pc+4 to LR
4. Set the corresponding exception vector for PC

On return, exception handling requires:
1. Recovering from SPSR to CPSR
2. Recovering from LR to PC
3.Noto: These operations can only be performed in the arm state

Control steps for interrupts:
1. To use interrupts: first you need to open a total interrupt
2. Set Interrupt Controller enable (vic0intenable)
3. Set the peripheral screen register: Mask
4. The corresponding PIN is set to trigger the interrupt
5. Set interrupt excitation mode: rising edge, falling edge ...
6. After the interrupt processing is completed need to clear pending, pending identified this peripheral has been interrupted, after the interrupt triggered automatically 1

The general saying is:
Total Interruption
Mask
Enable
Clear pending or it will be interrupted forever.

The following is a PWM-controlled kernel driver, which can be referenced by learning:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>/*copy_to_user copy_from_user*/
#include <asm/io.h>/*ioremap*/

#define Num_minors 1
#define MINOR_NR 0
#define DEV_NAME "S5pc100_pwm_char"
#define KBUF_SIZE 50

#define CMD_ON _io (' K ', 0)
#define Cmd_off _io (' K ', 1)

#define GPDCON 0xe0300080
#define Timer_base 0xea000000 This is the base address of the offset, the following register address plus this base number constitutes a real
#define TCFG0 0x0
#define Tcfg10x4
#define TCON 0x8
#define TCNTB1 0x18
#define TCMPB10X1C

#define Magic_number ' k '
#define BEEP_ON _io (magic_number,0)
#define Beep_off _io (magic_number,1)
#define SET_FRE _io (magic_number,2)

static void *vgpdcon;
static void *vtimer_base;

static struct Cdev S5pc100_pwm_cdev;
static int s5pc100_pwm_major = 250; /*documentation/devices.txt*/
Static dev_t S5pc100_pwm_devno;

Module_license ("GPL");


void Pwm_init (void)
{
/*init pin func as PWM tout1*/
Writel ((Readl (Vgpdcon) & ~ (0xf << 4)) | (0x2 << 4), Vgpdcon); Read and write first, avoid changing the other bits of the register, it is worth learning
/*init First prescaler:fout = pclk/(value + 1) */
Writel (vtimer_base + TCFG0);
/*init mux*/
Writel (Readl (vtimer_base + TCFG1) & ~ (0xf << 4), Vtimer_base + TCFG1);
/*init TCMPB1 tcntb1*/
Writel (vtimer_base + TCNTB1);
Writel (vtimer_base + TCMPB1);
/*manual update*/
Writel (Readl (vtimer_base + TCON) & ~ (0xf << 8) | (1 << 9),
Vtimer_base + TCON);
}

void Timer_start (void)
{
/*start Timer and enable auto reload*/
Writel (Readl (vtimer_base + TCON) & ~ (0xf << 8) | (1 << 11) | (1 << 8),
Vtimer_base + TCON);
}

void Timer_stop (void)
{
Writel (Readl (vtimer_base + TCON) & ~ (1 << 8),
Vtimer_base + TCON);
}

void set_tcntb1 (unsigned int value)
{
Writel (value, Vtimer_base + TCNTB1);
}

void set_tcmpb1 (unsigned int value)
{
Writel (value, Vtimer_base + TCMPB1);
}

static int S5pc100_pwm_open (struct inode *nodp, struct file *filp)
{
Pwm_init (); Initialize timer settings when opening the device for easy application operation
PRINTK (kern_info "S5PC100_PWM open! \ n ");
return 0;
}

static int s5pc100_pwm_release (struct inode *nodp, struct file *filp)
{
Timer_stop (); When exiting the program, stop the timer
PRINTK (kern_info "S5PC100_PWM release! \ n ");
return 0;
}


Static long
S5pc100_pwm_unlocked_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
Switch (CMD) {
Case BEEP_ON:
PRINTK (kern_info "cmd =%d arg =%lu \ n", CMD, ARG);
Timer_start ();
Break
Case Beep_off:
PRINTK (kern_info "cmd =%d arg =%lu \ n", CMD, ARG);
Timer_stop ();
Break
Case SET_FRE:
PRINTK (kern_info "cmd =%d arg =%lu \ n", CMD, ARG);
SET_TCNTB1 (1000000/ARG);
SET_TCMPB1 (1000000/(arg + arg));
Default
PRINTK (kern_info "No such command! \ n ");
Break
}
return 0;
}

struct File_operations s5pc100_pwm_ops = {
. open = S5pc100_pwm_open,
. Release = S5pc100_pwm_release,
. Unlocked_ioctl = S5pc100_pwm_unlocked_ioctl,
};

static int __init s5pc100_pwm_init (void)
{
int ret;

/*construct Cdev number*/
S5pc100_pwm_devno = MKDEV (S5pc100_pwm_major, MINOR_NR); S5pc100_pwm_major << 20 | S5pc100_pwm_minor;
ret = Register_chrdev_region (S5pc100_pwm_devno, num_minors, dev_name);

/*register Char Dev number*/
if (ret) {
PRINTK (kern_warning "Register_chrdev_region failed! \ n ");
ret = Alloc_chrdev_region (&s5pc100_pwm_devno, Minor_nr, Num_minors, dev_name);
if (ret) {
PRINTK (kern_warning "Alloc chrdev_region failed! \ n ");
Goto Err_0;
}
}

/*register Char device*/
/*cdev initialize*/
Cdev_init (&s5pc100_pwm_cdev, &s5pc100_pwm_ops);

/*add to kernel*/
ret = Cdev_add (&s5pc100_pwm_cdev, S5pc100_pwm_devno, num_minors);
if (ret) {
PRINTK (kern_warning "Cdev add failed! \ n ");
Goto err_1;
}

/*physical Address---> virtual address*/
Vgpdcon = Ioremap (Gpdcon, 4); Converts a physical address to a virtual address, with each address converted to four bytes
Vtimer_base = Ioremap (Timer_base, 0x20); Note here, because this is not for an address conversion, here represents a set of addresses, so the conversion address needs to be large enough to line * * *

PRINTK (kern_info "S5pc100_pwm_init! \ n ");
return 0;

/*error management*/
Err_1:
Unregister_chrdev_region (S5pc100_pwm_devno, num_minors);
ERR_0:
return ret;
}


static void __exit s5pc100_pwm_exit (void)
{
Iounmap (Vgpdcon);
Iounmap (vtimer_base);
Cdev_del (&s5pc100_pwm_cdev);
Unregister_chrdev_region (S5pc100_pwm_devno, num_minors);
PRINTK ("S5pc100_pwm_exit! \ n ");
}

Module_init (S5pc100_pwm_init);
Module_exit (S5pc100_pwm_exit);

Module_author ("[email protected]");
Module_description ("Just for test!");

LED Small Lamp driver Implementation program, very simple, but very valuable reference, these are the basis:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>/*copy_to_user copy_from_user*/
#include <asm/io.h>/*ioremap*/

#define Num_minors 1
#define MINOR_NR 0
#define DEV_NAME "S5pc100_led_char"
#define KBUF_SIZE 50

#define CMD_ON _io (' K ', 0)
#define Cmd_off _io (' K ', 1)

#define GPG3CON 0XE03001C0
#define GPG3DAT 0XE03001C4
static int *vgpg3con;
static int *vgpg3dat;

static struct Cdev S5pc100_led_cdev;
static int s5pc100_led_major = 250; /*documentation/devices.txt*/
Static dev_t S5pc100_led_devno;
static Char kbuf[kbuf_size];

Module_license ("GPL");

/**
* Led_init:init LED Pins
*/
void Led_init (void)
{
Writel ((Readl (Vgpg3con) & ~ (0xFFFF)) | 0x1111, Vgpg3con);
}

void Led_exit (void)
{
Writel (Readl (Vgpg3dat) & ~ (0xf), Vgpg3con);
}

static int S5pc100_led_open (struct inode *nodp, struct file *filp)
{
Led_init ();
PRINTK (kern_info "s5pc100_led open! \ n ");
return 0;
}
static int s5pc100_led_release (struct inode *nodp, struct file *filp)
{
Led_exit ();
PRINTK (kern_info "s5pc100_led release! \ n ");
return 0;
}


Static long
S5pc100_led_unlocked_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
Switch (CMD) {
Case CMD_ON: This parameter corresponds to the application's command
PRINTK (kern_info "cmd =%d arg =%lu \ n", CMD, ARG);
Writel (Readl (Vgpg3dat) | (1 << Arg), vgpg3dat); Notice the function of the parameter and the way of delivery
Break
Case Cmd_off:
PRINTK (kern_info "cmd =%d arg =%lu \ n", CMD, ARG);
Writel (Readl (Vgpg3dat) & ~ (1 << arg), vgpg3dat);
Break
Default
PRINTK (kern_info "No such command! \ n ");
Break
}
return 0;
}

struct File_operations s5pc100_led_ops = {
. open = S5pc100_led_open,
. Release = S5pc100_led_release,
. Unlocked_ioctl = S5pc100_led_unlocked_ioctl,
};

static int __init s5pc100_led_init (void)
{
int ret;

/*construct Cdev number*/
S5pc100_led_devno = MKDEV (S5pc100_led_major, MINOR_NR); S5pc100_led_major << 20 | S5pc100_led_minor;
ret = Register_chrdev_region (S5pc100_led_devno, num_minors, dev_name);

/*register Char Dev number*/
if (ret) {
PRINTK (kern_warning "Register_chrdev_region failed! \ n ");
ret = Alloc_chrdev_region (&s5pc100_led_devno, Minor_nr, Num_minors, dev_name);
if (ret) {
PRINTK (kern_warning "Alloc chrdev_region failed! \ n ");
Goto Err_0;
}
}

/*register Char device*/
/*cdev initialize*/
Cdev_init (&s5pc100_led_cdev, &s5pc100_led_ops);

/*add to kernel*/
ret = Cdev_add (&s5pc100_led_cdev, S5pc100_led_devno, num_minors);
if (ret) {
PRINTK (kern_warning "Cdev add failed! \ n ");
Goto err_1;
}

/*physical Address---> virtual address*/
Vgpg3con = Ioremap (Gpg3con, 4);
Vgpg3dat = Ioremap (Gpg3dat, 4);

PRINTK (kern_info "S5pc100_led_init! \ n ");
return 0;

/*error management*/
Err_1:
Unregister_chrdev_region (S5pc100_led_devno, num_minors);
ERR_0:
return ret;
}


static void __exit s5pc100_led_exit (void)
{
Iounmap (Vgpg3con);
Iounmap (Vgpg3dat);
Cdev_del (&s5pc100_led_cdev);
Unregister_chrdev_region (S5pc100_led_devno, num_minors);
PRINTK ("S5pc100_led_exit! \ n ");
}

Module_init (S5pc100_led_init);
Module_exit (S5pc100_led_exit);

Module_author ("[email protected]");
Module_description ("Just for test!");

Drive to implement LED,PWM and interrupt basics

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.