本篇我们来点亮iic协议的OLED屏幕(主控SSD1306)。
接线很简单,OLED屏幕的iic SCL、SDA线分别对接开发板的SCL(P0.16)、SDA(P0.17),他们也是开发板的i2c1模块。
涉及到ssd1306的函数接口,都已经定义在ssd1306.c和ssd1306.h中
我们简要看下ssd1306.h中都定义了哪些函数:
// 初始化 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);
而具体的实现都在ssd1306.c中
所有这些函数都引用了最核心的 SSD1306_Write 函数,也就是写寄存器函数。
// I2C 写入函数,带返回值 static int SSD1306_Write(uint8_t control, uint8_t *data, uint16_t len) { mxc_i2c_req_t req; //定义i2c结构体 req.i2c = MXC_I2C1; // 硬件I2C1 req.addr = SSD1306_ADDR; //i2c地址 uint8_t buffer[len + 1]; //+1位读/写位 buffer[0] = control; //读或者写 memcpy(buffer + 1, data, len); //把指令赋值给 buffer 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; }
这个函数先定义了一个i2c结构体,准备好各参数,包括地址、长度、发送内容等
最后一个MXC_I2C_MasterTransaction(&req) 就执行了,那具体是怎么执行的呢?
我们看下i2c.h中MXC_I2C_MasterTransaction的介绍。
int MXC_I2C_MasterTransaction(mxc_i2c_req_t *req); /** * @brief Performs a non-blocking I2C master transaction. * * Performs a non-blocking I2C master transaction. These actions will be performed: * 1. If necessary, generate a start condition on the bus. //启动通讯 * 2. Send the slave address with the low bit set to 0 (indicating a write).//写地址 * 3. Transmit req->tx_len bytes of req->tx_buff. //写数据 * 4. Generate a repeated start condition on the bus.//读启动 * 5. Send the slave address with the low bit set to 1 (indicating a read).//读地址 * 6. Receive req->rx_len bytes into req->rx_buf, acknowledging each byte.//读数据 * 7. Generate a stop (or repeated start) condition on the bus. //停止 * 8. Execute req->callback to indicate the transaction is complete. //通讯完成 * Steps 3-6 will be skipped if tx_len and rx_len are both 0. * Steps 2-4 will be skipped if tx_len equals 0. * Steps 4-6 will be skipped if rx_len equals 0. * * @note MXC_I2C_AsyncHandler() must be called periodically for this function * to operate properly. Ideally from the I2C ISR. * * @param req Pointer to details of the transaction. The memory * used by this parameter must remain available until * the callback is executed. * * @return Success/Fail, see \ref MXC_Error_Codes for a list of return codes. */
看完上述,我们大概明白了OLED是怎么执行的。
回到主程序main.c
#include <stdio.h> #include "board.h" #include "mxc_device.h" #include "mxc_delay.h" #include "i2c.h" #include "ssd1306.h" int i=0; 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 World!"); SSD1306_Print(0, 1, "SSD1306 0.96' Example"); SSD1306_Print(0, 2, "EEPW & DigiKeyLet's do 2025"); SSD1306_Print(0, 3, "Let's do 2025"); while (1) //绘制动态效果 { for(i=0;i<128;i++) { SSD1306_Print(i, 4, "*"); SSD1306_Print(127-i, 5, "#"); MXC_Delay(100); } } return 0; }
运行结果如图: