嵌入式试验报告.docx
《嵌入式试验报告.docx》由会员分享,可在线阅读,更多相关《嵌入式试验报告.docx(20页珍藏版)》请在冰点文库上搜索。
嵌入式试验报告
嵌入式试验报告
代码分析:
1.GPIO驱动分析:
Write,read,open,release四个函数与前边相同,只有基本操作和调试信息。
Ioctl函数为GPIO口的控制函数,其代码如下:
ssize_tSIMPLE_GPIO_LED_ioctl(structinode*inode,structfile*file,unsignedintcmd,longdata)
{
#ifdefOURS_GPIO_LED_DEBUG
printk("SIMPLE_GPIO_LED_ioctl[--kernel--]\n");//调试信息
#endif
switch(cmd)//io控制,cmd为输入的命令
{
caseLED_ON:
{GPCR3|=0x1;break;}//通过修改寄存器控制GPIO口电平
caseLED_OFF:
{GPSR3|=0x1;break;}
default:
{printk("lcdcontrol:
nocmdrun[--kernel--]\n");return(-EINVAL);//非法命令}
}
return0;
}
操作结构体:
structfile_operationsGPIO_LED_ctl_ops={
open:
SIMPLE_GPIO_LED_open,
read:
SIMPLE_GPIO_LED_read,
write:
SIMPLE_GPIO_LED_write,
ioctl:
SIMPLE_GPIO_LED_ioctl,
release:
SIMPLE_GPIO_LED_release,
};
建立操作映射,注册借口函数。
为应用层提供统一的接口。
驱动模块必须具备两个函数,就是初始化函数和卸载函数,其声明为如下两句:
module_init(pxa270_GPIO_LED_CTL_init);//加载模块时,系统调用pxa270_GPIO_LED_CTL_init函数初始化模块
module_exit(cleanup_GPIO_LED_ctl);//系统将调用该函数来卸载模块
这是linux驱动模块必备的。
初始化函数如下:
staticint__initpxa270_GPIO_LED_CTL_init(void)
{
intret=-ENODEV;//初始化失败将返回次错误信息
//调试信息
#ifdefOURS_GPIO_LED_DEBUG
printk("pxa270_GPIO_LED_CTL_init[--kernel--]\n");
#endif
//调用HW_GPIO_LED_CTL_init函数完成初始化
ret=HW_GPIO_LED_CTL_init();
if(ret)
returnret;
return0;
}
该函数通过调用HW_GPIO_LED_CTL_init函数实现初始化,该函数代码如下:
staticint__initHW_GPIO_LED_CTL_init(void)
{
intret=-ENODEV;
printk("hhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n\n");
showversion();//显示版本信息
//initGPIO
//初始化输出端口
GPDR3|=0x00000001;//设置输出端口模式
GPSR3|=0x00000001;//关闭led灯
//调试信息,显示寄存器信息
#ifdefOURS_GPIO_LED_DEBUG
printk("GPLR3=%x\n",GPLR3);
printk("GPDR3=%x\n",GPDR3);
#endif
//注册设备
ret=devfs_register_chrdev(SIMPLE_GPIO_LED_MAJOR//主设备号,"gpio_led_ctl",&GPIO_LED_ctl_ops//函数映射结构体);
if(ret<0)
{
printk("pxa270:
init_modulefailedwith%d\n[--kernel--]",ret);
returnret;
}
else
{
printk("pxa270gpio_led_driverregistersuccess!
!
!
[--kernel--]\n");
}
returnret;
}
模块卸载函数为cleanup_GPIO_LED_ctl,其代码实现如下:
staticvoid__exitcleanup_GPIO_LED_ctl(void)
{
#ifdefOURS_GPIO_LED_DEBUG
printk("cleanup_GPIO_LED_ctl[--kernel--]\n");
#endif
//注销主设备号,释放设备
devfs_unregister_chrdev(SIMPLE_GPIO_LED_MAJOR,"gpio_led_ctl");
}
测试函数:
通过调用ioctl函数实现对led灯的控制。
主函数实现如下:
intmain(void)
{
intfd;
intret;
char*i;
printf("\nstartgpio_led_drivertest\n\n");
fd=open(DEVICE_NAME,O_RDWR);
//系统通过函数映射调用SIMPLE_GPIO_LED_open函数打开设备
printf("fd=%d\n",fd);
if(fd==-1)
{
printf("opendevice%serror\n",DEVICE_NAME);
}
else
{
//通过调用SIMPLE_GPIO_LED_ioctl函数控制led灯
//主循环
while
(1)
{ioctl(fd,LED_OFF);
sleep
(1);//修改sleep时间即可改变led灯点亮的时间,如
sleep(7)即灭7秒
ioctl(fd,LED_ON);
sleep
(1);//sleep(5)亮5秒
}
//close
ret=close(fd);//
printf("ret=%d\n",ret);
printf("closegpio_led_drivertest\n");
}
return0;
}//endmain
通过该实验基本掌握了驱动模块的结构和一些函数的实现,如ioctl函数。
2、中断实验:
中断驱动模块的基本框架和GPIO的驱动模块基本相同,同样是通过module_init和module_exit来分别映射初始化和卸载模块。
具体实现如下:
module_init(pxa270_HELLO_CTL_init);
module_exit(cleanup_HELLO_ctl);
和GPIO驱动相同,加载模块时,系统调用pxa270_HELLO_CTL_init函数初始化模块;卸载模块时,调用cleanup_HELLO_ctl函数来卸载模块,并释放资源。
中断驱动的初始化与GPIO模块略有不同,除了要注册主设备好之外,还要注册中断向量和中断处理函数。
pxa270_HELLO_CTL_init通过调用HW_HELLO_CTL_init函数完成模块初始化。
pxa270_HELLO_CTL_init与GPIO初始化函数相同,HW_HELLO_CTL_init函数如下:
staticint__initHW_HELLO_CTL_init(void)
{
intret=-ENODEV;
//注册主设备号和函数映射
ret=devfs_register_chrdev(SIMPLE_INT_MAJOR,"int_ctl",&INT_ctl_ops);
showversion();
if(ret<0)
{
printk("pxa270init_modulefailedwith%d\n[--kernel--]",ret);
returnret;
}
else
{
printk("pxa270int_driverregistersuccess!
!
!
[--kernel--]\n");
}
//注册中断号和中断函数,定位中断源和irq处理函数
//SIMPLE_INT_IRQ宏定义为int中断的中断号,SIMPLE_INT_interrupt为中断处理函数,SA_INTERRUP表示处理该中断时,其他局部中断不可用
ret=request_irq(SIMPLE_INT_IRQ,&SIMPLE_INT_interrupt,SA_INTERRUPT,"int_ctl",NULL);
printk("\n...............\nret=%x\n...............\n",ret);
returnret;
}
由于产生中断后,系统会转到SIMPLE_INT_interrupt函数执行,所以模块其他函数如read,write,ioctl函数不会被调用
SIMPLE_INT_interrupt函数如下:
staticvoidSIMPLE_INT_interrupt(intnr,void*devid,structpt_regs*regs)
{
//每次中断计数加一
//通过修改该函数即可实现不同的功能,不过该函数最好不要使用复杂的函数,以免中断时间过长
SimpleINT_temp_count++;
printk("NowKeyinterrupt%doccur!
!
!
\n",SimpleINT_temp_count);
//可以将GPIO模块中的IOctl函数移植到该中断函数,便可以通过中断来控制LED的亮灭
}
模块卸载函数为cleanup_HELLO_ctl;
staticvoid__exitcleanup_HELLO_ctl(void)
{
#ifdefOURS_HELLO_DEBUG
printk("cleanup_INT_ctl[--kernel--]\n");
#endif
//注销主设备号
devfs_unregister_chrdev(SIMPLE_INT_MAJOR,"int_ctl");
//释放中断源
free_irq(SIMPLE_INT_IRQ,NULL);
}
3、数码管显示驱动实验
该驱动的初始化以及卸载函数和GPIO驱动模块基本相同,不再重复。
只有初始化模块是,初始化函数出了注册主设备号以外,还会调用gpio_init函数来初始化数码管
该函数如下:
voidgpio_init(void)
{
printk("GPDR2=%x\n",GPDR2);
GPDR2=GPDR2|(0x3<<26);//设置端口模式
printk("GPDR2=%x\n",GPDR2)
}
该模块通过write函数来写入数据,并控制数码管显示;write函数实现如下:
ssize_tSERIAL_LED_write(structfile*file,constchar*buf,size_tcount,loff_t*f_ops)
{
#ifdefOURS_SERIAL_LED_DEBUG
printk("SERIAL_LED_write[--kernel--]\n");
#endif
//输出数据到数码管
write_byte(*buf);
returncount;
}
可以看出,write函数通过同调用write_byte函数控制数码管,该函数如下:
voidwrite_byte(intdata)
{
inti;
for(i=0;i<8;i++)
{
write_bit(data<
}
}
该函数通过循环调用write_bit函数来串行输出每一位数据,一个字节8位,正好输出一个数字到寄存器;由于每次输出最高位,所以通过移位操作来循环输出8位二进制数。
Write_bit函数通过gpio串行输出数据到串并转换寄存器,从而控制数码管,write_bit函数实现如下:
voidwrite_bit(intdata)
{
GPCR2|=(0x1<<27);
if((data&0x80)==0x80)
{
GPSR2|=(0x1<<26);
}
else
{
GPCR2|=(0x1<<26);
}
GPSR2|=(0x1<<27);
}
具体控制流程不清楚。
测试文件:
intmain(void)
{
intfd;
intret;
inti,count;
//数码管显示数据。
十个数字的7段显示器的输入
intbuf[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//0123456789
intdata[10];
printf("\nstartserial_leddrivertest\n\n");
//打开设备
fd=open(DEVICE_NAME,O_RDWR);
printf("fd=%d\n",fd);
if(fd==-1)
{
printf("opendevice%serror\n",DEVICE_NAME);
}
else
{
while
(1)
{
//循环显示数字
for(count=0;count<10;count++)
{
data[0]=buf[count];
ret=write(fd,data,1);
sleep
(1);
}
}
}
ret=close(fd);
printf("ret=%d\n",ret);
printf("closeserial_leddrivertest\n");
return0;
}//endmain
4、LED点阵驱动程序设计
LED点阵驱动的初始化函数框架和前边几个相同,同样是pxa270_LED_CTL_init函数通过调用HW_LED_CTL_init函数完成LED点阵的初始化。
不过,LED点阵驱动初始化还要获取模块地址和完成点阵的初始化。
代码如下:
ioremap_addr=ioremap(0x0800c000,0x0f);//
outw(0x00ff,ioremap_addr);//openledary,allledon
点亮整个点阵。
同样卸载模块时,也要清除数据,这在cleanup_LED_ctl函数进行,所以该函数要加上如下代码:
outw(0x0000,ioremap_addr);//清除数据
devfs_unregister_chrdev(SIMPLE_LED_MAJOR,"led_ary_ctl");//注销设备
对点阵的操作在SIMPLE_LED_write函数实现,代码如下:
ssize_tSIMPLE_LED_write(structfile*file,constchar*buf,size_tcount,loff_t*f_ops)
{
inttmp_buf;
#ifdefOURS_LED_DEBUG
printk("SIMPLE_LED_write[--kernel--]\n");
#endif
//-------------------------------------------
tmp_buf=buf[1];//行选,低电平有效
tmp_buf=tmp_buf<<8;//左移位,高字节部分为行选
tmp_buf=tmp_buf|buf[0];//低字节部分为列选
#ifdefOURS_LED_DEBUG
printk("tmp=%x\n",tmp_buf);
#endif
outw(tmp_buf,ioremap_addr);//写入到控制存储器
//-------------------------------------------
returncount;
}
由于点阵的控制线对应数据线的低16位,其中低8位为列选,高8位为行选。
停止使用设备后,应清除数据,由SIMPLE_LED_release完成:
ssize_tSIMPLE_LED_release(structinode*inode,structfile*file)
{
#ifdefOURS_LED_DEBUG
printk("SIMPLE_LED_release[--kernel--]\n");
#endif
outw(0x0000,ioremap_addr);//关闭点阵
MOD_DEC_USE_COUNT;
return0;
}
测试文件:
测试文件主循环如下,其他部分和其他测试文件相同.
c=1;
r=1;
for(i=1;i<=8;i++){
buf[0]=c;//列选,高电平有效
buf[1]=~r;//行选,低电平有效
for(j=1;j<=8;j++){
write(fd,buf,2);
printf("buf[0],buf[1]:
[%x,%x]\n",buf[0],buf[1]);
usleep(200000);//sleep0.2second
c=c<<1;//列信号左移,即扫描点逐点行扫描
buf[0]=c;//column
}
c=1;
r=r<<1;//行移位,下移一行
}
5.AD驱动实验
该模块需要ad模块的支持,所以初始化函数会相对比较复杂。
不过初始化函数的结构依旧没有改变,同样是pxa270_AD_CTL_init函数通过调用HW_AD_CTL_init函数完成AD模块的初始化。
staticint__initHW_AD_CTL_init(void)
{
intret=-ENODEV;
showversion();
ad_ucb=ucb1x00_get();//获取AD模块的地址
//注册主设备号
ret=devfs_register_chrdev(ADCTL_MAJOR,"adctl",&adctl_ops);
if(ret<0)
{
printk("adctlinit_modulefailedwith%d\n[--kernel--]",ret);
returnret;
}
else
{
printk("adctlint_driverregistersuccess!
!
!
[--kernel--]\n");
}
//注册AD模块,设定为字符模块
adctl_dev_handle=devfs_register(NULL,"ad_ctl",DEVFS_FL_DEFAULT,ADCTL_MAJOR,0,S_IFCHR,&adctl_ops,NULL);
returnret;
}
同理,卸载模块时就要同时注销主设备号和AD模块。
所以,卸载函数改为:
staticvoid__exitcleanup_AD_ctl(void)
{
devfs_unregister(adctl_dev_handle);//注销AD模块
devfs_unregister_chrdev(ADCTL_MAJOR,"ad_ctl");//注销主设备号
}
AD模块的控制由adctl_ioctl函数实现:
ssize_tadctl_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)
{
intval;
#ifdefOURS_HELLO_DEBUG
//printk("SIMPLE_HELLO_ioctl[--kernel--]\n");
#endif
ucb0x00_adc_enable(ad_ucb);//使能AD转换
//读取AD转换后的数据,其中cmd指定转换通道
val=ucb0x00_adc_read(ad_ucb,cmd,0);
ucb0x00_adc_disable(ad_ucb);//停止AD转换
returnUCB_ADC_DAT(val);//返回转换的数据
}
使用时调用ioctl函数便可以实现数据采集。
可以得到,测试文件的核心部分:
intval0,val1;
char*i;
ioctl(fd);//通过函数映射调用adctl_ioctl函数
/*
for(i=0;i<50;i++)
{
val=ioctl(fd,UCB_ADC_INP_AD0,0);
printf("val=%x\n",val);
usleep(200000);
}
*/
for(i=0;i<50;i++)
{
//通过函数映射调用adctl_ioctl函数读取通道1
val0=ioctl(fd,UCB_ADC_INP_AD1,0);
usleep(100);//等待操作完成
//通过函数映射调用adctl_ioctl函数读取通道0
val1=ioctl(fd,UCB_ADC_INP_AD0,0);
printf("val0=%d\tval1=%d\n",val0,val1);
usleep(500000);
}
测试函数其他部分与其他的测试文件相同
6.DA驱动
DA模块的初始化除了要注册主设备号以外,还要初始化设备地址和初始输出数据,所以其初始化部分与其他模块有一些变化,不过结构依然不变,其主要初始化函数HW_DA_CTL_init如下:
staticint__initHW_DA_CTL_init(void)
{
intret=-