Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1064631
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-04 17:15:35

驱动代码
makefile文件

点击(此处)折叠或打开

  1. # File: Makefile
  2. # wangxiancai

  3. MODEXT = ko
  4. # INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.13/wxc/driver/chardriver/ko
  5. INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.30/wxc/driver/chardriver/ko
  6. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-
  7. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-
  8. # KERNELDIR=/home/wangxc/linux/kernel/kernel-2.6.13
  9. KERNELDIR=/home/wangxc/linux/kernel/kerner-2.6.30

  10. CC= $(CROSS)gcc
  11. LD= $(CROSS)ld

  12. #############################################################################
  13. # Compiler Flags
  14. #############################################################################
  15. EXTRA_CFLAGS += -I$(KERNELDIR)/include
  16. #############################################################################
  17. # Make Targets
  18. #############################################################################
  19. obj-m := spi_tq2440_info.o
  20. obj-m += oled_drv.o
  21. obj-m += flash_drv.o
  22. obj-m += spi_master.o
  23. default:
  24.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  25. # Otherwise we were called directly from the command line; invoke the kernel build system.

  26. install: default
  27.     rm -rf $(INSTALLDIR)/spi_tq2440_info.$(MODEXT)
  28.     cp -rf $(PWD)/spi_tq2440_info.$(MODEXT) $(INSTALLDIR)
  29.     rm -rf $(INSTALLDIR)/oled_drv.$(MODEXT)
  30.     cp -rf $(PWD)/oled_drv.$(MODEXT) $(INSTALLDIR)
  31.     rm -rf $(INSTALLDIR)/flash_drv.$(MODEXT)
  32.     cp -rf $(PWD)/flash_drv.$(MODEXT) $(INSTALLDIR)
  33.     rm -rf $(INSTALLDIR)/spi_master.$(MODEXT)
  34.     cp -rf $(PWD)/spi_master.$(MODEXT) $(INSTALLDIR)

  35. clean:
  36.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.bak modules.order Module.symvers
spi_tq2440_info.c文件

点击(此处)折叠或打开

  1. /*不知道怎么入手的时候,就参考别人的代码,从内核中寻找
  2. *模仿这个程序 : arch/blackfin/mach-bf527/boards/cm-bf527.c
  3. */

  4. /*
  5. TQ2440有2个SPI控制器:
  6. SPICLK0 : GPE13
  7. SPIMOSI0 : GPE12
  8. SPIMISO0 : GPE11

  9. SPICLK1 : GPG13
  10. SPIMOSI1 : GPG12
  11. SPIMISO1 : GPG11
  12. */
  13. #include <linux/module.h>
  14. #include <linux/device.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/spi/spi.h>
  17. #include <linux/irq.h>
  18. #include <linux/interrupt.h>
  19. #include <linux/jiffies.h>
  20. #include <linux/delay.h>
  21. #include <linux/io.h>

  22. #include <mach/regs-gpio.h>

  23. /*
  24. *单板相关的信息 : 需要定义一个 spi_board_info 结构体
  25. *比如 : 片选引脚,波特率,模式A/B,其他平台相关的数据
  26. *如果只有1个设备,则这个数组只有1项,有2个设备,则这个数组有2项
  27. */
  28. static struct spi_board_info spi_info_TQ2440[] = {
  29.         {
  30.               .modalias = "myoled", /* 对应的spi_driver名字也是"oled", 一旦名字匹配就会调用probe函数*/
  31.               .max_speed_hz = 10000000,    /* max spi clock (SCK) speed in HZ : 10MHZ 是10进制数*/
  32.               .bus_num = 0, /* TQ2440里OLED接在SPI CONTROLLER 0 */
  33.               .mode = SPI_MODE_0, /* SPI传输格式A:SPI_MODE_0 格式B:SPI_MODE_1 格式C:SPI_MODE_2 格式D:SPI_MODE_3*/
  34.               .chip_select = S3C2410_GPG1, //S3C2410_GPG(1), /*OLED片选引脚 oled_cs : GPG1, 它的含义由spi_master确定 */
  35.               /*在 spi_board_info 结构里面随便找个参数来 存储 OLED命令|数据引脚的信息*/
  36.               .platform_data = (const void *)S3C2410_GPF3, //(const void)S3C2410_GPF(3), /*OLED命令|数据引脚 oled_dc : GPF3, 它在spi_driver里使用 */     
  37.          },
  38.          {
  39.               .modalias = "myspi_flash", /* 对应的spi_driver名字也是"spi_flash" */
  40.               .max_speed_hz = 80000000,    /* max spi clock (SCK) speed in HZ : 80MHz 是10进制数*/
  41.               .bus_num = 0, /* TQ2440里 flash 接在SPI CONTROLLER 0 */
  42.               .mode = SPI_MODE_0, /* SPI传输格式A */
  43.               .chip_select = S3C2410_GPG10, //S3C2410_GPG(10), /*FLASH片选引脚 flash_cs : GPG10, 它的含义由spi_master确定 */
  44.          }
  45. };

  46. static int __init spi_info_TQ2440_init(void)
  47. {
  48. /*
  49. *功能 : 注册 spi_board_info 结构体 //单板相关的信息
  50. *注意 : 3.4.2内核,在 spi_board_info注册之后,如果内核里面有 匹配的spi_master(通过bus_num来匹配),
  51. * 就会自动构造一个 spi_device 结构体。
  52. * 再通过名字modalias 找到同名的对应的 spi_driver 比如spi_oled_drver, spi_flash_driver。
  53. * 或者 spi_oled_drver注册的时候,查找同名的对应的spi_board_info,找到之后,就调用 spi_oled_probe函数。
  54. *但是 : 在2.6.30内核,spi_board_info注册之后,只是把 spi_board_info结构注册到 board_info 链表。
  55. * 在 注册 spi_master 的时候,才通过bus_num来匹配,自动构造一个 spi_device 结构体。
  56. * 所以 : spi_board_info必须先注册,再注册 spi_master。
  57. */
  58.         printk("DEVICE:1_spi_info_TQ2440_init ####################\n");
  59.         spi_register_board_info(spi_info_TQ2440, ARRAY_SIZE(spi_info_TQ2440));
  60.         printk("DEVICE:2_spi_info_TQ2440_init ####################\n");

  61.         return 0;
  62. }

  63. static void __exit spi_info_TQ2440_exit(void)
  64. {
  65.         /*没有对应的unregister函数,这个出口函数可以不要*/
  66.         return;
  67. }

  68. module_init(spi_info_TQ2440_init);
  69. module_exit(spi_info_TQ2440_exit);
  70. MODULE_AUTHOR("WangXiancai");
  71. MODULE_LICENSE("GPL");


  72. /*
  73. struct spi_device {
  74.     struct device        dev;
  75.     struct spi_master    *master;
  76.     u32            max_speed_hz;
  77.     u8            chip_select;
  78.     u8            mode;
  79. #define    SPI_CPHA    0x01            // clock phase
  80. #define    SPI_CPOL    0x02            // clock polarity
  81. #define    SPI_MODE_0    (0|0)            // (original MicroWire)
  82. #define    SPI_MODE_1    (0|SPI_CPHA)
  83. #define    SPI_MODE_2    (SPI_CPOL|0)
  84. #define    SPI_MODE_3    (SPI_CPOL|SPI_CPHA)
  85. #define    SPI_CS_HIGH    0x04            // chipselect active high?
  86. #define    SPI_LSB_FIRST    0x08            // per-word bits-on-wire
  87. #define    SPI_3WIRE    0x10            // SI/SO signals shared
  88. #define    SPI_LOOP    0x20            // loopback mode
  89.     u8            bits_per_word;
  90.     int            irq;
  91.     void            *controller_state;
  92.     void            *controller_data;
  93.     char            modalias[32];

  94.     //
  95.     // likely need more hooks for more protocol options affecting how
  96.     // the controller talks to each chip, like:
  97.     // - memory packing (12 bit samples into low bits, others zeroed)
  98.     // - priority
  99.     // - drop chipselect after each word
  100.     // - chipselect delays
  101.     // - ...
  102.     //
  103. };

  104. */


  105. /*
  106. 编译这个文件会有警告:
  107. WARNING: "spi_register_board_info" [/home/wangxc/linux/rootfs/nfs_2.6.30/wxc/driver/chardriver/spi/spi_info.ko] undefined!
  108. 因为 内核中 spi_register_board_info 函数没有被 EXPORT_SYMBOL_GPL修饰,即没有导出给模块使用。
  109. 所以本文件只能编译进内核了,无法作为模块使用了。


  110. cp spi_tq2440_info.c /work/system/linux-3.4.2/drivers/spi/
  111. 修改drivers/spi/Makefile,添加这行:
  112. obj-$(CONFIG_SPI_S3C24XX) += spi_tq2440_info.o

  113. 本程序没有使用上面的方法,修改了内核:
  114. 在文件\kerner-2.6.30\drivers\spi\spi.c 339行增加下面一行:
  115. EXPORT_SYMBOL_GPL(spi_new_device);
  116. */
oled_drv.c文件

点击(此处)折叠或打开

  1. /*模仿这个程序 : drivers/input/touchsreen/ad7877.c
  2. */

  3. /*
  4. *本程序功能 : 编写 spi_device 对应的 spi_driver
  5. */
  6. #include <linux/module.h>
  7. #include <linux/device.h>
  8. #include <linux/init.h>
  9. #include <linux/delay.h>
  10. #include <linux/input.h>
  11. #include <linux/interrupt.h>
  12. #include <linux/slab.h>
  13. #include <linux/spi/spi.h>
  14. //#include <linux/spi/ad7877.h>
  15. #include <asm/irq.h>
  16. #include <asm/io.h>

  17. #include <asm/uaccess.h> /*这个头文件中包括 copy_from_user*/

  18. #include <mach/hardware.h>
  19. #include <mach/regs-gpio.h>

  20. static int major;
  21. static struct class *class;

  22. static int spi_oled_dc_pin;            /*oled命令|数据引脚*/
  23. static int spi_oled_chip_pin;        /*oled片选引脚*/
  24. static struct spi_device * spi_oled_dev; /*spi_write函数用*/
  25. static unsigned char * kernel_buf; /*定义一个缓冲区 spi_write函数用*/

  26. /*
  27. *函数功能 : 设置 OLED的命令|数据引脚 0 : 命令引脚 1 : 数据引脚
  28. */
  29. void oled_set_dc(unsigned char val)
  30. {
  31.         s3c2410_gpio_setpin(spi_oled_dc_pin, val);
  32. }

  33. /*
  34. *函数功能 : 设置 OLED片选引脚 0:选中 1:不选中
  35. *注意 : 片选引脚其实不需要我们来管,控制器会帮我们来做片选。
  36. */
  37. void oled_set_cs(unsigned char val)
  38. {
  39.         s3c2410_gpio_setpin(spi_oled_chip_pin, val);
  40. }

  41. /*
  42. *函数功能 : 通过SPI引脚,把命令cmd发送到OLED硬件上面去
  43. */
  44. void oled_write_cmd(unsigned char cmd)
  45. {
  46.         oled_set_dc(0); //设置 OLED的命令|数据引脚,0 : 命令引脚
  47.         oled_set_cs(0); //设置 OLED片选引脚 0:选中 //这里片选可以不做了

  48.         //spi_send_byte(cmd); //通过SPI引脚发送数据
  49.         spi_write(spi_oled_dev, &cmd, 1);

  50.         oled_set_cs(1); //设置 OLED片选引脚 1:不选中
  51.         oled_set_dc(1); //操作完成之后,恢复为 1 : 数据引脚
  52. }

  53. /*
  54. *函数功能 : 通过SPI引脚,把数据data发送到OLED硬件上面去
  55. */
  56. void oled_write_data(unsigned char data)
  57. {
  58.         oled_set_dc(1); //设置 OLED的命令|数据引脚,1 : 数据引脚
  59.         oled_set_cs(0); //设置 OLED片选引脚 0:选中

  60.         //spi_send_byte(data); //通过SPI引脚发送数据
  61.         spi_write(spi_oled_dev, &data, 1);

  62.         oled_set_cs(1); //设置 OLED片选引脚 1:不选中
  63.         oled_set_dc(1); //操作完成之后,继续为 1 : 数据引脚
  64. }

  65. /*
  66. *函数功能 : 设置oled处于页地址模式
  67. *在数据手册的第30页
  68. */
  69. void oled_set_page_addr_mode(void)
  70. {
  71.         oled_write_cmd(0x20);
  72.         oled_write_cmd(0x02);
  73. }

  74. /*
  75. *函数功能 : 找到oled显示屏,设置列地址+页地址。
  76. *函数参数 :
  77. * col : 0到127 列位置
  78. * page : 0到7 页位置
  79. *注意看数据手册 : 第30页+31页,在页模式下,设置页的起始地址
  80. */
  81. void oled_set_addr(unsigned int page, unsigned int col)
  82. {
  83.         oled_write_cmd(0xB0 + page);                //设置页地址

  84.         oled_write_cmd(col & 0xf);                    //先设置低4位列地址
  85.     oled_write_cmd(0x10 + (col >> 4));    //再设置高4位列地址
  86. }

  87. /*
  88. *函数功能 : oled全部点亮
  89. *注意 : 把8页全部写入1即可
  90. */
  91. void oled_all_light(void)
  92. {
  93.         int page, i;
  94.         for (page = 0; page < 8; page ++)
  95.         {
  96.                 oled_set_addr(page, 0);
  97.                 for (i = 0; i < 128; i++)
  98.                 {
  99.                         oled_write_data(1);
  100.                 }
  101.         }
  102. }

  103. /*
  104. *函数功能 : oled清屏
  105. *注意 : 把8页全部写入0即可
  106. */
  107. void oled_clear(void)
  108. {
  109.         int page;
  110.         int i;
  111.         for(page=0; page<8; page++)
  112.         {
  113.                 oled_set_addr(page, 0); //选中第page页,第0列
  114.                 for(i=0; i<128; i++) //128列
  115.                 {
  116.                         oled_write_data(0);
  117.                 }
  118.         }
  119. }

  120. /*
  121. *函数功能 : oled清1页
  122. *注意 : 把这页全部写入0即可
  123. */
  124. void oled_clear_page(int page)
  125. {
  126.         int i;

  127.         oled_set_addr(page, 0); //选中第page页,第0列
  128.         for(i=0; i<128; i++) //128列
  129.         {
  130.                 oled_write_data(0);
  131.         }
  132. }

  133. /*
  134. *函数功能 : OLED芯片的初始化
  135. *在数据手册的第29页,有初始化方法
  136. */
  137. void oled_init(void)
  138. {
  139.         /* 向OLED发命令以初始化 */
  140.     oled_write_cmd(0xAE); //display off
  141.     oled_write_cmd(0x00); //set lower column address
  142.     oled_write_cmd(0x10); //set higher column address
  143.     oled_write_cmd(0x40); //set display start line
  144.     oled_write_cmd(0xB0); //set page address
  145.     oled_write_cmd(0x81); //contract control
  146.     oled_write_cmd(0x66); //128
  147.     oled_write_cmd(0xA1); //set segment remap
  148.     oled_write_cmd(0xA6); //normal / reverse
  149.     oled_write_cmd(0xA8); //multiplex ratio
  150.     oled_write_cmd(0x3F); //duty = 1/64
  151.     oled_write_cmd(0xC8); //Com scan direction
  152.     oled_write_cmd(0xD3); //set display offset
  153.     oled_write_cmd(0x00);
  154.     oled_write_cmd(0xD5); //set osc division
  155.     oled_write_cmd(0x80);
  156.     oled_write_cmd(0xD9); //set pre-charge period
  157.     oled_write_cmd(0x1f);
  158.     oled_write_cmd(0xDA); //set COM pins
  159.     oled_write_cmd(0x12);
  160.     oled_write_cmd(0xdb); //set vcomh
  161.     oled_write_cmd(0x30);
  162.     oled_write_cmd(0x8d); //set charge pump enable
  163.     oled_write_cmd(0x14);

  164.     oled_set_page_addr_mode(); //设置oled处于页地址模式,默认也是处于页地址模式
  165.     oled_clear(); //oled清屏

  166.     oled_write_cmd(0xAF); //display ON
  167. }


  168. /*下面是ioctl识别的宏定义*/
  169. #define OLED_CMD_INIT 0x100001 /*初始化*/
  170. #define OLED_CMD_CLEAR_ALL 0x100002 /*清屏幕*/
  171. #define OLED_CMD_CLEAR_PAGE 0x100003 /*清除某1页*/
  172. #define OLED_CMD_SET_POS 0x100004 /*设置位置*/

  173. //static long oled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  174. /*为什么视频中可以用上面这个函数???
  175. 应用程序 系统调用的 调试 视频要看
  176. */
  177. static int oled_ioctl(struct inode * pinode, struct file * pfile, unsigned int cmd, unsigned long arg)
  178. {
  179.         int page;
  180.         int col;

  181.         switch(cmd)
  182.         {
  183.                 case OLED_CMD_INIT :
  184.                 {
  185.                         oled_init(); /*oled芯片硬件初始化*/
  186.                         printk("[%s][%s][%d] oled_init end\n", __FILE__, __FUNCTION__, __LINE__);
  187.                         break;
  188.                 }
  189.                 case OLED_CMD_CLEAR_ALL :
  190.                 {
  191.                         oled_clear(); /*oled清屏*/
  192.                         break;
  193.                 }
  194.                 case OLED_CMD_CLEAR_PAGE :
  195.                 {
  196.                         page = arg;
  197.                         oled_clear_page(page); /*oled清1页屏*/
  198.                         break;
  199.                 }
  200.                 case OLED_CMD_SET_POS :
  201.                 {
  202.                         /*约定arg 中0到7位是表示page; 8到15位是表示col*/
  203.                         page = arg & 0xff;
  204.                         col = (arg>>8) & 0xff;
  205.                         oled_set_addr(page, col);
  206.                         break;
  207.                 }
  208.                 default:
  209.                 {
  210.                         break;
  211.                 }
  212.         }

  213.         return 0;
  214. }

  215. static ssize_t oled_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  216. {
  217.         int ret;
  218.         
  219.         if(count > 4096)
  220.         {
  221.                 return -EINVAL;
  222.         }

  223.         ret = copy_from_user(kernel_buf, buf, count); /*用户空间 拷贝到 内核空间*/
  224.         spi_write(spi_oled_dev, kernel_buf, count);

  225.         return 0;
  226. }

  227. static struct file_operations oled_ops = {
  228.         .owner = THIS_MODULE,
  229.         //.unlocked_ioctl = oled_ioctl, //3.4.2内核,为什么可以用这个??
  230.         .ioctl = oled_ioctl,
  231.         .write = oled_write,
  232. };


  233. static int __devinit spi_oled_probe(struct spi_device *spi)
  234. {
  235.         printk("DEVICE:spi_oled_probe ####################\n");
  236.         /*获取2个引脚*/
  237.         spi_oled_dc_pin = (int)spi->dev.platform_data;
  238.         spi_oled_chip_pin = (int)spi->chip_select;

  239.         s3c2410_gpio_cfgpin(spi_oled_dc_pin, S3C2410_GPIO_OUTPUT); /*把oled 命令|数据引脚配置成 输出引脚*/ //#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1)
  240.     s3c2410_gpio_cfgpin(spi_oled_chip_pin, S3C2410_GPIO_OUTPUT); /*把oled 片选引脚配置成 输出引脚*/
  241.         
  242.         /*上面2行,等同于下面2行
  243.         s3c2410_gpio_cfgpin(spi_oled_chip_pin, S3C2410_GPG1_OUTP);    //#define S3C2410_GPG1_OUTP (0x01 << 2)
  244.         s3c2410_gpio_cfgpin(spi_oled_dc_pin, S3C2410_GPF3_OUTP);        //#define S3C2410_GPF3_OUTP (0x01 << 6)
  245.         上面函数执行,相对于设置了GPIO的控制寄存器+GPIO的数据寄存器                                                                                                                        
  246.         */
  247.         spi_oled_dev = spi; /*把 spi_device 保存下来*/
  248.         kernel_buf = kmalloc(4096, GFP_KERNEL); /*分配一个缓冲区*/

  249.         /*注册 file_operations*/
  250.         major = register_chrdev(0, "oled", &oled_ops);

  251.         /*文件系统里面的mdev自动创建设备节点*/
  252.         class = class_create(THIS_MODULE, "oled");
  253.         /* 为了让mdev根据这些信息来创建设备节点 */
  254.         device_create(class, NULL, MKDEV(major, 0), NULL, "oled"); /* /dev/oled */

  255.         return 0;
  256. }

  257. static int __devexit spi_oled_remove(struct spi_device *spi)
  258. {
  259.         device_destroy(class, MKDEV(major, 0));
  260.         class_destroy(class);
  261.         unregister_chrdev(major, "oled");
  262.         
  263.         kfree(kernel_buf);

  264.         return 0;
  265. }

  266. /*
  267. *spi_driver结构体 : 具体的spi接口设备的驱动代码
  268. */
  269. static struct spi_driver spi_oled_driver = {
  270.     .driver = {
  271.         .name    = "myoled", /*这个名字必须和 spi_board_info中的modalias成员相同*/
  272.         .owner    = THIS_MODULE,
  273.     },
  274.     .probe        = spi_oled_probe,
  275.     .remove        = __devexit_p(spi_oled_remove),
  276. };


  277. static int __init spi_oled_init(void)
  278. {
  279. /*
  280. *函数功能 : 注册spi驱动
  281. */
  282.         spi_register_driver(&spi_oled_driver);
  283.         return 0;
  284. }

  285. static void __exit spi_oled_exit(void)
  286. {
  287. /*
  288. *函数功能 : 注销spi驱动
  289. */
  290.         spi_unregister_driver(&spi_oled_driver);
  291. }

  292. module_init(spi_oled_init);
  293. module_exit(spi_oled_exit);
  294. MODULE_AUTHOR("WangXiancai");
  295. MODULE_LICENSE("GPL");
flash_drv.c文件

点击(此处)折叠或打开

  1. /* 参考:
  2.  * drivers\mtd\devices\mtdram.c
  3.  * drivers/mtd/devices/m25p80.c
  4.  */
  5. #include <linux/module.h>
  6. #include <linux/device.h>
  7. #include <linux/spi/spi.h>
  8. #include <asm/uaccess.h> /*这个头文件中包括 copy_from_user*/
  9. #include <mach/hardware.h>
  10. #include <mach/regs-gpio.h>

  11. #include <linux/types.h>
  12. #include <linux/kernel.h>
  13. #include <linux/string.h>
  14. #include <linux/ioport.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/delay.h>
  17. #include <linux/err.h>
  18. #include <linux/slab.h>
  19. #include <linux/clk.h>
  20. #include <linux/cpufreq.h>
  21. #include <asm/io.h>

  22. #include <sound/core.h>
  23. #include <linux/spi/spi.h>
  24. #include <linux/timer.h>

  25. #include <linux/slab.h>
  26. #include <linux/ioport.h>
  27. #include <linux/vmalloc.h>
  28. #include <linux/init.h>

  29. #include <linux/mtd/compatmac.h>
  30. #include <linux/mtd/mtd.h>
  31. #include <linux/mtd/mtdram.h>
  32. #include <linux/mtd/partitions.h>

  33. /* 本程序基本流程:
  34.  * 首先: 构造注册spi_driver
  35.  * 然后: 在spi_driver的probe函数里构造注册mtd_info
  36.  */

  37. static int spi_flash_chip_pin;        /*flash片选引脚*/
  38. static struct spi_device * spi_flash_dev; /*spi_write函数会用到它*/

  39. /*定义一个mtd信息 的结构体
  40. 块设备,都需要定义这个结构体,nandflash,norflash驱动里面也定义了这样一个结构体,但是用法不一样。*/
  41. static struct mtd_info my_mtd_info; /*定义成实例,就不需要再分配内存了*/


  42. /*
  43. *函数功能 : 选中flash的地址
  44. *注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
  45. * 这里使用 3个字节的地址来表示flash芯片的地址,先发送3个字节地址的最高位,最后发送最低位
  46. */
  47. void flash_select_addr(unsigned int addr)
  48. {
  49.         unsigned char tx_buf[3];    /*要发送的数据*/
  50.         tx_buf[0] = addr >> 16;
  51.         tx_buf[1] = addr >> 8;
  52.         tx_buf[2] = addr & 0xff;
  53.         /*
  54.         spi_send_byte(addr >> 16); //这个函数会截取参数的低8位
  55.         spi_send_byte(addr >> 8);
  56.         spi_send_byte(addr & 0xff);
  57.         */
  58.         spi_write(spi_flash_dev, tx_buf, 3); /*发送3个字节 整个发送过程中,这里都只有1次片选*/
  59. }

  60. /*
  61. *函数功能 : 设置 flash片选引脚 0:选中 1:不选中
  62. **注意 : 片选引脚其实不需要我们来管,控制器会帮我们来做片选。
  63. */
  64. void flash_set_cs(unsigned char val)
  65. {
  66.         s3c2410_gpio_setpin(spi_flash_chip_pin, val);
  67. /*
  68.         if(val == 1) //让 OLED的命令|数据引脚 输出高电平
  69.         {
  70.                 GPGDAT |= (1<<10); //GPG10输出高电平
  71.         }
  72.         else if(val == 0) //让 OLED的命令|数据引脚 输出低电平
  73.         {
  74.                 GPGDAT &= (~(1<<10)); //GPG10输出低电平
  75.         }
  76. */
  77. }

  78. /*
  79. *函数功能 : 读取flash芯片的厂家ID(0xef),设备ID(0x14)
  80. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 52页
  81. */
  82. void flash_read_id(unsigned char * mid, unsigned char * did)
  83. {
  84.         unsigned char tx_buf[4]; /*要发送的数据*/
  85.         unsigned char rx_buf[2];    /*收到的数据*/
  86.         tx_buf[0] = 0x90;
  87.         tx_buf[1] = 0;
  88.         tx_buf[2] = 0;
  89.         tx_buf[3] = 0;
  90.     
  91.         //flash_set_cs(0); //选中spi flash //可以不要片选了
  92.         
  93.         //spi_send_byte(0x90); //发出90命令
  94.         //flash_select_addr(0); //选中flash的0地址
  95.         //spi_write(spi_flash_dev, tx_buf, 4); /*之所以把上面2句合并:因为读取过程中,只能有1次片选*/

  96.         //m_id = spi_recv_byte(); //第1次读取厂家ID
  97.         //d_id = spi_recv_byte(); //第2次读取设备ID
  98.         
  99.         /*函数功能 : 先写:发送指定的字节数,再读:接受指定的字节数
  100.         *之所以把上面2句合并:因为写入+读取过程中,只能有1次片选,spi_master会做片选的事
  101.         */
  102.         spi_write_then_read(spi_flash_dev, tx_buf, 4, rx_buf, 2);
  103.         
  104.         //*mid = m_id;
  105.         //*did = d_id;
  106.         *mid = rx_buf[0];
  107.         *did = rx_buf[1];

  108.         //uart_printf("MID=[0x%x]\n", m_id); //0xEF
  109.         //uart_printf("DID=[0x%x]\n", d_id); //0x14

  110.         //flash_set_cs(1);
  111. }

  112. /*
  113. *函数功能 : flash芯片写入使能
  114. *函数参数 : enable 1 : 写使能 0 : 写禁止
  115. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 23 24页
  116. */
  117. void flash_write_enable(unsigned int enable)
  118. {
  119.         unsigned char tx_val;    /*要发送的数据*/        
  120.         tx_val = enable ? 0x06 : 0x04;
  121.         spi_write(spi_flash_dev, &tx_val, 1);
  122. /*
  123.         if(enable == 1) //写使能
  124.         {
  125.                 //flash_set_cs(0); //选中spi flash
  126.                 //spi_send_byte(0x06); //发出06命令
  127.                 spi_write(spi_flash_dev, &tx_val, 1);
  128.                 //flash_set_cs(1);
  129.         }
  130.         else if(enable == 0) //写禁止
  131.         {
  132.                 //flash_set_cs(0); //选中spi flash
  133.                 //spi_send_byte(0x04); //发出04命令
  134.                 spi_write(spi_flash_dev, &tx_val, 1);
  135.                 //flash_set_cs(1);
  136.         }
  137. */
  138. }

  139. /*
  140. *函数功能 : 读取flash芯片的状态寄存器1
  141. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
  142. */
  143. unsigned char flash_read_status_reg1(void)
  144. {
  145.         unsigned char tx_val = 0x05;
  146.         unsigned char rx_val;
  147.     
  148.         //flash_set_cs(0); //选中spi flash
  149.         
  150.         //spi_send_byte(0x05); //发出05命令
  151.         //val = spi_recv_byte();
  152.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  153.         spi_write_then_read(spi_flash_dev, &tx_val, 1, &rx_val, 1);
  154.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);

  155.         //flash_set_cs(1);
  156.         
  157.         return rx_val;
  158. }

  159. /*
  160. *函数功能 : 读取flash芯片的状态寄存器2
  161. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25页
  162. */
  163. unsigned char flash_read_status_reg2(void)
  164. {
  165.         unsigned char tx_val = 0x35;
  166.         unsigned char rx_val;
  167.     
  168.         //flash_set_cs(0); //选中spi flash
  169.         //spi_send_byte(0x35); //发出35命令
  170.         //val = spi_recv_byte();
  171.         spi_write_then_read(spi_flash_dev, &tx_val, 1, &rx_val, 1);

  172.         //flash_set_cs(1);
  173.         
  174.         return rx_val;        
  175. }

  176. /*
  177. *函数功能 : flash擦除+写入时,需要一定处理时间,这里判断是否操作完成
  178. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16页
  179. *注意 : 判断状态寄存器1的bit0:busy(0:内部操作完成,空闲 1:内部操作正在进行,)
  180. */
  181. void flash_wait_for_busy(void)
  182. {
  183.         unsigned char reg1_val;
  184.         
  185.         while(1) /*死循环,对cpu资源的浪费很大 : 应该采用休眠,因为休眠时,交出cpu的控制权*/
  186.         {
  187.                 reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值,注意:这一句一定要放在循环里面,刚开始没有放在循环里面,排错排了很久
  188.                 
  189.                 //uart_printf("****** reg1_val=[0x%x]*******\n", reg1_val);
  190.                 
  191.                 if((reg1_val & 0x1) == 0) //等待bit0=0:内部操作完成
  192.                 {
  193.                         break;
  194.                 }
  195.         /*函数功能 : 设置当前进程的状态
  196.         *TASK_INTERRUPTIBLE : 中断
  197.         */
  198.                 set_current_state(TASK_INTERRUPTIBLE);
  199.                 
  200.                 /* 休眠一段时间 */
  201.         /* 扇区擦除耗时时间 Sector erase time : 60ms
  202.          * 页写耗时时间 Page program time : 0.7ms
  203.          * 读状态寄存器耗时时间 Write status reg time : 10ms
  204.          */
  205.         schedule_timeout(HZ/100); /* HZ时长是1秒,休眠10MS后再次判断 */
  206.         }
  207.         //while((reg1_val & 0x1) == 1); //等待bit0=0,是1则一直循环
  208. }

  209. /*
  210. *函数功能 : 写flash芯片的状态寄存器
  211. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 25+26页
  212. *注意 : 写状态寄存器之前,也要 先让flash芯片写使能 + 去除状态寄存器的保护 15页
  213. * 工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
  214. * 写flash芯片的状态寄存器耗时大概是 10ms W25Q16DV-具有SPI接口的flash芯片 67页
  215. */
  216. void flash_write_status_reg(unsigned char reg1_val, unsigned char reg2_val)
  217. {
  218.         unsigned char tx_buf[3];    /*要发送的数据*/
  219.         tx_buf[0] = 0x01;
  220.         tx_buf[1] = reg1_val;
  221.         tx_buf[2] = reg2_val;
  222.     
  223.         flash_write_enable(1); //flash芯片写使能
  224.     
  225.         //flash_set_cs(0); //选中spi flash
  226.         //spi_send_byte(0x01); //发出01命令
  227.         //spi_send_byte(reg1_val); //写寄存器1
  228.         //spi_send_byte(reg2_val); //写寄存器2
  229.         spi_write(spi_flash_dev, tx_buf, 3);

  230.         //flash_set_cs(1);

  231.         flash_wait_for_busy(); //等待写数据完成
  232. }

  233. /*
  234. *函数功能 : 去除状态寄存器的写保护,去保护之后,才可以写状态寄存器
  235. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 15 16页
  236. *工厂出厂默认状态是 : 写使能之后,SRP1=0,SRP0=0, 就可以写状态寄存器了。我们这里还是重新设置一下
  237. */
  238. void flash_clean_protect_for_status_reg(void)
  239. {
  240.         unsigned char reg1_val = 0;
  241.         unsigned char reg2_val = 0;

  242.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
  243.         reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
  244.         
  245.         //状态寄存器1 的bit7:SRP0 要清0
  246.         reg1_val &= (~(1<<7));
  247.     
  248.         //状态寄存器2 的bit0:SRP1 要清0
  249.         reg2_val &= (~(1<<0));

  250.         flash_write_status_reg(reg1_val, reg2_val);
  251. }

  252. /*
  253. *函数功能 : 去除整个存储区间的写保护,去保护之后,才可以写flash存储空间
  254. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 16+17页
  255. *CMP=0, BP2=0, BP1=0, BP0=0
  256. */
  257. void flash_clean_protect_for_memory(void)
  258. {
  259.         unsigned char reg1_val = 0;
  260.         unsigned char reg2_val = 0;
  261.         
  262.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
  263.         reg2_val = flash_read_status_reg2(); //保存状态寄存器2中的值
  264.         
  265.         //状态寄存器1 的bit4:BP2 bit3:BP1, bit2:BP0 要清0
  266.         reg1_val &= (~(1<<4));
  267.         reg1_val &= (~(1<<3));
  268.         reg1_val &= (~(1<<2));

  269.         //状态寄存器2 的bit6:CMP 要清0
  270.         reg2_val &= (~(1<<6));

  271.         flash_write_status_reg(reg1_val, reg2_val);
  272. }

  273. /*
  274. *函数功能 : flash擦除1个扇区的内存空间
  275. *函数参数 :
  276. * addr : 将擦除的地址
  277. * 1, 这个地址值,只有前21位有效
  278. * 2, 因为是扇区擦除指令,所以一次最多擦除4K, 如果地址不是4K对齐,比如4094,
  279. * 则会擦除整个扇区空间,不是只擦除本扇区剩余的2个字节。绝对不会擦除接下来的扇区的空间
  280. *:会擦除 addr 所在的整个扇区4k空间
  281. * 3, 地址最好4K对齐
  282. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 43页
  283. *注意 : 我们使用的W25Q16DV芯片,flash内存是16Mbit=2M字节,共需要21根地址线来寻址
  284. * 用扇区擦除指令,每次可以擦除16页; 每个扇区=16页=4K字节; 整个flash共512个扇区
  285. * 擦除耗时大概是60ms W25Q16DV-具有SPI接口的flash芯片 67页
  286. */
  287. void flash_erase_memory(unsigned int addr)
  288. {
  289.         unsigned char tx_buf[4]; /*要发送的数据*/
  290.         tx_buf[0] = 0x20;
  291.         tx_buf[1] = addr >> 16;
  292.         tx_buf[2] = addr >> 8;
  293.         tx_buf[3] = addr & 0xff;
  294.     
  295.         flash_write_enable(1); //flash芯片写使能
  296.     
  297.         //flash_set_cs(0); //选中spi flash
  298.         //spi_send_byte(0x20); //发出20命令:扇区擦除指令
  299.         //flash_select_addr(addr); //发出地址
  300.         spi_write(spi_flash_dev, tx_buf, 4); /*发送命令 地址时,片选要一直维持低电平*/

  301.         //flash_set_cs(1);
  302.         
  303.         flash_wait_for_busy(); //等待擦除完成
  304. }

  305. /*
  306. *函数功能 : 向flash存储空间写数据
  307. *函数参数 :
  308. * addr : 将写入的地址
  309. * 1, 这个地址值,只有前21位有效
  310. * 2, 因为是页编程指令,所以一次最多可写1页, 如果地址不是页对齐256字节,比如254,则只写本页剩余的2个字节。绝对不会连续写到下面的页
  311. * 3, 写入的地址最好256字节对齐
  312. * buf : 将写入的数据
  313. * len : 将写入的数据的长度
  314. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 41页
  315. * 写flash存储空间耗时大概是 0.7ms W25Q16DV-具有SPI接口的flash芯片 67页

  316. 一个 spi_message 可能是由 多个 transfers 组成的
  317. static inline int
  318. spi_write(struct spi_device *spi, const u8 *buf, size_t len)
  319. {
  320.     struct spi_transfer    t = {
  321.             .tx_buf        = buf,
  322.             .len        = len,
  323.         };
  324.     struct spi_message    m;

  325.     spi_message_init(&m);
  326.     spi_message_add_tail(&t, &m);
  327.     return spi_sync(spi, &m);
  328. }
  329. */
  330. void flash_write_memory(unsigned int addr, char * buf, int len)
  331. {
  332.         //int i = 0;
  333.         unsigned char tx_buf[4]; /*要发送的数据*/
  334.         
  335.         struct spi_transfer    t[] = { /*这里初始化2个spi_transfer*/
  336.                 {        /*先发送4个字节*/
  337.                         .tx_buf        = tx_buf,
  338.                         .len        = 4,
  339.                 },
  340.                 {        /*再发出数据*/
  341.                         .tx_buf        = buf,
  342.                         .len        = len,
  343.                 },
  344.         };
  345.         struct spi_message    m;
  346.         
  347.         tx_buf[0] = 0x02;
  348.         tx_buf[1] = addr >> 16;
  349.         tx_buf[2] = addr >> 8;
  350.         tx_buf[3] = addr & 0xff;
  351.     
  352.         flash_write_enable(1); //flash芯片写使能
  353.         
  354.         spi_message_init(&m);                            /*初始化这个spi_message*/
  355.         spi_message_add_tail(&t[0], &m);    /*把 spi_transfer[0] 添加到 spi_message*/
  356.         spi_message_add_tail(&t[1], &m);    /*把 spi_transfer[1] 添加到 spi_message*/
  357.         spi_sync(spi_flash_dev, &m);            /*启动传输:发送spi_message*/

  358.         /*也可以把要发送的4个字节+buf,放到一个大的buffer中,再一起发送出去*/
  359.         /*
  360.         flash_set_cs(0); //选中spi flash
  361.         spi_send_byte(0x02); //发出02命令, 页编程指令,每次可编程256个字节;即使用页写命令,每次可写1页
  362.         flash_select_addr(addr); //发出地址
  363.         
  364.         for(i=0; i<len; i++)
  365.         {
  366.                 spi_send_byte(buf[i]); //一次写入一个字节的数据
  367.         }
  368.         
  369.         flash_set_cs(1);
  370.         */

  371.         flash_wait_for_busy(); //等待写数据完成
  372. }

  373. /*
  374. *函数功能 : 读取flash存储空间的数据
  375. *函数参数 :
  376. * addr : 将写入的地址
  377. * buf : 将写入的数据
  378. * len : 将写入的数据的长度
  379. *芯片手册 : W25Q16DV-具有SPI接口的flash芯片 27页

  380.     spi_write_then_read规定了tx_cnt+rx_cnt < 32
  381.     所以对于大量数据的读取,不能使用该函数

  382. */
  383. void flash_read_memory(unsigned int addr, char * buf, int len)
  384. {
  385.         unsigned char tx_buf[4]; /*要发送的数据*/
  386.         
  387.         struct spi_transfer    t[] = { /*这里初始化2个spi_transfer*/
  388.                 {        /*先发送4个字节*/
  389.                         .tx_buf        = tx_buf,
  390.                         .len        = 4,
  391.                 },
  392.                 {        /*再接受数据*/
  393.                         .rx_buf        = buf,
  394.                         .len        = len,
  395.                 },
  396.         };
  397.         struct spi_message    m;
  398.         
  399.         tx_buf[0] = 0x03;
  400.         tx_buf[1] = addr >> 16;
  401.         tx_buf[2] = addr >> 8;
  402.         tx_buf[3] = addr & 0xff;
  403.     
  404.         flash_write_enable(1); //flash芯片写使能
  405.         
  406.         spi_message_init(&m);                            /*初始化这个spi_message*/
  407.         spi_message_add_tail(&t[0], &m);    /*把 spi_transfer[0] 添加到 spi_message*/
  408.         spi_message_add_tail(&t[1], &m);    /*把 spi_transfer[1] 添加到 spi_message*/
  409.         spi_sync(spi_flash_dev, &m);            /*启动传输:发送spi_message*/
  410.         
  411.         /*
  412.         int i = 0;
  413.         //flash_set_cs(0); //选中spi flash
  414.         spi_send_byte(0x03); //发出03命令
  415.         flash_select_addr(addr); //发出地址
  416.         
  417.         for(i=0; i<len; i++)
  418.         {
  419.                 buf[i] = spi_recv_byte(); //一次读取一个字节的数据
  420.         }
  421.         
  422.         //flash_set_cs(1);
  423.         */
  424. }


  425. /*
  426. *函数功能 : flash初始化函数
  427. */
  428. void flash_init(void)
  429. {
  430.         unsigned char reg1_val;

  431.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  432.         reg1_val = flash_read_status_reg1(); //保存状态寄存器1中的值
  433.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  434.         flash_clean_protect_for_status_reg(); //去除状态寄存器的保护
  435.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  436.         flash_clean_protect_for_memory(); //去除整个存储区间的写保护
  437.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  438. }


  439. /********************************************************
  440. 上面是和裸机驱动很像的功能函数
  441. 下面才是linux驱动相关的函数
  442. *********************************************************/
  443. /*
  444. *函数功能 : spi flash的擦除函数
  445. *instr : 擦除信息结构体
  446. * instr->addr : 要擦除的起始地址
  447. * instr->len : 要擦除的长度
  448. */
  449. static int my_spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
  450. {
  451.         unsigned int addr = instr->addr; /*要擦除的起始地址*/
  452.         unsigned int i = 0;

  453.         /* 判断参数的地址是否对齐 */
  454.         if((addr & (my_mtd_info.erasesize - 1)) || (instr->len & (my_mtd_info.erasesize - 1)))
  455.         {
  456.                 printk("addr/len is not aligned\n");
  457.                 return -EINVAL;
  458.         }

  459.         for(i=0; i<instr->len; i+=4096) /*扇区擦除指令:1次擦除4K*/
  460.         {
  461.                 flash_erase_memory(addr); /*逐个扇区的擦除 : addr所在的扇区剩余的字节空间,都会擦除*/
  462.                 addr += 4096;
  463.         }

  464.         instr->state = MTD_ERASE_DONE;
  465.         mtd_erase_callback(instr);
  466.         return 0;
  467. }

  468. static int my_spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
  469. {
  470.         /*
  471.         from : 读取的地址
  472.         buf : 读取的数据,保存的位置
  473.         len : 读取的数据的长度
  474.         */
  475.         flash_read_memory(from, buf, len);
  476.         *retlen = len;

  477.         return 0;
  478. }

  479. /*
  480. *函数功能 : spi flash的写入函数
  481. *函数参数 :
  482. * to : 要写入的起始地址
  483. * len : 要写入的长度
  484. * retlen : 要写入的长度
  485. * buf : 要写入的数据
  486. */
  487. static int my_spi_flash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
  488. {
  489.         unsigned int addr = to;        /*要写入的起始地址*/
  490.         unsigned int i = 0;        
  491.         
  492.         /* 判断参数的地址是否对齐 */
  493.         if((to & (my_mtd_info.erasesize - 1)) || (len & (my_mtd_info.erasesize - 1)))
  494.         {
  495.                 printk("addr/len is not aligned\n");
  496.                 return -EINVAL;
  497.         }
  498.         
  499.         for(i=0; i<len; i+=256) /*多个页,要多次才可以写入*/
  500.         {
  501.                 flash_write_memory(addr, (unsigned char *)buf, 256); /*逐页的写 : 页编程指令,所以一次最多可写1页(256字节)*/
  502.                 addr += 256;    //写入的起始地址
  503.                 buf += 256;        //要写入的数据
  504.         }
  505.         
  506.         *retlen = len; /*要写入的起始地址*/
  507.         return 0;
  508. }

  509. static int __devinit spi_flash_probe(struct spi_device *spi)
  510. {
  511.         unsigned char mid = 0;
  512.         unsigned char did = 0;
  513.         
  514.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  515.         spi_flash_dev = spi; /*把 spi_device 保存下来*/
  516.         spi_flash_chip_pin = (int)spi->chip_select; /*获取片选引脚*/
  517.         s3c2410_gpio_cfgpin(spi_flash_chip_pin, S3C2410_GPIO_OUTPUT); /*把oled 片选引脚配置成 输出引脚*/

  518.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  519.         flash_init(); /*要先设置片选引脚,才可以做初始化的工作*/
  520.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  521.         flash_read_id(&mid, &did);
  522.         printk("SPI Flash ID: [0x%02x] [0x%02x]\n", mid, did);

  523.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  524.         memset(&my_mtd_info, 0, sizeof(my_mtd_info));        
  525.         /* Setup the MTD structure */
  526.         my_mtd_info.name = "my_spi_flash";
  527.         my_mtd_info.type = MTD_NORFLASH; /*内存类型*/
  528.         my_mtd_info.flags = MTD_CAP_NORFLASH;
  529.         my_mtd_info.size = 0x200000; /* flash的大小:2M 字节*/
  530.         my_mtd_info.writesize = 1; /*写的最小单位:1个字节*/
  531.         //my_mtd_info.writebufsize = 4096; /* 没有用到 */
  532.         my_mtd_info.erasesize = 4096; /* 擦除的最小单位:扇区=16页=4K字节 */

  533.         my_mtd_info.owner = THIS_MODULE;
  534.         my_mtd_info.erase = my_spi_flash_erase;
  535.         my_mtd_info.read = my_spi_flash_read;
  536.         my_mtd_info.write = my_spi_flash_write;

  537.         /* 添加分区 */
  538.         //add_mtd_partitions(my_mtd_info, my_spimtd_parts, 4);
  539.         //mtd_device_register(&my_mtd_info, NULL, 0); /*2.6.30内核,没有这个函数*/
  540.         
  541.         /*下面这个函数功能:就是把整个spiflash的空间,当做一个分区。*/
  542.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);
  543.         add_mtd_device(&my_mtd_info);
  544.         //printk("[%s][%s][%d] spi_flash_probe\n", __FILE__, __FUNCTION__, __LINE__);

  545.         return 0;
  546. }

  547. static int __devexit spi_flash_remove(struct spi_device *spi)
  548. {
  549.     //mtd_device_unregister(&spi_flash_dev);
  550.     del_mtd_partitions(&my_mtd_info);    /*对应 : add_mtd_partitions*/
  551.     return 0;
  552. }

  553. /*
  554. *spi_driver结构体 : 具体的spi接口设备的驱动代码
  555. */
  556. static struct spi_driver spi_flash_driver = {
  557.         .driver =
  558.         {
  559.                 .name    = "myspi_flash", /*这个名字必须和 spi_board_info中的modalias成员相同*/
  560.                 .owner    = THIS_MODULE,
  561.         },
  562.         .probe        = spi_flash_probe,
  563.         .remove        = __devexit_p(spi_flash_remove),
  564. };

  565. static int __init spi_flash_init(void)
  566. {
  567. /*
  568. *函数功能 : 注册spi驱动
  569. */
  570.         //printk("[%s][%s][%d] spi_flash_init\n", __FILE__, __FUNCTION__, __LINE__);
  571.         spi_register_driver(&spi_flash_driver);
  572.         //printk("[%s][%s][%d] spi_flash_init\n", __FILE__, __FUNCTION__, __LINE__);
  573.         return 0;
  574. }

  575. static void __exit spi_flash_exit(void)
  576. {
  577. /*
  578. *函数功能 : 注销spi驱动
  579. */
  580.         spi_unregister_driver(&spi_flash_driver);
  581. }

  582. module_init(spi_flash_init);
  583. module_exit(spi_flash_exit);
  584. MODULE_AUTHOR("WangXiancai");
  585. MODULE_LICENSE("GPL");

  586. /*
  587. 测试 :
  588. 0, # ls -lrt /dev/mtd* //说明新内核还没有nandflash,spiflash驱动
  589. ls: /dev/mtd*: No such file or directory

  590. 1, 安装驱动
  591. # insmod spi_tq2440_info.ko
  592. # insmod spi_master.ko
  593. # insmod flash_drv.ko
  594. SPI Flash ID: [0xef] [0x14]
  595. # ls -lrt /dev/mtd* //已经创建了1个mtd分区
  596. brw-rw---- 1 root root 31, 0 May 29 04:32 /dev/mtdblock0
  597. crw-rw---- 1 root root 90, 1 May 29 04:32 /dev/mtd0ro
  598. crw-rw---- 1 root root 90, 0 May 29 04:32 /dev/mtd0

  599. 2, # mkfs.vfat /dev/mtdblock0 //把该设备格式化成fat32文件系统

  600. # mkfs.ext3 /dev/sda6 注:把该设备格式化成ext3文件系统
  601. # mke2fs -j /dev/sda6 注:把该设备格式化成ext3文件系统
  602. # mkfs.ext2 /dev/sda6 注:把该设备格式化成ext2文件系统
  603. # mke2fs /dev/sda6 注:把该设备格式化成ext2文件系统
  604. # mkfs.reiserfs /dev/sda6 注:把该设备格式化成reiserfs文件系统
  605. # mkfs.vfat /dev/sda6 注:把该设备格式化成fat32文件系统
  606. # mkfs.msdos /dev/sda6 注:把该设备格式化成fat16文件系统,msdos文件系统就是fat16;
  607. # mkdosfs /dev/sda6 注:把该设备格式化成fat16文件系统,同mkfs.msdos

  608. 3, # mount -t vfat /dev/mtdblock0 /mnt //挂载块设备到 /mnt目录
  609. # cd /mnt
  610. # ls
  611. # touch 1.c
  612. # echo wangxiancai >> 1.c
  613. # l
  614. -rwxr-xr-x 1 root root 12 May 29 05:48 1.c
  615. # cat 1.c
  616. wangxiancai

  617. 4,
  618. # cd /
  619. # umount /mnt                             //卸载磁盘

  620. 5,重启 内核

  621. 6, 重安装驱动
  622. # insmod spi_tq2440_info.ko
  623. # insmod spi_master.ko
  624. # insmod flash_drv.ko
  625. SPI Flash ID: [0xef] [0x14]

  626. # cd /mnt
  627. # l //发现什么都没有
  628. # ls -lrt /dev/mtd* //已经创建了1个mtd分区
  629. brw-rw---- 1 root root 31, 0 May 29 04:32 /dev/mtdblock0
  630. crw-rw---- 1 root root 90, 1 May 29 04:32 /dev/mtd0ro
  631. crw-rw---- 1 root root 90, 0 May 29 04:32 /dev/mtd0

  632. # mount -t vfat /dev/mtdblock0 /mnt //挂载块设备到 /mnt目录
  633. # cd /mnt
  634. # ls //发现还有数据:这个说明磁盘设备里面已经有数据,flash块设备已经起到作用了
  635. */


  636. /*
  637. struct mtd_info {
  638.     u_char type;
  639.     uint32_t flags;
  640.     uint64_t size;     // Total size of the MTD

  641.     uint32_t erasesize;
  642.     uint32_t writesize;

  643.     uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
  644.     uint32_t oobavail; // Available OOB bytes per block

  645.     unsigned int erasesize_shift;
  646.     unsigned int writesize_shift;
  647.     unsigned int erasesize_mask;
  648.     unsigned int writesize_mask;

  649.     const char *name;
  650.     int index;

  651.     struct nand_ecclayout *ecclayout;

  652.     int numeraseregions;
  653.     struct mtd_erase_region_info *eraseregions;

  654.     int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
  655.     int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys);
  656.     void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
  657.     unsigned long (*get_unmapped_area) (struct mtd_info *mtd, unsigned long len, unsigned long offset, unsigned long flags);

  658.     struct backing_dev_info *backing_dev_info;


  659.     int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  660.     int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  661.     int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  662.     int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
  663.     int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);

  664.     int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  665.     int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  666.     int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  667.     int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  668.     int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  669.     int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
  670.     int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
  671.     void (*sync) (struct mtd_info *mtd);
  672.     int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
  673.     int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

  674.     int (*suspend) (struct mtd_info *mtd);
  675.     void (*resume) (struct mtd_info *mtd);
  676.     int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
  677.     int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
  678.     struct notifier_block reboot_notifier;
  679.     struct mtd_ecc_stats ecc_stats;
  680.     int subpage_sft;

  681.     void *priv;

  682.     struct module *owner;
  683.     struct device dev;
  684.     int usecount;

  685.     int (*get_device) (struct mtd_info *mtd);
  686.     void (*put_device) (struct mtd_info *mtd);
  687. };

  688. */
spi_master.c文件

点击(此处)折叠或打开

  1. /*
  2. 参考文件:linux/drivers/spi/spi_s3c24xx.c
  3. */

  4. /*
  5. *注意 : spi_master可以选择使用内核提供的代码,也可以自己编写,我们这里先使用内核提供的代码测试一下,
  6. *再使用自己编写的代码

  7. 使用内核提供的代码,要进行必要的检查:
  8. 检查代码,spi_master: drivers\spi\spi-s3c24xx.c
  9. 1,它注册了名为"s3c2410-spi"的platform_driver
  10.     需要有同名的platform_device
  11.     修改arch\arm\mach-s3c24xx\mach-smdk2440.c
  12. static struct platform_device *smdk2440_devices[] __initdata = {
  13.     &s3c_device_ohci,
  14.     &s3c_device_lcd,
  15.     &s3c_device_wdt,
  16.     &s3c_device_i2c0,
  17.     &s3c_device_iis,
  18. +    &s3c_device_spi0, //设备资源的定义在文件 : \kerner-2.6.30\arch\arm\plat-s3c24xx\devs.c
  19. +    &s3c_device_spi1,
  20. };

  21. 重新编译内核,启动开发板之后,
  22. 内核里面就有同名的 platform_device 和 platform_driver
  23. 就会导致 文件 drivers\spi\spi-s3c24xx.c 中 s3c24xx_spi_probe 函数被调用。
  24. 分析文件:\drivers\spi\spi_s3c24xx.c : s3c24xx_spi_probe函数。
  25. //通过下面一行发现:这个platform_data没有,需要重新定义, 在文件:\arch\arm\plat-s3c24xx\devs.c中定义
  26.     hw->pdata = pdata = pdev->dev.platform_data;
  27.     
  28. 2, 给platform_dev添加platform_data
  29.    arch\arm\plat-samsung\devs.c
  30.    
  31. 3, 编译安装驱动
  32. 内核提供的主控驱动是 : \kerner-2.6.30\drivers\spi\spi_s3c24xx.c
  33. 想利用主控驱动,就要配置内核,这个驱动肯定有对应的配置项
  34. makefile里面有 obj-$(CONFIG_SPI_S3C24XX)        += spi_s3c24xx.o
  35. make menuconfig /CONFIG_SPI_S3C24XX 就可以找到对应的配置项。

  36.    Symbol: SPI_S3C24XX [=n] //语法
  37.    Prompt: Samsung S3C24XX series SPI //提示
  38.       Defined at drivers/spi/Kconfig:183 //定义
  39.       Depends on: SPI && SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL
  40.       //依赖于 这几个选择被选中,才会出现 Samsung S3C24XX series SPI。如果没有被选中,我们还需要手工去选中,才可以继续下面的步骤。
  41.       Location: //具体位置
  42.         -> Device Drivers
  43.          -> SPI support (SPI [=y])
  44.       Selects: SPI_BITBANG
  45. <*> Samsung S3C24XX series SPI //把这个选上,内核就会编译这个驱动了
  46. 编译如果差头文件,还要去找其对应的头文件。

  47. */

  48. /*
  49. 功能 : 自己注册 spi_master
  50. 注意 : 对于每一个spi控制器,都要注册一个 spi_master。
  51. */


  52. #include <linux/init.h>
  53. #include <linux/spinlock.h>
  54. #include <linux/workqueue.h>
  55. #include <linux/interrupt.h>
  56. #include <linux/delay.h>
  57. #include <linux/errno.h>
  58. #include <linux/err.h>
  59. #include <linux/clk.h>
  60. #include <linux/platform_device.h>
  61. #include <linux/gpio.h>

  62. #include <linux/spi/spi.h>
  63. #include <linux/spi/spi_bitbang.h>
  64. #include <plat/regs-spi.h>

  65. #include <asm/io.h>
  66. #include <asm/dma.h>
  67. #include <mach/hardware.h>
  68. #include <mach/regs-gpio.h>

  69. /*用作私有数据*/
  70. struct my_spi_data{
  71.         unsigned int reg_base;                            /*spi相关寄存器的虚拟基地址*/
  72.         int irq_no;                                                    /*中断号*/
  73.         struct spi_transfer * cur_transfer; /*当前的一次传输*/
  74.         int cur_count;                                            /*当前已经传输的长度*/
  75.         struct completion complet; /*完成量completion接口*/
  76.         /*完成量|等待队列|信号量 有什么区别?*/
  77.         struct platform_device * pdev; /*平台设备*/
  78. };

  79. /*
  80. *函数功能 : 设置频率 oled最大是10Mhz, flash最大是80Mhz。
  81. * 设置传输模式
  82. */
  83. static int my_spi_setup(struct spi_device *spi)
  84. {
  85.         struct my_spi_data * spi_data = NULL; /*私有数据*/
  86.         struct clk * pclk = NULL; /*函数功能 : 取得PCLK时钟*/

  87.         int SPCON_cpol = 0; //SPCON0[2]
  88.         int SPCON_cpha = 0; //SPCON0[1]

  89.         int div; //SPPRE0[7:0]位 预分频值

  90.         spi_data = spi_master_get_devdata(spi->master); /*私有数据*/
  91.         if(spi_data == NULL)
  92.         {
  93.                 printk("[%s][%s][%d] spi_master_get_devdata err\n", __FILE__, __FUNCTION__, __LINE__);
  94.         }
  95.         pclk = clk_get(NULL, "pclk"); /*函数功能 : 取得PCLK时钟*/
  96.         clk_enable(pclk);
  97.         if(pclk == NULL)
  98.         {
  99.                 printk("[%s][%s][%d] clk_get err\n", __FILE__, __FUNCTION__, __LINE__);
  100.         }

  101.         //printk("[%s][%s][%d] spi->mode=[%d]\n", __FILE__, __FUNCTION__, __LINE__, spi->mode);
  102.         /* SPI的传输模式 : 由 SPCON0[2:1]=CPOL+CPHA2个位共同决定 */
  103.         if (spi->mode & 1)
  104.         {
  105.                 SPCON_cpha = 1;
  106.         }
  107.         if (spi->mode & 2)
  108.         {
  109.                 SPCON_cpol = 1;
  110.         }

  111.     /* 写入SPI控制寄存器 SPCON0*/
  112.     /*[6:5] : 01, 中断模式
  113.     * [4] : 1 = enable 使能时钟
  114.     * [3] : 1 = master 主机控制器
  115.     * [2] : CPOL位 0 = 高电平有效,在没有SPI的操作时,时钟线的电平
  116.     * [1] : CPHA位 0 = format A 格式A
  117.     * [0] : 0 = normal mode
  118.     */
  119.     /* 函数功能 : 向寄存器中写值 */
  120.         writeb((1<<5) | (1<<4) | (1<<3) | (SPCON_cpol << 2) | (SPCON_cpha << 1), spi_data->reg_base + S3C2410_SPCON);
  121.         //printk("[%s][%s][%d] SPCON=[0x%x]\n", __FILE__, __FUNCTION__, __LINE__, (*(volatile unsigned char *)(spi_data->reg_base + S3C2410_SPCON)));

  122.         /* 设置最大传输频率 : max_speed_hz */
  123.     /* 波特率寄存器 SPPRE0*/
  124.         /* OLED : 100ns, 10MHz //SPEC UG-2864TMBEG01 --OLED成品的数据手册 4线spi 17页
  125.     * FLASH : 104MHz 3.3V //W25Q16DV-具有SPI接口的flash芯片 66页
  126.     * 取10MHz PCLK=50M
  127.     * 10 = PCLK / 2 / (Prescaler value + 1) //2440最大提供25M频率
  128.     * Prescaler value = 1.5 取 2
  129.     * Baud rate = 50/2/3=8.3MHz //真实波特率
  130.     */
  131.     /*#define DIV_ROUND_UP(x,y) (((x) + ((y) - 1)) / (y)) //(x+y-1)/y
  132.     *宏的功能 : 实现x/y向上取整
  133.     */
  134.     //printk("[%s][%s][%d] clk_get_rate(pclk)=[%ld]\n", __FILE__, __FUNCTION__, __LINE__, clk_get_rate(pclk));
  135.         //printk("[%s][%s][%d] spi->max_speed_hz=[%d]\n", __FILE__, __FUNCTION__, __LINE__, spi->max_speed_hz);
  136.         div = DIV_ROUND_UP(clk_get_rate(pclk), (spi->max_speed_hz) * 2) - 1;
  137.         //printk("[%s][%s][%d] div=[%d]\n", __FILE__, __FUNCTION__, __LINE__, div);
  138.         if(div > 255)
  139.         {
  140.                 div = 255;
  141.         }
  142.         
  143.         /* 函数功能 : 向SPPRE0寄存器 中写 div预分频值 */
  144.         writeb(div, spi_data->reg_base + S3C2410_SPPRE);
  145.         //printk("[%s][%s][%d] my_spi_setup\n", __FILE__, __FUNCTION__, __LINE__);
  146.         clk_put(pclk);

  147.     return 0;
  148. }

  149. /*
  150. *函数功能 : spi中断服务程序
  151. *中断的触发 : 每完成发送一个数据 或者 每成功接收完成一个数据,就会触发一次中断。
  152. *
  153. *编程心得 : 之前,这个中断函数一直不能触发,找了很久的原因,
  154. * 拍错的过程非常的艰苦:1,怀疑 pclk 或者 spiclk 没有启动
  155. * 2,怀疑GPIO引脚设置出错
  156. * 3,怀疑spi相关控制器的寄存器设置出错
  157. * 4,怀疑中断注册失败
  158. * 最后发现居然是个很低级的错误,只是 if(spi_tran == NULL)的判断,在
  159. * 语句 spi_data = spi_master_get_devdata(master); 之前,肯定为空啊,所以感觉老是不能触发,
  160. * 写代码,要相信自己,大方向不会错,错的都是细节。
  161. */
  162. static irqreturn_t s3c2440_spi_interrupt(int irq, void *dev_id)
  163. {
  164.     struct spi_master *master = NULL;
  165.     struct my_spi_data * spi_data = NULL;                /*私有数据*/
  166.     struct spi_transfer * spi_tran = NULL;            /*当前的一次传输*/

  167.     master = (struct spi_master *)dev_id;
  168.     spi_data = spi_master_get_devdata(master);    /*私有数据*/
  169.     spi_tran = spi_data->cur_transfer;                    /*当前的一次传输*/
  170.     
  171.     if(spi_tran == NULL) /* 误触发 */
  172.     {
  173.             printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  174.                 return IRQ_HANDLED;
  175.     }
  176.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  177.     /* 处理 当前的一次传输 spi_tran */
  178.     if(spi_tran->tx_buf) /* 是发送 */
  179.     {
  180.         (spi_data->cur_count)++; /*发送完一个数据*/
  181.         
  182.         if(spi_data->cur_count < spi_tran->len)/* 没发完? */
  183.         {
  184.                 //printk("[%s][%s][%d] len=[%d] tx_buf[%d]=[0x%x]\n", __FILE__, __FUNCTION__, __LINE__, spi_tran->len, spi_data->cur_count, ((unsigned char *)(spi_tran->tx_buf))[spi_data->cur_count]);
  185.             writeb(((unsigned char *)(spi_tran->tx_buf))[spi_data->cur_count], spi_data->reg_base + S3C2410_SPTDAT);
  186.         }
  187.         else /*本次 spi_transfer 处理完毕*/
  188.         {
  189.                 /*函数功能 : complete只会唤醒一个等待线程*/
  190.             complete(&(spi_data->complet)); /* 本次 tx_buf 中数据全部发送完毕,则唤醒 */

  191.             //tran_end = 1; /* 睡眠函数的第2个参数为真 */
  192.                        //wake_up_interruptible(&tran_waitq); /* 唤醒休眠的进程 */

  193.             //printk("[%s][%s][%d] complete ok\n", __FILE__, __FUNCTION__, __LINE__);
  194.         }
  195.     }
  196.     else /* 接收 */
  197.     {
  198.         /*/存数据 */
  199.         ((unsigned char *)spi_tran->rx_buf)[spi_data->cur_count] = (unsigned char)readb(spi_data->reg_base + S3C2410_SPRDAT);
  200.         (spi_data->cur_count)++;
  201.                 //printk("[%s][%s][%d] \n", __FILE__, __FUNCTION__, __LINE__);
  202.         if(spi_data->cur_count < spi_tran->len)/* 没收完? */
  203.         {
  204.                 //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  205.             writeb(0xff, spi_data->reg_base + S3C2410_SPTDAT); /*再次随便发送一个数据*/
  206.         }
  207.         else /*本次 spi_transfer 处理完毕*/
  208.         {
  209.                         //tran_end = 1; /* 睡眠函数的第2个参数为真 */
  210.                        //wake_up_interruptible(&tran_waitq); /* 唤醒休眠的进程 */
  211.                         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  212.             complete(&(spi_data->complet)); /* 唤醒 */
  213.         }
  214.     }
  215.     //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  216.         return IRQ_HANDLED;
  217. }

  218. /*
  219. *函数功能 : spi总线收发函数
  220. *注意 : 1个 spi_transfer,是一个不可以打断的spi传输过程。
  221. * 应用程序write时,会调用oled驱动的oled_write函数,再调用spi_write,最终会调用本函数:my_spi_transfer
  222. * 每向oled写一个命令,spi_write写的长度是0, spi_message 里面只有一个 spi_transfer,里面传输长度是1,中断会触发1次
  223. * 每向oled写一个字符,spi_write写的长度是8(写2次共16个字模),spi_message 里面也只有一个 spi_transfer,里面传输长度是8,中断会触发8次
  224. */
  225. static int my_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
  226. {
  227.         struct spi_master *master = spi->master;
  228.         struct spi_transfer    * spi_tran = NULL;
  229.         struct my_spi_data * spi_data = spi_master_get_devdata(spi->master); /*私有数据*/

  230.         /* 1,选中芯片 */
  231.         s3c2410_gpio_setpin(spi->chip_select, 0); /* 默认为低电平选中 */
  232.         
  233.         /* 2,发送数据 */
  234.         /* 2.1 发送第1个 transfers 之前 要调用setup */
  235.         master->setup(spi);

  236.         /* 2.2 注意 : 一个 spi_message 可能是由 多个 transfers 组成的。
  237.         * 所以 : 从spi_message中逐个取出 transfers,处理它。
  238.         *          注意 : spi_transfer 结构有 transfer_list成员。
  239.         */
  240.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  241.         list_for_each_entry (spi_tran, &(mesg->transfers), transfer_list)
  242.         {
  243.                 /* 处理这个 spi_tran */
  244.                 spi_data->cur_transfer = spi_tran; /*保存当前的 spi_tran*/
  245.                 spi_data->cur_count = 0; /*当前处理数据个数*/

  246.                 /*函数功能 : 初始化完成量*/
  247.                 //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  248.                 init_completion(&(spi_data->complet));

  249.         if(spi_tran->tx_buf) /* 发送缓冲区存在 */
  250.         {
  251.                 //printk("[%s][%s][%d] tx_buf[0]=[0x%x]\n", __FILE__, __FUNCTION__, __LINE__, ((unsigned char *)(spi_tran->tx_buf))[0]);
  252.             writeb(((unsigned char *)(spi_tran->tx_buf))[0], spi_data->reg_base + S3C2410_SPTDAT); /* 它会触发中断 */
  253.             //printk("[%s][%s][%d] SPTDAT=[0x%x]\n", __FILE__, __FUNCTION__, __LINE__, (*(volatile unsigned char *)(spi_data->reg_base + S3C2410_SPTDAT)));
  254.             /*注意 : 如果 tx_buf 中有100个值,我这里只是把第1个值送到了寄存器中,其他值由中断服务程序来传输*/
  255.             
  256.             //wait_event_interruptible(tran_waitq, tran_end);
  257.             //tran_end = 0;
  258.             /*函数功能 : 注意wait_for_completion会产生一个非中断的等待,如果没有人来完成该任务,
  259.             则会产生一个不可杀的进程。所以这个要小心使用。
  260.             */
  261.             wait_for_completion(&(spi_data->complet)); /* 休眠阻塞 : 在中断处理程序中,当complete执行之后再唤醒这里,解除阻塞*/
  262.         }
  263.         else if(spi_tran->rx_buf) /* 接收缓冲区存在 */
  264.         {
  265.                 //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  266.             writeb(0xff, spi_data->reg_base + S3C2410_SPTDAT); /* 它会触发中断 */
  267.             //writeb(0xff, spi_data->reg_base + S3C2410_SPRDAT); //刚开始用这个,排错排了很久
  268.                         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  269.                         //wait_event_interruptible(tran_waitq, tran_end);
  270.             //tran_end = 0;
  271.             wait_for_completion(&(spi_data->complet)); /* 休眠阻塞 : 在中断处理程序中,当complete执行之后再唤醒这里,解除阻塞*/
  272.         }
  273.     }
  274.     
  275.         /* 2.3 唤醒等待的进程 */
  276.         mesg->status = 0; /*0:表示成功*/
  277.         mesg->complete(mesg->context);
  278.         
  279.         /* 3,取消片选 */
  280.         s3c2410_gpio_setpin(spi->chip_select, 1); /* 默认为高电平取消选中 */

  281.         return 0;
  282. }

  283. /*
  284. *函数功能 : SPI硬件控制器的初始化
  285. *函数参数 : 0:控制器0 1:控制器1
  286. */
  287. static void spi_controler_init(int which)
  288. {
  289.         struct clk * clk = NULL;
  290.         clk = clk_get(NULL, "spi");
  291.         if(clk != NULL)
  292.         {
  293.                 printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  294.         }

  295.         /* 使能spi 控制器 0/1的时钟 */
  296.         clk_enable(clk);
  297.         clk_put(clk);

  298.         /* GPIO */
  299.         if(which == 0) /* SPI 控制器 0 */
  300.         {
  301.         /*
  302.                  *时钟引脚 : GPE13,设置为 10:SPICLK0 引脚
  303.                  *主机数据输出引脚 : GPE12,设置为 10:SPIMOSI 引脚
  304.                  *主机数据输入引脚 : GPE11,设置为 10:SPIMISO 引脚
  305.          */
  306.         s3c2410_gpio_cfgpin(S3C2410_GPE11, (0x02 << (2*11)));
  307.         s3c2410_gpio_cfgpin(S3C2410_GPE12, (0x02 << (2*12)));
  308.         s3c2410_gpio_cfgpin(S3C2410_GPE13, (0x02 << (2*13)));
  309.     }
  310.     else if(which == 1) /* SPI 控制器 1 */
  311.     {
  312.         /*
  313.          * GPG5 SPIMISO
  314.          * GPG6 SPIMOSI
  315.          * GPG7 SPICLK
  316.          */
  317.         s3c2410_gpio_cfgpin(S3C2410_GPG5, (0x02 << (2*5)));
  318.         s3c2410_gpio_cfgpin(S3C2410_GPG6, (0x02 << (2*6)));
  319.         s3c2410_gpio_cfgpin(S3C2410_GPG7, (0x02 << (2*7)));
  320.     }
  321. }

  322. static struct spi_master * spi_master0;
  323. //static struct spi_master * spi_master1;

  324. static int __init spi_master_init(void)
  325. {
  326.         int rc;
  327.         int status;
  328.         
  329.         struct my_spi_data * spi_data0 = NULL; /*私有数据*/
  330.         //struct my_spi_data * spi_data1 = NULL;
  331.         struct platform_device * pdev0 = NULL;
  332.         //struct platform_device * pdev1 = NULL;
  333.         
  334.         /*函数功能 : 构造一个 platform_device 结构*/
  335.         pdev0 = platform_device_alloc("s3c2440_spi_0", 0); /*第2个参数可以随便写*/
  336.         //pdev1 = platform_device_alloc("s3c2440_spi_1", 1); /*第2个参数可以随便写*/
  337.         /*函数功能 : 分配了平台设备之后,就要加入到设备链表中,才可以使用*/
  338.     platform_device_add(pdev0);
  339.     //platform_device_add(pdev1);
  340.         /*
  341.         *1,函数功能 : 分配 spi_master 结构
  342.         *注意 : 这个分配函数,会在分配 spi_master结构体的空间之后,再分配一段空间。
  343.         * 这个多余分配出来的空间,可以用来存放自己定义的私有数据。第2个参数可以表示这个私有数据的大小。
  344.         */
  345.         spi_master0 = spi_alloc_master(&(pdev0->dev), sizeof(struct my_spi_data));
  346.         if(!spi_master0)
  347.         {
  348.                 printk("[%s][%s][%d] spi_alloc_master err\n", __FILE__, __FUNCTION__, __LINE__);
  349.                 return -ENOMEM;
  350.         }
  351.         //spi_master1 = spi_alloc_master(&(pdev1->dev), sizeof(struct my_spi_data));

  352.         /*2, 设置 spi_master */
  353.         spi_master0->bus_num = 0; /*会跟spi_board_info中的bus_num比较*/
  354.         //spi_master1->bus_num = 1;
  355.         
  356.         spi_master0->num_chipselect = 0xffff; /*最大片选数*/
  357.         //spi_master1->num_chipselect = 0xffff;
  358.         //spi_master0->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
  359.         //spi_master1->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

  360.         spi_master0->setup = my_spi_setup; /*设置 oled最大是10Mhz, flash最大是80Mhz*/
  361.         //spi_master1->setup = my_spi_setup;
  362.         spi_master0->transfer = my_spi_transfer; /*spi总线收发函数*/
  363.         //spi_master1->transfer = my_spi_transfer;

  364.         /*3, 设置私有数据 */
  365.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  366.         spi_data0 = spi_master_get_devdata(spi_master0);
  367.         //printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  368.         //spi_data1 = spi_master_get_devdata(spi_master1);
  369.         spi_data0->reg_base = (unsigned int)ioremap(0x59000000, 0x18);
  370.         //spi_data1->reg_base = (unsigned int)ioremap(0x59000020, 0x18);
  371.         spi_data0->irq_no = IRQ_SPI0;
  372.         //spi_data1->irq_no = IRQ_SPI1;
  373.         spi_data0->pdev = pdev0;
  374.         //spi_data1->pdev = pdev1;

  375.         /*4, 硬件 SPI 控制器 引脚 初始化 */
  376.         spi_controler_init(spi_master0->bus_num);
  377.         //spi_controler_init(spi_master1->bus_num);

  378.         /*5, 安装SPI中断处理函数 : SPI通过中断来传输数据
  379.         #define IRQ_EINT1            5
  380.         #define IRQ_EINT2            6
  381.         #define IRQ_EINT3            7
  382.         */ //0x20
  383.         rc = request_irq(spi_data0->irq_no, s3c2440_spi_interrupt, 0, "s3c2440_spi_0", spi_master0);
  384.         if(rc)
  385.         {
  386.                 printk("[%s][%s][%d] request_irq err\n", __FILE__, __FUNCTION__, __LINE__);
  387.                 return rc;
  388.         }
  389.         /*
  390.         rc = request_irq(spi_data1->irq_no, s3c2440_spi_interrupt, 0x20, "s3c2440_spi_1", spi_master1);
  391.         printk("[%s][%s][%d] rc=[%d]\n", __FILE__, __FUNCTION__, __LINE__, rc);
  392.         if(rc)
  393.         {
  394.                 printk("[%s][%s][%d] request_irq err\n", __FILE__, __FUNCTION__, __LINE__);
  395.                 return rc;
  396.         }
  397.         */

  398.         /*6, 注册 spi_master */
  399.         printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);
  400.         status = spi_register_master(spi_master0);
  401.         //status = spi_register_master(spi_master1);
  402.         if(status < 0)
  403.         {
  404.                 printk("[%s][%s][%d] spi_register_master err\n", __FILE__, __FUNCTION__, __LINE__);
  405.                 return status;
  406.         }
  407.         printk("[%s][%s][%d]\n", __FILE__, __FUNCTION__, __LINE__);

  408.         return 0;
  409. }

  410. static void __exit spi_master_exit(void)
  411. {
  412.         struct my_spi_data * spi_data0; /*私有数据*/
  413.         //struct my_spi_data * spi_data1;
  414.         
  415.         /*取得私有数据 */
  416.         spi_data0 = spi_master_get_devdata(spi_master0);
  417.         //spi_data1 = spi_master_get_devdata(spi_master1);
  418.         
  419.         /*1, 注销 spi_master 对应 spi_register_master */
  420.         spi_unregister_master(spi_master0);
  421.         //spi_unregister_master(spi_master1);
  422.         
  423.         /*5, 释放 platform_device 对应 platform_device_alloc */
  424.         platform_device_del(spi_data0->pdev);
  425.     platform_device_put(spi_data0->pdev);
  426.     //platform_device_del(spi_data1->pdev);
  427.     //platform_device_put(spi_data1->pdev);
  428.         /*2, 释放 中断 对应 request_irq */
  429.         free_irq(spi_data0->irq_no, spi_master0);
  430.         //free_irq(spi_data1->irq_no, spi_master1);
  431.         /*3, 虚拟地址释放 对应 ioremap */
  432.     iounmap((void *)(spi_data0->reg_base));
  433.     //iounmap((void *)(spi_data1->reg_base));
  434.     /*4, 释放 spi_master 对应 spi_alloc_master */
  435.     kfree(spi_master0);
  436.         //kfree(spi_master1);

  437. }

  438. module_init(spi_master_init);
  439. module_exit(spi_master_exit);
  440. MODULE_AUTHOR("WangXiancai");
  441. MODULE_LICENSE("GPL");

  442. /*
  443. 测试 :
  444. 1, 内核里面有自带的 spi_master 驱动程序,这里要先去掉
  445.    \kerner-2.6.30\drivers\spi\Makefile
  446.    32行 # obj-$(CONFIG_SPI_S3C24XX)        += spi_s3c24xx.o
  447.    也可以通过 make menuconfig 来 去掉 CONFIG_SPI_S3C24XX 选项,如果单板相关的信息可以单独注册。
  448. 2, 注意 : spi_board_info必须先注册,再注册 spi_master。
  449. # insmod spi_tq2440_info.ko
  450. DEVICE:1_spi_info_TQ2440_init ####################
  451. DEVICE: 1_spi_register_board_info at [drivers/spi/spi.c][373][spi_register_board_info]!
  452. DEVICE: 2_spi_register_board_info at [drivers/spi/spi.c][378][spi_register_board_info]!
  453. DEVICE:2_spi_info_TQ2440_init ####################
  454. # insmod spi_master.ko
  455. # insmod oled_drv.ko
  456. DEVICE:spi_oled_probe ####################
  457. # ls -lrt /dev/oled
  458. crw-rw---- 1 root root 252, 0 May 27 04:41 /dev/oled
  459. */






  460. /*
  461. struct spi_master {
  462.     struct device    dev;
  463.     s16            bus_num;
  464.     u16            num_chipselect;
  465.     u16            dma_alignment;
  466.     int            (*setup)(struct spi_device *spi);
  467.     int            (*transfer)(struct spi_device *spi,
  468.                         struct spi_message *mesg);
  469.     void            (*cleanup)(struct spi_device *spi);
  470. };

  471. struct spi_message
  472. {
  473.         struct list_head    transfers;
  474.         struct spi_device    *spi;
  475.         unsigned        is_dma_mapped:1;

  476.         void            (*complete)(void *context);
  477.         void            *context;
  478.         unsigned        actual_length;
  479.         int            status;

  480.         struct list_head    queue;
  481.         void            *state;
  482. };

  483. struct spi_transfer {
  484.     const void    *tx_buf;
  485.     void        *rx_buf;
  486.     unsigned    len;

  487.     dma_addr_t    tx_dma;
  488.     dma_addr_t    rx_dma;

  489.     unsigned    cs_change:1;
  490.     u8        bits_per_word;
  491.     u16        delay_usecs;
  492.     u32        speed_hz;

  493.     struct list_head transfer_list;
  494. };

  495. #define list_for_each_entry(pos, head, member)                \
  496. for(pos = list_entry((head)->next, typeof(*pos), member), prefetch(pos->member.next);
  497.         &pos->member != (head);
  498.         pos = list_entry(pos->member.next, typeof(*pos), member),    prefetch(pos->member.next))

  499. */


  500. /*
  501. 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束。
  502. 这个活动可能是,创建一个新的内核线程或者新的用户空间进程、对一个 已有进程的某个请求,
  503. 或者某种类型的硬件动作,等等。在这种情况下,我们可以使用信号量来同步这两个任务。
  504. 然而,内核中提供了另外一种机制 --completion接口。Completion是一种轻量级的机制,
  505. 他允许一个线程告诉另一个线程某个工作已经完成。

  506. 1, 结构与初始化
  507. Completion在内核中的实现基于等待队列(关于等待队列理论知识在前面的文章中有介绍),
  508. completion结构很简单:
  509.    
  510. struct completion
  511. {
  512.     unsigned int done; //用于同步的原子量
  513.     wait_queue_head_t wait; //等待事件队列
  514. };
  515.    
  516. 和信号量一样,初始化分为静态初始化和动态初始化两种情况:
  517.    
  518. 静态初始化:
  519. #define COMPLETION_INITIALIZER(work){ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work)。wait)}
  520. #define DECLARE_COMPLETION(work)struct completion work = COMPLETION_INITIALIZER(work)

  521. 动态初始化:
  522. static inline void init_completion(struct completion *x)
  523. {
  524.         x->done = 0;
  525.         init_waitqueue_head(&x->wait);
  526. }
  527. 可见,两种初始化都将用于同步的done原子量置位了0,后面我们会看到,该变量在wait相关函数中减一,
  528. 在complete系列函数中加一。

  529. 2, 实现
  530.    
  531. 同步函数一般都成对出现,completion也不例外,我们看看最基本的两个complete和wait_for_completion
  532. 函数的实现。
  533.    
  534. wait_for_completion最终由下面函数实现:
  535.    
  536. static inline long __sched do_wait_for_common(struct completion *x, long timeout, int state)
  537. {
  538.     if(!x->done)
  539.     {
  540.          DECLARE_WAITQUEUE(wait, current);
  541.          wait.flags |= WQ_FLAG_EXCLUSIVE;
  542.          __add_wait_queue_tail(&x->wait, &wait);
  543.         
  544.          do
  545.          {
  546.                  if (signal_pending_state(state, current))
  547.                  {
  548.                          timeout = -ERESTARTSYS;
  549.                         
  550.                          break;
  551.                  }
  552.                  __set_current_state(state);
  553.                  spin_unlock_irq(&x->wait.lock);
  554.                  timeout = schedule_timeout(timeout);
  555.                  spin_lock_irq(&x->wait.lock);
  556.          }
  557.          while (!x->done && timeout);
  558.          __remove_wait_queue(&x->wait, &wait);
  559.          if (!x->done)
  560.              {
  561.                  return timeout;
  562.              }
  563.     }
  564.     x->done--;
  565.     return timeout ?: 1;
  566. }
  567.    
  568. 而complete实现如下:
  569.    
  570. void complete(struct completion *x)
  571. {
  572.     unsigned long flags;
  573.     spin_lock_irqsave(&x->wait.lock, flags);
  574.     x->done++;
  575.     __wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
  576.     spin_unlock_irqrestore(&x->wait.lock, flags);
  577. }
  578.    
  579. 不看内核实现的源代码我们也能想到他的实现,不外乎在wait函数中循环等待done变为可用(正),
  580. 而另一边的complete函数为唤醒函数,当然是将done加一,唤醒待处理的函数。是的,从上面的代码看到,
  581. 和我们想的一样。内核也是这样做的。

  582. */
应用程序
makefile文件

点击(此处)折叠或打开

  1. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-gcc
  2. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-gcc
  3. all:spitest
  4. spitest:spitest.c
  5.     $(CROSS) -o spitest spitest.c
  6. clean:
  7.     rm -vf spitest.o spitest
spitest.c文件

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <fcntl.h>
  5. #include <sys/ioctl.h>
  6. #include <stdlib.h>
  7. #include <errno.h>

  8. #include "oled_font.h"

  9. /*下面是ioctl识别的宏定义 和内核中定义的一样*/
  10. #define OLED_CMD_INIT 0x100001
  11. #define OLED_CMD_CLEAR_ALL 0x100002
  12. #define OLED_CMD_CLEAR_PAGE 0x100003
  13. #define OLED_CMD_SET_POS 0x100004

  14. /* 命令行参数如下:
  15.  * spitest init                    //oled初始化
  16.  * spitest clear                //oled清屏
  17.  * spitest clear <page>    //oled清屏某一页
  18.  * spitest <page> <col> <string> //从指定的行+列,写一个字符串
  19.  */

  20. void print_usage(char *cmd);
  21. void oled_fix_put_char(int fd, unsigned int page, unsigned int col, unsigned char val);
  22. void oled_print(int fd, unsigned int page, unsigned int col, char * str);

  23. int main(int argc, char **argv)
  24. {
  25.     int do_init = 0;
  26.     int do_clear = 0;
  27.     int do_show = 0;
  28.     int page = -1;
  29.     int col;

  30.     int fd;
  31.         
  32.         /*判断参数的合法性*/
  33.     if (argc == 2 && !strcmp(argv[1], "init"))
  34.     {
  35.                 do_init = 1;
  36.                 printf("init:\n");
  37.     }
  38.     else if ((argc == 2) && !strcmp(argv[1], "clear"))
  39.     {
  40.                 do_clear = 1;
  41.                 printf("clear all:\n");
  42.     }
  43.     else if ((argc == 3) && !strcmp(argv[1], "clear"))
  44.     {
  45.                 do_clear = 1;
  46.                 page = strtoul(argv[2], NULL, 0);
  47.                 printf("clear page:\n");
  48.     }
  49.     else if (argc == 4)
  50.     {
  51.                 do_show = 1;
  52.                 page = strtoul(argv[1], NULL, 0);
  53.                 col = strtoul(argv[2], NULL, 0);
  54.                 
  55.                 printf("page=[%d]\n", page);
  56.                 printf("col=[%d]\n", col);
  57.     }
  58.     else
  59.     {
  60.                 print_usage(argv[0]);
  61.                 return -1;
  62.     }
  63.         
  64.         /*打开oled文件*/
  65.         printf("open begin:\n");
  66.     fd = open("/dev/oled", O_RDWR);
  67.     if(fd < 0)
  68.     {
  69.         printf("can't open /dev/oled\n");
  70.         return -1;
  71.     }
  72.     printf("open succ!\n");

  73.     if(do_init > 0) //oled初始化
  74.     {
  75.             printf("app:ioctl OLED_CMD_INIT begin:\n");
  76.         ioctl(fd, OLED_CMD_INIT);
  77.         printf("app:ioctl OLED_CMD_INIT succ!\n");
  78.     }
  79.     else if(do_clear > 0)
  80.     {
  81.         if (page == -1)
  82.         {
  83.             ioctl(fd, OLED_CMD_CLEAR_ALL); //oled清屏
  84.         }
  85.         else
  86.         {
  87.             if (page < 0 || page > 7)
  88.             {
  89.                 printf("page is 0,1,...,7\n");
  90.                 return -1;
  91.             }
  92.             ioctl(fd, OLED_CMD_CLEAR_PAGE, page); //oled清屏某一页
  93.         }
  94.     }
  95.     else if(do_show > 0)
  96.     {
  97.         if (page < 0 || page > 7)
  98.         {
  99.             printf("page is 0,1,...,7\n");
  100.             return -1;
  101.         }
  102.         if (col < 0 || col > 127)
  103.         {
  104.             printf("col is 0,1,...,127\n");
  105.             return -1;
  106.         }
  107.         oled_print(fd, page, col, argv[3]); //从指定的行+列,写一个字符串
  108.     }
  109.     return 0;
  110. }


  111. void print_usage(char *cmd)
  112. {
  113.     printf("Usage:\n");
  114.     printf("%s init\n", cmd);
  115.     printf("%s clear\n", cmd);
  116.     printf("%s clear \n", cmd);
  117.     printf("%s \n", cmd);
  118.     printf("page is 0,1,...,7\n");
  119.     printf("col is 0,1,...,127\n");
  120. }


  121. /*
  122. *函数功能 : 向oled显示屏,指定的列地址+页地址,显示一个字符。
  123. *函数参数 :
  124. * col : 0到127 val表示的字符,将要显示的列位置
  125. * page : 0到7 val表示的字符,将要显示的页位置
  126. *注意 : 一个字模跨2个page
  127. */
  128. void oled_fix_put_char(int fd, unsigned int page, unsigned int col, unsigned char val)
  129. {
  130.         int i;
  131.         /*得到字模*/
  132.         const unsigned char *zifu = oled_asc2_8x16[val - ' ']; //val - ' ' : 字符在字模数组中对应的下标

  133.         /*发给oled*/
  134.         //uart_printf("oled_fix_put_char page = [%d], col = [%d], val = [%c]\n\n", page, col, val);
  135.         //oled_set_addr(page, col); //设置位置,字符所占的第1个page
  136.         ioctl(fd, OLED_CMD_SET_POS, page | (col << 8));
  137.     /* 发出8字节数据 */
  138.     /*
  139.     for (i = 0; i < 8; i++)
  140.         {
  141.         oled_write_data(zifu[i]); //连续写,列地址就自动加1
  142.         }
  143.         */
  144.         write(fd, &zifu[0], 8);

  145.     //oled_set_addr(page+1, col); //1个字符占2页,字符所占的第2个page
  146.     /*约定第3个参数 中0到7位是表示page; 8到15位是表示col*/
  147.     ioctl(fd, OLED_CMD_SET_POS, (page+1) | (col << 8));
  148.     
  149.     /* 发出8字节数据 */
  150.     /*
  151.     for (i = 0; i < 8; i++)
  152.        {
  153.         oled_write_data(zifu[i+8]);
  154.         }
  155.         */
  156.         write(fd, &zifu[8], 8);
  157. }


  158. /*
  159. *函数功能 : 向oled显示屏,打印一个字符串,从指定的行,列开始打印,会自动换行
  160. *函数参数 :
  161. * col : 0到127 起始存放的 第1个字符的列
  162. * page : 0到7 起始存放的 第1个字符所在的页
  163. *注意 :
  164. * 1, 我们使用的OLED的分辨率=128*64 0:表示像素熄灭, 1:表示像素点亮
  165. * 2, 显存大小 = 128*64位=1K字节
  166. * 3, OLED主控芯片数据手册,SSD1306(36页)10.1.3节,设置显存的地址模式,提供了3种地址模式,
  167. * 最常用的是 : 页地址模式。
  168. * 4, 页地址模式:
  169. * 64行分为8页,每页对应8行。可以选中指定的页,再选中指定的列,再向里面写数据,
  170. * 每写1列,列地址就自动加1,1页写完之后,继续写,会自动跳到下一页开头位置继续写。
  171. * 5, 约定一个数字|英文字符的大小是 8*16, 宽:8列,:16行。
  172. * 这样,这个OLED屏,可以显示8行,每行8个字符
  173. */
  174. void oled_print(int fd, unsigned int page, unsigned int col, char * str)
  175. {
  176.         char * tmp;
  177.         tmp = str;
  178.         
  179.         ioctl(fd, OLED_CMD_CLEAR_PAGE, page); //oled清屏某一页
  180.         ioctl(fd, OLED_CMD_CLEAR_PAGE, page+1); //oled清屏某一页
  181.         while((*tmp) != '\0')
  182.         {
  183.                 //uart_printf("oled_print *tmp = [%c]\n\n", *tmp);
  184.                 oled_fix_put_char(fd, page, col, *tmp);
  185.                 col += 8; //每个字符占8列
  186.                 if(col > 127) //一行写满,自动换行
  187.                 {
  188.                         col = 0;
  189.                         page += 2; //每个字符占16行,跨2页
  190.                         if(page > 7) //8页都写满,再从最开始的位置,再写。
  191.                         {
  192.                                 page = 0;
  193.                         }
  194.                         ioctl(fd, OLED_CMD_CLEAR_PAGE, page); //oled清屏某一页
  195.                         ioctl(fd, OLED_CMD_CLEAR_PAGE, page+1); //oled清屏某一页
  196.                 }
  197.                 tmp++;        
  198.         }
  199. }


  200. /*
  201. 测试 :
  202. 1, 刚才开始的测试的时候,由于open函数使用的是 RDONLY 只读打开,驱动函数 write不能调用,
  203. 使用 O_RDWR 可读可写方式打开,才能正确
  204. 2,
  205. # ./iictest
  206. ./iictest r|R addr
  207. ./iictest w|W addr val
  208. # ./iictest w 100 56 //向100地址中写入数据56
  209. write address [100] with data [56]
  210. # ./iictest r 100 //从100地址中读取数据
  211. read address = [100]
  212. data = [56]
  213. # ./spitest init                    //oled初始化
  214. # ./spitest clear                //oled清屏
  215. # ./spitest clear 0    //oled清屏某一页
  216. # ./spitest 0 0 WangXiancai //从指定的行+列,写一个字符串
  217. */
oled_font.h文件

点击(此处)折叠或打开

  1. #ifndef _OLEDFONT_H_
  2. #define _OLEDFONT_H_

  3. /*
  4. 注意 : 下面的对2维数组的定义,每个元素都被赋值了,即为对象已经分配内存了,则不能在多个文件中包含这个头文件。
  5. 如果仅仅是一些声明,是可以在多个文件中包含这个头文件的。

  6. 注意 : 对象必须有且只有1个定义,但是它可以有多extern声明。
  7. */

  8. /*
  9. *字模 : 8*16像素 =:8位 +:16位
  10. *注意 : 下面列出来的字符是按照ascii码从空格开始,按照顺序列出来,这么做是为了方便取码
  11. *下面理解成 : 1维数组,这个数组的每个元素又是1维数组
  12. *在oled屏幕中的位置:
  13. * 比如字符A : 在数组中的下标是33, 则oled_asc2_8x16[33]可以表示1个1维数组名,这个1维数组可以取出16个字符
  14. * {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33
  15. *
  16. * 上面页,8列,从最左1列开始,依次对应上面16个字符的前面8个字符 : 0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00
  17. *
  18. * 下面模拟OLED的1个字符的显示 像素=8*16
  19. * |0|0|0|0|0|0|0|0| 字符的最低位
  20. * |0|0|0|0|0|0|0|0|
  21. * |0|0|0|0|0|0|0|0|
  22. * |0|0|0|1|0|0|0|0|
  23. * |0|0|0|1|0|0|0|0|
  24. * |0|0|0|1|1|0|0|0|
  25. * |0|0|1|0|1|0|0|0|
  26. * _____|0|0|1|0|1|0|0|0|_____ 字符的最高位
  27. * |0|0|1|0|0|1|0|0| 字符的最低位
  28. * |0|0|1|1|1|1|0|0|
  29. * |0|1|0|0|0|1|0|0|
  30. * |0|1|0|0|0|0|1|0|
  31. * |0|1|0|0|0|0|1|0|
  32. * |1|1|1|0|0|1|1|1|
  33. * |0|0|0|0|0|0|0|0|
  34. *          |0|0|0|0|0|0|0|0| 字符的最高位
  35. * 下面页,8列,从最左1列开始,依次对应上面16个字符的后面8个字符 ; 0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20
  36. * 说明 : 所有标1的地方,看起来是不是很像大写的字符A ???
  37. */
  38. const unsigned char oled_asc2_8x16[95][16]=
  39. {
  40.     {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0 数组下标 = 对应字符的asc2码 - 空格的asc码
  41.     {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1
  42.     {0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//






阅读(1075) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~