基于TQ2440的SPI驱动学习(OLED)

发布时间:2023-06-26  

平台简介

开发板:TQ2440 (NandFlash:256M  内存:64M)

u-boot版本:u-boot-2015.04

内核版本:Linux-3.14

作者:彭东林

邮箱:pengdonglin137@163.com

摘要

这篇博客的目的是简要分析两种spi驱动的实现,一种是利用Samsung的S3C2440自带的硬件SPI控制器,另一种是利用Linux内核已经写好的用GPIO模拟SPI时序,实现一个软件SPI控制器。操作的外设是韦东山的SPI视频教程中提供的OLED模块,同时分享一下在使用逻辑分析仪Saleae16调试SPI时遇到的问题。

相关的内核代码已经上传:git@code.csdn.net:pengdonglin137/linux-3-14-y.git

可以看看代码提交记录。

正文

SPI驱动实现之硬件控制器

一、驱动框架

 

image

 

二、代码

SPI硬件控制器

这里采用的是platform架构,分为device和driver两个部分。

1、platform_device

文件:arch/arm/plat-samsung/devs.c


   1: static struct resource s3c_spi0_resource[] = {

   2:     [0] = DEFINE_RES_MEM(S3C24XX_PA_SPI, SZ_32),

   3:     [1] = DEFINE_RES_IRQ(IRQ_SPI0),

   4: };

   5:  

   6: static void s3c24xx_spi_set_cs(struct s3c2410_spi_info *spi, int cs, int pol)

   7: {

   8:     gpio_set_value(cs, pol);

   9: }

  10:  

  11: static struct s3c2410_spi_info s3c_spi_info[] = {

  12:     {

  13:         .num_cs  = S3C_GPIO_END,

  14:         .bus_num = 0,

  15:         .set_cs = s3c24xx_spi_set_cs,

  16:     }

  17: };

  18:  

  19: struct platform_device s3c_device_spi0 = {

  20:     .name        = "s3c2410-spi",

  21:     .id        = 0,

  22:     .num_resources    = ARRAY_SIZE(s3c_spi0_resource),

  23:     .resource    = s3c_spi0_resource,

  24:     .dev        = {

  25:         .dma_mask        = &samsung_device_dma_mask,

  26:         .coherent_dma_mask    = DMA_BIT_MASK(32),

  27:         .platform_data        = (void *)s3c_spi_info,

  28:     }

  29: };

第15行是片选函数,它的第二个参数cs来自spi从设备的板级信息,表示这个从设备的片选引脚;


第14行表示spi控制器的编号是0,将来在spi从设备的板级信息中有体现,意思是将来这个spi从设备挂载在编号为0的spi总线下面;


第27行,在linux原生的代码中没有实现platform_data,在调用probe函数的时候会报错;


2、platform_driver

文件:drivers/spi/spi-s3c24xx.c


   1: MODULE_ALIAS("platform:s3c2410-spi");

   2: static struct platform_driver s3c24xx_spi_driver = {

   3:     .probe        = s3c24xx_spi_probe,

   4:     .remove        = s3c24xx_spi_remove,

   5:     .driver        = {

   6:         .name    = "s3c2410-spi",

   7:         .owner    = THIS_MODULE,

   8:         .pm    = S3C24XX_SPI_PMOPS,

   9:     },

  10: };

  11: module_platform_driver(s3c24xx_spi_driver);

  12:  

 


OLED 板级信息

这里调用了spi子系统提供的函数接口。


1、板级信息

文件:arch/arm/mach-s3c24xx/mach-tq2440.c


   1: /* SPI OLED */

   2: static struct spi_board_info tq2440_spi_board_info[] __initdata = {

   3:     {

   4:         .modalias    = "oled",

   5:         .max_speed_hz    = 10000000,

   6:         .bus_num    = 0,

   7:         .mode        = SPI_MODE_0,

   8:         .chip_select    = S3C2410_GPG(1),

   9:         .platform_data    = (const void *)S3C2410_GPF(3),

  10:     },

  11: };

  12:  

  13: static struct platform_device *tq2440_devices[] __initdata = {

  14:     ......

  15:     &s3c_device_spi0,

  16: };

  17:  

  18: static void __init tq2440_machine_init(void)

  19: {

  20: ......

  21:     spi_register_board_info(tq2440_spi_board_info, ARRAY_SIZE(tq2440_spi_board_info));

  22: ......

  23: }

  24:  

  25: MACHINE_START(TQ2440, "TQ2440")

  26: ......

  27:     .init_machine    = tq2440_machine_init,

  28: ......

  29: MACHINE_END

第4行,将来会跟驱动中的name进行匹配;


第5行,表示通信速率,这里设置的是10MHz;


第6行,表示使用的spi总线的编号是0;


第7行,表示使用的spi模式是0,这里要根据oled的芯片手册(SSD1306-Revision 1.1 (Charge Pump).pdf)


第8行,oled使用的片选引脚;


第9行,用于区分命令和数据模式的GPIO资源,这个会在驱动中解析;


第21行,注册spi从设备板级信息;


2、oled驱动

文件:drivers/spi/oled/spi_oled_drv.c


   1: #include

   2: #include

   3: #include

   4: #include

   5: #include

   6: #include

   7: #include

   8: #include

   9: #include

  10:  

  11: #include

  12: #include

  13:  

  14: #include

  15: #include

  16:  

  17: /* 构造注册 spi_driver */

  18:  

  19: static int major;

  20: static struct class *class;

  21:  

  22: static int spi_oled_dc_pin;

  23: static struct spi_device *spi_oled_dev;

  24: static unsigned char *ker_buf;

  25:  

  26: static void OLED_Set_DC(char val)

  27: {

  28:     gpio_set_value(spi_oled_dc_pin, val);

  29: }

  30:  

  31: static void OLEDWriteCmd(unsigned char cmd)

  32: {

  33:     OLED_Set_DC(0); /* command */

  34:     spi_write(spi_oled_dev, &cmd, 1);

  35:     OLED_Set_DC(1); /*  */

  36: }

  37:  

  38: static void OLEDWriteDat(unsigned char dat)

  39: {

  40:     OLED_Set_DC(1); /* data */

  41:     spi_write(spi_oled_dev, &dat, 1);

  42:     OLED_Set_DC(1); /*  */

  43: }

  44:  

  45: static void OLEDSetPageAddrMode(void)

  46: {

  47:     OLEDWriteCmd(0x20);

  48:     OLEDWriteCmd(0x02);

  49: }

  50:  

  51: static void OLEDSetPos(int page, int col)

  52: {

  53:     OLEDWriteCmd(0xB0 + page); /* page address */

  54:  

  55:     OLEDWriteCmd(col & 0xf);   /* Lower Column Start Address */

  56:     OLEDWriteCmd(0x10 + (col >> 4));   /* Lower Higher Start Address */

  57: }

  58:  

  59:  

  60: static void OLEDClear(void)

  61: {

  62:     int page, i;

  63:     for (page = 0; page < 8; page ++)

  64:     {

  65:         OLEDSetPos(page, 0);

  66:         for (i = 0; i < 128; i++)

  67:             OLEDWriteDat(0);

  68:     }

  69: }

  70:  

  71: void OLEDClearPage(int page)

  72: {

  73:     int i;

  74:     OLEDSetPos(page, 0);

  75:     for (i = 0; i < 128; i++)

  76:         OLEDWriteDat(0);    

  77: }

  78:  

  79: void OLEDInit(void)

  80: {

  81:     /* 向OLED发命令以初始化 */

  82:     OLEDWriteCmd(0xAE); /*display off*/ 

  83:     OLEDWriteCmd(0x00); /*set lower column address*/ 

  84:     OLEDWriteCmd(0x10); /*set higher column address*/ 

  85:     OLEDWriteCmd(0x40); /*set display start line*/ 

  86:     OLEDWriteCmd(0xB0); /*set page address*/ 

  87:     OLEDWriteCmd(0x81); /*contract control*/ 

  88:     OLEDWriteCmd(0x66); /*128*/ 

  89:     OLEDWriteCmd(0xA1); /*set segment remap*/ 

  90:     OLEDWriteCmd(0xA6); /*normal / reverse*/ 

  91:     OLEDWriteCmd(0xA8); /*multiplex ratio*/ 

  92:     OLEDWriteCmd(0x3F); /*duty = 1/64*/ 

  93:     OLEDWriteCmd(0xC8); /*Com scan direction*/ 

  94:     OLEDWriteCmd(0xD3); /*set display offset*/ 

  95:     OLEDWriteCmd(0x00); 

  96:     OLEDWriteCmd(0xD5); /*set osc division*/ 

  97:     OLEDWriteCmd(0x80); 

  98:     OLEDWriteCmd(0xD9); /*set pre-charge period*/ 

  99:     OLEDWriteCmd(0x1f); 

 100:     OLEDWriteCmd(0xDA); /*set COM pins*/ 

 101:     OLEDWriteCmd(0x12); 

 102:     OLEDWriteCmd(0xdb); /*set vcomh*/ 

 103:     OLEDWriteCmd(0x30); 

 104:     OLEDWriteCmd(0x8d); /*set charge pump enable*/ 

 105:     OLEDWriteCmd(0x14); 

 106:  

 107:     OLEDSetPageAddrMode();

 108:  

 109:     OLEDClear();

 110:  

 111:     OLEDWriteCmd(0xAF); /*display ON*/    

 112: }

 113:  

 114:  

 115: #define OLED_CMD_INIT       0x100001

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>