- 2019-02-15
-
发表了主题帖:
芯灵思SinlinxA33开发板Linux内核 tasklet 机制(附实测代码)
Linux 中断编程分为中断顶半部,中断底半部
中断顶半部: 做紧急,耗时短的事情,同时还启动中断底半部。
中断底半部: 做耗时的事件,这个事件在执行过程可以被中断。
中断底半部实现方法: tasklet,工作队列,软中断等机制实现。实际上是把耗时事件推后执行,不在中断程序执行。
什么是tasklet?
Tasklet 一词的原意是“小片任务”的意思,这里是指一小段可执行的代码,且通常以函数的形式出现。这个 tasklet 绑定的函数在一个时刻只能在一个 CPU 上运行 ,tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简单的。一个使用 tasklet 的中断程序首先会通过执行中断处理程序来快速完成上半部分的工作,接着通过调用 tasklet 使得下半部分的工作得以完成。可以看到,下半部分被上半部分所调用,至于下半部分何时执行则属于内核的工作。
tasklet 机制核心数据结构
tasklet 机制核心数据结构
Interrupt.h linux-3.5\include\Linux
struct tasklet_struct
{
struct tasklet_struct *next; // tasklet_struct 结构链表
unsigned long state; //当前这个 tasklet 是否已经被调度
atomic_t count;
void (*func)(unsigned long); //指向 tasklet 绑定的函数的指针
unsigned long data; //传递给tasklet 绑定的函数的参数
};复制代码
tasklet 相关 API
初始化相关
1. 静态初始化 DECLARE_TASKLET(name, func, data)
作用:定义一个名字为 name 的 tasklet_struct 结构变量,并且初始化这个结构。 所定义的这个 tasklet 是可以被调度,默认是处于被使能状态。
2. 静态初始化 DECLARE_TASKLET_DISABLED(name, func, data)
作用:定义一个名字为 name 的 tasklet_struct 结构变量,并且初始化这个结构。所定义的这个 tasklet 是不能被调度,默认是处于被禁能状态。 要调度这个 tasklet 需要先使能。
3.动态初始化
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
作用:初始化一个 tasklet_struct 结构变量,初始化的结构默认是处于激活状态,可以被调度。
tasklet使能函数
1. void tasklet_disable(struct tasklet_struct *t)
作用:函数激活给定的 tasklet被 tasklet_schedule 调度
2. void tasklet_enable (struct tasklet_struct *t)
作用:函数禁止给定的 tasklet被 tasklet_schedule 调度
tasklet 调度函数
void tasklet_schedule (struct tasklet_struct *t)
作用:调用 tasklet_schedule 函数去通知内核帮我们调度所绑定的函数
void tasklet_kill(struct tasklet_struct *t);
作用:取消调度函数
编程步骤
Step1 定义并静态初始化tasklet_struct 结构变量
Step2 编写tasklet服务函数
Step3 在适当的地地方进行调度
Step4 在适当的地地方取消调度
实验平台:芯灵思SINA33开发板
嵌入式linux 开发板交流 QQ:641395230
驱动代码:
#include
#include
#include
void tasklet_fun(unsigned long data);
//Step1 定义并静态初始化tasklet_struct 结构变量
DECLARE_TASKLET(mytasklet, tasklet_fun, 651);
//Step2 tasklet服务函数
void tasklet_fun(unsigned long data)
{
static unsigned long count = 0;
printk("count:%lu,%s is call! data:%lu\r\n",count++,__FUNCTION__,data);
tasklet_schedule(&mytasklet); //在工作函数中重新调度自己,这样会循环调用tasklet_fun
}
static int __init mytasklet_init(void)
{
//Step3 开始调度 mytasklet
tasklet_schedule(&mytasklet);
printk("%s is call!\r\n",__FUNCTION__);
return 0;
}
static void __exit mytasklet_exit(void) //Module exit function specified by module_exit()
{
//Step4 删除 tasklet
tasklet_kill(&mytasklet);
}
module_init(mytasklet_init);
module_exit(mytasklet_exit);
MODULE_LICENSE("GPL");
复制代码
实验现象
- 2019-02-13
-
发表了主题帖:
芯灵思SinlinxA33开发板Linux中断编程4-最终代码(1)
按键驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_BUTTON "mybtn"
#define BTN_MAJOR 255
static char keybuf[] = {"0"};
static struct miscdevice misc = {
.minor = BTN_MAJOR,
.name = DEVICE_BUTTON,
};
irqreturn_t key_isr(int irq, void* dev)
{
int btn = 0;
//读取按键状态
btn= !gpio_get_value(GPIOL(14));
printk("key %s\r\n", btn ? "down" : "up");
//把按键状态更新到对应的按缓冲中
keybuf = “0” + btn;
return IRQ_HANDLED;
}
static ssize_t btn_read (struct file *flp, char __user *buff, size_t count, loff_t * off)
{
int ret ;
if(!count) {
return 0;
}
ret = copy_to_user(buff, keybuf, count);
if(ret) {
printk("error:copy_to_user\r\n");
return -EFAULT;
}
return count;
}
static const struct file_operations dev_fops = {
.read = btn_read ,
.owner = THIS_MODULE,
};
static int __init btn_init(void)
{
int ret;
int irq;
int flags;
flags = IRQ_TYPE_EDGE_BOTH;
irq = gpio_to_irq( GPIOL(14) );
//发生中断号为irq的中断会执行key_isr函数。注册成功会在/proc/irq号/KEY文件夹出现或 cat /proc/interrupts, 记录了本中断发生的次数cat /proc/irq/442/spurious
ret = request_irq(irq,key_isr, flags, "KEY",NULL);
if(ret < 0)
{
irq = gpio_to_irq( GPIOL(14) );
disable_irq(irq);
free_irq(irq, NULL);
return ret;
}
ret = misc_register(&misc);
printk(KERN_EMERG " Device registered \n ");
return ret;
}
static void __exit btn_exit(void)
{
int irq;
irq = gpio_to_irq( GPIOL(14) );
disable_irq(irq);
free_irq(irq, NULL);
misc_deregister(&misc);
printk(KERN_EMERG " Equipment logged out \n");
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");
复制代码
测试应用代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEV_NAME "/dev/mybtn"
int main(int argc, char *args[])
{
int fd = 0;
int ret = 0;
unsigned char recv_buf[1] = {"0"};
fd = open(DEV_NAME, O_RDONLY);
//fd = open(DEV_NAME, O_RDONLY|O_NONBLOCK);
if(fd < 0) {
perror("open");
}
while(1) {
strcpy(recv_buf, "0000");
//读取按键数据
ret = read(fd, recv_buf, 1);
if((ret < 0) && (errno != EAGAIN)) {
perror("read");
exit(-1);
}
//输出按键状态
printf("%s\r\n", recv_buf);
}
return 0;
}
复制代码
Makefile代码:
KERN_DIR = /work/lichee/linux-3.4
all:
make -C $(KERN_DIR) M=`pwd` modules
arm-none-linux-gnueabi-gcc btntest.c -o btntest
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += btn_drv.o
复制代码
遗憾的是,代码虽然能编译成功,但是驱动加载有错误,等找出原因再发帖补充,同时也希望大神指点
开发板交流群 QQ:641395230
此内容由EEWORLD论坛网友babyking原创,如需转载或用于商业用途需征得作者同意并注明出处
- 2019-02-12
-
发表了主题帖:
芯灵思Sinlinx A33开发板 Linux内核等待队列poll ---阻塞与非阻塞
阻塞与非阻塞的概念
阻塞:阻塞调用是指调用结果返回之前,当前进程程会被挂起(休眠)。函数只有在得到结果之后才会返回。默认情况下,文件都是以这种方式打开。
非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前进程程,而会立刻返回。应用程序可选择以阻塞或非阻塞方式打开设备文件,然后设备进行读写操作,如果驱动的读写函数支持阻塞和非阻塞功能,这两种打开方式才会有区别。
阻塞示例 :fd = open("/xxx/word", O_RDONLY ); // 默认阻塞方式打开
如果此时没有数据可以读取,则执行休眠
如果有数据可以读取,则马上读取数据,不休眠,读取数据后马上返回。
非阻塞示例 :fd = open("/xxx/word", O_RDONLY | O_NONBLOCK ); //非阻塞方式打开
如果此时已经有数据可以读取,则读取数据再返回。
如果没有数据可以读,也马上返回,但是返回一个错误码。
1)驱动中如何得到用户空间应用程序打开的方式?
open一个设备,内核会创建一个file结构,并且把打开方式的数值存放到file结构成员f_flags成员中,驱动程序的read,write 接口可以使用参数file指针取得文件打开方式。file结构中有一个成员是f_flags ,创建时候,内核会把open 函数的最后一个参数 flag 数值保存在 f_flags 变量中。
static ssize_t xxx_read(struct file *pfile, char user *buf, size_t count, loff_t *poff)
{
……
//判断当前是否有按键动作
if(没有按键动作)
{
//判断 pfile->f_flags 成员是否设置 O_NONBLOCK
if(pfile->f_flags & O_NONBLOCK) //表示用户空间使用非阻塞打开
{
return - EAGAIN; //返回一个错误码,告诉用户空间你可以再尝试读取
}
//阻塞方式打开,没有数据就休眠,不马上返回else
{
//休眠,等待有按键动作唤醒进程。
}
}
}
2)如何知道是否有按键动作?
如果按键按键或松开时刻,会产生一个中断,所以,在中断程序设置一个标志即可。
定义一个全局变量,初始值为 0,表示没有按键动作发生,在中断程序中设置这个变量值为 1,表示发生按键动作。
3)如何让进程进入休眠状态?
最简单,最直接的休眠方式: msleep 函数
这个函数:一旦调用,则调用进程会休眠指定长的时间,时间一到内核会唤醒这个进程.
//休眠,等待有按键动作唤醒进程。
while(press == 0)
msleep(5); // 休眠5ms
- 2019-02-11
-
发表了主题帖:
芯灵思Sinlinx A33开发板 Linux中断编程 3--- 应用程序
应用程序代码参考
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <errno.h>
#define DEV_NAME "/dev/mybtn"
int main(int argc, char *args[])
{
int fd = 0;
int ret = 0;
unsigned char recv_buf[1] = {"0"};
fd = open(DEV_NAME, O_RDONLY);
//fd = open(DEV_NAME, O_RDONLY|O_NONBLOCK);
if(fd < 0) {
perror("open");
}
while(1) {
strcpy(recv_buf, "0000");
//读取按键数据
ret = read(fd, recv_buf, 1);
if((ret < 0) && (errno != EAGAIN)) {
perror("read");
exit(-1);
}
//输出按键状态
printf("%s\r\n", recv_buf);
}
return 0;
}
- 2019-02-01
-
发表了主题帖:
芯灵思Sinlinx A33开发板 Linux中断编程 2--- 程序框架
根据上一个帖子的分析,想要实现按键中断,首先得知道引脚对应的中断号,LRADC0对应的中断编号#define SUNXI_IRQ_LRADC (SUNXI_GIC_START + 30) /* 62*/
通过static inline int gpio_to_irq(SUNXI_IRQ_LRADC)这个函数确定这个IO上的外部中断编号
程序流程,当引脚电平发生变化触发中断,进入中断函数把按键状态更新,此时在应用程序读相应设备,则可以知道引脚状态
驱动程序框架
#define BTN_MAJOR 255
static struct miscdevice misc = {
.minor = BTN_MAJOR, //次设备号
.name = DEVICE_BUTTON,//设备名
.fops = &dev_fops, //文件操作方法
};
//按键标志,'0'表示没有按键,'1'表示按下了
static char keybuf[1] = {‘0’};
//中断处理函数声明
irqreturn_t key_isr(int irq, void* dev);
//读函数,读取引脚状态
static ssize_t btn_read (struct file *flp, char __user *buff, size_t count, loff_t * off);
//file_operations结构体
static const struct file_operations dev_fops = {
.read = btn_read ,
.owner = THIS_MODULE,
};
//初始化函数
static int __init btn_init(void);
//注销函数
static void __exit btn_exit(void);
首先先看初始化函数static int __init btn_init(void);
static int __init btn_init(void)
{
int ret;
int irq;//中断号
int flags; //触发标志
flags = IRQ_TYPE_EDGE_BOTH; //设置为双边触发
//得到中断号
irq = gpio_to_irq(SUNXI_IRQ_LRADC);
//注册中断
ret = request_irq(irq,key_isr, flags, “key”, NULL);
if(ret < 0)
break;
//如果不是成功,注销已经注册的中断
if(ret < 0) {
irq = gpio_to_irq(SUNXI_IRQ_LRADC);
disable_irq(irq);
free_irq(irq, NULL);
return ret;
}
//注册杂项设备
ret = misc_register(&misc);
printk(KERN_EMERG " Device registered \n ");
return ret;
}
注销函数 static void __exit btn_exit(void);
static void __exit btn_exit(void)
{
int irq;
//注销中断
irq = gpio_to_irq(SUNXI_IRQ_LRADC);
disable_irq(irq);
free_irq(irq, NULL);
//注销杂项设备
misc_deregister(&misc);
printk(KERN_EMERG " Equipment logged out \n");
}
读函数,读取引脚状态 static ssize_t btn_read (struct file *flp, char __user *buff, size_t count, loff_t * off);
static ssize_t tiny4412_read (struct file *flp, char __user *buff, size_t count, loff_t * off)
{
int ret ;
//count为0,直接返回
if(!count) {
return 0;
}
//复制数据到用户空间
ret = copy_to_user(buff, keybuf, count);
if(ret) {
printk("error:copy_to_user\r\n");
return -EFAULT;
}
return count;
}
中断服务函数irqreturn_t key_isr1(int irq, void* dev)
irqreturn_t key_isr1(int irq, void* dev)
{
//存放按键状态
int btn = 0;
btn= !gpio_get_value(SUNXI_IRQ_LRADC );
//把按键状态更新到对应的按缓冲中
keybuf[0] = dn + '0';
//输出按键提示
printk("key %s\r\n", dn ? "down" : "up");
return IRQ_HANDLED;//目前只是实现一个按键的,这个是共享中断的情况才用到, 在中断到来时,会遍历共享此中断的所有中断处理程序, 直到某一个中断服务函数时返回 IRQ_HANDLED。
}
未完待续... ....
- 2019-01-31
-
发表了主题帖:
芯灵思Sinlinx A33开发板 Linux中断编程 1--- 原理说明
本节实验目标实现按键触发中断终端显示按键松开或按下
实验平台 芯灵思Sinlinx A33 开发板
step1 查看原理图,三个按键都连接到LRADC0引脚,通过判断电压大小来确定是按的哪个键。
step2 内核关于 CPU 的中断号linux 中断注册函数中的 irq 中断号并不是芯片物理上的编号,而是由芯片商在移植 Linux 系统时定在构架相
关的头文件中定义好的, 在内核源码中,名字一般是 irqs.h。
打开vim /root/work/sinlinx/a33/lichee/linux-3.4/arch/arm/mach-sunxi/include/mach/irqs.h
这里全志A33 是#include "sun8i/irqs-sun8iw5p1.h"
打开vim /root/work/sinlinx/a33/lichee/linux-3.4/arch/arm/mach-sunxi/include/mach/sun8i/irqs-sun8iw5p1.h
不知道开发板用的哪个平台,直接在.config中找
由此找到芯片在内核中的中断号
step 3 简要介绍中断驱动要用到的函数
查看 irq.h 文件 里面有关于中断的函数结构体声明 /root/work/sinlinx/a33/lichee/linux-3.4/include/linux/irq.h
/**
* struct irq_data - per irq and irq chip data passed down to chip functions
* @irq: interrupt number
* @hwirq: hardware interrupt number, local to the interrupt domain
* @node: node index useful for balancing
* @state_use_accessors: status information for irq chip functions.
* Use accessor functions to deal with it
* @chip: low level interrupt hardware access
* @domain: Interrupt translation domain; responsible for mapping
* between hwirq number and linux irq number.
* @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
* @msi_desc: MSI descriptor
* @affinity: IRQ affinity on SMP
*
* The fields here need to overlay the ones in irq_desc until we
* cleaned up the direct references and switched everything over to
* irq_data.
*/
struct irq_data {
unsigned int irq;
unsigned long hwirq;
unsigned int node;
unsigned int state_use_accessors;
struct irq_chip *chip;
struct irq_domain *domain;
void *handler_data;
void *chip_data;
struct msi_desc *msi_desc;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
#endif
};
struct irqaction 结构体在 /root/work/sinlinx/a33/lichee/linux-3.4/include/linux/interrupt.h
/**
* struct irqaction - per interrupt action descriptor
* @handler: interrupt handler function
* @flags: flags (see IRQF_* above)
* @name: name of the device
* @dev_id: cookie to identify the device
* @percpu_dev_id: cookie to identify the device
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @dir: pointer to the proc/irq/NN/name entry
* @thread_fn: interrupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts
* @thread_flags: flags related to @thread
* @thread_mask: bitmask for keeping track of @thread activity
*/
struct irqaction {
irq_handler_t handler; 中断服务函数 handler
unsigned long flags;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
在interrupt.h 中有许多和中断相干的函数
exmple:
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
功能 向内核注册一个中断服务函数,当发生中断号为 irq 的中断时候,会执行 handler 指针函数。
void free_irq(unsigned int irq, void *dev_id)
功能 从内核中断链表上删除一个中断结构
void disable_irq(unsigned int irq)
功能 关闭指定的中断,并等待中断服务函数运行结束后才会返回, 在中断函数外调用,
不能在中断服务程序中调用。
void disable_irq_nosync(unsigned int irq)
功能 关闭指定的中断,不等待中断服务函数结束,调用完这个函数立即返回。 可以中断服务函数
中调用。
void enable_irq(unsigned int irq)
功能 使能指定的中断
宏 local_save_flags(flags)
功能 禁止本 CPU 全部中断,并保存 CPU 状态信息。
宏local_irq_disable()
功能 禁止本 CPU 全部中断
Linux 内核和 GPIO 口相关的内核 API
exmple:
static inline int gpio_get_value(unsigned int gpio)
功能 获取指定 IO 口的电平状态
返回 IO 电平状态,非 0:表示高电平 , 0 表示低电平
static inline void gpio_set_value(unsigned int gpio, int value)
功能 设置 gpio 口的电平状态为 value
返回 IO 电平状态,非 0:表示高电平 , 0 表示低电平
static inline int gpio_to_irq(unsigned int gpio)
功能 通过 gpio 口编号获得出现这个 IO 上的外部中断编号
返回 这个 IO 上对应的外部中断编号
step 4关于 Linux 中断共享
共享中断是指多个设备共享一根中断线的情况, 在中断到来时,会遍历共享此中断的所有中断处理程序, 直
到某一个中断服务函数时返回 IRQ_HANDLED。
- 2019-01-09
-
加入了学习《是德两分钟导师系列课程第一季》,观看 触发偶发信号的诀窍
-
加入了学习《是德两分钟导师系列课程第一季》,观看 神奇的采集模式
-
发表了主题帖:
芯灵思SinlinxA33 简单QT控制led(io控制)
本帖最后由 babyking 于 2019-1-9 13:08 编辑
需要之前看过 “SinlinxA33搭建Qt App开发环境编写helloworld” “芯灵思Sinlinx A33实现linux led驱动” 这两篇帖子,了解QT编程主要代码:
#include "widget.h"
#include "led.h"
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
Led *led = new Led(); //led对象
QPushButton *led_off = new QPushButton("LED_OFF",this);
QPushButton *led_on = new QPushButton("LED ON",this);
led_on->setGeometry(75, 50, 75, 40); //设置按钮位置
led_off->setGeometry(300, 50, 75, 40);
connect(led_off,SIGNAL(clicked(bool)),led,SLOT(led_off()));//消息函数
connect(led_on,SIGNAL(clicked(bool)),led,SLOT(led_on()));
}复制代码#include "led.h"
Led::Led()
{
}
int Led::led_on(void)
{
int fd;
int val = 1;
fd = open("/dev/ledzzzzzzzz", O_RDWR);
if (fd < 0)
{
return -1;
}
else
write(fd, &val, 1);
return 0;
}
int Led::led_off(void)
{
int fd;
int val = 0;
fd = open("/dev/ledzzzzzzzz", O_RDWR);
if (fd < 0)
{
return -1;
}
else
write(fd, &val, 1);
return 0;
}
Led::~Led(void)
{
}复制代码
-
加入了学习《是德两分钟导师系列课程第一季》,观看 噪声基础知识
- 2019-01-02
-
发表了主题帖:
芯灵思SinlinxA33开发板 使用MIPI DSI屏幕
芯灵思SinlinxA33开发板 使用MIPI DSI屏幕
我这里用的是从芯灵思买的mipi屏幕,其它型号屏幕也可以参考
首先准备好相关屏幕驱动mb709_mipi.c
将到mb709_mipi.c 拷贝到linux内核下panels目录 /root/work/sinlinx/a33/lichee/linux-3.4/drivers/video/sunxi/lcd/panels
修改panels目录下panels.c文件
修改上层Makefile文件
在/root/work/sinlinx/a33/lichee/sinlinx_config 添加 lcd相关配置文件dsi1024x600.fex(对这一步疑惑的请看我之前发的帖子 “SinA332.0开发板Linux&Qt调整VGA分辨率为1280x720 ”)
最后重新选择屏幕编译内核,实测可以
- 2018-12-26
-
发表了主题帖:
芯灵思SinlinxA33开发板无线wifi设置
1)启动无线网卡
ifconfig wlan0 up
2)列出可用网络
iwlist wlan0 scan
3)连接到网络热点
iwconfig wlan0 essid "xxxx" 注:xxxx为热点的名字,连接的热点一定不能有密码,否则无法获取IP 地址
4)获取IP 地址
udhcpc -i wlan0
- 2018-12-25
-
发表了主题帖:
芯灵思Sinlinx A33实现linux led驱动
实验原理
在芯灵思开发板上,没有led灯模块,只能通过引脚电平观察: 这里我选择LS-INT引脚。全志A33一共有10组IO口,每组IO有9个相关功能控制器,LS-INT属于PB7,相关寄存器如图本次实验只用到这两个寄存器,在程序中命名为gpio_con,gpio_dat ,设置为输出引脚。1)注册 class_register(class) 将class注册到内核中。调用前,必须手动分配class内存;调用后,必须设置class的name等参数注册 class_create(owner,name) 创建class并将class注册到内核中。返回值为class结构体指针。注销 void class_unregister(struct class *cls) 注销class,与class_register()配对使用。注销 void class_destroy(struct class *cls) 注销class,与class_create()配对使用内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。2)void* ioremap(unsigned long phys_addr , unsigned long size , unsigned long flags)用mmap映射一个设备意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或写入,实际上就是对设备的访问。解除映射:void iounmap(void* addr)//取消ioremap所映射的IO地址
3)register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);但其实这个函数是linux版本2.4之前的注册方式,它的原理是:
(1)确定一个主设备号,如果major=0,则会自动分配设备号
(2)构造一个file_operations结构体, 然后放在chrdevs数组中
(3)注册:register_chrdev,cat /proc/devices查看内核中已经注册过的字符设备驱动(和块设备驱动),注意这里并不是驱动文件设备节点!
4) Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用5) class_device_create() 调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。
大致用法如下:struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);
这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件。
总体代码框架
1)先要有file_operations先要有引脚初始化函数myled_init(void),在myled_init里面注册class并将class类注册到内核中,创建设备节点,初始化引脚已经将寄存器地址映射到虚拟内存中,最后调用module_init(myled_init)驱动的加载就靠它
2)创建这个file_operations结构体
static struct file_operations myled_oprs = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
}; 下面就围绕这个结构体写函数led_write() led_open() led_release()
3)最后要注销设备.... ....
不是很详细,因为详细写起来太多了,附实测代码,参考下
LED驱动代码:#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major;
static struct class *led_class;
volatile unsigned long *gpio_con = NULL;
volatile unsigned long *gpio_dat = NULL;
static int led_open (struct inode *node, struct file *filp)
{
/* PB7 - 0x01C20824 */
if (gpio_con) {
printk("ioremap 0x%x\n", gpio_con);
}
else {
return -EINVAL;
}
return 0;
}
static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
unsigned char val;
copy_from_user(&val, buf, 1);
if (val)
{
*gpio_dat |= (1
- 2018-12-21
-
发表了主题帖:
芯灵思Sinlinx A33实现led驱动上
1)sysconfig配置系统,作为一个通用的软件平台,还希望通过它,可以适应用户不同的方案。通过给出一个对应的配置
用户的方案就可以自动运行,而不需要修改系统里面的代码,或者重新给出参数。
sysconfig生成 script.bin
2)script.bin是什么?
script.bin是被全志SOC内核驱动或LiveSuit使用的针对特定目标板的二进制配置文件,包含如何设置基于A10/A20目标版的各种外设,端口,I/O针脚信息。
其对应的可读文本文件格式为FEX,可以利用 Sunxi-tools在二进制和文本文件之间进行转换。更多关于FEX配置的信息可以参考http://linux-sunxi.org/Fex_Guide
3)linux内核有gpiolib标准的gpio操作接口. 但这套接口只能配置输入,输出,获取或设置IO口的电平.
但GPIO口是多功能, 还有上/下拉功能. 全志的GPIO额外的配置在script.bin里指定(有些SOC是提供额外的函数接口来配置).
script.bin是由sdk里的工具fex2bin把fex配置文件生成bin文件. bin文件也可由工具bin2fex转成fex文件.
在script.fex里的GPIO配置:
Port:端口+组内序号<功能分配><内部电阻状态><驱动能力><输出电平状态>
4)在将uboot和kernel烧写到TF卡之前,需要先编译生成两个启动参数文件:boot.scr和script.bin。
- 2018-12-19
-
发表了主题帖:
芯灵思SinlinxA33 SD卡烧写
工具:
烧写软件:PhoenixCard
读卡器
镜像文件
按图示操作
等待烧写完成
此内容由EEWORLD论坛网友babyking原创,如需转载或用于商业用途需征得作者同意并注明出处
拔出sd卡重新上电,系统烧写完成。
- 2018-12-18
-
发表了主题帖:
芯灵思Sinlinx A33开发板串口的使用
串口在设备文件下显示为: /dev/ttyS0 /dev/ttyS1 /dev/ttyS2 等
ls /dev/ttyS*
开发板连接的是串口ttyS2
执行:echo Today is December 18, 2018 > /dev/ttyS2
串口会接收Today is December 18, 2018
执行:cat /dev/ttyS2
会等待接收数据,此时在串口助手中发送"I know",终端会显示收到的数据"I know".
- 2018-12-14
-
发表了主题帖:
芯灵思SinlinxA33 修改配置文件更改输出串口
开发平台:芯灵思SinlinxA33
芯灵思linux&qt镜像默认用的是ttyS2串口打印输出,如果想使用其它串口需要修改配置文件,这里我将串口更改为ttyS0,ttyS0和SD卡引脚复用,所以用ttyS0就不能使用SD卡。
Step 1:首先打开配置文件sys_config.fex
cd /root/work/sinlinx/a33/lichee/tools/pack/chips/sun8iw5p1/configs/sinlinx/sys_config.fex
根据原理图修改[uart_para]参数
[uart_para]
uart_debug_port = 0
uart_debug_tx = port:PF02<3><1><default><default>
uart_debug_rx = port:PF04<3><1><default><default>
注意:引脚复用需要更改为 3 UART3_RTS
Step 2:更改[mmc0_para]参数
[mmc0_para]
sdc_used = 0 禁止SD卡
sdc_detmode = 2
sdc_buswidth = 4
sdc_d1 = port:PF00<2><1><2><default>
sdc_d0 = port:PF01<2><1><2><default>
sdc_clk = port:PF02<2><1><2><default>
sdc_cmd = port:PF03<2><1><2><default>
sdc_d3 = port:PF04<2><1><2><default>
sdc_d2 = port:PF05<2><1><2><default>
sdc_det = port:PB04<4><1><2><default>
Step :重新编译,烧写镜像,OK
- 2018-12-13
-
发表了主题帖:
芯灵思SinlinxA33 lvds屏幕配置
芯灵思SinlinxA33开发板支持lcd,lvds,dsi屏幕接口,这节通过制作添加lvds配置文件,使其支持lvds屏幕。
打开/root/work/sinlinx/a33/lichee/sinlinx_config目录,新建ldvs1024x600.fex文件
将lcd1024x600.fex文件内容拷贝到ldvs1024x600.fex里面,
修改 lcd_if = 3 使其支持lvds
修改lcd_lvds_if = 0 这个根据自己屏幕修改,单连接 or 双连接
lcdd0 = port:PD18<2><0><2><default>
lcdd1 = port:PD19<2><0><2><default>
lcdd2 = port:PD20<2><0><2><default>
lcdd3 = port:PD21<2><0><2><default>
lcdd4 = port:PD22<2><0><2><default>
lcdd5 = port:PD23<2><0><2><default>
lcdd6 = port:PD24<2><0><2><default>
lcdd7 = port:PD25<2><0><2><default>
lcdd8 = port:PD26<2><0><2><default>
lcdd9 = port:PD27<2><0><2><default>
这些引脚也是按照原理图进行配置
然后就是根据屏幕手册修改时序... ...
这样lvds配置文件制作好了
最后根据我之前写的帖子修改 ”SinA332.0开发板Linux&Qt调整VGA分辨率为1280x720“
最后制作成的镜像就支持lvds显示
- 2018-12-12
-
发表了主题帖:
SinlinxA33 设置qt程序自启动
对于开发板开机启动程序的设置可以这样做通过串口连接开发板
# vi /etc/profile
可以看到黄色框的就是qt自启动的命令,仿照这个替换为自己的qt程序
我的qt程序在 /root/01 程序名字为01
最后开发板上电,可以看到启动程序是自己的qt程序
- 2018-12-11
-
发表了主题帖:
SinlinxA33搭建Qt App开发环境编写helloworld
Step 1 在虚拟机(CentOS7)上安装Qt Creator
将qt-creator-opensource-linux-x86_64-3.5.1.run 拷贝到虚拟机中,双击安装,全部点下一步即可。Step 2 Qt Creater 设置打开Qt Creator (Applications->Programming->Qt Creator)1)添加新的设备点击Tools->Options...->Devices点Add 添加新设备,配置如下图所示,其中Host name 处填开发板的IP,用户名root,密码sinlinx (此为开发板linux系统root密码)2)连接好网线,在串口终端设置开发板ip,开启ssh# ifconfig eth0 192.168.0.123 netmask 255.255.255.0# /sinlinx/sshd start点击test按钮,出现下图,说明开发板与虚拟机成功连接3)设置编译运行选项点击Tools->Options...->Build&Run
打开Compilers 选项卡添加编译工具,完成后点Apply 保存。打开Qt Versions 添加QT 版本,完成后点Apply 保存。打开Kits 添加配置。如果没有cmake,需要手动安装cmake yum install cmakeStep 3 编写第一个QT程序打开Qt Creator,新建一个Qt Widgets Application1)添加安装路径打开HelloWorld.pro,在最后添加如下代码:target.path = /root
INSTALLS += target2)添加运行参数 -qws
点击Projects->Run
3)点击运行,会在开发板/root目录生成Hello_World的可执行文件,./Hello_World即可运行