嵌入式系统开发流程实验二

首页-达尔闻    基础应用    嵌入式系统开发流程实验二

开发驱动前的第一步是如何将文件加载进开发板,这里我通过挂载nfs文件系统,然后复制到对应目录夹来去解决OK

经过实践发现确实可行,但是现在有一个问题是,我如果加了执行选项,会出错,所以替换为读写选项,待复制完成之后,通过chmod指令增加执行权限:

 

chmod 777 * //开启rwx全部权限

在复制老师的.ko文件到目录下首先遇到: 

/home/edision/leddrv # ./led.ko

./led.ko: line 1: syntax error: unexpected word (expecting ")")

第一步先把led_demo.c编译成可执行文件 led_demo

 arm‐linux‐gcc ‐static led_demo.c ‐o led_demo

第二步使用insmod加载模块:

 insmod led.ko

第三步使用mknod:

 mknod /dev/led_demo c 250 0

这里注意dev目录下的名称(小坑)要与led_demo.c里面名称保持一致

1

第四步执行led_demo:

  ./led_demo

 

程序设计思想:

watchdog驱动led1灯没0.5s完成一次亮灭:

watchdog设计计时器,每隔一段时间产生中断来去发送每一次发送亮灭的指令,之后清除中断进行下一次控制。

write/read函数控制led2­led4状态的亮灭:

通过应用层程序发送信息,信息由两部分组成,第一部分决定具体LED灯的型号(在这里为LED2­4),第二部分是控制此型号灯的亮灭,每次通过write函数负责数据发送,read函数来去读LED灯的状态。这里我列出一个表格,里面标注了每个数值对应的led灯状态。

下面将结合程序具体展示我的设计思路:

首先定义watchdog和led等的寄存器地址,时钟,主设备号和此设备号,以及设备数量等变量。

这里创造了一个类,负责创造设备节点。

然后定义四个文件结构体中的函数:

 

open:参数用两个,一个为inode节点,一个为file结构体。inode包含文件的基础信息以及数据块的指针。file结构体从属于进程,用来描述某个进程正使用的设备文件。open函数的的作用主要是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址,然后使用private_data来挂载简单的字符驱动设备。使用private_data的原因是需要给设备分配自定义的数据结构,防止多个相同设备共用这些自定义数据结构,会引起冲突

 

close:留给应用层程序调用。

write:应用层程序定义字符串数组(经测试字符串会以ASCII码的形式存储在对应地址)。这里我选择字符'0'­'9'作为输入,每次读取一个字符值,将ASCII码译码后,低两位作为led等型号选择,第三位作为控制信号来去控制亮灭。

 

read:将dev数组中的数值赋给字符串数组,打印信息,匹配表格获得led灯状态。

之后在file_operations结构变量中,逐个匹配函数

watchdog中断处理函数:由led1_on_off变量值控制led1灯的亮灭,寄存器配置完成后,将led_on_off数值进行翻转,然后清除中断标志(非常重要),等待下一次中断的到来。

 

setup:配置主设备号和从设备号,并完成设备内存的分配。

 init:配置寄存器虚拟地址到物理地址的映射,以及相关参数的配置,内存分配,创立设备节点。

static int led_demo_init(void)
{
int err = ‐1;
dev_t dev;
struct device *temp = NULL;
int ret;
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ watchdog part ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
wtcon = (unsigned int *)ioremap(0x10060000, 4);
wtdat = (unsigned int *)ioremap(0x10060004, 4); 11 wtcnt = (unsigned int *)ioremap(0x10060008, 4);
wtclrint = (unsigned int *)ioremap(0x1006000c, 4);
ret = request_irq(IRQ_WDT, do_irq, 0, "led1_wtc", "zq");
if(ret < 0)
{
printk("request irq!\n");
return 1;
}
wtclk = clk_get(NULL, "watchdog");
clk_enable(wtclk);
writel((readl(wtcon) & ~0xffff) | 0x6334, wtcon);
//writel((readl(wtcnt) & ~0xffff) | 0x3d09, wtcnt);
writel((readl(wtcnt) & ~0xffff) | 0x8000, wtcnt);
//writel((readl(wtdat) & ~0xffff) | 0x3d09, wtdat);
writel((readl(wtdat) & ~0xffff) | 0x8000, wtdat);
printk("wdt set ok!\n");
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
printk(KERN_ALERT"Initializing led demo device.\n");
err = alloc_chrdev_region(&dev, 0, number_of_dev, LED_DEMO_DEVICE_NODE_ NAME);
if(err < 0)
{
printk(KERN_ALERT"fail to alloc char dev region.\n");
goto fail;
}
led_demo_major = MAJOR(dev);
led_demo_minor = MINOR(dev);
led_dev = kmalloc(sizeof(struct led_demo_dev), GFP_KERNEL);
if(!led_dev)
{
err = ‐ENOMEM;
printk(KERN_ALERT"Failed to alloc led device.\n");
goto unregister;
}
err = __led_setup_dev(led_dev);
if (err < 0)
{
printk(KERN_ALERT"Failed to setup led device.\n");
goto clean_up;
}
GPM4CON = (unsigned int *)ioremap(EXYNOS4412_GPM4CON, 4);
if(!GPM4CON)
{
err = ‐ENOMEM;
goto destroy_cdev;
}
GPM4DAT = (unsigned int *)ioremap(EXYNOS4412_GPM4DAT, 4);
if(!GPM4DAT)
{
err = ‐ENOMEM;
goto unmap1;
}
writel((readl(GPM4CON) & ~0xffff) | 0x1111, GPM4CON);
writel(readl(GPM4DAT)| 0xf, GPM4DAT);
led_demo_class = class_create(THIS_MODULE, LED_DEMO_DEVICE_CLASS_NAME);
if(IS_ERR(led_demo_class))
{
err = PTR_ERR(led_demo_class);
printk(KERN_ALERT"Failed to create led demo class.\n");
goto unmap2;
}
temp = device_create(led_demo_class, NULL, dev, NULL, "%s", LED_DEMO_DE VICE_FILE_NAME);
if(IS_ERR(temp))
{
err = PTR_ERR(temp);
printk(KERN_ALERT"Failed to create led demo device.\n");
goto destroy_class;
}
dev_set_drvdata(temp, (void *)led_dev);
printk(KERN_ALERT"Succeed to initialize led demo device.\n");
return 0;
destroy_class:
class_destroy(led_demo_class);
unmap2:
iounmap(GPM4DAT);
unmap1:
iounmap(GPM4CON);
destroy_cdev:
cdev_del(&(led_dev‐>dev));
clean_up:
kfree(led_dev);
unregister:
unregister_chrdev_region(MKDEV(led_demo_major, led_demo_minor), number of_dev);
fail:
return err;
}

exit:删除设备节点,删除映射空间,释放存储空间。

应用层程序:打开驱动设备后,分别使用writeread函数写入和读取设备。

 

程序顶层框图:

 

应用程序调用框图:

222

 

感受体会:

作为一名微电子专业的学生,我研究生主要研究方向是处理器架构的设计。因此,对于处理器的硬件工作模式比较熟悉,也明白软件需要经过编译、链接成底层硬件能够识别的指令。但是随着时代的进步和技术的发展,传统处理器已经进入了发展的瓶颈期,特别是一些特定专用设备的硬件资源非常有限,成本要求与实时性相应要求比较苛刻,嵌入式系统就显得尤为重要。在1811月份,我在进行实习期间,主要做的是关于ARM芯片SOC环境的验证,其中一个主要验证的内容是配置寄存器来去实现不同系统不同功能。我一次接触到了MCU的概念,通过一个控制核来去驱动外设硬件的运行。当时思考的一个点在于驱动的概念是什么,以及硬件实现完毕后用户如何通过软件来去调用硬件实现特定的功能。知道学习了嵌入式系统开发之后,理解了一些之前一些比较疑问的点。

首先本次设计驱动的第一个作业是裁剪内核中的watchdog,让我理解了嵌入式系统的一个重要特性:软硬件可裁剪。通过在驱动程序调用watchdog作为计数器时钟,我们可以控制led灯的亮灭,实现了不同硬件之间的协作运行,以及中断的实现方式,极大扩展了从顶层理解硬件的概念。

第二部分是通过writeread函数配置寄存器,以实现led2­led4灯的亮灭。从硬件角度理解的话,不同的寄存器可以被硬件读取实现的不同行为,将软硬件层合作实现嵌入式开发的步骤清晰展示出来。

嵌入式系统的开发是一项对设计人员综合素质要求高的岗位,要求开发具有很强的综合专业知识,能够将软硬件有机结合起来。因此学习这门课对于我对软硬件的理解又提升了一个层次,在这里非常感谢老师的辛苦授课,希望在以后的学习中能够再进一步。

2019年7月19日 17:10
收藏