nokia5110.c 完整代码
c
/**
* @file nokia5110.c
* @brief Nokia 5110 LCD Display Driver Implementation
*/
#include "nokia5110.h"
#include "stdlib.h"
/* Private variables ---------------------------------------------------------*/
static uint8_t LCD_Buffer[LCD_WIDTH * LCD_HEIGHT / 8]; // 显示缓冲区
// 字符字模数组 (ASCII 32-127,每个字符6x8像素,占用6字节)
// 这是一个简化的字模,实际应用中您可能需要更完整的字模
const uint8_t LCD5110_Font[][6] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' ' (Space)
{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'
{0x3F, 0x40, 0x38, 0x40, 0x3F, 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}, // '['
{0x55, 0x2A, 0x55, 0x2A, 0x55, 0x00}, // '\' (Backslash)
{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}, // '}'
{0x02, 0x01, 0x02, 0x04, 0x02, 0x00}, // '~' (DEL or non-extended)
};
/* Private function prototypes -----------------------------------------------*/
static void LCD_Write(uint8_t data, LcdCmdDataType type);
static void LCD_Reset(void);
static void LCD_WriteCommand(uint8_t cmd);
static void LCD_WriteData(uint8_t data);
static void LCD_Update(void);
/* Private functions ---------------------------------------------------------*/
/**
* @brief 写入一个字节到LCD
* @param data: 要写入的数据
* @param type: 数据类型 (命令或数据)
* @retval None
*/
static void LCD_Write(uint8_t data, LcdCmdDataType type)
{
// 设置DC引脚,决定是命令还是数据
if (type == LCD_CMD) {
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET); // 命令模式
} else {
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); // 数据模式
}
// 拉低片选,选择LCD
HAL_GPIO_WritePin(LCD_SCE_GPIO_Port, LCD_SCE_Pin, GPIO_PIN_RESET);
// 发送数据(SPI模拟)
for (uint8_t i = 0; i < 8; i++) {
// 准备数据位
if (data & 0x80) {
HAL_GPIO_WritePin(LCD_DI_GPIO_Port, LCD_DI_Pin, GPIO_PIN_SET); // 数据位为1
} else {
HAL_GPIO_WritePin(LCD_DI_GPIO_Port, LCD_DI_Pin, GPIO_PIN_RESET); // 数据位为0
}
// 时钟上升沿
HAL_GPIO_WritePin(LCD_SCLK_GPIO_Port, LCD_SCLK_Pin, GPIO_PIN_SET);
// 时钟下降沿,数据被采样
HAL_GPIO_WritePin(LCD_SCLK_GPIO_Port, LCD_SCLK_Pin, GPIO_PIN_RESET);
// 移动到下一位
data <<= 1;
}
// 拉高片选,取消选择
HAL_GPIO_WritePin(LCD_SCE_GPIO_Port, LCD_SCE_Pin, GPIO_PIN_SET);
}
/**
* @brief 写入命令到LCD
* @param cmd: 命令字节
* @retval None
*/
static void LCD_WriteCommand(uint8_t cmd)
{
LCD_Write(cmd, LCD_CMD);
}
/**
* @brief 写入数据到LCD
* @param data: 数据字节
* @retval None
*/
static void LCD_WriteData(uint8_t data)
{
LCD_Write(data, LCD_DATA);
}
/**
* @brief 硬件复位LCD
* @retval None
*/
static void LCD_Reset(void)
{
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
HAL_Delay(100);
}
/**
* @brief 更新LCD显示(将缓冲区内容写入LCD)
* @retval None
*/
static void LCD_Update(void)
{
for (uint8_t y = 0; y < LCD_HEIGHT / 8; y++) {
LCD_WriteCommand(LCD_SETXADDR | 0); // 设置X地址为0
LCD_WriteCommand(LCD_SETYADDR | y); // 设置Y地址
for (uint8_t x = 0; x < LCD_WIDTH; x++) {
LCD_WriteData(LCD_Buffer[x + (y * LCD_WIDTH)]);
}
}
}
/* Exported functions --------------------------------------------------------*/
/**
* @brief 初始化LCD
* @retval None
*/
void LCD_Init(void)
{
// 硬件复位
LCD_Reset();
// 进入扩展命令模式
LCD_WriteCommand(LCD_FUNCTIONSET_EXTENDED);
// 设置LCD偏置电压
LCD_WriteCommand(LCD_BIAS_MODE | 0x02);
// 设置温度系数
LCD_WriteCommand(LCD_TEMP_COEF | 0x01);
// 设置对比度 (VOP)
LCD_WriteCommand(LCD_VOP | 0x40);
// 进入基本命令模式
LCD_WriteCommand(LCD_FUNCTIONSET_BASIC);
// 设置显示模式
LCD_WriteCommand(LCD_DISPLAYCONTROL_NORMAL);
// 清空显示
LCD_Clear();
}
/**
* @brief 清空LCD显示
* @retval None
*/
void LCD_Clear(void)
{
// 清空缓冲区
for (uint16_t i = 0; i < sizeof(LCD_Buffer); i++) {
LCD_Buffer[i] = 0x00;
}
// 更新显示
LCD_Update();
}
/**
* @brief 设置光标位置
* @param x: 水平位置 (0-83)
* @param y: 垂直位置 (0-5)
* @retval None
*/
// ... existing code ...
void LCD_SetCursor(uint8_t x, uint8_t y)
{
if (x >= LCD_WIDTH || y >= LCD_HEIGHT / 8) {
return; // 超出范围
}
LCD_WriteCommand(LCD_SETXADDR | x);
LCD_WriteCommand(LCD_SETYADDR | y);
}
/**
* @brief 写入单个字符到LCD
* @param ch: 要写入的字符
* @retval None
*/
void LCD_WriteChar(char ch, uint8_t reverse)
{
// 检查字符是否在可显示范围内
if(ch < 32 || ch > 127) return;
// 从字符字模数组中获取字符点阵数据
const uint8_t* font = LCD5110_Font[ch - 32]; // 直接访问数组元素
for(uint8_t i = 0; i < 6; i++) {
if(reverse) {
LCD_WriteData(~font[i]); // 反转显示
} else {
LCD_WriteData(font[i]);
}
}
}
/**
* @brief 写入字符串到LCD
* @param str: 要写入的字符串
* @retval None
*/
void LCD_WriteString(const char* str, uint8_t reverse)
{
while(*str) {
LCD_WriteChar(*str++, reverse);
}
}
/**
* @brief 写入二进制数值到LCD(以二进制格式显示)
* @param value: 要显示的8位二进制值
* @retval None
*/
void LCD_WriteBinary(uint8_t value)
{
char binary_str[9]; // 8位二进制 + '\0'
for(int i = 7; i >= 0; i--) {
binary_str[7-i] = ((value >> i) & 0x01) ? '1' : '0';
}
binary_str[8] = '\0';
LCD_WriteString(binary_str,0);
}
/**
* @brief 设置LCD对比度
* @param contrast: 对比度值 (0-0x7F)
* @retval None
*/
void LCD_SetContrast(uint8_t contrast)
{
// 进入扩展命令模式
LCD_WriteCommand(LCD_FUNCTIONSET_EXTENDED);
// 设置对比度 (VOP)
LCD_WriteCommand(LCD_VOP | (contrast & 0x7F));
// 回到基本命令模式
LCD_WriteCommand(LCD_FUNCTIONSET_BASIC);
}
/**
* @brief 反转显示
* @param invert: 1为反转显示,0为正常显示
* @retval None
*/
void LCD_InvertDisplay(uint8_t invert)
{
LCD_WriteCommand(invert ? LCD_DISPLAYCONTROL_INVERSE : LCD_DISPLAYCONTROL_NORMAL);
}
/**
* @brief 打开LCD显示
* @retval None
*/
void LCD_DisplayOn(void)
{
LCD_WriteCommand(LCD_DISPLAYCONTROL_NORMAL);
}
/**
* @brief 关闭LCD显示
* @retval None
*/
void LCD_DisplayOff(void)
{
LCD_WriteCommand(LCD_DISPLAYCONTROL_BLANK);
}
/**
* @brief 绘制单个像素点
* @param x: X坐标 (0-83)
* @param y: Y坐标 (0-47)
* @param color: 像素颜色 (0-清除, 1-绘制)
* @retval None
*/
void LCD_DrawPixel(uint8_t x, uint8_t y, uint8_t color)
{
if(x >= LCD_WIDTH || y >= LCD_HEIGHT) return;
uint16_t index = x + (y / 8) * LCD_WIDTH; // 计算字节在缓冲区中的位置
uint8_t bit = y % 8; // 计算在字节中的位位置
if(color) {
LCD_Buffer[index] |= (1 << bit); // 设置位
} else {
LCD_Buffer[index] &= ~(1 << bit); // 清除位
}
// 更新显示
LCD_Update();
}
/**
* @brief 绘制线段
* @param x1, y1: 起始坐标
* @param x2, y2: 终止坐标
* @param color: 线条颜色 (0-清除, 1-绘制)
* @retval None
*/
void LCD_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color)
{
int dx = x2 - x1;
int dy = y2 - y1;
int steps;
// 确定步数
if(abs(dx) > abs(dy)) {
steps = abs(dx);
} else {
steps = abs(dy);
}
float x_increment = (float)dx / steps;
float y_increment = (float)dy / steps;
float x = x1;
float y = y1;
for(int i = 0; i <= steps; i++) {
LCD_DrawPixel((uint8_t)x, (uint8_t)y, color);
x += x_increment;
y += y_increment;
}
}
/**
* @brief 绘制矩形框
* @param x1, y1: 矩形左上角坐标
* @param x2, y2: 矩形右下角坐标
* @param color: 矩形颜色 (0-清除, 1-绘制)
* @retval None
*/
void LCD_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color)
{
// 绘制四条边
LCD_DrawLine(x1, y1, x2, y1, color); // 上边
LCD_DrawLine(x1, y2, x2, y2, color); // 下边
LCD_DrawLine(x1, y1, x1, y2, color); // 左边
LCD_DrawLine(x2, y1, x2, y2, color); // 右边
}
/**
* @brief 绘制填充矩形
* @param x1, y1: 矩形左上角坐标
* @param x2, y2: 矩形右下角坐标
* @param color: 矩形颜色 (0-清除, 1-绘制)
* @retval None
*/
void LCD_DrawFilledRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color)
{
// 确保坐标顺序正确
if(x1 > x2) { uint8_t temp = x1; x1 = x2; x2 = temp; }
if(y1 > y2) { uint8_t temp = y1; y1 = y2; y2 = temp; }
// 绘制每一行
for(uint8_t y = y1; y <= y2; y++) {
LCD_DrawLine(x1, y, x2, y, color);
}
}
/**
* @brief 绘制圆形
* @param x0, y0: 圆心坐标
* @param radius: 半径
* @param color: 圆形颜色 (0-清除, 1-绘制)
* @retval None
*/
void LCD_DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color)
{
int x = 0;
int y = radius;
int d = 3 - 2 * radius;
// 如果半径为0,只绘制一个点
if(radius == 0) {
LCD_DrawPixel(x0, y0, color);
return;
}
// 绘制初始点
LCD_DrawPixel(x0 + x, y0 + y, color);
LCD_DrawPixel(x0 - x, y0 + y, color);
LCD_DrawPixel(x0 + x, y0 - y, color);
LCD_DrawPixel(x0 - x, y0 - y, color);
LCD_DrawPixel(x0 + y, y0 + x, color);
LCD_DrawPixel(x0 - y, y0 + x, color);
LCD_DrawPixel(x0 + y, y0 - x, color);
LCD_DrawPixel(x0 - y, y0 - x, color);
while(y >= x) {
x++;
if(d > 0) {
y--;
d = d + 4 * (x - y) + 10;
} else {
d = d + 4 * x + 6;
}
LCD_DrawPixel(x0 + x, y0 + y, color);
LCD_DrawPixel(x0 - x, y0 + y, color);
LCD_DrawPixel(x0 + x, y0 - y, color);
LCD_DrawPixel(x0 - x, y0 - y, color);
LCD_DrawPixel(x0 + y, y0 + x, color);
LCD_DrawPixel(x0 - y, y0 + x, color);
LCD_DrawPixel(x0 + y, y0 - x, color);
LCD_DrawPixel(x0 - y, y0 - x, color);
}
}
/**
* @brief 绘制位图
* @param x, y: 位图绘制起始坐标
* @param bitmap: 位图数据指针
* @param w: 位图宽度
* @param h: 位图高度
* @retval None
*/
void LCD_DrawBitmap(uint8_t x, uint8_t y, const uint8_t* bitmap, uint8_t w, uint8_t h)
{
uint8_t byte_width = (w + 7) / 8; // 计算位图每行占用的字节数
for(uint8_t i = 0; i < h; i++) {
for(uint8_t j = 0; j < w; j++) {
uint8_t byte_index = i * byte_width + j / 8;
uint8_t bit_index = 7 - (j % 8);
uint8_t pixel = (bitmap[byte_index] >> bit_index) & 1;
LCD_DrawPixel(x + j, y + i, pixel);
}
}
}
/* USER CODE BEGIN 1 */
// ... existing code ...
/* Private variables ---------------------------------------------------------*/
// ... existing code ...
/* USER CODE END 1 */