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. 通过OCCI连接oracle(C++)

    OCCI介绍 OCCI:Oracle C++调用接口(OCCI),即Oracle的C++API,允许你使用面向对象的特性.本地类.C++语言的方法来访问Oracle数据库. OCCI优势 基于标准C+ ...

  2. php date("Y-m-d H:i:s") 出现警告信息

    使用: $createtime=date("Y/m/d H:i:s", time()+16*60*60); $createtime=date("Y-m-d H:i:s&q ...

  3. JQmeter显示进度条

    <script type="text/javascript"> $(function(){ var percent = "${evICVDBean.resul ...

  4. bom和dom总结

    BOM1.1    介绍    1.BOM是browser object model的缩写,简称浏览器对象模型    2.BOM提供了独立于内容而与浏览器窗口进行交互的对象    3.由于BOM主要用 ...

  5. 第二篇:Retrofit调用流程图和使用到的设计模式

    2016-05-08 09:35:58 这篇文章解析一下Retrofit的调用流程 1. 先看一下我们是如何使用Retrofit的,代码如下: public interface WeatherData ...

  6. C# 中 多线程同步退出方案 CancellationTokenSource

    C# 中提供多线程同步退出机制,详参对象: CancellationTokenSource CancellationTokenSource 中暂未提供复位操作,因此当调用Cancle 之后,若再次调用 ...

  7. Bootstrap <基础十八>面包屑导航(Breadcrumbs)

    面包屑导航(Breadcrumbs)是一种基于网站层次信息的显示方式.以博客为例,面包屑导航可以显示发布日期.类别或标签.它们表示当前页面在导航层次结构内的位置. Bootstrap 中的面包屑导航( ...

  8. 嵌入式Linux的调试技术

    本节我们研究嵌入式Linux的调试技术,对于复杂的Linux驱动及HAL等程序库,需要使用各种方法对其进行调试.刚开始讲了打印内核调试信息:printk,这个函数的用法与printf函数类似,只不过p ...

  9. com.android.internal.os.ZygoteInit$MethodAndArgsCaller 解决

    好久没写博客了,带着点小愧疚来,添上几个字: 这是今天遇到的一个bug,之前也遇到过,为了后面方便,就记下. bug提示:com.android.internal.os.ZygoteInit$Meth ...

  10. JQuery 判断不同浏览器

    if($.browser.msie) {          //IE浏览器alert("this is msie"); } else if($.browser.safari)   ...