学习了一段时间的嵌入式系统了,感觉它的复杂就体现在要和硬件打交道并且还要在操作系统中控制硬件。开发板上有一个实例,我把它改了一下便于理解,以作为以后学习的参考。
本人描述能力差,还是直接上图和代码吧。
硬件原理图:
驱动代码:
/*
*************magic_leds.c*******************
* s3c2410+linux 2.6.24.4通过SPI控制双数码管显示的驱动程序参考代码
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/unistd.h>
#include <asm/plat-s3c24xx/regs-spi.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>
#define SPI_MAJORE 25
#define DEVICE_NAME "S3C2410_SPI0"
struct class *leds_class;
struct cdev dev;
#define rSPCON0 ((volatile unsigned long *)r_SPCON0)
#define rSPSTA0 ((volatile unsigned long *)r_SPSTA0)
#define rSPPIN0 ((volatile unsigned long *)r_SPPIN0)
#define rSPPRE0 ((volatile unsigned long *)r_SPPRE0)
#define rSPTDAT0 ((volatile unsigned long *)r_SPTDAT0)
#define rSPRDAT0 ((volatile unsigned long *)r_SPRDAT0)
#define rCLKCON ((volatile unsigned long *)r_CLKCON)
unsigned long *r_SPCON0,*r_SPSTA0,*r_SPPIN0,*r_SPPRE0,*r_SPTDAT0,*r_SPRDAT0,*r_CLKCON;
int address_map(void)
{
//将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问;
r_CLKCON = ioremap(0x4C00000c,4); //从s3c2410芯片手册中知道,这映射的是“时钟产生控制寄存器”的地址
r_SPCON0 = ioremap(0x59000000,4); //映射的是“SPI channel 0 control register”,串行外部接口通道0控制寄存器
r_SPSTA0 = ioremap(0x59000004,4); //映射的是“SPI channel 0 status register”,串行外部接口通道0状态寄存器
r_SPPRE0 = ioremap(0x5900000c,4); //映射的是“SPI cannel 0 baud rate prescaler register”,串行外部接口通道0波特率分频寄存器
r_SPTDAT0 = ioremap(0x59000010,4); //映射的是“SPI channel 0 Tx data register”,串行外部接口通道0传送数据寄存器
r_SPRDAT0 = ioremap(0x59000014,4); //映射的是“SPI channel 0 Rx data register”,串行外部接口通道0接收数据寄存器
return 0;
}
static ssize_t spi_open(struct inode *inode, struct file *file)
{
unsigned long temp;
s3c2410_gpio_cfgpin(S3C2410_GPH0,S3C2410_GPH0_OUTP);
s3c2410_gpio_pullup(S3C2410_GPH0, 0);
temp = __raw_readl(rCLKCON);
temp |= 0x40000; //根据s3c2410芯片手册可以知道,这是设置“Control PCLK into SPI block.”
__raw_writel(temp,rCLKCON);
__raw_writel(0x0d,rSPPRE0); //根据s3c2410芯片手册可以知道,这是设置Prescaler Value,“Determine SPI clock rate as above equation. Baud rate = PCLK / 2 / (Prescaler value + 1)”
__raw_writel(0x19,rSPCON0); //查看芯片手册可知,这是在设置I2C的时钟可用,设置USB为master端
return 0;
}
static ssize_t spi_release(struct inode *inode, struct file *file)
{
printk("it has been released!\n");
return 0;
}
static ssize_t spi_read(struct file *file, char __user *buffer, long count, loff_t *ppos)
{
printk("it is reading from pin!\n");
return 1;
}
static ssize_t spi_write(struct file *file, const char __user *buffer, long count, loff_t *ppos)
{
int i;
unsigned char ptr[count];
printk("start writing!\n");
if (copy_from_user(ptr, buffer, count))return -EFAULT;
s3c2410_gpio_setpin(S3C2410_GPH0,0);
for(i=0;i<count;i++)
{
while(((__raw_readl(rSPSTA0)) & 0x01) == 0x00);
{
__raw_writel(ptr[i],rSPTDAT0); //向传送寄存器中写入数据,芯片手册上有“SPI channel 0 Tx data register”
printk("writing ptr[%d]=%X \n",i,ptr[i]);
udelay(10);
}
}
s3c2410_gpio_setpin(S3C2410_GPH0,1);
return count;
}
static ssize_t spi_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
printk("it is the ioctl model!\n");
return 0;
}
static struct file_operations s3c2410_spi0_fops=
{
.owner = THIS_MODULE,
.open = spi_open,
.write = spi_write,
.read = spi_read,
.ioctl = spi_ioctl,
.release = spi_release,
};
static void spi_setup_cdev(void)
{
int err,devno = MKDEV(SPI_MAJORE,0);
cdev_init(&dev,&s3c2410_spi0_fops);
dev.owner = THIS_MODULE;
dev.ops = &s3c2410_spi0_fops;
err = cdev_add(&dev,devno,1);
if(err)
printk(KERN_NOTICE"Error cdev_add!!!");
}
static int __init s3c2410_spi0_init(void)
{
int ret;
dev_t devno = MKDEV(SPI_MAJORE,0);
address_map();
ret = register_chrdev_region(devno, 1, DEVICE_NAME);
printk(DEVICE_NAME "initializing\n");
if (ret<0)
{
printk(DEVICE_NAME "cann't register major numer\n");
return ret;
}
printk(DEVICE_NAME "initialized\n");
spi_setup_cdev();
leds_class=class_create(THIS_MODULE,"led_class");
if(IS_ERR(leds_class))
{
printk("err:failed in creating class.\n");
return -1;
}
class_device_create(leds_class,NULL,MKDEV(SPI_MAJORE,0),NULL,"S3C2410_SPI%d",0);
return 0;
}
static void __exit s3c2410_spi0_exit(void)
{
class_device_destroy(leds_class,MKDEV(SPI_MAJORE,0));
class_destroy(leds_class);
cdev_del(&dev);
unregister_chrdev_region(MKDEV(SPI_MAJORE,0), 1);
}
module_init(s3c2410_spi0_init);
module_exit(s3c2410_spi0_exit);
MODULE_AUTHOR("xw_tech@126.com");
MODULE_DESCRIPTION("led driver for UP_Magic");
MODULE_LICENSE("GPL");
测试代码:
/*
************magicleds_test.c******************
*双数码管显示的测试代码
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
//#include <sys/ioctl.h>
int MAX_LEN=255;
int main()
{
int fd;
int i;
char c,sc;
unsigned char buf1[32]={0xc0,0xc0,0xf9,0xf9,0xa4,0xa4,0xb0,0xb0,
0x99,0x99,0x92,0x92,0x82,0x82,0xf8,0xf8,
0x80,0x80,0x90,0x90,0x88,0x88,0x83,0x83,
0xc6,0xc6,0xa1,0xa1,0x86,0x86,0x8e,0x8e,
};
unsigned char buf[2]={0xff,0xff};
fd=open("/dev/S3C2410_SPI0",O_RDWR);
if(fd < 0){
printf("####spi device open fail####\n");
return (-1);
}
write(fd,&buf,2); //将buf中的两个字节写入到fd对应的文件中
while(1)
for(i=0;i<32;)
{
write(fd,buf1+i,2);
sleep(1);
write(fd,buf,2);
i=i+2;
}
close(fd);
return 0;
}
运行过程控制台截图:
由于本人菜鸟一个,错误之处在所难免,欢迎指正。
(---------------------完----------------------)