2015.4.8
星期三 晴天

今天老师讲的内容是内核编写led和pwm驱动,实现花样灯和放歌的功能。理解应用和驱动的对接,最后自己实现了在放歌的时候根据
歌曲的节奏亮灭一个小灯,应为两个独立的驱动都已经写好,想要组合其实很简单,只要在主调函数里面打开两个驱动的设备节点,
分别进行操作并有机的组合在一起就行了。最后老师复习了中断的一些基础知识,总结一下:

异常处理:
当异常发送时:
nand flash 拷贝到sdram中运行,这是和nor flash 的区别之一

1.拷贝cpsr到spsr
2.设置适当的cpsr位
2.1 改变处理器状态进入arm态
2.2 改变处理器模式进入相应的异常模式
2.3 设置中断禁止位禁止相应中断
3.保存返回地址pc+4到lr
4.设置pc相应的异常向量

返回时,异常处理需要:
1.从SPSR恢复到CPSR
2.从LR恢复到PC
3.Noto: 这些操作只能在ARM态执行

中断的控制步骤:
1.要想使用中断:首先需要打开总中断
2.设置中断控制器使能(vic0intenable)
3.设置外设屏蔽寄存器:mask
4.相应管脚设成可以触发中断
5.设置中断激发方式:上升沿,下降沿......
6.中断处理完成后需要清pending, pending 标识这个外设曾经发生过中断,中断触发后自动置1

笼统的说就是:
总中断
mask
enable
清pending 不然会一直中断

下面是pwm控制的内核驱动程序,可参考学习:
#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 这是偏移量的基址,下面的寄存器地址加上这个基址构成真正的地址
#define TCFG0 0x0
#define TCFG1 0x4
#define TCON 0x8
#define TCNTB1 0x18
#define TCMPB1 0x1c

#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); 先读后写,避免改变寄存器的其他位,值得学习
/*init first prescaler: fout = pclk/(value + 1) */
writel(65, vtimer_base + TCFG0);
/*init mux*/
writel(readl(vtimer_base + TCFG1) & ~(0xf << 4), vtimer_base + TCFG1);
/*init TCMPB1 TCNTB1*/
writel(1000, vtimer_base + TCNTB1);
writel(500, 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(); 在打开设备的时候初始化定时器的设置,方便应用的操作
printk(KERN_INFO "s5pc100_pwm open! \n");
return 0;
}

static int s5pc100_pwm_release(struct inode *nodp, struct file *filp)
{
timer_stop(); 在退出程序的时候,停止定时器
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); 将物理地址转化成虚拟地址,每个地址转换为四字节
vtimer_base = ioremap(TIMER_BASE, 0x20); ***这里需要注意一下,因为这里不是针对一个地址转化,这里表示一组地址,所以转换地址需要够大才行***

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("minj@farsight.com.cn");
MODULE_DESCRIPTION("just for test!");

led小灯的驱动实现程序,很简单,但是很有参考价值,这些都是基础:

#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: 这个参数相当于应用的命令
printk(KERN_INFO "cmd = %d arg = %lu \n", cmd, arg);
writel(readl(vgpg3dat) | (1 << arg), vgpg3dat); 注意参数的作用和传递方式
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("minj@farsight.com.cn");
MODULE_DESCRIPTION("just for test!");

驱动实现led,pwm和中断基础知识的更多相关文章

  1. Ok6410裸机驱动学习(二)ARM基础知识

    1.ARM工作模式 ARM微处理器支持7种工作模式,分别为: l  用户模式(usr):ARM处理器正常的程序执行状态(Linux用户态程序) l  快速中断模式(fiq):用于高速数据传输或通道处理 ...

  2. 使用寄存器点亮LED——前言基础知识

    在点亮LED之前,我们需要具备一些基础知识: GPIO—general purpose intput output 是通用输入输出端口的简称,简单来说就是软件可控制的引脚,STM32芯片的GPIO引脚 ...

  3. [Windows驱动开发](二)基础知识——数据结构

    本节主要介绍驱动开发的一些基础知识. 1. 驱动程序的基本组成 1.1. 最经常见到的数据结构 a. DRIVER_OBJECT驱动对象 // WDK中对驱动对象的定义 // 每个驱动程序都会有一个唯 ...

  4. 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章       STM32H7的TIM定时器基础知识和H ...

  5. 【STM32H7教程】第47章 STM32H7的FMC总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第47章       STM32H7的FMC总线基础知识和HA ...

  6. 嵌入式系统基础知识(一): 系统结构和嵌入式Linux

    目录 一. 嵌入式体系结构 二. 开发过程中的分工 三. 嵌入式软件体系结构 四. 嵌入式Linux 一. 嵌入式体系结构 <嵌入式系统设计师教程>这本书的前三章脉络很清晰, 按照嵌入式系 ...

  7. 【RL-TCPnet网络教程】第18章 BSD Sockets基础知识

    第18章      BSD Sockets基础知识 本章节为大家讲解BSD Sockets,需要大家对BSD Sockets有个基础的认识,方便后面章节Socket实战操作. (本章的知识点主要整理自 ...

  8. 【RL-TCPnet网络教程】第5章 PHY芯片和STM32的MAC基础知识

    第5章        PHY芯片和STM32的MAC基础知识 本章节为大家讲解STM32自带的MAC和PHY芯片的基础知识,为下一章底层驱动的讲解做一个铺垫. 5.1   初学者重要提示 5.2    ...

  9. 转帖--计算机网络基础知识大总汇 https://www.jianshu.com/p/674fb7ec1e2c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

    计算机网络基础知识大总汇 龙猫小爷 关注 2016.09.14 23:01* 字数 12761 阅读 30639评论 35喜欢 720 一.什么是TCP/IP 网络和协议 1.     TCP/IP是 ...

随机推荐

  1. 初识less

    1 less 安装使用 安装 sudo npm install node-less 使用 mkdir less cd /less lessc demo1.less > test1.css les ...

  2. div跟随页面滚动

    $(document).ready(function(){ var timer; $(window).scroll(function (){ clearInterval(timer); var top ...

  3. CA02检验计划批量导入 模板在文件

    *&---------------------------------------------------------------------* *& PROGRAM NAME(EN) ...

  4. protocol buffer c++ python库安装

    c++库安装较简单,不要用源码,还得下载依赖,就被墙了 https://github.com/google/protobuf/releases  下载一个最新的release安装 #protoc -- ...

  5. php简单单例模式

    所谓单例模式,适用于使用一个对象可以完成所有的业务逻辑的类(一般不考虑继承的类) //单例模式 function getInstance($class_name){ //创建一个存储各种需要单例的类的 ...

  6. Linux小知识积累

    1.Linux图形界面和字符命令行界面的切换 从图形界面切换到字符界面,使用快捷键 Ctrl+Alt+F1 从字符界面切换到图形界面,使用快捷键 Ctrl+Alt+F7 2.解压文件 tar -xzv ...

  7. Gulp的使用教程

  8. 前端工程师IE6兼容性问题随笔(未完待续)

    1 height.在IE6下元素高度小于19px的时候,会被当做19px来处理.解决办法:用overflow:hidden;来处理.box{height:2px;background:red;over ...

  9. hadoop输入分片计算(Map Task个数的确定)

    作业从JobClient端的submitJobInternal()方法提交作业的同时,调用InputFormat接口的getSplits()方法来创建split.默认是使用InputFormat的子类 ...

  10. java获取当前时间前一周、前一月、前一年的时间

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar c = Calend ...