前言

感芯科技推出了一款64线程的MCU,此前在感芯科技的公众号申请了一块免费的开发板,进行了简单的评测使用,整体上使用体验良好。首先这款开发板的特性是64线程,高实时和高确定性,其线程频率由内部时钟静态片分,换言之就是线程逻辑上独立,适用于高实时的应用场景——它就是 MC3172

基础信息

这块开发板目前已经调试过PWM呼吸灯,UART,SPI,OLED亮屏等功能,整体上手难度一般,开发板图例如下,更多相关信息请前往文章底部进入开发板官网链接进行查看:

MC3172外观

点亮OLED

本次例程针对SH1106,这块OLED采用SPI协议,屏幕大小为1.3英寸,分辨率为128 * 64。

话不多说,直接上代码:

/*  LED 128*64 7 PINs SPI Protocal Driver
 *   @Date 2022-07-16
 *   @Author Winter
 *   @License Apache Licence 2.0
 */

#include "MC3172.h"

#define OLED_COLUMN_NUMBER 128 // Screen resolution width
#define OLED_ROW_NUMBER 64     // Screen resolution height
#define OLED_COLUMN_OFFSET 2
#define OLED_PAGE_NUMBER OLED_ROW_NUMBER / 8 // how many pages to divide ROW

#define OLED_POS_PORT GPIOC_BASE_ADDR
#define OLED_GPCOM_PORT GPCOM9_BASE_ADDR
#define OLED_GPCOM_TX_RP GPCOM9_TX_RP
#define OLED_GPCOM_TX_WP GPCOM9_TX_WP

static u8 video_buffer[1024]; // Video Memory

// RST(RES): Reset Signal Input, High for normal operation
// Position: PORTC[2]
#define SET_SPI_RST_0 GPIO_SET_OUTPUT_PIN_TO_0(OLED_POS_PORT, (GPIO_PIN2))
#define SET_SPI_RST_1 GPIO_SET_OUTPUT_PIN_TO_1(OLED_POS_PORT, (GPIO_PIN2))

// DC: Data/Command Control
// Position: PORTC[5]
#define SET_SPI_DC_0 GPIO_SET_OUTPUT_PIN_TO_0(OLED_POS_PORT, (GPIO_PIN5))
#define SET_SPI_DC_1 GPIO_SET_OUTPUT_PIN_TO_1(OLED_POS_PORT, (GPIO_PIN5))

// CS: Chip Select
// Position: PORTC[4]
#define SET_SPI_CS_0 GPIO_SET_OUTPUT_PIN_TO_0(OLED_POS_PORT, (GPIO_PIN4))
#define SET_SPI_CS_1 GPIO_SET_OUTPUT_PIN_TO_1(OLED_POS_PORT, (GPIO_PIN4))

// SH1106 commands for SPI
const unsigned char _init_cmd[25] = {
    0xAE, //Set display off

    0xD5, //Set frequency division
    0x80, //[3:0],Set display divide ratio;[7:4],(OSC drequency)

    0xA8, //Set multiplex ratio.
    0X3F, //default is 1/64

    0xD3, //Set display offset
    0X00, //default is 0

    0x40, //set display start line [5:0]

    0x8D, //Set charge pump
    0x14, 

    0x20, //Set address mode
    0x02, //[1:0],00,column first;01,row first;10,page first;default is 10;

    0xA1, //Set segment Re-map,bit0:0,0->0;1,0->127;

    0xC8, //Set COM output scan direction,bit3:0,normal mode;1,redefine mode COM[N-1]->COM0;N:driver channeles (C0 display inverse) C8

    0xDA, //Set COM I/O
    0x12, //[5:4]

    0x81, // Set contrast
    0x66, // Set brightness, 1~255;default is 0X7F

    0xD9, //Set pre-charge period
    0xf1, //[3:0],PHASE 1;[7:4],PHASE 2;

    0xDB, //Set VCOMH deselect level
    0x30, //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

    0xA4, //Set entire display on/off ;bit0:1,on;0,off;

    0xA6, //Set display mode;bit0:1,Normal;0,Inverse

    0xAF, //Set display on
};

void _oled_init_io(void)
{
    // CS:PORTC[4] DC:PORTC[5] RES:PORTC[2] SDA:PORTC[6] SCK:PORTC[7]

    INTDEV_SET_CLK_RST(OLED_POS_PORT, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV32)); // 133/32

    GPIO_SET_OUTPUT_EN_VALUE(OLED_POS_PORT, (GPIO_PIN2 | GPIO_PIN3 | GPIO_PIN4 | GPIO_PIN5), GPIO_SET_ENABLE);

    INTDEV_SET_CLK_RST(OLED_GPCOM_PORT, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV32));

    GPCOM_SET_OUT_PORT(OLED_GPCOM_PORT, (GPCOM_P0_OUTPUT_DISABLE | GPCOM_P1_OUTPUT_DISABLE | GPCOM_P2_OUTPUT_ENABLE | GPCOM_P2_IS_MASTER_OUT | GPCOM_P3_OUTPUT_ENABLE | GPCOM_P3_IS_MASTER_CLK));

    GPCOM_SET_COM_MODE(OLED_GPCOM_PORT, (GPCOM_SPI_MASTER_MODE0 | GPCOM_TX_MSB_FIRST | GPCOM_RX_MSB_FIRST));

    GPCOM_SET_COM_SPEED(OLED_GPCOM_PORT, 4156250, 1000000);

    GPCOM_SET_OVERRIDE_GPIO(OLED_GPCOM_PORT, (GPCOM_P2_OVERRIDE_GPIO | GPCOM_P3_OVERRIDE_GPIO));
}

void _oled_send_byte(unsigned char byte)
{
    u8 tx_data_wp = 0;
    tx_data_wp = GPCOM_GET_TX_WP(OLED_GPCOM_PORT);

    GPCOM_SEND_TX_DATA(OLED_GPCOM_PORT, tx_data_wp + 0, byte); // Write data to cache 0 buffer.
    tx_data_wp += 1;

    GPCOM_SEND_TX_WP(OLED_GPCOM_PORT, tx_data_wp);

    while (OLED_GPCOM_TX_RP != OLED_GPCOM_TX_WP)
    { // waiting for transmission
    };

} // SPI_SendByte

void _oled_send_cmd(unsigned char o_command)
{
    SET_SPI_DC_0;
    SET_SPI_CS_0;
    _oled_send_byte(o_command);
    SET_SPI_CS_1;
    // SPI_DC_1;
}
void _oled_send_data(unsigned char o_data)
{
    SET_SPI_DC_1;
    SET_SPI_CS_0;
    _oled_send_byte(o_data);
    SET_SPI_CS_1;
}

void _oled_set_column(unsigned char column)
{
    column += OLED_COLUMN_OFFSET;
    _oled_send_cmd(0x10 | (column >> 4));   //Setting the column higher bits.
    _oled_send_cmd(0x00 | (column & 0x0F)); //Setting the column lower bits.
}

void _oled_set_page(unsigned char page)
{
    _oled_send_cmd(0xb0 + page);
}

void _oled_clear(void)
{
    unsigned char page, column;
    for (page = 0; page < OLED_PAGE_NUMBER; page++) // page loop
    {
        _oled_set_page(page);
        _oled_set_column(0);
        for (column = 0; column < OLED_COLUMN_NUMBER; column++) // column loop
        {
            _oled_send_data(0x00);
        }
    }
}
void _oled_fullfilled(void)
{
    unsigned char page, column;
    for (page = 0; page < OLED_PAGE_NUMBER; page++) // page loop
    {
        _oled_set_page(page);
        _oled_set_column(0);
        for (column = 0; column < OLED_COLUMN_NUMBER; column++) // column loop
        {
            _oled_send_data(0xff);
        }
    }
}

void _oled_init_cmds(void)
{
    unsigned char i;
    for (i = 0; i < 25; i++)
    {
        _oled_send_cmd(_init_cmd[i]);
    }
}

void _frame_refresh(const unsigned char *ptr_pic)
{
    unsigned char page, column;
    for (page = 0; page < (OLED_ROW_NUMBER / 8); page++) // page loop
    {
        _oled_set_page(page);
        _oled_set_column(0);
        for (column = 0; column < OLED_COLUMN_NUMBER; column++) // column loop
        {
            _oled_send_data(*ptr_pic++);
        }
    }
}

void _frame_refresh_inverse(const unsigned char *ptr_pic)
{
    unsigned char page, column, data;
    for (page = 0; page < (OLED_ROW_NUMBER / 8); page++) // page loop
    {
        _oled_set_page(page);
        _oled_set_column(0);
        for (column = 0; column < OLED_COLUMN_NUMBER; column++) // column loop
        {
            data = *ptr_pic++;
            data = ~data;
            _oled_send_data(data);
        }
    }
}

int oled_init(void)
{
    _oled_init_io();
    SET_SPI_RST_0;
    delay_ms(500);
    SET_SPI_RST_1;
    delay_ms(500);
    _oled_init_cmds();
    _oled_fullfilled();
    delay_ms(500);
    _oled_clear();
}

void oled_refresh()
{
    _frame_refresh(&video_buffer[0]);
}

// Inverse the bit value for each pixel.
void oled_refresh_inverse()
{
    _frame_refresh_inverse(&video_buffer[0]);
}

在以上的代码中video_buffer为屏幕展示的内容,在此处提供一个内容帧用于验证参考:

//replace vide_buffer with below:
static u8 video_buffer[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,8,56,248,96,0,0,0,0,0,0,0,0,0,0,128,128,192,64,64,64,96,32,32,32,32,96,64,64,64,192,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,64,32,48,16,8,12,4,4,6,6,2,2,2,2,2,2,2,4,4,4,4,4,12,8,12,15,11,8,8,8,8,8,12,4,2,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,2,6,4,12,16,48,96,192,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,14,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,224,240,224,0,0,0,0,0,0,0,0,0,128,96,32,0,0,0,0,0,0,192,128,224,76,246,112,244,200,200,240,240,112,144,32,0,0,0,0,0,1,6,56,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,224,32,32,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,15,15,0,0,0,0,0,0,224,28,3,0,0,0,0,192,224,48,250,63,239,247,187,217,239,255,123,28,7,11,1,2,0,0,0,0,0,0,0,0,0,224,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,255,176,240,255,240,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,96,30,3,0,0,0,0,0,1,6,8,24,48,32,33,33,99,67,67,67,67,131,131,129,128,128,128,128,64,64,64,64,96,32,32,32,48,24,12,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,7,6,3,6,12,8,8,24,16,48,32,32,32,96,64,64,64,64,64,64,192,128,192,192,96,96,80,88,68,70,65,65,64,64,64,64,64,192,128,0,0,0,0,0,0,192,32,24,4,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,224,240,172,131,160,192,128,192,192,254,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,48,24,8,12,14,1,0,0,0,0,0,0,0,0,7,62,112,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

在代码中嵌入该帧内容,运行后你将会获得一个外星人版本的史努比!!!

史努比

扩展

笔者在以上基础上进行了扩展,实现了随画随显功能,效果如下图,有兴趣使用该开发板的网友可以尝试更多有意思的创意和体验。

请输入图片描述

结语

领取开发板至今大概有一个月时间,笔者在工作之余去体验了该开发板,最大的体验是多线程的无感调度(硬件实现),使用该特性可以实现更多需要高独立性高实时性的应用,一起期待更多有意思的创意吧!

更多内容相关

官方代码仓库:gxchip (gitee.com),官网:感芯科技

最后修改:2023 年 08 月 02 日
如果觉得我的文章对你有用,请随意赞赏