【AT-START-F423测评】硬件I2c驱动OLED屏

【前言】
OLED为常用的显示外设之一,他可以使用spi或者i2c接口来实现驱动。而以I2C为常见,它只需要两个IO就可以实现驱动,对于IO紧的MCU来说更为方便,而且可以与其它的I2C设备共同一个总线。AT32F423拥用3个I2C总线,可以搭载多个i2c设备。同时AT32F423也可以通过IO的复用,更换不同的IO来实现i2c。I2c驱动也可以用普通GPIO,软件摸拟时序来实现,这样使得可以接入更多的I2C的总线设备。本次实验使用i2c硬件、阻塞式的实现驱动。AT32F423的总线也可以用中断、DMA的方式来驱动,这样占用CPU的资源更小,传输速度更快。
【接线图】
这次i2c我选择i2c1,IO选择PB6为SCL,PB7为SDA。与OLED的接线图如下表:
OLED          开发板
SCL—————PB6
SDA—————PB7
GND-----------GND
VCC------------3.3V
【I2C总线初始化】
根据数据手册PB6、PB7的引脚复用GPIO_MUX_4,详见下图:
 


在IO初始化中,我们使用i2c的复用配置函数gpio_pin_mux_config来定义:
    /* gpio configuration */
    gpio_pin_mux_config(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_GPIO_PinsSource, I2Cx_SCL_GPIO_MUX);
gpio_pin_mux_config(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_GPIO_PinsSource, I2Cx_SDA_GPIO_MUX);
然后开启i2c的时序,以及gpio的时钟:
/* i2c periph clock enable */
    crm_periph_clock_enable(I2Cx_CLK, TRUE);
    crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);
    crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);
初始化i2c的引脚,由于i2c需要上拉电平,我们这里配置其为GPIO_PULL_UP模式。
最后我们配置i2c的总线频率为200K
具体的代码见void II2CGpioInit(void)函数。

复制

void II2CGpioInit(void)

{

        #if 0

        gpio_init_type gpio_init_struct;

        /* enable gpioc periph clock */

  crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);

        

        

  gpio_default_para_init(&gpio_init_struct);



  /* gpio output config */

  gpio_bits_write(GPIOC, IO_SCL_PIN | IO_SDA_PIN, TRUE);

        

         gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;

  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;

  gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;

  gpio_init_struct.gpio_pins = IO_SCL_PIN | IO_SDA_PIN;

  gpio_init_struct.gpio_pull = GPIO_PULL_UP;

  gpio_init(GPIOC, &gpio_init_struct);

        #else

                gpio_init_type gpio_init_structure;

          crm_periph_clock_enable(I2Cx_CLK, TRUE);

    crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);

    crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);



    /* gpio configuration */

    gpio_pin_mux_config(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_GPIO_PinsSource, I2Cx_SCL_GPIO_MUX);



    gpio_pin_mux_config(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_GPIO_PinsSource, I2Cx_SDA_GPIO_MUX);



    /* configure i2c pins: scl */

    gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

    gpio_init_structure.gpio_mode           = GPIO_MODE_MUX;

    gpio_init_structure.gpio_out_type       = GPIO_OUTPUT_OPEN_DRAIN;

    gpio_init_structure.gpio_pull           = GPIO_PULL_UP;



    gpio_init_structure.gpio_pins           = I2Cx_SCL_GPIO_PIN;

    gpio_init(I2Cx_SCL_GPIO_PORT, &gpio_init_structure);



    /* configure i2c pins: sda */

    gpio_init_structure.gpio_pins           = I2Cx_SDA_GPIO_PIN;

    gpio_init(I2Cx_SDA_GPIO_PORT, &gpio_init_structure);

                

                hi2cx.i2cx = I2Cx_PORT;



  /* i2c config */

  i2c_config(&hi2cx);

        #endif

}


【i2c发送数据】
在AT32F423中的固件库中,官方给我们提供了一个i2c_application函数,函数封装了常用的数据发送函数。这个函数位于\middlewares\i2c_application_library目录下面。
 


这个函数中定义了i2c事件,传输数据的宽度,i2c的传输模式,i2c的传输反馈状态,i2c的事件状态等的枚举。
文件中还封装了i2c所有的发送、状态获取、接收、DMA发送、中断发送、阻寒式发送的函数,大家可以详细的去阅读他的源码,对掌握i2c非常有用:
 


我这里也是采用了官方封装的发送函数来传输一个byte的函数:i2c_master_transmit。用这个函数,实现了向oled写一个字节,写命令、写数据的三个功能函数:

复制

/*****************************************************************************

 * [url=home.php?mod=space&uid=139335]@name[/url]       :void Write_IIC_Byte(uint8_t IIC_Byte)

 * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-09-13 

 * [url=home.php?mod=space&uid=42490]@function[/url]   :Write a byte of content with iic bus

 * [url=home.php?mod=space&uid=2814924]@parameters[/url] :IIC_Byte

 * @retvalue   :None

******************************************************************************/

void Write_IIC_Byte(uint8_t IIC_Byte) {

        i2c_status_type i2c_status;

        #if 0

        uint8_t i;

        uint8_t m,da;

        da=IIC_Byte;

        OLED_SCL_CLR();

        for(i=0;i<8;i++) {

                m=da;

                m=m&0x80;

                if(m==0x80) {

                        OLED_SDA_SET();

                } else {

                        OLED_SDA_CLR();

                }

                da=da<<1;

                OLED_SCL_SET();

                OLED_SCL_CLR();

        }

        #else

        uint8_t buff[2] = {0};

        buff[0] = IIC_Byte;

        i2c_status = i2c_master_transmit(&hi2cx, I2Cx_ADDRESS, buff, 1, 1000);

        if(i2c_status != I2C_OK)

        {

                printf("erro send %d",i2c_status);

        }

        #endif

}



/*****************************************************************************

 * [url=home.php?mod=space&uid=139335]@name[/url]       :void Write_IIC_Command(uint8_t IIC_Command)

 * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-09-13 

 * [url=home.php?mod=space&uid=42490]@function[/url]   :Write a byte of command to oled screen

 * [url=home.php?mod=space&uid=2814924]@parameters[/url] :IIC_Command:command to be written

 * @retvalue   :None

******************************************************************************/

void Write_IIC_Command(uint8_t IIC_Command) {

        i2c_status_type i2c_status;

        #if 0

        IIC_Start();

        Write_IIC_Byte(IIC_SLAVE_ADDR);            //Slave address,SA0=0

        IIC_Wait_Ack();        

        Write_IIC_Byte(0x00);                        //write command

        IIC_Wait_Ack();        

        Write_IIC_Byte(IIC_Command); 

        IIC_Wait_Ack();        

        IIC_Stop();

        #else

        uint8_t buff[2] = {0};

        buff[0] = 0x00;

        buff[1] = IIC_Command;

        i2c_status = i2c_master_transmit(&hi2cx, I2Cx_ADDRESS, buff, 2, 1000);

        if(i2c_status != I2C_OK)

        {

                printf("erro send %d",i2c_status);

        }

        #endif

}



/*****************************************************************************

 * @name       :void Write_IIC_Data(uint8_t IIC_Data)

 * @date       :2018-09-13 

 * @function   :Write a byte of data to oled screen

 * @parameters :IIC_Data:data to be written

 * @retvalue   :None

******************************************************************************/

void Write_IIC_Data(uint8_t IIC_Data) {

        i2c_status_type i2c_status;

        #if 0

        IIC_Start();

        Write_IIC_Byte(IIC_SLAVE_ADDR);                        //D/C#=0; R/W#=0

        IIC_Wait_Ack();        

        Write_IIC_Byte(0x40);                        //write data

        IIC_Wait_Ack();        

        Write_IIC_Byte(IIC_Data);

        IIC_Wait_Ack();        

        IIC_Stop();

        #else

        uint8_t buff[2] = {0};

        buff[0] = 0x40;

        buff[1] = IIC_Data;

        i2c_status = i2c_master_transmit(&hi2cx, I2Cx_ADDRESS, buff, 2, 1000);

        if(i2c_status != I2C_OK)

        {

                printf("erro send %d",i2c_status);

        }

        #endif

}


【OLED初始化】
我的oled的芯片是ssd1306,其他的oled大多数是通用的初始化函数,我这里采用的页写入模式为分页式,配置函数如下:

复制

/*******************************************************************

 * @name       :void OLED_Init(void)

 * @date       :2018-08-27

 * @function   :initialise OLED SSD1306 control IC

 * @parameters :None

 * @retvalue   :None

********************************************************************/                                     

void OLED_Init(void) {

        II2CGpioInit();

         delay_ms(200);



/**************初始化SSD1306*****************/        

//        OLED_Display_Off(); //power off

        OLED_WR_Byte(0xAE,OLED_CMD);//--display off        

        OLED_WR_Byte(0x20,OLED_CMD);//---set low column address

        OLED_WR_Byte(0x10,OLED_CMD);//---set high column address

        OLED_WR_Byte(0xb0,OLED_CMD);

        OLED_WR_Byte(0xC8,OLED_CMD);//-not offset

        OLED_WR_Byte(0x00,OLED_CMD);// contract control

        OLED_WR_Byte(0x10,OLED_CMD);

        OLED_WR_Byte(0x40,OLED_CMD);//--128 

        OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register

        OLED_WR_Byte(0xFF,OLED_CMD);

        OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap 

        OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse        

        OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)

        OLED_WR_Byte(0x3F,OLED_CMD);        

        OLED_WR_Byte(0xa4,OLED_CMD);//-set display offset

        OLED_WR_Byte(0xd3,OLED_CMD);

        OLED_WR_Byte(0x00,OLED_CMD);

        OLED_WR_Byte(0xD5,OLED_CMD);//set osc division

        OLED_WR_Byte(0xF0,OLED_CMD);        

        OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period

        OLED_WR_Byte(0x22,OLED_CMD);        

        OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion

        OLED_WR_Byte(0x12,OLED_CMD);        

        OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh

        OLED_WR_Byte(0x20,OLED_CMD);        

        OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable

        OLED_WR_Byte(0x14,OLED_CMD);        

        OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel

        //OLED_Display_On(); // power on

}  


【字库】
字库我这里采用的是网友提供的,如有侵权请及时通知我,其中的我这里特别要说明一下如何生成我的汉字字库,我这里使用的工具是PCtolCD2002。汉字取模采用阴码、逐行式,顺向、C51格式,配置如下图:


字宽与高采用16*16,宋体:
 


生成字模后加入到字库函数中:
 


【测试代码】
在主函数中,我先初始化oled,清屏,然后编写测试函数:
 


【实现的效果】
 


【试用心得】
1、网上驱动OLED的例程非常多,网友@suncat0504,也提前写了oled的软件驱动例程: https://bbs.21ic.com/icview-3336944-1-1.html。我所用的库也是在他的提供的例程上修改过来的。但是他的驱动例程好象在我的这里运行不了。我在他的基础上修改了一些参数,比如页的大小、修改为硬件驱动等。"
2、在测试中需要用逻辑分析仪来查看一下时序。
3、其实要驱动AT32F423的I2C,最好是去看看官方的一些例程,例程中详细的列出了多种方式的i2c的通信方式。
4、当然这次的驱动只是实现驱动而已,速度只用到了200K。如果需要用到高的刷新率,最好使用dma的方式来实现数据的传输,同时采用双缓存来实时更新数据,以实现高频的数据刷新。当然也可以移植u8g2等第三方库来实现更多的显示功能等。
5、同时也**有其他的大佬们能把dma等高效率的驱动奉献出来。
【附件】:OLED驱动包,如需要移植,可以把包下载后,导入工程就可以了。
 


 

 ssd1306.zip (19.16 KB)。
---------------------
作者:lulugl
链接:https://bbs.21ic.com/icview-3337916-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值