驱动实现led,pwm和中断基础知识
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和中断基础知识的更多相关文章
- Ok6410裸机驱动学习(二)ARM基础知识
1.ARM工作模式 ARM微处理器支持7种工作模式,分别为: l 用户模式(usr):ARM处理器正常的程序执行状态(Linux用户态程序) l 快速中断模式(fiq):用于高速数据传输或通道处理 ...
- 使用寄存器点亮LED——前言基础知识
在点亮LED之前,我们需要具备一些基础知识: GPIO—general purpose intput output 是通用输入输出端口的简称,简单来说就是软件可控制的引脚,STM32芯片的GPIO引脚 ...
- [Windows驱动开发](二)基础知识——数据结构
本节主要介绍驱动开发的一些基础知识. 1. 驱动程序的基本组成 1.1. 最经常见到的数据结构 a. DRIVER_OBJECT驱动对象 // WDK中对驱动对象的定义 // 每个驱动程序都会有一个唯 ...
- 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章 STM32H7的TIM定时器基础知识和H ...
- 【STM32H7教程】第47章 STM32H7的FMC总线基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第47章 STM32H7的FMC总线基础知识和HA ...
- 嵌入式系统基础知识(一): 系统结构和嵌入式Linux
目录 一. 嵌入式体系结构 二. 开发过程中的分工 三. 嵌入式软件体系结构 四. 嵌入式Linux 一. 嵌入式体系结构 <嵌入式系统设计师教程>这本书的前三章脉络很清晰, 按照嵌入式系 ...
- 【RL-TCPnet网络教程】第18章 BSD Sockets基础知识
第18章 BSD Sockets基础知识 本章节为大家讲解BSD Sockets,需要大家对BSD Sockets有个基础的认识,方便后面章节Socket实战操作. (本章的知识点主要整理自 ...
- 【RL-TCPnet网络教程】第5章 PHY芯片和STM32的MAC基础知识
第5章 PHY芯片和STM32的MAC基础知识 本章节为大家讲解STM32自带的MAC和PHY芯片的基础知识,为下一章底层驱动的讲解做一个铺垫. 5.1 初学者重要提示 5.2 ...
- 转帖--计算机网络基础知识大总汇 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是 ...
随机推荐
- netty4.0.x源码分析—bootstrap
Bootstrap的意思就是引导,辅助的意思,在编写服务端或客户端程序时,我们都需要先new一个bootstrap,然后基于这个bootstrap调用函数,添加eventloop和handler,可见 ...
- java系统高并发解决方案-转
转载博客地址:http://blog.csdn.net/zxl333/article/details/8685157 一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图 ...
- Windows Store App JavaScript 开发:WinJS库控件
在介绍了如何使用标准的HTML控件以及WinJS库中提供的新控件之后,下面来着重介绍WinJS库中几种常用的控件. (1)ListView控件 在开发Windows应用商店应用时可以使用ListVie ...
- 实现UniqueAttribute唯一性约束,sqlunique约束[转]
using System; using System.ComponentModel.DataAnnotations; using System.Data.Entity; namespace Zwj.T ...
- MVC模式与Android
MVC模式是软件工程中的一种软件架构,“Model-View-Controller”的缩写,中文翻译为“模型-视图-控制器”. MVC模式将一个交互式应用程序分为3各组件: 1.Model(模型):业 ...
- jquery ajax跨域调用
客户端: //ajax跨域调用的方法 $.ajax({ url:dustUrl+"/screenshot/getDevices.do", type: "get" ...
- Monkey for iOS(CrashMonkey4IOS)
CrashMonkey4IOS介绍 支持真机测试.模拟器测试 支持收集系统日志(Systemlog).崩溃日志(Crashlog).instrument行为日志 支持测试报告截图,绘制行为轨迹 支持测 ...
- Android中View的事件分发机制
简介 事件也称MotionEvent,事件分发机制就是对MotionEvent事件的分发过程,即当一个MotionEvent发生之后,系统需要把这个事件传递给一个具体的View. 点击事件的分发过程由 ...
- 【JavaScript】固定布局轮播图特效
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Android 设置ListView当前显示的item
项目中可能会有这种需求:动态设置ListView显示的item 这种需求可能会出现在不同的情况下,有的是打开页面就要显示在特定的位置,也有的是浏览列表时实时更新数据并且改变了集合中数据,或者是某种条件 ...