@#@ · 2022年08月17日 · 湖北

【MM32F5270开发板试用】快速移植STM32应用到MM32F5270(以OLED为例)

最近在做几个嵌入式项目,一直使用的是STM32F429作为主控制芯片。从去年开始,ST的芯片全系涨价,价格高到离谱,并且市场上充斥着翻新芯片,有时候有钱都不一定能买到靠谱的原装芯片。考虑到项目最终落地需要考虑到芯片供应问题,所以一直在寻找国产替代。正好看到极术社区的MM32F5270适用活动,初步看芯片性能以及外设接口能满足现在的项目需求,所以申请一个板子来看下从STM32移植到MM32F5270的工作量。

一 项目简介

最近做的项目是一个气体分析仪,基本原理框图如下。
image.png

用STM32跑PID算法,控制比例阀开度,将输入气流稳定到设置值,然后由气体检测器进行采样并处理。这个项目目前已经开始小批量出货,这里就不过多说明技术细节。

由于项目客制化需求比较多,要求能够通过键盘选择不同功能,涉及到多级菜单。所以大量的工作都在自定义的菜单设计和实现上,这里就先试试移植OLED驱动代码到MM32F5270看看工作量。

二 环境准备

请参考https://aijishu.com/a/1060000...准备开发环境。记住,一定要下载MDK5.37版本才能使用灵动微电子的MM32 pack包。

因为我习惯用JLINK调试器做开发,所以这里我就直接使用JLINK作为调试工具。JLINK的插头直接插上去就可以,有防呆设计,不用担心会插错,下面是JLINK连接特写。
4ca70a8a5765f431bd80ff2a9d4345a.jpg
插上JLINK后,在魔术棒页面DEBUG tab里选择JLINK/J-TRACE Cortex
image.png
点击setting,在setting页面选择SW
image.png

接下来就可以愉快的使用JLINK进行MM32F5270开发板的调试了。

三 代码移植

初步看了下Mind SDK的文件结构,对所有的外设,都有对应的驱动代码,驱动代码对底层的硬件操作做了很好的封装。看了下对应的driver_example以及demo_apps,提供的都是类似于如下的代码文件组织结构
image.png
对于简单的应用来说,这种结构可以满足需求。但是对于一些外设较多的项目,这种组织形式在逻辑上不是很清晰,且代码复用性不高。所以这里按照项目的需求,设计了按hardware和module分类的方法,项目部分文件结构如下:
image.png

其中hardware目录是用到的外设driver,module目录里会放跟硬件无关的功能模块,比如流量控制功能,气体采样功能(当然目前为空,但是因为这是硬件无关逻辑,所以基本上可以不做改动直接移到MM32F5270)等。这里以tim_basic例程为基础,来移植OLED驱动。

  1. 拆分clock_init.c/clock_init.h文件,这里将所有外设的clokc外设移除,只留下主时钟的初始化。

    void BOARD_InitBootClocks(void)
    {
     CLOCK_ResetToDefault();
     CLOCK_BootToHSE120MHz();
    }
  2. 将uart拆分到单独文件,以后在不同项目可以进行复用
    demo_uart.h

    #ifndef _UART_H_
    #define _UART_H_
    
    #include "clock_init.h"
    
    /* DEBUG UART. */
    #define BOARD_DEBUG_UART_PORT        UART1
    #define BOARD_DEBUG_UART_BAUDRATE    9600u
    #define BOARD_DEBUG_UART_FREQ        CLOCK_APB2_FREQ
    
    void uart1_init(void);
    #endif
    

    demo_uart.c

    #include "demo_uart.h"
    #include <stdio.h>
    
    #include "hal_common.h"
    #include "hal_gpio.h"
    #include "hal_uart.h"
    #include "hal_rcc.h"
    
    static void uart1_gpio_init(void)
    {
     /* PB6 - UART1_TX. */
     GPIO_Init_Type gpio_init;
     gpio_init.Pins  = GPIO_PIN_6;
     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &gpio_init);
     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);
    
     /* PB7 - UART1_RX. */
     gpio_init.Pins  = GPIO_PIN_7;
     gpio_init.PinMode  = GPIO_PinMode_In_Floating;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &gpio_init);
     GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);
    
    }
    
    static void uart1_port_init(void)
    {
     UART_Init_Type uart_init;
    
     uart_init.ClockFreqHz   = BOARD_DEBUG_UART_FREQ;
     uart_init.BaudRate      = BOARD_DEBUG_UART_BAUDRATE;
     uart_init.WordLength    = UART_WordLength_8b;
     uart_init.StopBits      = UART_StopBits_1;
     uart_init.Parity        = UART_Parity_None;
     uart_init.XferMode      = UART_XferMode_RxTx;
     uart_init.HwFlowControl = UART_HwFlowControl_None;
     UART_Init(BOARD_DEBUG_UART_PORT, &uart_init);
     UART_Enable(BOARD_DEBUG_UART_PORT, true);
    
    }
    
    void uart1_init(void)
    {
     /* UART1. */
     RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART1, true);
     RCC_ResetAPB2Periphs(RCC_APB2_PERIPH_UART1);
    
     /* GPIOB. */
     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
     RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOB);
    
     uart1_gpio_init();
    
     uart1_port_init();
    }
    
    #if defined(__ARMCC_VERSION)
    int fputc(int c, FILE *f)
    {
     (void)(f);
     while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     UART_PutData(BOARD_DEBUG_UART_PORT, (uint8_t)(c));
     return c;
    }
    
    int fgetc(FILE *f)
    {
     (void)(f);
     while ( 0u == (UART_STATUS_RX_DONE & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     return UART_GetData(BOARD_DEBUG_UART_PORT);
    }
    
    #elif defined(__GNUC__)
    
    /*
     * Called by libc stdio fwrite functions
     */
    int _write(int fd, char *ptr, int len)
    {
     int i = 0;
    
     /*
      * write "len" of char from "ptr" to file id "fd"
      * Return number of char written.
      *
     * Only work for STDOUT, STDIN, and STDERR
      */
     if (fd > 2)
     {
         return -1;
     }
    
     while (*ptr && (i < len))
     {
         while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
         {}
         UART_PutData(BOARD_DEBUG_UART_PORT, (uint8_t)(*ptr));
         i++;
         ptr++;
     }
    
     return i;
    }
    
    /*
     * Called by the libc stdio fread fucntions
     *
     * Implements a buffered read with line editing.
     */
    int _read(int fd, char *ptr, int len)
    {
     int my_len;
    
     if (fd > 2)
     {
         return -1;
     }
    
     my_len = 0;
     while (len > 0)
     {
         while ( 0u == (UART_STATUS_RX_DONE & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
         {}
         *ptr = UART_GetData(BOARD_DEBUG_UART_PORT);
         len--;
         my_len++;
    
         if ( (*ptr == '\r') || (*ptr == '\n') || (*ptr == '\0') )
         {
             break;
         }
    
         ptr++;
     }
    
     return my_len; /* return the length we got */
    }
    
    
    int putchar(int c)
    {
     while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     UART_PutData(BOARD_DEBUG_UART_PORT, (uint8_t)(c));
     return c;
    }
    
    int getchar(void)
    {
     while ( 0u == (UART_STATUS_RX_DONE & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     return UART_GetData(BOARD_DEBUG_UART_PORT);
    }
    
    #elif (defined(__ICCARM__))
    /* These function __write and __read is used to support IAR toolchain to printf and scanf. */
    
    int fputc(int ch, FILE *f)
    {
     while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     UART_PutData(BOARD_DEBUG_UART_PORT, (uint8_t)(ch));
     return ch;
    }
    
    int fgetc(FILE *f)
    {
     while ( 0u == (UART_STATUS_RX_DONE & UART_GetStatus(BOARD_DEBUG_UART_PORT)) )
     {}
     return UART_GetData(BOARD_DEBUG_UART_PORT);
    }
    
    #endif
    
  3. OLED会用到SPI,这里用SPI3作为控制接口
    demo_spi.h

    #ifndef _DEMO_SPI_H_
    #define _DEMO_SPI_H_
    #include "hal_common.h"
    #include "clock_init.h"
    
    #define BOARD_LOOP_SPI_PORT          SPI3
    //#define BOARD_LOOP_SPI_BAUDRATE      1000000u /* 400khz. */
    #define BOARD_LOOP_SPI_BAUDRATE      400000u /* 400khz. */
    
    #define BOARD_LOOP_SPI_FREQ          CLOCK_APB1_FREQ
    
    void spi3_init(void);
    
    /* SPI tx. */
    void spi3_putbyte(uint8_t c);
    
    /* SPI rx. */
    uint8_t spi3_getbyte(void);
    #endif
    

    demo_spi.c

    #include "demo_spi.h"
    #include "hal_rcc.h"
    #include "hal_spi.h"
    #include "hal_gpio.h"
    
    static void spi3_gpio_init(void)
    {
     GPIO_Init_Type gpio_init;
    
     /* SPI3_NSS  - PA15. */
    /*
     gpio_init.Pins  = GPIO_PIN_15;
     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOA, &gpio_init);
     GPIO_PinAFConf(GPIOA, GPIO_PIN_15, GPIO_AF_6);
    */
    
     /* SPI3_MOSI - PC12. */
     gpio_init.Pins  = GPIO_PIN_12;
     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOC, &gpio_init);
     GPIO_PinAFConf(GPIOC, GPIO_PIN_12, GPIO_AF_6);
    
     /* SPI3_MISO - PC11. */
     gpio_init.Pins  = GPIO_PIN_11;
     gpio_init.PinMode  = GPIO_PinMode_In_Floating;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOC, &gpio_init);
     GPIO_PinAFConf(GPIOC, GPIO_PIN_11, GPIO_AF_6);
    
     /* SPI3_SCK  - PC10. */
     gpio_init.Pins  = GPIO_PIN_10;
     gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOC, &gpio_init);
     GPIO_PinAFConf(GPIOC, GPIO_PIN_10, GPIO_AF_6);
    
    }
    
    static void spi3_port_init(void)
    {
     /* Setup SPI module. */
     SPI_Master_Init_Type spi_init;
     spi_init.ClockFreqHz = BOARD_LOOP_SPI_FREQ;
     spi_init.BaudRate = BOARD_LOOP_SPI_BAUDRATE;
     spi_init.XferMode = SPI_XferMode_TxRx;
     spi_init.PolarityPhase = SPI_PolarityPhase_Alt0;
     spi_init.DataWidth = SPI_DataWidth_8b;
     spi_init.LSB = false;
     spi_init.CSMode = SPI_CSMode_NonAuto;
     SPI_InitMaster(BOARD_LOOP_SPI_PORT, &spi_init);
    
     /* Enable SPI. */
     SPI_Enable(BOARD_LOOP_SPI_PORT, true);
    
    }
    
    void spi3_init(void)
    {
     /* GPIOA. */
     /*
     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOA, true);
     RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOA);
     */
    
     /* GPIOC. */
     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOC, true);
     RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOC);
    
     /* SPI3. */
     RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_SPI3, true);
     RCC_ResetAPB1Periphs(RCC_APB1_PERIPH_SPI3);
    
     spi3_gpio_init();
     spi3_port_init();
    }
    
    /* SPI tx. */
    void spi3_putbyte(uint8_t c)
    {
     /* Polling for tx empty. */
     while ( SPI_STATUS_TX_FULL & SPI_GetStatus(BOARD_LOOP_SPI_PORT) )
     {}
     SPI_PutData(BOARD_LOOP_SPI_PORT, c);
    }
    
    /* SPI rx. */
    uint8_t spi3_getbyte(void)
    {
     /* Polling for rx done. */
     while (0u == (SPI_STATUS_RX_DONE & SPI_GetStatus(BOARD_LOOP_SPI_PORT)) )
     {}
     return SPI_GetData(BOARD_LOOP_SPI_PORT);
    }
    
    
  4. 顺便将LED4, LED5的控制单独拉出,可以作为debug的辅助手段
    led.h

    #ifndef _LED_H_
    #define _LED_H_
    
    #include "type_def.h"
    
    enum LED_NUM {
     LED4,
     LED5,
    };
    
    void led_init(void);
    
    void led_on(u8 led);
    
    void led_off(u8 led);
    #endif
    

    led.c

    #include "led.h"
    #include "hal_rcc.h"
    #include "hal_gpio.h"
    
    static void led_gpio_init(void)
    {
     GPIO_Init_Type gpio_init;
    
     /* LED4. */
     gpio_init.Pins  = GPIO_PIN_0;
     gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOI, &gpio_init);
    
     /* LED5. */
     gpio_init.Pins  = GPIO_PIN_2;
     gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
     gpio_init.Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOD, &gpio_init);
    
    }
    
    void led_init(void)
    {
     /* GPIOD. */
     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOD, true);
     RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOD);
    
     /* GPIOI. */
     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOI, true);
     RCC_ResetAHB1Periphs(RCC_AHB1_PERIPH_GPIOI);
    
     led_gpio_init();
    
    }
    
    void led_on(u8 led)
    {
     switch (led) {
     case LED4:
         GPIO_WriteBit(GPIOI, GPIO_PIN_0, 0u);
         break;
     case LED5:
         GPIO_WriteBit(GPIOD, GPIO_PIN_2, 0u);
         break;
     default:
         break;
     }
    }
    
    void led_off(u8 led)
    {
     switch (led) {
     case LED4:
         GPIO_WriteBit(GPIOI, GPIO_PIN_0, 1u);
         break;
     case LED5:
         GPIO_WriteBit(GPIOD, GPIO_PIN_2, 1u);
         break;
     default:
         break;
     }
    }
  5. 以上完成后,OLED驱动移植的前期准备工作已经完成,现在开始将STM32上的OLED控制代码移到MM32F5270上来。OLED使用的是中景园的3.12寸 256X64 单色OLED屏幕,淘宝连接https://item.taobao.com/item....
    控制引脚如图所示
    image.png

原始STM32控制代码如下所示:
stm32的oled.h

#ifndef __OLED_H
#define __OLED_H 

#include "sys.h"

#define OLED_USE_SOFT_SPI 0


#define OLED_CS_Pin GPIO_PIN_12
#define OLED_CS_GPIO_Port GPIOH
#define OLED_DC_Pin GPIO_PIN_12
#define OLED_DC_GPIO_Port GPIOB
#define OLED_RES_Pin GPIO_PIN_11
#define OLED_RES_GPIO_Port GPIOH

#if OLED_USE_SOFT_SPI
#define SOFT_SPI_MOSI_Pin GPIO_PIN_7
#define SOFT_SPI_MOSI_GPIO_Port GPIOF
#define SOFT_SPI_MISO_Pin GPIO_PIN_8
#define SOFT_SPI_MISO_GPIO_Port GPIOF
#define SOFT_SPI_SCK_Pin GPIO_PIN_9
#define SOFT_SPI_SCK_GPIO_Port GPIOF
#endif

#define USE_HORIZONTAL 1  //设置显示方向 0:正向显示;1:旋转180度显示

#ifndef PIN_RESET
#define PIN_RESET 0
#define PIN_SET 1
#endif

void OLED_WR_REG(u8 reg);
void OLED_WR_Byte(u8 dat);
void Column_Address(u8 a,u8 b);
void Row_Address(u8 a,u8 b);
void OLED_Fill(u16 xstr,u8 ystr,u16 xend,u8 yend,u8 color);
void OLED_ShowChinese(u8 x,u8 y,u8 *s,u8 sizey,u8 mode);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *dp,u8 sizey,u8 mode);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey,u8 mode);
void OLED_DrawBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode);
void OLED_DrawSingleBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode);
void OLED_Init(void);

void OLED_GPIO_Init(void);

#endif

stm32的oled.c

#include "oledfont.h"
#include "delay.h"
#include "oled.h"
#include "osal.h"
#include "spi.h"

#if OLED_USE_SOFT_SPI
static void OLED_SCL_Clr()
{
    HAL_GPIO_WritePin(SOFT_SPI_SCK_GPIO_Port, SOFT_SPI_SCK_Pin, GPIO_PIN_RESET); //SCL
}

static void OLED_SCL_Set()
{
    HAL_GPIO_WritePin(SOFT_SPI_SCK_GPIO_Port, SOFT_SPI_SCK_Pin, GPIO_PIN_SET);
}

static void OLED_SDA_Clr()
{
    HAL_GPIO_WritePin(SOFT_SPI_MOSI_GPIO_Port, SOFT_SPI_MOSI_Pin, GPIO_PIN_RESET); //SDA
}

static void OLED_SDA_Set()
{
    HAL_GPIO_WritePin(SOFT_SPI_MOSI_GPIO_Port, SOFT_SPI_MOSI_Pin, GPIO_PIN_SET);
}
#endif

static void OLED_RES_Clr()
{
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_RESET); //RES
}

static void OLED_RES_Set()
{
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_SET);
}

static void OLED_DC_Clr()
{
    HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, GPIO_PIN_RESET); //DC
}

static void OLED_DC_Set()
{
    HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, GPIO_PIN_SET);
}

static void OLED_CS_Clr()
{
    HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_RESET); //CS
}

static void OLED_CS_Set()
{
    HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_SET);
}

static void OLED_Write_Byte(u8 dat)
{
#if OLED_USE_SOFT_SPI
    u8 i;
    for(i=0;i<8;i++)
    {              
        OLED_SCL_Clr();
        //delay_us(200);
        if(dat&0x80)
        {
           OLED_SDA_Set();
        }
        else 
        {
           OLED_SDA_Clr();
        }
        //delay_us(20);
        OLED_SCL_Set();
        //delay_us(200);
        dat<<=1;   
    }
#else
    SPI_Write_Byte(2, dat);             
#endif
}

void OLED_WR_Bus(u8 dat)
{
    OLED_CS_Clr();
    OLED_Write_Byte(dat);          
    OLED_CS_Set();
}

void OLED_WR_REG(u8 reg)
{      
    OLED_DC_Clr();          
  OLED_WR_Bus(reg);
  OLED_DC_Set();    
}

void OLED_WR_Byte(u8 dat)
{      
  OLED_WR_Bus(dat);
}

void Column_Address(u8 a,u8 b)
{
    OLED_WR_REG(0x15);       // Set Column Address
    OLED_WR_Byte(0x1c+a);
    OLED_WR_Byte(0x1c+b);
}

void Row_Address(u8 a,u8 b)
{
    OLED_WR_REG(0x75);       // Row Column Address
    OLED_WR_Byte(a);
    OLED_WR_Byte(b);
    OLED_WR_REG(0x5C);    //写RAM命令
}

void OLED_Fill(u16 xstr,u8 ystr,u16 xend,u8 yend,u8 color)
{
    u8 x,y;
    xstr/=4;
    xend/=4;
    Column_Address(xstr,xend-1);
    Row_Address(ystr,yend-1);
    for(x=xstr;x<xend;x++)
    {
        for(y=ystr;y<yend;y++)
        {
            OLED_WR_Byte(color);
            OLED_WR_Byte(color);
    }
  }
}

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey,u8 mode)
{
    u8 c,i,k,m,t=4,size2,data1,DATA=0;
    size2=(sizey/16+((sizey%16)?1:0))*sizey;
    c=chr-' ';
    Column_Address(x/4,x/4+sizey/8-1);
    Row_Address(y,y+sizey-1);
    for(i=0;i<size2;i++)
    {
        if(sizey==16)
        {
            data1=ascii_1608[c][i];//8x16 ASCII码
        }
        else if(sizey==24)
        {
            data1=ascii_2412[c][i];//12x24 ASCII码
        }
        else if(sizey==32)
        {
            data1=ascii_3216[c][i];//16x32 ASCII码
        }

        if(sizey%16)
        {
            m=sizey/16+1;
            if(i%m) t=2;
            else t=4;
        }
        for(k=0;k<t;k++)
        {
            if(data1&(0x01<<(k*2+0)))
            {
                DATA=0xf0;
            }
            if(data1&(0x01<<(k*2+1)))
            {
                DATA|=0x0f;
            }
            if(mode)
            {
                OLED_WR_Byte(~DATA);
            }else
            {
                OLED_WR_Byte(DATA);
            }
            DATA=0;
        }
  }
}

void OLED_ShowString(u8 x,u8 y,u8 *dp,u8 sizey,u8 mode)
{
    while(*dp!='\0')
    {
      OLED_ShowChar(x,y,*dp,sizey,mode);
        dp++;
        x+=sizey/2;
    }
}

u32 oled_pow(u8 m,u8 n)
{
    u32 result=1;
    while(n--)result*=m;    
    return result;
}

void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey,u8 mode)
{             
    u8 t,temp;
    u8 enshow=0;
    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)*t,y,' ',sizey,mode);
                continue;
            }else enshow=1; 
              
        }
         OLED_ShowChar(x+(sizey/2)*t,y,temp+'0',sizey,mode); 
    }
}

void OLED_DrawBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode)
{
    u16 i,num;
    length=(length/4+((length%4)?1:0))*4;
    num=length/2*width;
    x/=4;
    length/=4;
    Column_Address(x,x+length-1);
    Row_Address(y,y+width-1);
    for(i=0;i<num;i++)
    {
        if(mode)
        {
            OLED_WR_Byte(~BMP[i]);
        }else
        {
            OLED_WR_Byte(BMP[i]);
        }
    }
}

void OLED_DrawSingleBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode)
{
    u8 k,DATA=0;
    u16 i,num;
    length=(length/8+((length%8)?1:0))*8;
    num=length*width/8;
    x/=4;
    length/=4;
    Column_Address(x,x+length-1);
    Row_Address(y,y+width-1);
    for(i=0;i<num;i++)
    {
        for(k=0;k<4;k++)
        {
            if(BMP[i]&(0x01<<(k*2+0)))
            {
                DATA=0xf0;
            }
            if(BMP[i]&(0x01<<(k*2+1)))
            {
                DATA|=0x0f;
            }
            if(mode)
            {
                OLED_WR_Byte(~DATA);
            }else
            {
                OLED_WR_Byte(DATA);
            }
            DATA=0;
        }
    }
}

void OLED_Init(void)
{    
    OLED_RES_Clr();    
    delay_ms(10);    
    OLED_RES_Set();
    OLED_WR_REG(0xfd);  /*Command Lock*/ 
    OLED_WR_Byte(0x12);

    OLED_WR_REG(0xae); //Sleep In 

    
    OLED_WR_REG(0xb3);    //Set Display Clock Divide Ratio/Oscillator Frequency 
    OLED_WR_Byte(0x91);

    OLED_WR_REG(0xca);    //Set Multiplex Ratio 
    OLED_WR_Byte(0x3f);

    OLED_WR_REG(0xa2);    //Set Display Offset 
    OLED_WR_Byte(0x00);    //
    
    OLED_WR_REG(0xa1);    //Set Display Start Line 
    OLED_WR_Byte(0x00);    //
    
    OLED_WR_REG(0xa0);    //Set Re-Map $ Dual COM Line Mode
    if(USE_HORIZONTAL)
    {
      OLED_WR_Byte(0x14);
    }
    else
    {
      OLED_WR_Byte(0x06);
    }
    
    OLED_WR_REG(0xB5);    //Set GPIO 
    OLED_WR_Byte(0x00);
        
    OLED_WR_REG(0xab);    //Function Selection
    OLED_WR_Byte(0x01);    //
    
    OLED_WR_REG(0xb4);    //Enable External VSL 
    OLED_WR_Byte(0xa0);    //
    OLED_WR_Byte(0xfd);    //
    
    OLED_WR_REG(0xc1);    //Set Contrast Current
    OLED_WR_Byte(0xff);    
    
    OLED_WR_REG(0xc7);    //Master Contrast Current Control
    OLED_WR_Byte(0x0f);    //

    OLED_WR_REG(0xb9);    //Select Default Linear Gray Scale Table 
    
    OLED_WR_REG(0xb1);    //Set Phase Length
    OLED_WR_Byte(0xe2);    
    
    OLED_WR_REG(0xd1);    //Enhance Driving Scheme Capability 
    OLED_WR_Byte(0x82);    
    OLED_WR_Byte(0x20);    

    OLED_WR_REG(0xbb);    //Set Pre-Charge Voltage 
    OLED_WR_Byte(0x1f);    

    OLED_WR_REG(0xb6);    //Set Second Pre-Charge Period 
    OLED_WR_Byte(0x08);    

    OLED_WR_REG(0xbe);    //Set VCOMH Deselect Level 
    OLED_WR_Byte(0x07);        

    OLED_WR_REG(0xa6);    //Set Display Mode
    
    OLED_Fill(0,0,256,64,0x00);   //Clear Screen

    OLED_WR_REG(0xaf);    //Sleep Out
    LOG_INFO("oled init done");
}


void OLED_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
     /* GPIO Ports Clock Enable */
     __HAL_RCC_GPIOB_CLK_ENABLE();
     __HAL_RCC_GPIOH_CLK_ENABLE();

      /*Configure GPIO pin Output Level */
      HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_RESET);

    /*Configure GPIO pin Output Level */
#if OLED_USE_SOFT_SPI
      HAL_GPIO_WritePin(GPIOF, SOFT_SPI_MOSI_Pin|SOFT_SPI_SCK_Pin, GPIO_PIN_RESET);
#endif

    
    /*Configure GPIO pins : OLED_CS_Pin OLED_DC_Pin OLED_RES_Pin */
    GPIO_InitStruct.Pin = OLED_CS_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_CS_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = OLED_DC_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_DC_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = OLED_RES_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(OLED_RES_GPIO_Port, &GPIO_InitStruct);
#if OLED_USE_SOFT_SPI
    /*Configure GPIO pins : OLED_CS_Pin OLED_DC_Pin OLED_RES_Pin */
    GPIO_InitStruct.Pin = SOFT_SPI_MOSI_Pin | SOFT_SPI_SCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SOFT_SPI_MISO_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
#endif
}

分析OLED的LOG,发现只需要更改RES, DC, CS的GPIO控制即可。将OLED的GPIO控制由STM32 HAL库控制更改为MM32F5270 HAL控制,修改代码如下:
MM32F5270的oled.h

#ifndef __OLED_H
#define __OLED_H 

#include "type_def.h"

#define USE_HORIZONTAL 1  //设置显示方向 0:正向显示;1:旋转180度显示

#define OLED_USE_SOFT_SPI 0


void OLED_control_gpio_init(void);

void OLED_WR_REG(u8 reg);
void OLED_WR_Byte(u8 dat);
void Column_Address(u8 a,u8 b);
void Row_Address(u8 a,u8 b);
void OLED_Fill(u16 xstr,u8 ystr,u16 xend,u8 yend,u8 color);
void OLED_ShowChinese(u8 x,u8 y,u8 *s,u8 sizey,u8 mode);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *dp,u8 sizey,u8 mode);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey,u8 mode);
void OLED_DrawBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode);
void OLED_DrawSingleBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode);
void OLED_Init(void);

#endif

MM32F5270的oled.c

#include "oledfont.h"
#include "delay.h"
#include "oled.h"
#include "demo_spi.h"
#include "hal_rcc.h"
#include "hal_gpio.h"
#include "stdio.h"

~~*void OLEDcontrolgpioinit(void)
{
    //GPIOA
    RCCEnableAHB1Periphs(RCCAHB1PERIPHGPIOA, true);
    RCCResetAHB1Periphs(RCCAHB1PERIPHGPIOA);
#if OLEDUSESOFTSPI
    RCCEnableAHB1Periphs(RCCAHB1PERIPHGPIOC, true);
    RCCResetAHB1Periphs(RCCAHB1PERIPHGPIOC);
#endif

    GPIOInitType gpioinit;

    //RES PA10
    gpioinit.Pins  = GPIOPIN10;
    gpioinit.PinMode  = GPIOPinModeOutPushPull;
    gpioinit.Speed = GPIOSpeed50MHz;
    GPIOInit(GPIOA, &gpioinit);

    //DC PA12
    gpioinit.Pins  = GPIOPIN12;
    gpioinit.PinMode  = GPIOPinModeOutPushPull;
    gpioinit.Speed = GPIOSpeed50MHz;
    GPIOInit(GPIOA, &gpioinit);

    //CS PA15
    gpioinit.Pins  = GPIOPIN15;
    gpioinit.PinMode  = GPIOPinModeOutPushPull;
    gpioinit.Speed = GPIOSpeed50MHz;
    GPIOInit(GPIOA, &gpioinit);

#if OLEDUSESOFTSPI
    //soft SCL PC10
    gpioinit.Pins  = GPIOPIN10;
    gpioinit.PinMode  = GPIOPinModeOutPushPull;
    gpioinit.Speed = GPIOSpeed50MHz;
    GPIOInit(GPIOC, &gpioinit);

    //soft MOSI PC12
    gpioinit.Pins  = GPIOPIN12;
    gpioinit.PinMode  = GPIOPinModeOutPushPull;
    gpioinit.Speed = GPIOSpeed50MHz;
    GPIOInit(GPIOC, &gpioinit);
#endif

}

#if OLEDUSESOFTSPI
static void OLEDSOFTSPISCKClr()
{
    GPIOWriteBit(GPIOC, GPIOPIN10, 0u); //RES
}

static void OLEDSOFTSPISCKSet()
{
    GPIOWriteBit(GPIOC, GPIOPIN10, 1u); //RES
}

static void OLEDSOFTSPISDAClr()
{
    GPIOWriteBit(GPIOC, GPIOPIN12, 0u); //RES
}


static void OLEDSOFTSPISDASet()
{
    GPIOWriteBit(GPIOC, GPIOPIN12, 1u); //RES
}

#endif
static void OLEDRESClr()
{
    GPIOWriteBit(GPIOA, GPIOPIN10, 0u); //RES
}

static void OLEDRESSet()
{
    GPIOWriteBit(GPIOA, GPIOPIN10, 1u); //RES
}

static void OLEDDCClr()
{
    GPIOWriteBit(GPIOA, GPIOPIN12, 0u); //RES
}

static void OLEDDCSet()
{
    GPIOWriteBit(GPIOA, GPIOPIN12, 1u); //RES
}

static void OLEDCSClr()
{
    GPIOWriteBit(GPIOA, GPIOPIN15, 0u); //RES
}*~~

static void OLED_CS_Set()
{
    GPIO_WriteBit(GPIOA, GPIO_PIN_15, 1u); //RES
}

static void OLED_Write_Byte(u8 dat)
{
#if OLED_USE_SOFT_SPI
    u8 i;
    for(i=0;i<8;i++)
    {              
        OLED_SOFT_SPI_SCK_Clr();
        //delay_us(20);
        if(dat&0x80)
        {
           OLED_SOFT_SPI_SDA_Set();
        }
        else 
        {
           OLED_SOFT_SPI_SDA_Clr();
        }
        //delay_us(20);
        OLED_SOFT_SPI_SCK_Set();
        //delay_us(200);
        dat<<=1;   
    }
#else
    spi3_putbyte(dat);
#endif
}

void OLED_WR_Bus(u8 dat)
{
    OLED_CS_Clr();
    OLED_Write_Byte(dat);
    OLED_CS_Set();
}

void OLED_WR_REG(u8 reg)
{      
    OLED_DC_Clr();          
    OLED_WR_Bus(reg);
    OLED_DC_Set();    
}

void OLED_WR_Byte(u8 dat)
{      
  OLED_WR_Bus(dat);
}

void Column_Address(u8 a,u8 b)
{
    OLED_WR_REG(0x15);       // Set Column Address
    OLED_WR_Byte(0x1c+a);
    OLED_WR_Byte(0x1c+b);
}

void Row_Address(u8 a,u8 b)
{
    OLED_WR_REG(0x75);       // Row Column Address
    OLED_WR_Byte(a);
    OLED_WR_Byte(b);
    OLED_WR_REG(0x5C);    //写RAM命令
}

void OLED_Fill(u16 xstr,u8 ystr,u16 xend,u8 yend,u8 color)
{
    u8 x,y;
    xstr/=4;
    xend/=4;
    Column_Address(xstr,xend-1);
    Row_Address(ystr,yend-1);
    for(x=xstr;x<xend;x++)
    {
        for(y=ystr;y<yend;y++)
        {
            OLED_WR_Byte(color);
            OLED_WR_Byte(color);
    }
  }
}

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey,u8 mode)
{
    u8 c,i,k,m,t=4,size2,data1,DATA=0;
    size2=(sizey/16+((sizey%16)?1:0))*sizey;
    c=chr-' ';
    Column_Address(x/4,x/4+sizey/8-1);
    Row_Address(y,y+sizey-1);
    for(i=0;i<size2;i++)
    {
        if(sizey==16)
        {
            data1=ascii_1608[c][i];//8x16 ASCII码
        }
        else if(sizey==24)
        {
            data1=ascii_2412[c][i];//12x24 ASCII码
        }
        else if(sizey==32)
        {
            data1=ascii_3216[c][i];//16x32 ASCII码
        }
        if(sizey%16)
        {
            m=sizey/16+1;
            if(i%m) t=2;
            else t=4;
        }
        for(k=0;k<t;k++)
        {
            if(data1&(0x01<<(k*2+0)))
            {
                DATA=0xf0;
            }
            if(data1&(0x01<<(k*2+1)))
            {
                DATA|=0x0f;
            }
            if(mode)
            {
                OLED_WR_Byte(~DATA);
            }else
            {
                OLED_WR_Byte(DATA);
            }
            DATA=0;
        }
  }
}

void OLED_ShowString(u8 x,u8 y,u8 *dp,u8 sizey,u8 mode)
{
    while(*dp!='\0')
    {
      OLED_ShowChar(x,y,*dp,sizey,mode);
        dp++;
        x+=sizey/2;
    }
}

u32 oled_pow(u8 m,u8 n)
{
    u32 result=1;
    while(n--)result*=m;    
    return result;
}

void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey,u8 mode)
{             
    u8 t,temp;
    u8 enshow=0;
    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)*t,y,' ',sizey,mode);
                continue;
            }else enshow=1; 
              
        }
         OLED_ShowChar(x+(sizey/2)*t,y,temp+'0',sizey,mode); 
    }
}

void OLED_DrawBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode)
{
    u16 i,num;
    length=(length/4+((length%4)?1:0))*4;
    num=length/2*width;
    x/=4;
    length/=4;
    Column_Address(x,x+length-1);
    Row_Address(y,y+width-1);
    for(i=0;i<num;i++)
    {
        if(mode)
        {
            OLED_WR_Byte(~BMP[i]);
        }else
        {
            OLED_WR_Byte(BMP[i]);
        }
    }
}
void OLED_DrawSingleBMP(u8 x,u8 y,u16 length,u8 width,const u8 BMP[],u8 mode)
{
    u8 k,DATA=0;
    u16 i,num;
    length=(length/8+((length%8)?1:0))*8;
    num=length*width/8;
    x/=4;
    length/=4;
    Column_Address(x,x+length-1);
    Row_Address(y,y+width-1);
    for(i=0;i<num;i++)
    {
        for(k=0;k<4;k++)
        {
            if(BMP[i]&(0x01<<(k*2+0)))
            {
                DATA=0xf0;
            }
            if(BMP[i]&(0x01<<(k*2+1)))
            {
                DATA|=0x0f;
            }
            if(mode)
            {
                OLED_WR_Byte(~DATA);
            }else
            {
                OLED_WR_Byte(DATA);
            }
            DATA=0;
        }
    }
}

void OLED_Init(void)
{    
    OLED_RES_Clr();    
    delay_ms(10);    
    OLED_RES_Set();
    OLED_WR_REG(0xfd);  /*Command Lock*/ 
    OLED_WR_Byte(0x12);

    OLED_WR_REG(0xae); //Sleep In 

    
    OLED_WR_REG(0xb3);    //Set Display Clock Divide Ratio/Oscillator Frequency 
    OLED_WR_Byte(0x91);

    OLED_WR_REG(0xca);    //Set Multiplex Ratio 
    OLED_WR_Byte(0x3f);

    OLED_WR_REG(0xa2);    //Set Display Offset 
    OLED_WR_Byte(0x00);    //
    
    OLED_WR_REG(0xa1);    //Set Display Start Line 
    OLED_WR_Byte(0x00);    //
    
    OLED_WR_REG(0xa0);    //Set Re-Map $ Dual COM Line Mode
    if(USE_HORIZONTAL)
    {
      OLED_WR_Byte(0x14);
    }
    else
    {
      OLED_WR_Byte(0x06);
    }
    
    OLED_WR_REG(0xB5);    //Set GPIO 
    OLED_WR_Byte(0x00);
        
    OLED_WR_REG(0xab);    //Function Selection
    OLED_WR_Byte(0x01);    //
    
    OLED_WR_REG(0xb4);    //Enable External VSL 
    OLED_WR_Byte(0xa0);    //
    OLED_WR_Byte(0xfd);    //
    
    OLED_WR_REG(0xc1);    //Set Contrast Current
    OLED_WR_Byte(0xff);    
    
    OLED_WR_REG(0xc7);    //Master Contrast Current Control
    OLED_WR_Byte(0x0f);    //

    OLED_WR_REG(0xb9);    //Select Default Linear Gray Scale Table 
    
    OLED_WR_REG(0xb1);    //Set Phase Length
    OLED_WR_Byte(0xe2);    
    
    OLED_WR_REG(0xd1);    //Enhance Driving Scheme Capability 
    OLED_WR_Byte(0x82);    
    OLED_WR_Byte(0x20);    

    OLED_WR_REG(0xbb);    //Set Pre-Charge Voltage 
    OLED_WR_Byte(0x1f);    

    OLED_WR_REG(0xb6);    //Set Second Pre-Charge Period 
    OLED_WR_Byte(0x08);    

    OLED_WR_REG(0xbe);    //Set VCOMH Deselect Level 
    OLED_WR_Byte(0x07);        

    OLED_WR_REG(0xa6);    //Set Display Mode
    
    OLED_Fill(0,0,256,64,0x00);   //Clear Screen

    OLED_WR_REG(0xaf);    //Sleep Out
    printf("oled init done\n");
}

对应只需要改动几个控制GPIO的具体实现,可以非常快速的进行移植

  1. 最后放上main函数,在main函数里要对用到的各个硬件做初始化,代码如下:

    /*
     * Copyright 2021 MindMotion Microelectronics Co., Ltd.
     * All rights reserved.
     *
     * SPDX-License-Identifier: BSD-3-Clause
     */
    
    #include <stdint.h>
    #include <stdio.h>
    #include "clock_init.h"
    #include "demo_uart.h"
    #include "conv_timer.h"
    #include "demo_spi.h"
    #include "led.h"
    #include "delay.h"
    #include "oled.h"
    /*
     * Functions.
     */
    
    int main(void)
    {
     BOARD_InitBootClocks();
     timer1_init();
     uart1_init();
    #if !OLED_USE_SOFT_SPI
     spi3_init();
    #endif
     led_init();
    
     OLED_control_gpio_init();
     OLED_Init();
     //OLED_ShowString(0, 0, "hello world", 16, 0);
     printf("\r\ntim_basic example.\r\n");
    
     timer1_start();
     led_off(LED4);
     led_off(LED5);
    
    
     while (1)
     {
         //spi3_putbyte(0xaa);
         led_on(LED4);
         delay_1ms();
         led_off(LED4);
         delay_1ms();
         OLED_ShowString(0, 0, "     ARM STAR-MC1 MM32F5270", 16, 0);
         OLED_ShowString(0, 16, "            aijishu", 16, 0);
         OLED_ShowString(0, 32, "      https://aijishu.com", 16, 0);
    
     }
    }
    
    /* EOF. */
    

显示效果如图所示:
OLED.jpg

四 总结

MindSDK已经提供了封装良好的驱动代码,能够非常方便的做开发,对于STM32平台的应用,能够非常快速的进行移植,所需要的工作量非常小。
STM32CubeMX只是提供了一个可视化的配置界面,但是其生成的代码对于一些比较大型的项目来说,其组织形式和代码复用性相比MindSDK并不会有优势。

MindSDK目前提供的轮子,已经能够应付一些基本需求。但是目前,MindSDK缺乏对FreeRTOS,RTThread等RTOS的支持。如果官方能够解决RTOS的问题,相信我们能够基于MindSDK用MM32F5230作出更多有趣的应用。

五 参考链接

【灵动官网】PLUS-F5270开发板介绍(含资料链接): https://www.mindmotion.com.cn...
【灵动官网】MM32F5270芯片介绍(含资料链接): https://www.mindmotion.com.cn...
【极术社区】基于灵动MM32F5系列芯片的PLUS-F5270开发板资料(包含逐飞科技网盘链接): https://aijishu.com/a/1060000...
【极术社区】[MM32F5270开发板试用] 基本开发环境搭建篇: https://aijishu.com/a/1060000...
【极术社区】带灵动微MM32F5的Plus-F5270开发板怎么玩? https://aijishu.com/a/1060000...

推荐阅读
关注数
6151
内容数
276
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息