首先感谢aijishu & 灵动微电子给与的测试机会。本次测试基于SSD1306驱动芯片的OLED屏幕,接口为I2C。
一、硬件
*逐飞科技的Plus-F5270是2022年全国大学生物联网设计竞赛用开发板,它拥有一颗安谋科技“星辰”STAR-MC1架构的灵动微MM32F5277E高性能芯片,集成DSP和FPU。
*
板子很漂亮,黑色PCB,所有针脚都引出,板载了很多传感器,能满足不同场景开发需求。
OLED采用的驱动芯片为SSD1306,尺寸为0.96,来自HiSpark IoT套件。
二、基于MindSDK驱动OLED
从灵动微电子官方下载的plus-f5270_mdk,driver_examples文件夹中提供了如ADC、CRC、DAC等23类驱动的案例。
因为本次驱动的是I2C的OLED,直接选择I2C文件夹,选择一个i2c_master_basic的case作为基础来修改。
1、Pin初始化
I2C1用的是PC6(SCL)、PC7(SDA),所以直接使用MindSDK的代码即可,方便快捷安全。
    /* PC6 - I2C1_SCL. */
    gpio_init.Pins  = GPIO_PIN_6;
    gpio_init.PinMode  = GPIO_PinMode_AF_OpenDrain;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, gpio_init.Pins, GPIO_AF_4);
    /* PC7 - I2C1_SDA. */
    gpio_init.Pins  = GPIO_PIN_7;
    gpio_init.PinMode  = GPIO_PinMode_AF_OpenDrain;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, gpio_init.Pins, GPIO_AF_4);2、I2C初始化
I2C1的初始化,也用MindSDK的代码,改了几个参数(APP_I2C_TARGET_ADDR改成OLED的0x78, 波特率变成400K等),挺顺滑的。
/* Initialize I2C */
void i2c_init(void)
{
    /* Setup I2C initialization values. */
      //RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_I2C1, true);
    I2C_Master_Init_Type i2c_init;
    i2c_init.ClockFreqHz = BOARD_I2C_FREQ;
    i2c_init.BaudRate = I2C_BaudRate_400K;
    /* Initialize I2C master. */
    I2C_InitMaster(BOARD_I2C_PORT, &i2c_init);
    /* The target device address needs to be configured before enabling. */
    I2C_SetTargetAddr(BOARD_I2C_PORT, APP_I2C_TARGET_ADDR);
    /* Enable I2C. */
    I2C_Enable(BOARD_I2C_PORT, true);
    
    GPIO_WriteBit(GPIOC, GPIO_PIN_7, 0u);
    GPIO_WriteBit(GPIOC, GPIO_PIN_6, 0u);
}3、SSD1306初始化
SSD1306相关驱动来自于gitee大佬的作品(SSD1306),将ssd1306、ssd1306_conf、ssd1306_fonts文件提取出来。
一是增加一个基于NOP的delay实现:
void HAL_Delay(uint32_t ms)
{
   uint32_t i, j;
    
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < (CLOCK_SYS_FREQ / 1000u); j++)
        {
            __NOP();            
        }
    }
}I2C sendData函数基于MindSDK的hal_i2c做了修改:
static uint32_t ssd1306_SendData(uint8_t* data, size_t size)
{
    static I2C_MasterXfer_Type app_i2c_xfer = {0};
    app_i2c_xfer.WaitTimes = APP_I2C_TIMEOUT_TIME;
    app_i2c_xfer.TxBuf     = data;
    app_i2c_xfer.TxLen     = size;
    I2C_Enable(BOARD_I2C_PORT, true);  
    if ( false == I2C_MasterWriteBlocking(BOARD_I2C_PORT, &app_i2c_xfer) )
    {
            printf("ssd1306_SendData err \r\n");
        return 1;
    }
    return 0;
}然后直接用SSD1306 Init代码:
// Initialize the oled screen
void ssd1306_Init(void) {
    // Reset OLED
    //ssd1306_Reset();
    // Wait for the screen to boot
    HAL_Delay(200);
    // Init OLED
    ssd1306_SetDisplayOn(0); //display off
    ssd1306_WriteCommand(0x20); //Set Memory Addressing Mode
    ssd1306_WriteCommand(0x00); // 00b,Horizontal Addressing Mode; 01b,Vertical Addressing Mode;
                                // 10b,Page Addressing Mode (RESET); 11b,Invalid
    ssd1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode,0-7
#ifdef SSD1306_MIRROR_VERT
    ssd1306_WriteCommand(0xC0); // Mirror vertically
#else
    ssd1306_WriteCommand(0xC8); //Set COM Output Scan Direction
#endif
    ssd1306_WriteCommand(0x00); //---set low column address
    ssd1306_WriteCommand(0x10); //---set high column address
    ssd1306_WriteCommand(0x40); //--set start line address - CHECK
    ssd1306_SetContrast(0xFF);
#ifdef SSD1306_MIRROR_HORIZ
    ssd1306_WriteCommand(0xA0); // Mirror horizontally
#else
    ssd1306_WriteCommand(0xA1); //--set segment re-map 0 to 127 - CHECK
#endif
#ifdef SSD1306_INVERSE_COLOR
    ssd1306_WriteCommand(0xA7); //--set inverse color
#else
    ssd1306_WriteCommand(0xA6); //--set normal color
#endif
// Set multiplex ratio.
#if (SSD1306_HEIGHT == 128)
    // Found in the Luma Python lib for SH1106.
    ssd1306_WriteCommand(0xFF);
#else
    ssd1306_WriteCommand(0xA8); //--set multiplex ratio(1 to 64) - CHECK
#endif
#if (SSD1306_HEIGHT == 32)
    ssd1306_WriteCommand(0x1F); //
#elif (SSD1306_HEIGHT == 64)
    ssd1306_WriteCommand(0x3F); //
#elif (SSD1306_HEIGHT == 128)
    ssd1306_WriteCommand(0x3F); // Seems to work for 128px high displays too.
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endif
    ssd1306_WriteCommand(0xA4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    ssd1306_WriteCommand(0xD3); //-set display offset - CHECK
    ssd1306_WriteCommand(0x00); //-not offset
    ssd1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequency
    ssd1306_WriteCommand(0xF0); //--set divide ratio
    ssd1306_WriteCommand(0xD9); //--set pre-charge period
    ssd1306_WriteCommand(0x11); // 0x22 by default
    ssd1306_WriteCommand(0xDA); //--set com pins hardware configuration - CHECK
#if (SSD1306_HEIGHT == 32)
    ssd1306_WriteCommand(0x02);
#elif (SSD1306_HEIGHT == 64)
    ssd1306_WriteCommand(0x12);
#elif (SSD1306_HEIGHT == 128)
    ssd1306_WriteCommand(0x12);
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endif
    ssd1306_WriteCommand(0xDB); //--set vcomh
    ssd1306_WriteCommand(0x30); //0x20,0.77xVcc, 0x30,0.83xVcc
    ssd1306_WriteCommand(0x8D); //--set DC-DC enable
    ssd1306_WriteCommand(0x14); //
    ssd1306_SetDisplayOn(1); //--turn on SSD1306 panel
    // Clear screen
    ssd1306_Fill(White);
    // Flush buffer to screen
    ssd1306_UpdateScreen();
    // Set default values for screen object
    SSD1306.CurrentX = 0;
    SSD1306.CurrentY = 0;
    SSD1306.Initialized = 1;
}4、main()
main()主要测试OLED是否工作正常,代码如下:
int main(void)
{
    BOARD_Init();
    printf("I2C example\r\n");
    /* Initialize I2C. */
    HAL_Delay(100);
    i2c_init();
    HAL_Delay(100);
    ssd1306_Init();
    HAL_Delay(100);
    ssd1306_Fill(White);
    ssd1306_SetCursor(0, 0);
    ssd1306_DrawString("Hello MM32F5270!", Font_7x10, Black);
    while (1)
    {
        printf("press any key to write i2c-eeprom.\r\n");
        getchar();
    }
}三、测试
接线如下图。

 
                