shuichan_old/V1.5/STM32F103_App1/HardWare/OLED/oled.c

296 lines
8.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 <string.h>
#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_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~316px 行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 8x16Address = (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;
}
/*==== 整行显示:计算像素宽度,不足 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; }
}