简介
在本章节中我们将使用MAX78000的I2C来驱动SSD1306,根据原理图得知I2C1已经连接了一个传感器,好在它还有额外的引出PIN,现在使用杜邦线将SSD1306连接到对应的PIN上。
然后打开Example下的I2C_Scan的代码,可以根据代码中发现(或者原理图)其中定义的I2CPin 为16和17,如果烧录上述Demo的话,其扫描到的I2C地址为0x3C
然后我们可以从网上查找现成的SSD1306驱动并且将其I2C的实现修改为MAX78000实现即可。如下所示。
#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文件
#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进行显示,这样也省去了每次计算坐标的麻烦。 缺点是不够灵活。
#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; }
在主函数中进行调用。 然后便可以观察到屏幕已经正常的点亮。 黄蓝屏,真的很好看!
插曲
是时候换一个电烙铁了,焊接这个排针真的给我折磨疯了,这个黄花电烙铁头氧化太严重了。焊接一个排针要好久好久。焊接好了之后发现不小心把开发板上的绝缘图层刮露铜了。然后开发板经常启动不了。不知道怎么回事。后来尝试使用吹风机一加热就可以正常启动了。 不加热启动不了。后来发现是因为晶振不起振的问题。我还拆下了一个起振的电容。我自己是根本装不上去,太小了。现在那个电容已经不知道飞到哪里去了。 所以起振电容目前只有一个,我的开发板变成了需要钥匙启动的。 每次要是想能正常启动,必须找一个杜邦线插到晶振上五秒时间,然后晶振就起振了。