/* oled.c - 128x64 OLED + 晶联讯 GB2312 字库 IC 驱动(GPIO 模拟 SPI) * 移植自《485模拟磅秤数据带CRC》工程 oledisp.c,引脚改为本板: * CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 */ #include "oled.h" #include #define OLED_CLK_PORT GPIOC #define OLED_CLK_PIN GPIO_PIN_12 #define OLED_MOSI_PORT GPIOC #define OLED_MOSI_PIN GPIO_PIN_9 #define OLED_DC_PORT GPIOC #define OLED_DC_PIN GPIO_PIN_8 #define OLED_CS1_PORT GPIOC #define OLED_CS1_PIN GPIO_PIN_7 #define OLED_FSO_PORT GPIOC #define OLED_FSO_PIN GPIO_PIN_6 #define OLED_CS2_PORT GPIOB #define OLED_CS2_PIN GPIO_PIN_15 #define lcd_cs1(a) HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, (GPIO_PinState)(a)) #define lcd_dc(a) HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, (GPIO_PinState)(a)) #define lcd_mosi(a) HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, (GPIO_PinState)(a)) #define lcd_clk(a) HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, (GPIO_PinState)(a)) #define rom_cs2(a) HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, (GPIO_PinState)(a)) #define ROM_OUT HAL_GPIO_ReadPin(OLED_FSO_PORT, OLED_FSO_PIN) static uint8_t s_row = 0; /* 当前页地址(16px 行 × 2) */ static uint8_t s_col = 0; /* 当前列(像素) */ /*==== FSO 脚输入/输出切换(读字库时为输入)====*/ static void fso_input(void) { GPIO_InitTypeDef g = {0}; g.Pin = OLED_FSO_PIN; g.Mode = GPIO_MODE_INPUT; g.Pull = GPIO_NOPULL; HAL_GPIO_Init(OLED_FSO_PORT, &g); } static void fso_output(void) { GPIO_InitTypeDef g = {0}; g.Pin = OLED_FSO_PIN; g.Mode = GPIO_MODE_OUTPUT_PP; g.Pull = GPIO_PULLUP; g.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(OLED_FSO_PORT, &g); } /*==== GPIO 初始化 ====*/ static void oled_gpio_init(void) { GPIO_InitTypeDef g = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); /* 先置好空闲电平再开推挽,避免毛刺 */ HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, GPIO_PIN_SET); g.Mode = GPIO_MODE_OUTPUT_PP; g.Pull = GPIO_PULLUP; g.Speed = GPIO_SPEED_FREQ_LOW; g.Pin = OLED_CLK_PIN; HAL_GPIO_Init(OLED_CLK_PORT, &g); g.Pin = OLED_MOSI_PIN; HAL_GPIO_Init(OLED_MOSI_PORT, &g); g.Pin = OLED_DC_PIN; HAL_GPIO_Init(OLED_DC_PORT, &g); g.Pin = OLED_CS1_PIN; HAL_GPIO_Init(OLED_CS1_PORT, &g); g.Pin = OLED_CS2_PIN; HAL_GPIO_Init(OLED_CS2_PORT, &g); fso_output(); } /*==== 写字节到屏幕(DC=0 指令 / DC=1 数据)====*/ static void oled_write(uint8_t dc, uint8_t dat) { lcd_dc(dc); lcd_cs1(0); for (int i = 0; i < 8; i++) { lcd_clk(0); lcd_mosi(dat & 0x80); lcd_clk(1); dat <<= 1; } lcd_dc(1); lcd_cs1(1); } /*==== 设置页/列地址 ====*/ static void lcd_address(uint8_t page, uint8_t column) { oled_write(0, 0xb0 + column); /* 页地址 */ oled_write(0, ((page & 0xf0) >> 4) | 0x10); /* 列地址高 4 位 */ oled_write(0, (page & 0x0f)); /* 列地址低 4 位 */ } /*==== 关显示(黑屏,断电预警时调用)====*/ void oled_off(void) { oled_write(0, 0xAE); /* display off */ } /*==== 全屏清屏 ====*/ void oled_clear(void) { lcd_cs1(0); rom_cs2(1); for (uint8_t i = 0; i < 8; i++) { oled_write(0, 0xb0 + i); oled_write(0, 0x00); oled_write(0, 0x10); for (uint8_t j = 0; j < 128; j++) oled_write(1, 0x00); } lcd_cs1(1); } /*==== 屏幕初始化(SSD1306 兼容序列)====*/ void oled_init(void) { HAL_Delay(400); /* 等屏幕/字库上电稳定 */ oled_gpio_init(); lcd_cs1(0); rom_cs2(1); oled_write(0, 0xAE); /* display off */ oled_write(0, 0x20); /* 寻址模式 */ oled_write(0, 0x10); /* 水平寻址 */ oled_write(0, 0xb0); oled_write(0, 0xc8); /* COM 扫描方向 */ oled_write(0, 0x00); oled_write(0, 0x10); oled_write(0, 0x40); /* 起始行 */ oled_write(0, 0x81); /* 对比度 */ oled_write(0, 0xFF); oled_write(0, 0xa1); /* 段重映射 */ oled_write(0, 0xa6); /* 正常显示 */ oled_write(0, 0xa8); /* 复用率 */ oled_write(0, 0x3F); oled_write(0, 0xa4); oled_write(0, 0xd3); /* 显示偏移 */ oled_write(0, 0x00); oled_write(0, 0xd5); /* 时钟分频 */ oled_write(0, 0xf0); oled_write(0, 0xd9); /* 预充电 */ oled_write(0, 0x22); oled_write(0, 0xda); /* COM 硬件配置 */ oled_write(0, 0x12); oled_write(0, 0xdb); /* VCOMH */ oled_write(0, 0x20); oled_write(0, 0x8d); /* DC-DC */ oled_write(0, 0x14); oled_write(0, 0xaf); /* display on */ lcd_cs1(1); oled_clear(); } /*==== 显示 16x16 点阵(汉字)====*/ static void display_graphic_16x16(uint8_t page, uint8_t column, const uint8_t *dp) { lcd_cs1(0); rom_cs2(1); for (uint8_t j = 2; j > 0; j--) { lcd_address(column, page); for (uint8_t i = 0; i < 16; i++) oled_write(1, *dp++); page++; } lcd_cs1(1); } /*==== 显示 8x16 点阵(ASCII)====*/ static void display_graphic_8x16(uint8_t page, uint8_t column, const uint8_t *dp) { lcd_cs1(0); for (uint8_t j = 2; j > 0; j--) { lcd_address(column, page); for (uint8_t i = 0; i < 8; i++) oled_write(1, *dp++); page++; } lcd_cs1(1); } /*==== 字库 IC 写字节(指令/地址)====*/ static void rom_write_byte(uint8_t dat) { for (int i = 0; i < 8; i++) { lcd_mosi(dat & 0x80); dat <<= 1; lcd_clk(0); lcd_clk(1); } } /*==== 字库 IC 读一字节 ====*/ static uint8_t rom_read_byte(void) { uint8_t ret = 0; lcd_clk(1); fso_input(); for (int i = 0; i < 8; i++) { lcd_clk(0); ret <<= 1; if (ROM_OUT) ret |= 1; lcd_clk(1); } fso_output(); return ret; } /*==== 从字库 IC 连续读 DataLen 字节 ====*/ static void rom_read_n(uint8_t ah, uint8_t am, uint8_t al, uint8_t *buf, uint8_t len) { rom_cs2(0); lcd_cs1(1); lcd_clk(0); rom_write_byte(0x03); /* 读指令 */ rom_write_byte(ah); rom_write_byte(am); rom_write_byte(al); for (uint8_t i = 0; i < len; i++) buf[i] = rom_read_byte(); rom_cs2(1); } /*==== 定位:row 0~3(16px 行),col8 为 8 像素单位 ====*/ void oled_locate(uint8_t row, uint8_t col8) { s_row = row * 2; s_col = col8 * 8; } /*==== 显示 GBK 字符串(16x16 汉字 / 8x16 ASCII,编码换算见晶联讯字库手册)====*/ void oled_print(const char *s) { uint8_t fontbuf[32]; uint8_t y = s_row; uint8_t x = s_col; const uint8_t *text = (const uint8_t *)s; uint32_t fontaddr; s_col += strlen(s) * 8; /* 位置自加(按 8px/字符计) */ uint8_t i = 0; while (text[i] > 0x00) { if ((text[i] >= 0xb0 && text[i] <= 0xf7) && text[i+1] >= 0xa1) { /* GB2312 汉字:Address = ((MSB-0xB0)*94 + (LSB-0xA1) + 846) * 32 */ fontaddr = (text[i] - 0xb0) * 94; fontaddr += (text[i+1] - 0xa1) + 846; fontaddr *= 32; rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8), (uint8_t)fontaddr, fontbuf, 32); display_graphic_16x16(y, x, fontbuf); i += 2; x += 16; } else if ((text[i] >= 0xa1 && text[i] <= 0xa3) && text[i+1] >= 0xa1) { /* GB2312 全角符号:Address = ((MSB-0xA1)*94 + (LSB-0xA1)) * 32 */ fontaddr = (text[i] - 0xa1) * 94; fontaddr += (text[i+1] - 0xa1); fontaddr *= 32; rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8), (uint8_t)fontaddr, fontbuf, 32); display_graphic_16x16(y, x, fontbuf); i += 2; x += 16; } else if (text[i] >= 0x20 && text[i] <= 0x7e) { /* ASCII 8x16:Address = (char-0x20)*16 + 0x3CF80 */ fontaddr = (text[i] - 0x20) * 16 + 0x3cf80; rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8), (uint8_t)fontaddr, fontbuf, 16); display_graphic_8x16(y, x, fontbuf); i += 1; x += 8; } else { i++; } } } /*==== 当前列右移 px 像素 ====*/ void oled_xoff(uint8_t px) { s_col += px; } /*==== 文本像素宽度:汉字/全角 16px,ASCII 8px ====*/ uint8_t oled_text_w(const char *s) { const uint8_t *p = (const uint8_t *)s; uint32_t w = 0; while (*p) { if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; } else { w += 8; p += 1; } } return (uint8_t)(w > 128 ? 128 : w); } /*==== 整行显示:计算像素宽度,不足 128px 用空格补齐(覆盖上次内容)====*/ void oled_print_line(const char *s) { const uint8_t *p = (const uint8_t *)s; uint32_t w = 0; while (*p) { if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; } /* 汉字/全角 */ else { w += 8; p += 1; } /* ASCII */ } oled_print(s); while (w < 128) { oled_print(" "); w += 8; } }