研究了几天驱动程序设计,终于把程序调通了,现在把一些资料和心得记录如下,当作自己的工作笔记。该程序是基于mini2440开发板的,只是一个简单的基本程序,并没有将所有的东西考虑进去,希望大家谅解。其间参考了很多网络资源,有的部分甚至没有修改,如初始化等,在这一并表示感谢。(有事情,下次再写了,先给出原程序和测试测序)
linux的驱动程序设计主要是要填充file_operations结构体,以及设备和程序的注册,具体解释网上很多了,我也有一些资料,但是没办法上传给大家,这里就不细说了,再有现在好像内核的驱动多是基于platform的,本人正在学习,希望能和大家交流。
驱动程序:
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/poll.h>
#define DEVICE_NAME "SPI"//设备名
#define SPI_MAJOR 200//主设备号
//相关寄存器虚拟地址映射,通过映射,就可以给寄存器直接赋值了
//当然内核中提供了很多操作寄存器的内核函数,可以达到同样的功能
//这些函数在gpio.c文件中,arch/arm/mach-s3c2410目录下,可能会有差别,可以自己搜索一下
//s3c2410_gpio_cfgpin()的作用是规定io口的用途:如作为外部功能模块(10),还是普通输入口(00),还是普通的输
//出口(01)s3c2440通常用两位来决定一个端口的功能,具体参见手册。
//s3c2410_gpio_pullup()该函数设置是否要内部电阻上拉:0不上拉,1上拉
//s3c2410_gpio_setpin()该函数向端口输出数据
//s3c2410_gpio_setcfg()读取端口配置
//还有功能函数set_gpio_bit、clr_gpio_bit、set_gpio_ctrl、write_gpio_bit、read_gpio_bit等,网上有很多关于这
//些函数的具体用法
#define r_clkcon *(volatile unsigned long *)ioremap(0x4C00000c,4)//相关寄存器虚拟地址映射
#define spi_gpgcon *(volatile unsigned long *)ioremap(0x56000060,4) //端口G控制寄存器
#define spi_gpgdat *(volatile unsigned long *)ioremap(0x56000064,4) //端口G的数据寄存器
#define spi_gpgup *(volatile unsigned long *)ioremap(0x56000068,4) //端口G的上拉电阻控制寄存器
#define spi_gpecon *(volatile unsigned long *)ioremap(0x56000040,4) //端口E的
#define spi_gpedat *(volatile unsigned long *)ioremap(0x56000044,4)
#define spi_gpeup *(volatile unsigned long *)ioremap(0x56000048,4)
#define spi_spcon0 *(volatile unsigned long *)ioremap(0x59000000,4) //spi相关寄存器
#define spi_spsta0 *(volatile unsigned long *)ioremap(0x59000004,4)
#define spi_sppin0 *(volatile unsigned long *)ioremap(0x59000008,4)
#define spi_sppre0 *(volatile unsigned long *)ioremap(0x5900000c,4)
#define spi_sptdat0 *(volatile unsigned long *)ioremap(0x59000010,4)
#define spi_sprdat0 *(volatile unsigned long *)ioremap(0x59000014,4)
#define spi_gpbcon *(volatile unsigned long *)ioremap(0x56000010,4)
#define spi_gpbdat *(volatile unsigned long *)ioremap(0x56000014,4)
#define spi_gpbup *(volatile unsigned long *)ioremap(0x56000018,4)
static char dataTx[2]="";
static char dataRx[2]="";
static int s3c2440_spi_close(struct inode *inode,struct file *filp)
{
printk("s3c2410-spi closed/n");
return 0;
}
static int s3c2440_spi_open(struct inode *inode,struct file *filp)
{ int config;
int value;
printk("spi open begin../n");
r_clkcon |= 0x40000;//这句必须最先设置,否则由于spi模块没有时钟,后续的操作都不能实现
value=(int)r_clkcon;
printk("r_CLKCON=0x%x/n",value);
/gb
spi_gpbcon &=0xffff0;
spi_gpbcon |=0x00005;//GB0/GB1=OUTPUT
spi_gpbdat |=0x0001;//EN=1
spi_gpbdat &=0x7ffc;//STC=0
spi_gpbup &=0x7ffb;
//gb
spi_gpgcon &=0xFFFFFFCF;//GPGCON-2=11 ,
spi_gpgcon |=0x00000030;
value=(int)spi_gpgcon;
printk("spi_gpgcon=0x%x/n",value);
spi_gpgup &= 0xFFFB;//GPGUP-2=1
spi_gpgup |=0x0004;
value=(int)spi_gpgup;
printk("spi_gpgup=0x%x/n",value);
/*********************************************************
spi_gpecon &=0xF03FFFFF;//GPECON-11,12,13=10
spi_gpecon |=0x05000000; //0xa8
value=(int)spi_gpecon;
printk("spi_gpecon=0x%x/n",value);
**********************************************************/
spi_gpecon &=0xF03FFFFF;//GPECON-11,12,13=10
spi_gpecon |=0x0A800000;
value=(int)spi_gpecon;
printk("spi_gpecon=0x%x/n",value);
spi_gpeup &=0xC7FF;//GPEUP-13=1 GPGUP-12,11=0
spi_gpeup |=0x2000;
value=(int)spi_gpeup;
printk("spi_gpeup=0x%x/n",value);
printk("config SPI.../n");
// spi_spcon0=0x00;
config=(int)spi_spcon0;
printk("spi_spcon0=0x%x/n",config);
spi_sppin0=(0<<2)|(0<<1)|(0<<0);
config=(int)spi_sppin0;
printk("spi_sppin0=0x%x/n",config);
spi_sppre0=0xFF;//SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1);
//spi_spcon0=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);
printk("Open spi successfully/n");
return 0;
}
static ssize_t s3c2440_spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)
{
int spcon;
char i=0;
copy_from_user(dataTx,buf,sizeof(dataTx));
/*****************************
tx=dataTx[0];
spi_gpedat &=0xc7ff;
for(i=0;i<8;i++)
{
spi_gpedat &=0xdfff;
udelay(10);
if(tx&0x80)
{
spi_gpedat |=0x1000;
}
else
{
spi_gpedat &=0xefff;
}
udelay(10);
spi_gpedat |=0x2000;
udelay(10);
tx=tx<<1;
}
****/
//spcon=(int)spi_spsta0;
printk("spi_spsta%d/n",(int)spi_spsta0);//the first sta
spi_sptdat0=dataTx[0];
// spcon=(int)spi_spsta0;
printk("spi_spsta%d/n",(int)spi_spsta0);//the first sta
spi_spcon0=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(1<<2)|(0<<1)|(0<<0);//必须先使能spi时钟
//spcon=(int)spi_spsta0;
//printk("spi_spsta%d/n",spcon);//the first sta
for(i=0;i<10;i++)//这是手册要求的,必须要做
{
spi_sptdat0=0xff;
}
printk("spi_spsta%d/n",(int)spi_spsta0);//the first sta
//spi_gpgdat &=0xfffb;
if((spi_spsta0&0x01)==1) //data Tx/Rx ready
{
spi_sptdat0=dataTx[0];
// spcon=(int)spi_spsta0;
printk("spi_spsta%d/n",(char)spi_spsta0);//the first sta
printk("txt char=%d/n",dataTx[0]);
printk("txt char=%c/n",dataTx[0]);
spcon=(int)spi_sptdat0;
printk("spi_sptdat0=%d/n",spcon);
spi_gpbdat |=0x0002;//STC=1
udelay(10);
spi_gpbdat &=0x7ffe;//EN=0
spcon=(int)spi_spcon0;
printk("spi_spcon=0x%x/n",spcon);
spcon=(int)spi_spsta0;
printk("spi_spsta%d/n",spcon);//the first sta
//spi_spcon0=(0<<6)|(0<<5)|(0<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);
//spi_spcon0=0x00; //Polling,dis-sck,master,low,format A,nomal
// udelay(10);
return 0;
}
//填充file_operations结构体
static struct file_operations s3c2410_spi_fops = {
.owner = THIS_MODULE,
.open =s3c2440_spi_open,
.write =s3c2440_spi_write,
.release=s3c2440_spi_close,
};
static int __init dev_init(void)
{
int ret;
ret = register_chrdev(SPI_MAJOR,DEVICE_NAME,&s3c2410_spi_fops);//设备注册
if(ret < 0)
{
printk(DEVICE_NAME "can't get major number/n");
return ret;
}
devfs_mk_cdev(MKDEV(SPI_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
printk (DEVICE_NAME"/tinitialized/n");
return 0;
}
static void __exit dev_exit(void)
{
devfs_remove(DEVICE_NAME);
unregister_chrdev(SPI_MAJOR, DEVICE_NAME);
printk("Good-bye, SPI module was removed!/n");
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
驱动程序写完之后,模仿mini2440的hello_module驱动,完成添加,编译和加载即可
测试程序
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
int main()
{
int fd;
char gao=0xaa;
printf("now spi testing");
fd = open("/dev/SPI", O_RDWR);
if (fd<0)
{
perror("Can't Open SPI Port");
return -1;
}
sleep(10);
while(1)
{
write(fd,gao,sizeof(gao));
printf("testing");
sleep(1);
}
}