zoukankan      html  css  js  c++  java
  • S3c2440裸机spi编程3.gpio模拟spi驱动OLED

    操作OLED,通过三条线(SCK、DO、CS)与OLED相连,这里没有DI是因为2440只会向OLED传数据而不用接收数据。

    gpio_spi.c来实现gpio模拟spi,负责spi通讯。对于OLED,有专门的指令和数据格式,要传输的数据内容,在oled.c这一层来实现,负责组织数据。

    因此,我们需要实现以上两个文件。

     

    1.SPI初始化

    新建一个gpio_spi.c文件,实现SPI初始化SPIInt()

     

    1.1 GPIO init(pinmux管脚等配置)

    上图J3为板子pin2pin到OLED的底座。

    GPF1作为OLED片选引脚,设置为输出;
    GPG4作为OLED的数据(Data)/命令(Command)选择引脚,设置为输出;
    GPG5作为SPI的MISO,设置为输入(实际用不到);
    GPG6作为SPI的MOSI,设置为输出;
    GPG7作为SPI的时钟CLK,设置为输出;

     

     

     根据gpio相关寄存器进行配置如下:用gpio配置成spi使用的各个引脚。

    void SPIInit(void){
        /* 初始化引脚 */
        SPI_GPIO_Init();
    }
    static void SPI_GPIO_Init(void){
        /* GPF1 as OLED_CSn output */
        GPFCON &= ~(3<<(1*2));
        GPFCON |= (1<<(1*2));
        GPFDAT |= (1<<1);//取消OLED_CSn片选,pull up
       /* GPG2 FLASH_CSn output
        * GPG4 OLED_DC   output
        * GPG5 SPIMISO   input
        * GPG6 SPIMOSI   output
        * GPG7 SPICLK    output
        */
        GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));
        GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2)));
        GPGDAT |= (1<<2);//取消FLASH_CSn 片选,pull up
    }

    2.OLED初始化 

    再新建一个oled.c文件,以实现初始化OLEDInit(),这里就对应power up sequence。

    void OLEDInit(void){
        /* 向OLED发命令以初始化 */
        OLEDWriteCmd(0xAE); /*display off*/
        OLEDWriteCmd(0x00); /*set lower column address*/
        OLEDWriteCmd(0x10); /*set higher column address*/
        OLEDWriteCmd(0x40); /*set display start line*/
        OLEDWriteCmd(0xB0); /*set page address*/
        OLEDWriteCmd(0x81); /*contract control*/
        OLEDWriteCmd(0x66); /*128*/
        OLEDWriteCmd(0xA1); /*set segment remap*/
        OLEDWriteCmd(0xA6); /*normal / reverse*/
        OLEDWriteCmd(0xA8); /*multiplex ratio*/
        OLEDWriteCmd(0x3F); /*duty = 1/64*/
        OLEDWriteCmd(0xC8); /*Com scan direction*/
        OLEDWriteCmd(0xD3); /*set display offset*/
        OLEDWriteCmd(0x00);
        OLEDWriteCmd(0xD5); /*set osc division*/
        OLEDWriteCmd(0x80);
        OLEDWriteCmd(0xD9); /*set pre-charge period*/
        OLEDWriteCmd(0x1f);
        OLEDWriteCmd(0xDA); /*set COM pins*/
        OLEDWriteCmd(0x12);
        OLEDWriteCmd(0xdb); /*set vcomh*/
        OLEDWriteCmd(0x30);
        OLEDWriteCmd(0x8d); /*set charge pump enable*/
        OLEDWriteCmd(0x14);
    }

    D/C即数据(Data)/命令(Command)选择引脚,它为高电平时,OLED即认为收到的是数据;它为低电平时,OLED即认为收到的是命令。先设置为命令模式,再片选OLED,再传输命令,再恢复成原来的模式和取消片选。

    2.1 实现OLED写功能

    写命令和写数据:

    static void OLEDWriteCmd(unsigned char cmd){
        OLED_Set_DC(0); /* command */
        OLED_Set_CS(0); /* select OLED */
        SPISendByte(cmd);
        OLED_Set_CS(1); /* de-select OLED */
        OLED_Set_DC(1); /*  gpio output default is pull up*/
    }
    static void OLEDWriteDat(unsigned char data){
        OLED_Set_DC(1); /* data*/
        OLED_Set_CS(0); /* select OLED */
    SPISendByte(data);
    OLED_Set_CS(1); /* de-select OLED */
    }

    命令模式和片选就是单纯的gpio操作,非常简单如下:

    static void OLED_Set_DC(char val){
        if (val)
            GPGDAT |= (1<<4);
        else
            GPGDAT &= ~(1<<4);
    }
    static void OLED_Set_CS(char val){
        if (val)
            GPFDAT |= (1<<1);
        else
            GPFDAT &= ~(1<<1);
    }

    2.2 SPISendByte()

    还剩下SPISendByte()函数,它属于SPI协议,放在gpio_spi.c里面:

    void SPISendByte(unsigned char val){
        int i;
        for (i = 0; i < 8; i++){
            SPI_Set_CLK(0);
            SPI_Set_DO(val & 0x80);//MSB
            SPI_Set_CLK(1);
            val <<= 1;
        }
    }
    static void SPI_Set_CLK(char val){
        if (val)
            GPGDAT |= (1<<7);
        else
            GPGDAT &= ~(1<<7);
    }
    static void SPI_Set_DO(char val){
        if (val)
            GPGDAT |= (1<<6);
        else
            GPGDAT &= ~(1<<6);
    }

    发送数据要满足SPI的时序要求,参考前面的介绍:

     

    SPISendByte是把一个byte数据从高位往低位依次发送到DO。spi配置模式0, 主控先设置CLK为低,由于是MSB, 先传送高位,然后CLK为高,在CLK这个上升沿,DO的数据被锁存,OLED就读取了一位数据。接着左移一位,传输下一位。通过SPI_Set_CLK()和SPI_Set_DO()配置SCK和DO的时序,用gpio模拟出了spi。至此,SPI初始化和OLED初始化就基本完成了,接下来就是OLED显示部分。

    这里gpio模拟spi传送时主控没有加延时控制SCK的频率,那是由于jz2440本身cpu运行就很慢,这里不延时也是能满足该款外设的spi传输时序,如果cpu很快,那么需要控制spi时序。

    3.驱动显示OLED

    如何在OLED上显示一个字符?根据前面一节OLED面板的显示原理。代码实现如下:

    static void OLEDSetPos(int page, int col){
        OLEDWriteCmd(0xB0 + page); /* page address */
        OLEDWriteCmd(col & 0xf);   /* Lower Column Start Address */
        OLEDWriteCmd(0x10 + (col >> 4));   /* Lower Higher Start Address */
    }
    /* page: 0-7
     * col : 0-127
     * 字符: 8x16象素
     */
    void OLEDPutChar(int page, int col, char c){
        int i = 0;
        /* 得到字模 */
        const unsigned char *dots = oled_asc2_8x16[c - ' '];
        /* 发给OLED */
        OLEDSetPos(page, col);
        /* 发出8字节数据 */
        for (i = 0; i < 8; i++)
            OLEDWriteDat(dots[i]);
        OLEDSetPos(page+1, col);
        /* 发出8字节数据 */
        for (i = 0; i < 8; i++)
            OLEDWriteDat(dots[i+8]);
    }
    /* page: 0-7
     * col : 0-127
     * 字符: 8x16象素
     */
    void OLEDPrint(int page, int col, char *str){
        int i = 0;
        while (str[i]){
            OLEDPutChar(page, col, str[i]);
            col += 8;
            if (col > 127) {
                col = 0;
                page += 2;
            }
            i++;
        }
    }
    static void OLEDSetPos(int page, int col){
        OLEDWriteCmd(0xB0 + page); /* page address */
        OLEDWriteCmd(col & 0xf);   /* Lower Column Start Address */
        OLEDWriteCmd(0x10 + (col >> 4));   /* Lower Higher Start Address */
    }
    static void OLEDClear(void){
        int page, i;
        for (page = 0; page < 8; page ++){
            OLEDSetPos(page, 0);
            for (i = 0; i < 128; i++)
                OLEDWriteDat(0);
        }
    }

     完整代码如下:

    /************************** gpio_spi.c ****************/
    #include "s3c24xx.h"
    /* 用GPIO模拟SPI */
    static void SPI_GPIO_Init(void)
    {
        /* GPF1 OLED_CSn output */
        GPFCON &= ~(3<<(1*2));
        GPFCON |= (1<<(1*2));
        GPFDAT |= (1<<1);
    
        /* GPG2 FLASH_CSn output
        * GPG4 OLED_DC   output
        * GPG5 SPIMISO   input
        * GPG6 SPIMOSI   output
        * GPG7 SPICLK    output
        */
        GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));
        GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2)));
        GPGDAT |= (1<<2);
    }
    
    static void SPI_Set_CLK(char val)
    {
        if (val)
            GPGDAT |= (1<<7);
        else
            GPGDAT &= ~(1<<7);
    }
    
    static void SPI_Set_DO(char val)
    {
        if (val)
            GPGDAT |= (1<<6);
        else
            GPGDAT &= ~(1<<6);
    }
    
    void SPISendByte(unsigned char val)
    {
        int i;
        for (i = 0; i < 8; i++)
        {
            SPI_Set_CLK(0);
            SPI_Set_DO(val & 0x80);
            SPI_Set_CLK(1);
            val <<= 1;
        }
        
    }
    
    void SPIInit(void)
    {
        /* 初始化引脚 */
        SPI_GPIO_Init();
    }
    
    /******************* oled.c****************/
    #include "oledfont.h"
    #include "gpio_spi.h"
    #include "s3c24xx.h"
    static void OLED_Set_DC(char val)
    {
        if (val)
            GPGDAT |= (1<<4);
        else
            GPGDAT &= ~(1<<4);
    }
    static void OLED_Set_CS(char val)
    {
        if (val)
            GPFDAT |= (1<<1);
        else
            GPFDAT &= ~(1<<1);
    }
    static void OLEDWriteCmd(unsigned char cmd)
    {
        OLED_Set_DC(0); /* command */
        OLED_Set_CS(0); /* select OLED */
    
        SPISendByte(cmd);
    
        OLED_Set_CS(1); /* de-select OLED */
        OLED_Set_DC(1); /*  */
    }
    static void OLEDWriteDat(unsigned char dat)
    {
        OLED_Set_DC(1); /* data */
        OLED_Set_CS(0); /* select OLED */
    
        SPISendByte(dat);
    
        OLED_Set_CS(1); /* de-select OLED */
        OLED_Set_DC(1); /*  */
    }
    static void OLEDSetPageAddrMode(void)
    {
        OLEDWriteCmd(0x20);
        OLEDWriteCmd(0x02);
    }
    static void OLEDSetPos(int page, int col)
    {
        OLEDWriteCmd(0xB0 + page); /* page address */
    
        OLEDWriteCmd(col & 0xf);   /* Lower Column Start Address */
        OLEDWriteCmd(0x10 + (col >> 4));   /* Lower Higher Start Address */
    }
    static void OLEDClear(void)
    {
        int page, i;
        for (page = 0; page < 8; page ++)
        {
            OLEDSetPos(page, 0);
            for (i = 0; i < 128; i++)
                OLEDWriteDat(0);
        }
    }
    void OLEDInit(void)
    {
        /* 向OLED发命令以初始化 */
        OLEDWriteCmd(0xAE); /*display off*/ 
        OLEDWriteCmd(0x00); /*set lower column address*/ 
        OLEDWriteCmd(0x10); /*set higher column address*/ 
        OLEDWriteCmd(0x40); /*set display start line*/ 
        OLEDWriteCmd(0xB0); /*set page address*/ 
        OLEDWriteCmd(0x81); /*contract control*/ 
        OLEDWriteCmd(0x66); /*128*/ 
        OLEDWriteCmd(0xA1); /*set segment remap*/ 
        OLEDWriteCmd(0xA6); /*normal / reverse*/ 
        OLEDWriteCmd(0xA8); /*multiplex ratio*/ 
        OLEDWriteCmd(0x3F); /*duty = 1/64*/ 
        OLEDWriteCmd(0xC8); /*Com scan direction*/ 
        OLEDWriteCmd(0xD3); /*set display offset*/ 
        OLEDWriteCmd(0x00); 
        OLEDWriteCmd(0xD5); /*set osc division*/ 
        OLEDWriteCmd(0x80); 
        OLEDWriteCmd(0xD9); /*set pre-charge period*/ 
        OLEDWriteCmd(0x1f); 
        OLEDWriteCmd(0xDA); /*set COM pins*/ 
        OLEDWriteCmd(0x12); 
        OLEDWriteCmd(0xdb); /*set vcomh*/ 
        OLEDWriteCmd(0x30); 
        OLEDWriteCmd(0x8d); /*set charge pump enable*/ 
        OLEDWriteCmd(0x14); 
    
        OLEDSetPageAddrMode();
    
        OLEDClear();
        
        OLEDWriteCmd(0xAF); /*display ON*/    
    }
    
    /* page: 0-7
     * col : 0-127
     * 字符: 8x16象素
     */
    void OLEDPutChar(int page, int col, char c)
    {
        int i = 0;
        /* 得到字模 */
        const unsigned char *dots = oled_asc2_8x16[c - ' '];
    
        /* 发给OLED */
        OLEDSetPos(page, col);
        /* 发出8字节数据 */
        for (i = 0; i < 8; i++)
            OLEDWriteDat(dots[i]);
    
        OLEDSetPos(page+1, col);
        /* 发出8字节数据 */
        for (i = 0; i < 8; i++)
            OLEDWriteDat(dots[i+8]);
    
    }
    
    /* page: 0-7
     * col : 0-127
     * 字符: 8x16象素
     */
    void OLEDPrint(int page, int col, char *str)
    {
        int i = 0;
        while (str[i])
        {
            OLEDPutChar(page, col, str[i]);
            col += 8;
            if (col > 127)
            {
                col = 0;
                page += 2;
            }
            i++;
        }
    }
    

      

  • 相关阅读:
    Solr6.6环境安装及core的创建(win7环境)
    使用Druid作为数据源
    Windows远程数据同步工具cwRsync
    解读zookeeper的配置项
    堵塞与非堵塞原理
    Apache Hadoop2.0之HDFS均衡操作分析
    转到简书去了
    淘宝技术这十年概要
    Facebook广告API系列 Business Manager
    Facebook广告API系列 3 Ads Management
  • 原文地址:https://www.cnblogs.com/fuzidage/p/15697593.html
Copyright © 2011-2022 走看看