2218120025 发表于 2025-1-4 22:31

【AT-START-L021测评】0.96寸OLED驱动

       OLED是常见的显示模块,本次是用雅特力AT-START-L021来驱动主控芯片为SSD1306的0.96寸OLED模块,使用的是IIC进行通信。
       首先是在新创建的工程中添加i2c_application.c(在BSP文件夹下)和at32l021_i2c.c和at32l021_dma.c(在FIRMWARE文件夹下)。其中at32l021_dma.c在i2c_application.c中引用,但是实际的工程中没有使用到,去掉编译会有问题。
       其次是在main函数中调用IIC初始化相关函数,这里参考了IIC例程。在main.c中添加如下的IIC对应的GPIO口初始化函数
void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
gpio_init_type gpio_init_structure;

if(hi2c->i2cx == I2Cx_PORT)
{
    /* 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);

    /* 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_NONE;

    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);

    /* config i2c */
    i2c_init(hi2c->i2cx, 0x0F, I2Cx_CLKCTRL);

    i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, I2Cx_ADDRESS);
}
}       在main中添加如下的宏定义
#define I2C_TIMEOUT                      0xFFFFFFF

//#define I2Cx_CLKCTRL                   0xF070F7F7   //10K
//#define I2Cx_CLKCTRL                   0x60F06C6C   //50K
#define I2Cx_CLKCTRL                     0x60F03333   //100K
//#define I2Cx_CLKCTRL                   0x20C02C4F   //200K

//#define I2Cx_ADDRESS                     0xA0

#define I2Cx_PORT                        I2C1
#define I2Cx_CLK                         CRM_I2C1_PERIPH_CLOCK
#define I2Cx_DMA                         DMA1
#define I2Cx_DMA_CLK                     CRM_DMA1_PERIPH_CLOCK

#define I2Cx_SCL_GPIO_CLK                CRM_GPIOB_PERIPH_CLOCK
#define I2Cx_SCL_GPIO_PIN                GPIO_PINS_6
#define I2Cx_SCL_GPIO_PinsSource         GPIO_PINS_SOURCE6
#define I2Cx_SCL_GPIO_PORT               GPIOB
#define I2Cx_SCL_GPIO_MUX                GPIO_MUX_1

#define I2Cx_SDA_GPIO_CLK                CRM_GPIOB_PERIPH_CLOCK
#define I2Cx_SDA_GPIO_PIN                GPIO_PINS_7
#define I2Cx_SDA_GPIO_PinsSource         GPIO_PINS_SOURCE7
#define I2Cx_SDA_GPIO_PORT               GPIOB
#define I2Cx_SDA_GPIO_MUX                GPIO_MUX_1      在main中添加对应的.h调用代码
#include "i2c_application.h"      main函数中调用IIC初始化的代码如下
hi2cx.i2cx = I2Cx_PORT;

/* i2c config */
i2c_config(&hi2cx);
      之后是添加OLED的驱动,这里是参考常用的OLED驱动,只是修改发送数据的代码,OLED.c的代码如下
#include "oled.h"
#include "oledfont.h"
#include "i2c_application.h"

i2c_handle_type OLEDhi2cx;

void OLED_WriteByte(uint8_t data, uint8_t mode);
void OLED_Clear(void);
void OLED_SetPos(uint8_t x, uint8_t y);
void OLED_DrawBMP(uint8_t x, uint8_t y, uint8_t sizex, uint8_t sizey, const uint8_t BMP[]);
/*
函 数 名:OLED_WriteByte
功    能:发送一个字节
参数描述:data:发送数据
         mode:模式选择   0:命令   1:数据
返 回 值:无
*/
void OLED_WriteByte(uint8_t data, uint8_t mode)
{
    uint8_t u8Senddata = {0};
    u8Senddata = mode;
    u8Senddata = data;
    i2c_master_transmit(&OLEDhi2cx,OLED_ADDR, u8Senddata, 2, 0xFF);
}

/*
函 数 名:OLED_Clear
功    能:清屏
参数描述:无
返 回 值:无
*/
void OLED_Clear(void)
{
    uint8_t i,n;      
    for(i=0; i<OLED_PAGE_SUM; i++)
    {
      OLED_WriteByte (OLED_REG_PAGE_SET + i,OLED_SEND_CMD);    /* 设置页地址(0~7) */
      OLED_WriteByte (OLED_REG_LINE_L_SET,OLED_SEND_CMD);      /* 设置显示位置—列低地址 */
      OLED_WriteByte (OLED_REG_LINE_H_SET,OLED_SEND_CMD);      /* 设置显示位置—列高地址 */   
      for(n=0; n<128; n++)
      {
            OLED_WriteByte(0,OLED_SEND_DATA);
      }//更新显示
    }
}

/*
函 数 名:OLED_SetPos
功    能:坐标设置
参数描述:x:横坐标
         Y:纵坐标
返 回 值:无
*/
void OLED_SetPos(uint8_t x, uint8_t y)
{
OLED_WriteByte(0xb0 + y, OLED_SEND_CMD);
OLED_WriteByte(((x & 0xf0) >> 4) | 0x10, OLED_SEND_CMD);
OLED_WriteByte((x & 0x0f), OLED_SEND_CMD);
}   

/*
函 数 名:OLED_DrawBMP
功    能:显示图片
参数描述:x, y:起始坐标
      sizex, sizey:终止坐标
      BMP:图像数据
返 回 值:无
*/
void OLED_DrawBMP(uint8_t x, uint8_t y, uint8_t sizex, uint8_t sizey, const uint8_t BMP[])
{   
uint16_t j=0;
uint8_t i, m, temp;
sizey = sizey / 8 + ((sizey % 8) ? 1 : 0);
for(i = 0; i < sizey; i++)
{
    OLED_SetPos(x, i + y);
    for(m = 0; m < sizex; m++)
    {
       temp = BMP;
       OLED_WriteByte(temp, OLED_SEND_DATA);
    }
}
}
//屏幕旋转180度
void OLED_DisplayTurn(uint8_t i)
{
if(i==0)
    {
      OLED_WriteByte(0xC8,OLED_SEND_CMD);//正常显示
      OLED_WriteByte(0xA1,OLED_SEND_CMD);
    }
else
    {
      OLED_WriteByte(0xC0,OLED_SEND_CMD);//反转显示
      OLED_WriteByte(0xA0,OLED_SEND_CMD);
    }
}
/*
函 数 名:OLED_Init
功    能:OLED初始化
参数描述:无
返 回 值:无
*/
void OLED_Init()
{
    OLEDhi2cx.i2cx = I2C1;
    OLED_WriteByte(0xAE,OLED_SEND_CMD);//--turn off oled panel
    OLED_WriteByte(0x00,OLED_SEND_CMD);//---set low column address
    OLED_WriteByte(0x10,OLED_SEND_CMD);//---set high column address
    OLED_WriteByte(0x40,OLED_SEND_CMD);//--set start line addressSet Mapping RAM Display Start Line (0x00~0x3F)
    OLED_WriteByte(0x81,OLED_SEND_CMD);//--set contrast control register
    OLED_WriteByte(0xCF,OLED_SEND_CMD); // Set SEG Output Current Brightness
    OLED_WriteByte(0xA1,OLED_SEND_CMD);//--Set SEG/Column Mapping   0xa0左右反置 0xa1正常
    OLED_WriteByte(0xC8,OLED_SEND_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_WriteByte(0xA6,OLED_SEND_CMD);//--set normal display
    OLED_WriteByte(0xA8,OLED_SEND_CMD);//--set multiplex ratio(1 to 64)
    OLED_WriteByte(0x3f,OLED_SEND_CMD);//--1/64 duty
    OLED_WriteByte(0xD3,OLED_SEND_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
    OLED_WriteByte(0x00,OLED_SEND_CMD);//-not offset
    OLED_WriteByte(0xd5,OLED_SEND_CMD);//--set display clock divide ratio/oscillator frequency
    OLED_WriteByte(0x80,OLED_SEND_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_WriteByte(0xD9,OLED_SEND_CMD);//--set pre-charge period
    OLED_WriteByte(0xF1,OLED_SEND_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_WriteByte(0xDA,OLED_SEND_CMD);//--set com pins hardware configuration
    OLED_WriteByte(0x12,OLED_SEND_CMD);
    OLED_WriteByte(0xDB,OLED_SEND_CMD);//--set vcomh
    OLED_WriteByte(0x40,OLED_SEND_CMD);//Set VCOM Deselect Level
    OLED_WriteByte(0x20,OLED_SEND_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_WriteByte(0x02,OLED_SEND_CMD);//
    OLED_WriteByte(0x8D,OLED_SEND_CMD);//--set Charge Pump enable/disable
    OLED_WriteByte(0x14,OLED_SEND_CMD);//--set(0x10) disable
    OLED_WriteByte(0xA4,OLED_SEND_CMD);// Disable Entire Display On (0xa4/0xa5)
    OLED_WriteByte(0xA6,OLED_SEND_CMD);// Disable Inverse Display On (0xa6/a7)
    OLED_Clear();
    OLED_WriteByte(0xAF,OLED_SEND_CMD); /*display ON*/
    OLED_DisplayTurn(1);//0正常显示 1翻转180度显示
}

/*
函 数 名:OLED_Init
功    能:OLED初始化
参数描述:无
返 回 值:无
*/
void OLED_Test(void)
{
    OLED_DrawBMP(0, 0, 128, 64, BMP1);
}


//在指定位置显示一个字符
//x:0~127
//y:0~63         
//sizey:选择字体 6x88x16
void OLED_ShowChar(uint8_t x, uint8_t y,const uint8_t chr, uint8_t sizey)
{      
    uint8_t c = 0, sizex = sizey / 2, temp;
    uint16_t i = 0, size1;
    if (sizey == 8)
    {
      size1=6;
    }
    else
    {
      size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
    }
    c = chr - ' ';//得到偏移后的值
   
    OLED_SetPos(x,y);
    for(i = 0; i < size1; i++)
    {
      if(((i % sizex) == 0) && (sizey != 8))
      {
            OLED_SetPos(x,y++);
      }
      if( sizey == 8)
      {
            temp = asc2_0806;
            OLED_WriteByte(temp,OLED_SEND_DATA);//6X8字号
      }
      else if(sizey == 16)
      {
            temp=asc2_1608;
            OLED_WriteByte(temp,OLED_SEND_DATA);//8x16字号
      }
      else
      {
            return;
      }
}
}
//显示一个字符号串
void OLED_ShowString(uint8_t x, uint8_t y,const char *chr, uint8_t sizey)
{
uint8_t j = 0;
while (chr != '\0')
{   
    OLED_ShowChar(x, y, chr, sizey);
    if(sizey == 8)
    {
      x+=6;
    }
    else
    {
      x += (sizey / 2);
    }
}
}
//m^n函数
uint32_t oled_pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;   
return result;
}
//显示数字
//x,y :起点坐标
//num:要显示的数字
//len :数字的位数
//sizey:字体大小      
void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char sizey)
{         
uint8_t t,temp,m=0;
uint8_t enshow=0;
if(sizey==8)m=2;
for(t=0;t<len;t++)
{
    temp=(num/oled_pow(10,len-t-1))%10;
    if(enshow==0&&t<(len-1))
    {
      if(temp==0)
      {
      OLED_ShowChar(x+(sizey/2+m)*t,y,' ',sizey);
      continue;
      }else enshow=1;
    }
    OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);
}
}


      其中在OLED.c中定义了对应的IIC句柄
i2c_handle_type OLEDhi2cx;      在OLED_Init函数中添加了句柄的赋值
OLEDhi2cx.i2cx = I2C1;      主要是和main函数中初始化的IIC是一个IIC外设。
      OLED发送函数使用i2c_application.c中的i2c_master_transmit函数,发送函数定义如下
void OLED_WriteByte(uint8_t data, uint8_t mode)
{
    uint8_t u8Senddata = {0};
    u8Senddata = mode;
    u8Senddata = data;
    i2c_master_transmit(&OLEDhi2cx,OLED_ADDR, u8Senddata, 2, 0xFF);
}       引脚连接图如下

       实现效果如下

      工程文件:

呐咯密密 发表于 2025-1-7 15:23

这是使用的图片显示吗

2218120025 发表于 2025-1-8 09:06

呐咯密密 发表于 2025-1-7 15:23
这是使用的图片显示吗

代码   用专门的工具把图片转换成显示的点的数组

WhisperingTrees 发表于 2025-1-21 09:39

我在用这种屏幕的时候经常会遇到部分位置乱码,特别是在不断更新的数据显示的时候。
页: [1]
查看完整版本: 【AT-START-L021测评】0.96寸OLED驱动