學習了一段時間的嵌入式系統了,感覺它的複雜就體現在要和硬體打交道並且還要在作業系統中控制硬體。開發板上有一個執行個體,我把它改了一下便於理解,以作為以後學習的參考。
本人描述能力差,還是直接和代碼吧。
硬體原理圖:
驅動代碼:
/**************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;}
運行過程式控制制台:
由於本人菜鳥一個,錯誤之處在所難免,歡迎指正。
(---------------------完----------------------)