我们将使用MAX78000的I2C来驱动SSD1306,根据原理图得知I2C1已经连接了一个传感器,好在它还有额外的引出PIN,现在使用杜邦线将SSD1306连接到对应的PIN上。
然后打开Example下的I2C_Scan的代码,可以根据代码中发现(或者原理图)其中定义的I2CPin 为16和17,如果烧录上述Demo的话,其扫描到的I2C地址为0x3C
然后我们可以从网上查找现成的SSD1306驱动并且将其I2C的实现修改为MAX78000实现即可。如下所示。
view plaincopy to clipboardprint?#ifndef SSD1306_H
#define SSD1306_H
#include <stdint.h>
#define SSD1306_ADDR 0x3C
#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
#define SSD1306_PAGE_NUM (SSD1306_HEIGHT / 8)
#define SSD1306_COMMAND 0x00
#define SSD1306_DATA 0x40
#ifdef __cplusplus
extern "C"
{
#endif
// 初始化 SSD1306
void SSD1306_Init(void);
// 清屏
void SSD1306_Clear(void);
// 显示测试图案
void SSD1306_DisplayDemo(void);
// 显示缓冲区数据
void SSD1306_DisplayFrame(uint8_t *buffer);
// 打印字符串(支持 6x8 ASCII)
void SSD1306_Print(uint8_t x, uint8_t page, const char *str);
// 全屏缓冲区
extern uint8_t SSD1306_Buffer[SSD1306_PAGE_NUM * SSD1306_WIDTH];
// 打印整数
void SSD1306_PrintInt(uint8_t x, uint8_t page, int32_t num);
// 打印浮点数
void SSD1306_PrintFloat(uint8_t x, uint8_t page, float num, uint8_t decimals);
#ifdef __cplusplus
}
#endif
#endif // SSD1306_H
然后便是对应的C文件
view plaincopy to clipboardprint?
#include "ssd1306.h"
#include "i2c.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include <string.h>
#include <stdio.h>
// 6x8 ASCII 字体表
static const uint8_t Font6x8[95][6] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' '
{0x00, 0x00, 0x5F, 0x00, 0x00, 0x00}, // '!'
{0x00, 0x07, 0x00, 0x07, 0x00, 0x00}, // '"'
{0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00}, // '#'
{0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00}, // '$'
{0x23, 0x13, 0x08, 0x64, 0x62, 0x00}, // '%'
{0x36, 0x49, 0x55, 0x22, 0x50, 0x00}, // '&'
{0x00, 0x05, 0x03, 0x00, 0x00, 0x00}, // '''
{0x00, 0x1C, 0x22, 0x41, 0x00, 0x00}, // '('
{0x00, 0x41, 0x22, 0x1C, 0x00, 0x00}, // ')'
{0x14, 0x08, 0x3E, 0x08, 0x14, 0x00}, // '*'
{0x08, 0x08, 0x3E, 0x08, 0x08, 0x00}, // '+'
{0x00, 0x50, 0x30, 0x00, 0x00, 0x00}, // ','
{0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, // '-'
{0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, // '.'
{0x20, 0x10, 0x08, 0x04, 0x02, 0x00}, // '/'
{0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00}, // '0'
{0x00, 0x42, 0x7F, 0x40, 0x00, 0x00}, // '1'
{0x42, 0x61, 0x51, 0x49, 0x46, 0x00}, // '2'
{0x21, 0x41, 0x45, 0x4B, 0x31, 0x00}, // '3'
{0x18, 0x14, 0x12, 0x7F, 0x10, 0x00}, // '4'
{0x27, 0x45, 0x45, 0x45, 0x39, 0x00}, // '5'
{0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00}, // '6'
{0x01, 0x71, 0x09, 0x05, 0x03, 0x00}, // '7'
{0x36, 0x49, 0x49, 0x49, 0x36, 0x00}, // '8'
{0x06, 0x49, 0x49, 0x29, 0x1E, 0x00}, // '9'
{0x00, 0x36, 0x36, 0x00, 0x00, 0x00}, // ':'
{0x00, 0x56, 0x36, 0x00, 0x00, 0x00}, // ';'
{0x08, 0x14, 0x22, 0x41, 0x00, 0x00}, // '<'
{0x14, 0x14, 0x14, 0x14, 0x14, 0x00}, // '='
{0x00, 0x41, 0x22, 0x14, 0x08, 0x00}, // '>'
{0x02, 0x01, 0x51, 0x09, 0x06, 0x00}, // '?'
{0x32, 0x49, 0x79, 0x41, 0x3E, 0x00}, // '@'
{0x7E, 0x11, 0x11, 0x11, 0x7E, 0x00}, // 'A'
{0x7F, 0x49, 0x49, 0x49, 0x36, 0x00}, // 'B'
{0x3E, 0x41, 0x41, 0x41, 0x22, 0x00}, // 'C'
{0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00}, // 'D'
{0x7F, 0x49, 0x49, 0x49, 0x41, 0x00}, // 'E'
{0x7F, 0x09, 0x09, 0x09, 0x01, 0x00}, // 'F'
{0x3E, 0x41, 0x49, 0x49, 0x7A, 0x00}, // 'G'
{0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00}, // 'H'
{0x00, 0x41, 0x7F, 0x41, 0x00, 0x00}, // 'I'
{0x20, 0x40, 0x41, 0x3F, 0x01, 0x00}, // 'J'
{0x7F, 0x08, 0x14, 0x22, 0x41, 0x00}, // 'K'
{0x7F, 0x40, 0x40, 0x40, 0x40, 0x00}, // 'L'
{0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00}, // 'M'
{0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00}, // 'N'
{0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00}, // 'O'
{0x7F, 0x09, 0x09, 0x09, 0x06, 0x00}, // 'P'
{0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00}, // 'Q'
{0x7F, 0x09, 0x19, 0x29, 0x46, 0x00}, // 'R'
{0x46, 0x49, 0x49, 0x49, 0x31, 0x00}, // 'S'
{0x01, 0x01, 0x7F, 0x01, 0x01, 0x00}, // 'T'
{0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00}, // 'U'
{0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00}, // 'V'
{0x7F, 0x20, 0x18, 0x20, 0x7F, 0x00}, // 'W'
{0x63, 0x14, 0x08, 0x14, 0x63, 0x00}, // 'X'
{0x07, 0x08, 0x70, 0x08, 0x07, 0x00}, // 'Y'
{0x61, 0x51, 0x49, 0x45, 0x43, 0x00}, // 'Z'
{0x00, 0x7F, 0x41, 0x41, 0x00, 0x00}, // '['
{0x02, 0x04, 0x08, 0x10, 0x20, 0x00}, // '\'
{0x00, 0x41, 0x41, 0x7F, 0x00, 0x00}, // ']'
{0x04, 0x02, 0x01, 0x02, 0x04, 0x00}, // '^'
{0x40, 0x40, 0x40, 0x40, 0x40, 0x00}, // '_'
{0x00, 0x01, 0x02, 0x04, 0x00, 0x00}, // '`'
{0x20, 0x54, 0x54, 0x54, 0x78, 0x00}, // 'a'
{0x7F, 0x48, 0x44, 0x44, 0x38, 0x00}, // 'b'
{0x38, 0x44, 0x44, 0x44, 0x20, 0x00}, // 'c'
{0x38, 0x44, 0x44, 0x48, 0x7F, 0x00}, // 'd'
{0x38, 0x54, 0x54, 0x54, 0x18, 0x00}, // 'e'
{0x08, 0x7E, 0x09, 0x01, 0x02, 0x00}, // 'f'
{0x0C, 0x52, 0x52, 0x52, 0x3E, 0x00}, // 'g'
{0x7F, 0x08, 0x04, 0x04, 0x78, 0x00}, // 'h'
{0x00, 0x44, 0x7D, 0x40, 0x00, 0x00}, // 'i'
{0x20, 0x40, 0x44, 0x3D, 0x00, 0x00}, // 'j'
{0x7F, 0x10, 0x28, 0x44, 0x00, 0x00}, // 'k'
{0x00, 0x41, 0x7F, 0x40, 0x00, 0x00}, // 'l'
{0x7C, 0x04, 0x18, 0x04, 0x78, 0x00}, // 'm'
{0x7C, 0x08, 0x04, 0x04, 0x78, 0x00}, // 'n'
{0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // 'o'
{0x7C, 0x14, 0x14, 0x14, 0x08, 0x00}, // 'p'
{0x08, 0x14, 0x14, 0x18, 0x7C, 0x00}, // 'q'
{0x7C, 0x08, 0x04, 0x04, 0x08, 0x00}, // 'r'
{0x48, 0x54, 0x54, 0x54, 0x20, 0x00}, // 's'
{0x04, 0x3F, 0x44, 0x40, 0x20, 0x00}, // 't'
{0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00}, // 'u'
{0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00}, // 'v'
{0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00}, // 'w'
{0x44, 0x28, 0x10, 0x28, 0x44, 0x00}, // 'x'
{0x0C, 0x50, 0x50, 0x50, 0x3C, 0x00}, // 'y'
{0x44, 0x64, 0x54, 0x4C, 0x44, 0x00}, // 'z'
{0x00, 0x08, 0x36, 0x41, 0x00, 0x00}, // '{'
{0x00, 0x00, 0x7F, 0x00, 0x00, 0x00}, // '|'
{0x00, 0x41, 0x36, 0x08, 0x00, 0x00}, // '}'
{0x10, 0x08, 0x08, 0x10, 0x08, 0x00}, // '~'
};
// 是否开启调试日志
#define SSD1306_DEBUG 0 // 1: 开启日志,0: 关闭日志
#if SSD1306_DEBUG
#define SSD1306_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define SSD1306_LOG(fmt, ...) // 什么都不做
#endif
// SSD1306 全屏缓冲区
uint8_t SSD1306_Buffer[SSD1306_PAGE_NUM * SSD1306_WIDTH];
// I2C 写入函数,增加返回值日志
static int SSD1306_Write(uint8_t control, uint8_t *data, uint16_t len)
{
mxc_i2c_req_t req;
req.i2c = MXC_I2C1; // 根据你的硬件选择
req.addr = SSD1306_ADDR;
uint8_t buffer[len + 1];
buffer[0] = control;
memcpy(buffer + 1, data, len);
req.tx_buf = buffer;
req.tx_len = len + 1;
req.rx_buf = NULL;
req.rx_len = 0;
req.restart = 0;
req.callback = NULL;
int ret = MXC_I2C_MasterTransaction(&req);
if (ret != 0)
{
SSD1306_LOG("[SSD1306] I2C Write failed, addr=0x%02X, control=0x%02X, ret=%d\n", SSD1306_ADDR, control, ret);
}
else
{
SSD1306_LOG("[SSD1306] I2C Write success, addr=0x%02X, control=0x%02X, len=%d\n", SSD1306_ADDR, control, len);
}
MXC_Delay(MXC_DELAY_MSEC(5)); // 给总线一些时间
return ret;
}
// 初始化 SSD1306,增加日志
void SSD1306_Init(void)
{
SSD1306_LOG("[SSD1306] Init start\n");
uint8_t init_cmds[] = {
0xAE, 0x20, 0x00, 0xB0, 0xC8, 0x00, 0x10, 0x40,
0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F, 0xA4, 0xD3, 0x00,
0xD5, 0xF0, 0xD9, 0x22, 0xDA, 0x12, 0xDB, 0x20, 0x8D, 0x14,
0xAF};
int ret = SSD1306_Write(SSD1306_COMMAND, init_cmds, sizeof(init_cmds));
if (ret != 0)
{
SSD1306_LOG("[SSD1306] Init failed\n");
return;
}
SSD1306_Clear();
SSD1306_LOG("[SSD1306] Init done\n");
}
// 清屏
void SSD1306_Clear(void)
{
memset(SSD1306_Buffer, 0x00, sizeof(SSD1306_Buffer));
SSD1306_DisplayFrame(SSD1306_Buffer);
SSD1306_LOG("[SSD1306] Clear screen\n");
}
// 显示缓冲区
void SSD1306_DisplayFrame(uint8_t *buffer)
{
for (uint8_t page = 0; page < SSD1306_PAGE_NUM; page++)
{
uint8_t set_page_cmds[] = {0xB0 | page, 0x00, 0x10};
if (SSD1306_Write(SSD1306_COMMAND, set_page_cmds, sizeof(set_page_cmds)) != 0)
{
SSD1306_LOG("[SSD1306] Set page %d failed\n", page);
}
if (SSD1306_Write(SSD1306_DATA, buffer + page * SSD1306_WIDTH, SSD1306_WIDTH) != 0)
{
SSD1306_LOG("[SSD1306] Write page %d data failed\n", page);
}
}
}
// 打印字符串
void SSD1306_Print(uint8_t x, uint8_t page, const char *str)
{
while (*str)
{
if (x > SSD1306_WIDTH - 6)
{
x = 0;
page++;
if (page >= SSD1306_PAGE_NUM)
break;
}
uint8_t c = *str;
if (c < ' ' || c > '~')
c = ' '; // 非法字符用空格
memcpy(&SSD1306_Buffer[page * SSD1306_WIDTH + x], Font6x8[c - ' '], 6);
x += 6;
str++;
}
SSD1306_DisplayFrame(SSD1306_Buffer);
SSD1306_LOG("[SSD1306] Print string done\n");
}
// 打印整数
void SSD1306_PrintInt(uint8_t x, uint8_t page, int32_t num)
{
char buf[12]; // 足够容纳 -2147483648\0
sprintf(buf, "%ld", num);
SSD1306_Print(x, page, buf);
}
// 打印浮点数
void SSD1306_PrintFloat(uint8_t x, uint8_t page, float num, uint8_t decimals)
{
char buf[20]; // 足够容纳浮点数
char format[8];
sprintf(format, "%%.%df", decimals); // 生成格式字符串,例如 "%.2f"
sprintf(buf, format, num);
SSD1306_Print(x, page, buf);
}
我上述只包括了一个字库,并且显示的话不是按照Xy进行显示的,而是根据Line进行显示,这样也省去了每次计算坐标的麻烦。 缺点是不够灵活。
view plaincopy to clipboardprint?#include <stdio.h>
#include "board.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include "i2c.h"
#include "ssd1306.h"
int main(void)
{
printf("SSD1306 0.96 inch Example\n");
// 初始化 I2C
int res = MXC_I2C_Init(MXC_I2C1, 1, 0);
printf("I2C Init: %d\n", res);
MXC_I2C_SetFrequency(MXC_I2C1, 400000);
printf("I2C Frequency: %d\n", MXC_I2C_GetFrequency(MXC_I2C1));
// 初始化 OLED
SSD1306_Init();
// 清屏
SSD1306_Clear();
// 打印文字
SSD1306_Print(0, 0, "Hello MAX78000!");
SSD1306_Print(0, 1, "SSD1306 OLED Demo");
SSD1306_Print(0, 2, "Hello EEPW!");
SSD1306_Print(0, 3, "Hello Let’s do !");
SSD1306_PrintInt(0, 4, 33);
SSD1306_PrintFloat(0, 5, 77.7, 2);
while (1)
{
MXC_Delay(MXC_DELAY_MSEC(1000));
}
return 0;
}
在主函数中进行调用。 然后便可以观察到屏幕已经正常的点亮。 黄蓝屏,真的很好看!