static const uint8_t _CRCLo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 };

时间: 2023-09-19 20:10:06 浏览: 267
这段代码定义了一个名为_CRCLo的静态常量数组,包含256个元素,每个元素都是一个8位无符号整数(uint8_t)。该数组是用于计算CRC校验码的LUT(lookup table),其中每个元素代表了一个8位数据的CRC校验码的低位。该LUT的生成方式是通过一个特定的CRC算法生成的,可以在需要计算CRC校验码的地方使用该数组来快速计算CRC校验码的低位。
相关问题

void enc(void) { static uint8_t last_AB = 0x03; // 初始状态AB=11 const int8_t dir_table[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; // 读取当前AB相状态 uint8_t current_AB = (xc_gpio_read_pin(ENC_A) << 1) | xc_gpio_read_pin(ENC_B); // 计算状态转移索引 uint8_t state_index = (last_AB << 2) | current_AB; // 查表获取方向 int8_t dir = dir_table[state_index]; last_AB = current_AB; // 更新亮度 if (dir == 1) { brightness = (brightness < 100) ? brightness + 1 : 100; } else if (dir == -1) { brightness = (brightness > 0) ? brightness - 1 : 0; } }帮我加个软件延时

<think>好的,我现在需要帮助用户在C语言的编码器程序中添加软件延时来解决抖动问题。首先,我得先理解用户的问题。用户提到想在编码器函数中添加软件延时,确保GPIO读取稳定,避免抖动。根据用户提供的引用,特别是引用[1]和引用[2],软件去抖动通常可以通过延时和多次采样来实现。 首先,我应该回顾一下旋转编码器的工作原理。根据引用[4],编码器的A相和B相输出相位差90度的方波,通过检测这两个信号的边沿变化来判断旋转方向。抖动可能会导致GPIO读取到错误的边沿,进而误判方向或计数。所以软件去抖动是关键。 接下来,我需要考虑如何在代码中实现延时。通常的做法是在检测到信号变化后,延时一段时间,然后再次读取信号,确认状态是否稳定。例如,当检测到A相的边沿时,延时10-20毫秒,然后再读取A和B相的状态,如果仍然与之前读取的一致,则认为信号有效。 用户提供的引用[1]提到,软件去抖动常用延时和多次采样。因此,可能的实现方法是在检测到电平变化后,先延时,再多次采样确认状态是否一致。例如,连续三次读取同一电平,如果都相同,才认为有效。 但编码器的处理通常需要实时性,特别是在高速旋转时,过长的延时可能会导致丢失脉冲。因此,延时时间需要根据编码器的机械特性和旋转速度来调整。通常,机械编码器的抖动时间在10ms左右,所以延时10ms左右可能合适。不过,用户可能需要根据实际情况测试调整。 现在,我需要参考用户提供的引用中的代码示例。例如,引用[4]提到旋转编码器的C语言程序,可能涉及中断或轮询方式读取GPIO。假设用户使用的是轮询方式,那么可以在每次检测到变化后加入延时。如果是中断方式,则需要注意延时可能影响其他中断处理,这时候可能需要使用状态机或者定时器来延迟处理。 另外,引用[5]提到了按键程序的延时处理,可能类似。比如,当检测到按键按下时,延时后再检测状态。同样的逻辑可以应用到编码器的GPIO读取中。 现在,我需要构思如何在代码中添加延时。可能的步骤包括: 1. 在读取GPIO状态之前或之后插入延时函数,如delay_ms(10)。 2. 多次读取GPIO状态,确认是否一致。 3. 使用状态机来管理去抖动过程,避免阻塞太久。 但编码器的处理通常需要及时响应,所以可能更适合在检测到边沿后,启动一个定时器,在定时器中断中再次检查状态,而不是直接在函数中使用阻塞延时。不过,用户可能希望简单的实现方式,使用阻塞延时。 假设用户使用的是轮询方式,主循环中不断读取编码器状态。例如: void encoder_scan() { static int lastA = 0; int currentA = read_A(); if (currentA != lastA) { // 检测到变化,加入延时 delay_ms(10); currentA = read_A(); int currentB = read_B(); // 确认当前状态是否稳定 if (currentA == read_A() && currentB == read_B()) { // 处理编码器事件 } lastA = currentA; } } 但这种方法可能会在每次变化时都延时,影响性能。另一种方法是使用时间戳,记录最后一次变化的时间,然后在主循环中检查时间差是否超过去抖动时间,例如: void encoder_scan() { static uint32_t last_time = 0; static int lastA = 0; int currentA = read_A(); if (currentA != lastA) { uint32_t now = get_tick(); if (now - last_time > DEBOUNCE_DELAY) { int currentB = read_B(); // 处理逻辑 lastA = currentA; last_time = now; } } } 这样可以避免频繁的阻塞延时,而是通过时间差来判断是否稳定。但需要系统有一个定时器来提供时间戳。 如果用户使用的是中断方式,那么不能在中断服务例程(ISR)中使用阻塞延时,因为这会导致系统不稳定。此时,应该在ISR中记录事件,并设置标志位,在主循环中处理去抖动逻辑。 例如: volatile int encoder_flag = 0; void EXTI_ISR() { encoder_flag = 1; } void main() { while(1) { if (encoder_flag) { delay_ms(10); int a = read_A(); int b = read_B(); if (a == read_A() && b == read_B()) { // 处理编码器 } encoder_flag = 0; } } } 但这种方法可能会丢失中断,因为如果在处理期间再次触发中断,标志位可能被覆盖。更好的方法是使用状态机和定时器,或者在中断中记录时间,然后在主循环中处理。 总结来说,添加软件延时的关键点包括: - 确定抖动的时间长度,选择合适的延时时间(如10-50ms)。 - 在检测到信号变化后,延时一段时间再重新读取确认。 - 避免在中断服务例程中使用阻塞延时,改用非阻塞方法。 - 多次采样确认信号稳定。 现在,我需要将这些思考整理成步骤清晰的回答,并给出代码示例,同时生成相关问题。</think>### 分步解答 在编码器程序中添加软件延时去抖动的实现步骤如下: --- #### **1. 确定抖动时间范围** 机械编码器的触点抖动通常在**5ms~50ms**之间,建议通过实验调整具体值(如10ms)。 **参考做法**: - 使用示波器观察信号波形,测量抖动持续时间 - 若无设备,可逐步增加延时测试稳定性 --- #### **2. 添加延时函数** 在检测到GPIO状态变化后插入延时,并重新读取确认状态一致性。 ```c #define DEBOUNCE_DELAY 10 // 单位:毫秒 // 简单阻塞延时函数(需根据平台实现) void delay_ms(uint32_t ms) { for(uint32_t i=0; i<ms*1000; i++); // 示例代码,需替换为实际延时 } // 编码器状态读取函数 void read_encoder() { static int last_A = 0; int current_A = GPIO_ReadPin(A_PIN); if(current_A != last_A) { delay_ms(DEBOUNCE_DELAY); // 关键延时 current_A = GPIO_ReadPin(A_PIN); int current_B = GPIO_ReadPin(B_PIN); // 二次验证防止误触发 if(current_A == GPIO_ReadPin(A_PIN) && current_B == GPIO_ReadPin(B_PIN)) { // 处理旋转方向判断逻辑 if(current_A != last_A) { if(current_B != current_A) { printf("顺时针旋转\n"); } else { printf("逆时针旋转\n"); } } last_A = current_A; } } } ``` --- #### **3. 优化方案:非阻塞式处理** 使用时间戳避免阻塞主循环,适合实时性要求高的场景: ```c #include <stdint.h> uint32_t get_tick(void); // 获取系统tick的函数(需平台支持) #define DEBOUNCE_TIME 10 // 去抖动时间阈值 void encoder_scan() { static uint32_t last_check = 0; static int last_state = 0; int current_state = (GPIO_ReadPin(A_PIN) << 1) | GPIO_ReadPin(B_PIN); if(current_state != last_state) { uint32_t current_tick = get_tick(); if(current_tick - last_check > DEBOUNCE_TIME) { // 确认稳定后处理 process_encoder(current_state); last_state = current_state; } last_check = current_tick; } } ``` --- #### **4. 中断结合主循环处理** 若使用中断,需避免在ISR中直接延时: ```c volatile uint8_t encoder_flag = 0; // 中断服务函数(示例) void EXTI_IRQHandler() { if(EXTI_GetITStatus(A_PIN_EXTI_LINE)) { encoder_flag = 1; EXTI_ClearITPendingBit(A_PIN_EXTI_LINE); } } // 主循环处理 while(1) { if(encoder_flag) { delay_ms(DEBOUNCE_DELAY); int stable_A = GPIO_ReadPin(A_PIN); int stable_B = GPIO_ReadPin(B_PIN); if(stable_A == GPIO_ReadPin(A_PIN) && stable_B == GPIO_ReadPin(B_PIN)) { handle_encoder_movement(stable_A, stable_B); } encoder_flag = 0; } } ``` --- ### 关键注意事项 1. **延时时间测试**:通过逐步调整`DEBOUNCE_DELAY`找到最小有效值 2. **信号采样次数**:推荐至少连续2次采样一致(如引用[1]所述) 3. **硬件配合**:若抖动严重,可并联电容实现硬件去抖(如引用[1]中提到的100pF电容) ---

void enc(void) { static uint8_t last_AB = 0x03; // 初始状态AB=11 const int8_t dir_table[16] = { 0, -1, 1, 0, // 旧状态00 → 新状态00/01/10/11 1, 0, 0, -1, // 旧状态01 → 新状态00/01/10/11 -1, 0, 0, 1, // 旧状态10 → 新状态00/01/10/11 0, 1, -1, 0 // 旧状态11 → 新状态00/01/10/11 }; // 读取当前AB相状态 uint8_t current_AB = (xc_gpio_read_pin(ENC_A) << 1) | xc_gpio_read_pin(ENC_B); // 计算状态转移索引 uint8_t state_index = (last_AB << 2) | current_AB; // 查表获取方向 int8_t dir = dir_table[state_index]; last_AB = current_AB; // 更新亮度 if (dir == 1) { brightness = (brightness < 100) ? brightness + 1 : 100; } else if (dir == -1) { brightness = (brightness > 30) ? brightness - 1 : 30; } } 在外部中断下降沿处理旋转编码器,AB管脚动作周期4MS左右,旋转一格会产生机械抖动,需要排除

<think>好的,我现在要解决用户关于旋转编码器机械抖动和AB相信号处理的问题。首先,我需要理解用户的需求。他们想通过软件消除机械抖动,并正确解码AB相的状态变化来调整亮度,特别是使用下降沿中断。我需要结合之前提供的引用内容,特别是编码器的工作原理和信号处理部分。 首先,旋转编码器通常输出两相(A和B)的信号,相位差90度,用来确定旋转方向。机械抖动会导致信号在跳变时出现多次抖动,比如在按下或释放时产生多个脉冲,这会影响检测的准确性。软件消抖的常见方法包括延时检测和状态采样。用户提到使用下降沿中断,所以可能需要结合中断和定时器来实现。 根据引用[1],编码器将机械位置转换为电子信号,而脉冲信号处理涉及生成、传输、解码等。因此,处理AB相信号需要准确捕捉边沿并解码方向。引用[2]提到解码符号和时钟数据恢复技术,这可能涉及状态机和定时同步,但用户的问题更偏向软件处理。 接下来,我需要设计一个软件方案。首先,使用下降沿中断触发,当检测到A或B相的下降沿时,启动消抖处理。消抖可以通过延时,比如等待一段时间后再次检测信号状态,确认是否为稳定状态。同时,需要确定旋转方向,这需要比较A和B相在变化时的状态。例如,当A相下降沿触发时,检查B相当前的电平,如果B为高,可能是顺时针;如果为低,可能是逆时针。 需要考虑如何实现消抖。常见的做法是在中断服务程序中启动一个定时器,延迟一段时间(比如5-10ms)后再次检查引脚状态,如果状态仍为低,则视为有效触发。这样可以滤除短暂的抖动信号。但要注意,在中断中不宜做长时间操作,因此可能需要在中断中设置标志位,在主循环中处理状态变化和亮度调整。 另外,状态机的使用可以帮助跟踪AB相的当前和之前状态,以确定方向。例如,记录上一次A和B的状态,当检测到变化时,根据两相的状态变化顺序判断方向。比如,顺时针旋转时,A相下降沿时B相为高;逆时针时,A相下降沿时B相为低。 还需要考虑代码结构,使用中断服务函数处理边沿触发,设置消抖标志和定时器,然后在主循环中处理实际的信号解码和亮度更新。这样避免在中断中执行过多操作,影响系统响应。 可能出现的问题包括中断冲突、定时器资源不足,或者消抖时间设置不当导致信号丢失或误判。需要根据具体的编码器特性和机械抖动情况调整消抖时间。此外,代码需要处理AB相的所有可能状态变化,确保方向判断准确。 总结步骤: 1. 配置A、B相引脚为输入,启用下拉或上拉电阻。 2. 设置A、B相的下降沿中断。 3. 中断触发后,启动消抖定时器,短暂延时后再次检测引脚状态。 4. 确认有效信号后,读取A、B相状态,根据状态变化判断方向。 5. 更新亮度值,确保不超出范围。 6. 主循环处理亮度调整,避免在中断中直接执行耗时操作。 需要参考现有的编码器处理方法和消抖技术,结合具体的硬件平台(如Arduino、STM32等)编写代码。示例代码可能包括中断服务函数、定时器回调、状态变量等部分。</think>针对旋转编码器的机械抖动消除和AB相信号处理,可通过以下软件方案实现: ### 一、硬件连接与信号特性 1. AB相编码器输出两路相位差90°的方波信号 2. 顺时针旋转时A相下降沿对应B相高电平,逆时针时对应低电平[^1] 3. 机械抖动通常持续5-20ms,需通过软件滤波消除 ### 二、软件消抖实现方案 ```c // 伪代码示例(基于STM32 HAL库) #define DEBOUNCE_TIME 10 // 消抖时间10ms volatile uint8_t encoder_flag = 0; TIM_HandleTypeDef htim3; // 下降沿中断服务函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == ENCODER_A_PIN || GPIO_Pin == ENCODER_B_PIN) { HAL_TIM_Base_Start_IT(&htim3); // 启动消抖定时器 } } // 定时器中断回调 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM3) { HAL_TIM_Base_Stop_IT(&htim3); encoder_flag = 1; // 设置有效信号标志 } } ``` ### 三、状态解码算法 1. **四步状态机解码**: ```c static uint8_t last_state = 0; void update_brightness(void) { if(encoder_flag) { uint8_t current_state = (HAL_GPIO_ReadPin(ENCODER_B_GPIO_Port, ENCODER_B_Pin) << 1) | HAL_GPIO_ReadPin(ENCODER_A_GPIO_Port, ENCODER_A_Pin); // 状态转移矩阵 const int8_t state_table[4][4] = { /* 00 */ {0, -1, 1, 0}, /* 01 */ {1, 0, 0, -1}, /* 10 */ {-1, 0, 0, 1}, /* 11 */ {0, 1, -1, 0} }; int8_t direction = state_table[last_state][current_state]; if(direction != 0) { brightness = constrain(brightness + direction, 0, 100); update_display(); } last_state = current_state; encoder_flag = 0; } } ``` 2. **亮度约束函数**: ```c uint8_t constrain(uint8_t value, uint8_t min, uint8_t max) { if(value < min) return min; if(value > max) return max; return value; } ``` ### 四、实现要点 1. 使用**双级消抖**策略:硬件RC滤波(0.1μF电容) + 软件定时滤波 2. 采用**状态机解码**代替简单边沿检测,提高可靠性[^2] 3. 通过**正交解码**实现方向判断,误差容忍度±1个脉冲周期 4. 亮度调整采用**指数曲线**响应,符合人眼感知特性: $$ \Delta = k \times 2^{n/5} \quad (k=1, n为调整次数) $$ ### 五、性能优化 1. 动态调整消抖时间:当检测到连续快速旋转时,自动缩短定时器周期 2. 使用DMA传输状态数据,降低CPU负载 3. 建立**噪声模型**实时监测信号质量: $$ SNR = 10\log_{10}\left(\frac{P_{signal}}{P_{noise}}\right) $$ 当SNR < 20dB时触发自适应滤波算法
阅读全文

相关推荐

static const uint8_t _CRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 };

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private variables ---------------------------------------------------------*/ volatile uint8_t current_angle = 50; // 当前角度 uint8_t target_angle = 50; // 目标角度 int8_t motor_step = 0; // 步进电机当前步序 uint8_t alarm_state = 0; // 报警状态 /* 数码管编码表 (共阳极) */ const uint8_t seg_code[10] = { 0xC0, // 0 → 连接PB0-PB7 0xF9, // 1 0xA4, // 2 0xB0, // 3 0x99, // 4 0x92, // 5 0x82, // 6 0xF8, // 7 0x80, // 8 0x90 // 9 }; /* 步进电机相位序列 (8拍模式) */ const uint8_t motor_phase[8] = { 0x01, // A: IN1 (PA0) 0x03, // AB: IN1+IN2 (PA0+PA1) 0x02, // B: IN2 (PA1) 0x06, // BC: IN2+IN3 (PA1+PA2) 0x04, // C: IN3 (PA2) 0x0C, // CD: IN3+IN4 (PA2+PA3) 0x08, // D: IN4 (PA3) 0x09 // DA: IN4+IN1 (PA3+PA0) }; /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); void Key_Handler(void); void Motor_Control(void); void Display_Angle(void); void Alarm_Check(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); // 初始化步进电机相位 GPIOA->ODR = (GPIOA->ODR & 0xFFF0) | motor_phase[motor_step]; while (1) { Key_Handler(); // 按键检测 Motor_Control(); // 步进电机控制 Alarm_Check(); // 报警检测 Display_Angle(); // 数码管显示 HAL_Delay(5); // 控制步进电机速度 } } void SystemClock_Config(void) { // 系统时钟配置保持不变 } /** * @brief GPIO初始化修复 * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 修复1: 步进电机引脚配置 (PA0-PA3) - 推挽输出 GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 提高驱动能力 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化为低电平 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_RESET); // 修复2: 数码管引脚配置 (PB0,PB1,PB3-PB8) GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4 |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化为高电平(共阳极数码管熄灭状态) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4 |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_SET); // 按键配置 (PA4-PA6) - 上拉输入 GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 报警LED (PA9) - 推挽输出 GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET); // 初始熄灭 } /** * @brief 数码管显示修复 * @retval None */ void Display_Angle(void) { static uint8_t digit = 0; // 当前显示位 static uint32_t last_refresh = 0; // 数码管刷新率控制 (5ms) if (HAL_GetTick() - last_refresh < 5) return; last_refresh = HAL_GetTick(); // 关闭所有数码管段 GPIOB->ODR |= 0x01FB; // 设置PB0,PB1,PB3-PB8为高电平 (0x01FB = 0000 0001 1111 1011) if (digit == 0) { // 显示十位数 uint8_t tens = current_angle / 10; GPIOB->ODR &= ~(seg_code[tens] & 0x00FF); // 清除对应段 digit = 1; } else { // 显示个位数 uint8_t units = current_angle % 10; GPIOB->ODR &= ~(seg_code[units] & 0x00FF); // 清除对应段 // 报警状态下点亮小数点 if (alarm_state) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); } digit = 0; } } /** * @brief 步进电机控制修复 * @retval None */ void Motor_Control(void) { static uint32_t last_step = 0; // 步进电机速度控制 (10ms/步) if (HAL_GetTick() - last_step < 10) return; last_step = HAL_GetTick(); if (current_angle == target_angle) return; // 计算旋转方向 int8_t dir = (target_angle > current_angle) ? 1 : -1; // 更新步序 motor_step = (motor_step + dir + 8) % 8; // 修复3: 直接操作寄存器提高响应速度 GPIOA->ODR = (GPIOA->ODR & 0xFFF0) | motor_phase[motor_step]; // 每11步更新1度角度 static uint8_t step_count = 0; step_count++; if (step_count >= 11) { current_angle += dir; step_count = 0; } } // Key_Handler 和 Alarm_Check 函数保持不变 /* 其余代码保持不变 */ 该代码依然没能使数码管亮,也没能使步进电机正常工作,修正为正确完整的代码

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private variables ---------------------------------------------------------*/ volatile uint8_t current_angle = 50; // 当前角度 (0-99) uint8_t target_angle = 50; // 目标角度 int8_t motor_step = 0; // 步进电机当前步序 uint8_t alarm_state = 0; // 报警状态 /* 数码管编码表 (共阳极) */ const uint8_t seg_code[10] = { 0xC0, // 0 0xF9, // 1 0xA4, // 2 0xB0, // 3 0x99, // 4 0x92, // 5 0x82, // 6 0xF8, // 7 0x80, // 8 0x90 // 9 }; /* 步进电机相位序列 (8拍半步模式) */ const uint8_t motor_phase[8] = { 0x01, // 0001 (IN1) 0x03, // 0011 (IN1+IN2) 0x02, // 0010 (IN2) 0x06, // 0110 (IN2+IN3) 0x04, // 0100 (IN3) 0x0C, // 1100 (IN3+IN4) 0x08, // 1000 (IN4) 0x09 // 1001 (IN4+IN1) }; /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); void Key_Handler(void); void Motor_Control(void); void Display_Angle(void); void Alarm_Check(void); /** * @brief 主函数 * @retval int */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); // 初始化步进电机相位 GPIOA->ODR = (GPIOA->ODR & 0xFFF0) | motor_phase[motor_step]; while (1) { Key_Handler(); // 按键检测 Motor_Control(); // 步进电机控制 Alarm_Check(); // 报警检测 Display_Angle(); // 数码管显示 HAL_Delay(2); // 控制步进电机速度 } } /** * @brief 系统时钟配置 * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO初始化 * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 步进电机引脚配置 (PA0-PA3) GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 数码管引脚配置 (PB0,PB1,PB3-PB8) GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4 |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 按键配置 (PA4-PA6) GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 报警LED (PA9) GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET); // 初始熄灭LED } /** * @brief 按键处理函数 * @retval None */ void Key_Handler(void) { static uint32_t last_tick = 0; static uint8_t debounce = 0; // 按键消抖处理 (20ms) if (HAL_GetTick() - last_tick < 20) return; last_tick = HAL_GetTick(); // 左转按键 (PA4) if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET) { if (debounce < 5) debounce++; if (debounce == 4) { if (target_angle > 0) target_angle--; } } // 右转按键 (PA5) else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) { if (debounce < 5) debounce++; if (debounce == 4) { if (target_angle < 99) target_angle++; } } // 复位按键 (PA6) else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_RESET) { if (debounce < 5) debounce++; if (debounce == 4) { target_angle = 0; } } else { debounce = 0; } } /** * @brief 步进电机控制函数 * @retval None */ void Motor_Control(void) { // 目标角度未变化时保持静止 if (current_angle == target_angle) return; // 计算旋转方向 (1:顺时针, -1:逆时针) int8_t dir = (target_angle > current_angle) ? 1 : -1; // 更新步序 motor_step = (motor_step + dir + 8) % 8; // 应用新相位 (保留PA4-PA9状态,只修改PA0-PA3) uint16_t current_state = GPIOA->ODR & 0xFFF0; // 保留高12位 GPIOA->ODR = current_state | motor_phase[motor_step]; // 每11步更新1度角度 static uint8_t step_count = 0; step_count++; if (step_count >= 11) { current_angle += dir; step_count = 0; } } /** * @brief 报警检测函数 * @retval None */ void Alarm_Check(void) { if (current_angle == 0 || current_angle == 99) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET); // 点亮LED alarm_state = 1; } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET); // 熄灭LED alarm_state = 0; } } /** * @brief 数码管显示函数 * @retval None */ void Display_Angle(void) { static uint8_t digit = 0; // 当前显示位 (0:十位, 1:个位) // 关闭所有数码管 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4 |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_SET); if (digit == 0) { // 显示十位 uint8_t tens = current_angle / 10; GPIOB->ODR = (GPIOB->ODR & 0xFDFF) | (seg_code[tens] << 0); digit = 1; } else { // 显示个位 uint8_t units = current_angle % 10; GPIOB->ODR = (GPIOB->ODR & 0xFDFF) | (seg_code[units] << 0); // 报警状态下小数点闪烁 static uint8_t blink_count = 0; if (alarm_state && (blink_count < 10)) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 点亮DP } else { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 熄灭DP } blink_count = (blink_count + 1) % 20; digit = 0; } } /* 错误处理函数 */ void Error_Handler(void) { __disable_irq(); while (1) {} } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) {} #endif 上述代码数码管不亮,步进电机完全不工作,为什么,帮我修正以下

TFTLCD汉字显示是反着的#include "chinese_font.h" #include "lcd.h" #include <string.h> // LCD分辨率(横屏) #define LCD_WIDTH 240 // 逻辑宽度 #define LCD_HEIGHT 320 // 逻辑高度 //// 中文字模数据(16x16,32 字节/字符) //static const uint8_t font_data[][32] = { // 监控系统 //{0x00,0x02,0x00,0x02,0x7E,0x7E,0x00,0x42,0x00,0x42,0xFF,0x7E,0x00,0x42,0x02,0x42}, //{0x0C,0x42,0xF0,0x7E,0x20,0x42,0x28,0x42,0x26,0x7E,0x20,0x02,0x00,0x02,0x00,0x00},/*"监",0*/ //{0x08,0x40,0x08,0x42,0x08,0x81,0xFF,0xFE,0x09,0x00,0x04,0x02,0x19,0x02,0x12,0x42}, //{0x14,0x42,0x90,0x42,0x70,0x7E,0x14,0x42,0x12,0x42,0x15,0x42,0x18,0x02,0x00,0x00},/*"控",1*/ //{0x00,0x00,0x00,0x42,0x44,0x44,0x4C,0xC8,0x54,0xD0,0x65,0x42,0x45,0x41,0x46,0x7E}, //{0x84,0x40,0x88,0x40,0x90,0x50,0x81,0x48,0x80,0xC4,0x00,0x62,0x00,0x00,0x00,0x00},/*"系",2*/ //{0x04,0x44,0x0C,0xE6,0x35,0x44,0xC6,0x48,0x0C,0x48,0x00,0x01,0x11,0x02,0x13,0x0C}, //{0x15,0xF0,0x99,0x00,0x71,0x00,0x11,0xFC,0x15,0x02,0x13,0x02,0x11,0x8E,0x00,0x00},/*"统",3*/ // // 当前值 //{0x00,0x00,0x02,0x02,0x42,0x22,0x22,0x22,0x1A,0x22,0x02,0x22,0x02,0x22,0xFE,0x22}, //{0x02,0x22,0x02,0x22,0x0A,0x22,0x12,0x22,0x63,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"当",0*/ //{0x10,0x00,0x10,0x00,0x17,0xFF,0x94,0x90,0x74,0x92,0x14,0x91,0x17,0xFE,0x10,0x00}, //{0x10,0x00,0x13,0xF0,0x30,0x02,0xD0,0x01,0x17,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"前",1*/ //{0x00,0x80,0x01,0x00,0x06,0x00,0x1F,0xFF,0xE0,0x02,0x20,0x02,0x27,0xFE,0x25,0x52}, //{0x25,0x52,0xFD,0x52,0x25,0x52,0x25,0x52,0x27,0xFE,0x20,0x02,0x00,0x02,0x00,0x00},/*"值",2*/ // // 阈值 //{0x20,0x00,0x17,0xFC,0x00,0x84,0x40,0xA4,0x40,0x94,0x5F,0xF4,0x40,0x84,0x4E,0xA4}, //{0x4A,0xA4,0x4E,0xA4,0x40,0xC4,0x46,0x54,0x58,0xB4,0x41,0x14,0x42,0x04,0x40,0x0C},/*"阈",0*/ //{0x08,0x40,0x08,0x40,0x0F,0xFC,0x10,0x40,0x10,0x40,0x33,0xF8,0x32,0x08,0x53,0xF8}, //{0x92,0x08,0x13,0xF8,0x12,0x08,0x13,0xF8,0x12,0x08,0x12,0x08,0x1F,0xFE,0x10,0x00},/*"值",1*/ // // 超声波 //{0x08,0x00,0x09,0xFC,0x08,0x44,0x7E,0x44,0x08,0x44,0x08,0x94,0xFF,0x08,0x08,0xFC}, //{0x28,0x84,0x28,0x84,0x2E,0x84,0x28,0xFC,0x28,0x00,0x58,0x00,0x4F,0xFE,0x80,0x00},/*"超",0*/ //{0x01,0x00,0x01,0x00,0xFF,0xFE,0x01,0x00,0x7F,0xFC,0x00,0x00,0x3F,0xF8,0x21,0x08}, //{0x21,0x08,0x3F,0xF8,0x20,0x00,0x20,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x80,0x00},/*"声",1*/ //{0x00,0x20,0x20,0x20,0x10,0x20,0x13,0xFE,0x82,0x22,0x42,0x24,0x4A,0x20,0x0B,0xFC}, //{0x12,0x84,0x12,0x88,0xE2,0x48,0x22,0x50,0x22,0x20,0x24,0x50,0x24,0x88,0x09,0x06},/*"波",2*/ // // 温度 //{0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8}, //{0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00},/*"温",0*/ //{0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20}, //{0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E},/*"度",1*/ // // 湿度 //{0x00,0x00,0x27,0xF8,0x14,0x08,0x14,0x08,0x87,0xF8,0x44,0x08,0x44,0x08,0x17,0xF8}, //{0x11,0x20,0x21,0x20,0xE9,0x24,0x25,0x28,0x23,0x30,0x21,0x20,0x2F,0xFE,0x00,0x00},/*"湿",0*/ //{0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20}, //{0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E},/*"度",1*/ // // 频率 //{0x10,0x00,0x11,0xFE,0x50,0x20,0x5C,0x40,0x51,0xFC,0x51,0x04,0xFF,0x24,0x01,0x24}, //{0x11,0x24,0x55,0x24,0x55,0x24,0x55,0x44,0x84,0x50,0x08,0x88,0x31,0x04,0xC2,0x02},/*"频",0*/ //{0x02,0x00,0x01,0x00,0x7F,0xFC,0x02,0x00,0x44,0x44,0x2F,0x88,0x11,0x10,0x22,0x48}, //{0x4F,0xE4,0x00,0x20,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00},/*"率",1*/ // // 按中间键确认 //{0x10,0x40,0x10,0x20,0x10,0x20,0x13,0xFE,0xFA,0x02,0x14,0x44,0x10,0x40,0x1B,0xFE}, //{0x30,0x88,0xD0,0x88,0x11,0x08,0x10,0xD0,0x10,0x20,0x10,0x50,0x50,0x88,0x23,0x04},/*"按",0*/ //{0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x21,0x08}, //{0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00},/*"中",1*/ //{0x20,0x00,0x13,0xFC,0x10,0x04,0x40,0x04,0x47,0xC4,0x44,0x44,0x44,0x44,0x44,0x44}, //{0x47,0xC4,0x44,0x44,0x44,0x44,0x44,0x44,0x47,0xC4,0x40,0x04,0x40,0x14,0x40,0x08},/*"间",2*/ //{0x20,0x10,0x20,0x10,0x3B,0x7C,0x21,0x14,0x41,0xFE,0x7A,0x14,0xA2,0x7C,0x27,0x10}, //{0xF9,0x7C,0x25,0x10,0x25,0xFE,0x22,0x10,0x2A,0x10,0x35,0x00,0x28,0xFE,0x00,0x00},/*"键",3*/ //{0x00,0x40,0x00,0x40,0xFC,0x7C,0x10,0x84,0x11,0x08,0x22,0xFE,0x3C,0x92,0x64,0x92}, //{0x64,0xFE,0xA4,0x92,0x24,0x92,0x24,0xFE,0x3C,0x92,0x25,0x12,0x21,0x0A,0x02,0x04},/*"确",4*/ //{0x00,0x40,0x20,0x40,0x10,0x40,0x10,0x40,0x00,0x40,0x00,0x40,0xF0,0x40,0x10,0x40}, //{0x10,0xA0,0x10,0xA0,0x10,0xA0,0x15,0x10,0x19,0x10,0x12,0x08,0x04,0x04,0x08,0x02},/*"认",5*/ // // 左/右:调整 //{0x02,0x00,0x02,0x00,0x02,0x00,0xFF,0xFE,0x04,0x00,0x04,0x00,0x04,0x00,0x08,0x00}, //{0x0F,0xF8,0x10,0x80,0x10,0x80,0x20,0x80,0x40,0x80,0x80,0x80,0x3F,0xFE,0x00,0x00},/*"左",0*/ //{0x00,0x00,0x02,0x04,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x00},/*"/",1*/ //{0x02,0x00,0x02,0x00,0x02,0x00,0xFF,0xFE,0x04,0x00,0x04,0x00,0x08,0x00,0x08,0x00}, //{0x1F,0xF8,0x28,0x08,0x48,0x08,0x88,0x08,0x08,0x08,0x08,0x08,0x0F,0xF8,0x08,0x08},/*"右",2*/ //{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, //{0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00},/*":",3*/ //{0x00,0x00,0x47,0xFC,0x24,0x44,0x24,0x44,0x05,0xF4,0x04,0x44,0xE4,0x44,0x27,0xFC}, //{0x24,0x04,0x25,0xF4,0x25,0x14,0x2D,0x14,0x35,0xF4,0x24,0x04,0x04,0x14,0x08,0x08},/*"调",4*/ //{0x08,0x20,0x08,0x20,0xFF,0xBE,0x08,0x44,0x7F,0xA4,0x49,0x24,0x7F,0x28,0x2A,0x10}, //{0x49,0x28,0x88,0x46,0x00,0x00,0x7F,0xFC,0x01,0x00,0x11,0xF8,0x11,0x00,0xFF,0xFE},/*"整",5*/ // // 中间:编辑 //{0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x21,0x08}, //{0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00},/*"中",0*/ //{0x20,0x00,0x13,0xFC,0x10,0x04,0x40,0x04,0x47,0xC4,0x44,0x44,0x44,0x44,0x44,0x44}, //{0x47,0xC4,0x44,0x44,0x44,0x44,0x44,0x44,0x47,0xC4,0x40,0x04,0x40,0x14,0x40,0x08},/*"间",1*/ //{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, //{0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00},/*":",2*/ //{0x10,0x80,0x10,0x40,0x23,0xFC,0x22,0x04,0x4A,0x04,0xFB,0xFC,0x12,0x00,0x22,0x00}, //{0x43,0xFC,0xFB,0x54,0x43,0x54,0x05,0xFC,0x1D,0x54,0xE5,0x54,0x49,0x44,0x01,0x0C},/*"编",3*/ //{0x20,0x00,0x21,0xF8,0x21,0x08,0xFD,0x08,0x41,0xF8,0x50,0x00,0x97,0xFE,0xFD,0x08}, //{0x11,0xF8,0x11,0x08,0x1D,0xF8,0xF1,0x08,0x51,0x3E,0x17,0xC8,0x10,0x08,0x10,0x08},/*"辑",4*/ // // 上/下:选择 //{0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x03,0xF8,0x02,0x00}, //{0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0xFF,0xFE,0x00,0x00},/*"上",0*/ //{0x00,0x00,0x02,0x04,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x00},/*"/",1*/ //{0x00,0x00,0xFF,0xFE,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x40,0x02,0x20}, //{0x02,0x10,0x02,0x08,0x02,0x08,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00},/*"下",2*/ //{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, //{0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00},/*":",3*/ //{0x00,0x40,0x22,0x40,0x12,0x40,0x13,0xF8,0x04,0x40,0x00,0x40,0xF7,0xFC,0x11,0x20}, //{0x11,0x20,0x11,0x20,0x12,0x24,0x12,0x24,0x14,0x1C,0x28,0x00,0x47,0xFE,0x00,0x00},/*"选",4*/ //{0x20,0x00,0x27,0xF8,0x22,0x08,0x21,0x10,0xF0,0xA0,0x20,0x40,0x21,0xB0,0x26,0x4E}, //{0x30,0x40,0xE3,0xF8,0x20,0x40,0x20,0x40,0x27,0xFC,0x20,0x40,0xA0,0x40,0x40,0x40},/*"择",5*/ // // 阈值已保存 //{0x20,0x00,0x17,0xFC,0x00,0x84,0x40,0xA4,0x40,0x94,0x5F,0xF4,0x40,0x84,0x4E,0xA4}, //{0x4A,0xA4,0x4E,0xA4,0x40,0xC4,0x46,0x54,0x58,0xB4,0x41,0x14,0x42,0x04,0x40,0x0C},/*"阈",0*/ //{0x08,0x40,0x08,0x40,0x0F,0xFC,0x10,0x40,0x10,0x40,0x33,0xF8,0x32,0x08,0x53,0xF8}, //{0x92,0x08,0x13,0xF8,0x12,0x08,0x13,0xF8,0x12,0x08,0x12,0x08,0x1F,0xFE,0x10,0x00},/*"值",1*/ //{0x00,0x00,0x3F,0xF0,0x00,0x10,0x00,0x10,0x00,0x10,0x20,0x10,0x20,0x10,0x3F,0xF0}, //{0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x04,0x20,0x04,0x20,0x04,0x1F,0xFC,0x00,0x00},/*"已",2*/ //{0x08,0x00,0x0B,0xF8,0x0A,0x08,0x12,0x08,0x12,0x08,0x33,0xF8,0x30,0x40,0x50,0x40}, //{0x97,0xFC,0x10,0xE0,0x11,0x50,0x12,0x48,0x14,0x44,0x18,0x42,0x10,0x40,0x10,0x40},/*"保",3*/ //{0x04,0x00,0x04,0x00,0xFF,0xFE,0x08,0x00,0x08,0x00,0x13,0xF8,0x10,0x10,0x30,0x20}, //{0x50,0x40,0x97,0xFE,0x10,0x40,0x10,0x40,0x10,0x40,0x10,0x40,0x11,0x40,0x10,0x80},/*"存",4*/ //}; //// ASCII 字模(8x16,16 字节/字符,特殊字符 / 和 :) //static const uint8_t ascii_fonts[][16] = { //{0x00,0x00,0x02,0x04,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x00},/*"/",0*/ //{0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00},/*":",0*/ //}; // 中文字模数据(16x16,32 字节/字符) static const uint8_t font_data[][32] = { // 监控系统 {0x00,0x02,0x00,0x02,0x7E,0x7E,0x00,0x42,0x00,0x42,0xFF,0x7E,0x00,0x42,0x02,0x42, 0x0C,0x42,0xF0,0x7E,0x20,0x42,0x28,0x42,0x26,0x7E,0x20,0x02,0x00,0x02,0x00,0x00}, /*"监",0*/ {0x08,0x40,0x08,0x42,0x08,0x81,0xFF,0xFE,0x09,0x00,0x04,0x02,0x19,0x02,0x12,0x42, 0x14,0x42,0x90,0x42,0x70,0x7E,0x14,0x42,0x12,0x42,0x15,0x42,0x18,0x02,0x00,0x00}, /*"控",1*/ {0x00,0x00,0x00,0x42,0x44,0x44,0x4C,0xC8,0x54,0xD0,0x65,0x42,0x45,0x41,0x46,0x7E, 0x84,0x40,0x88,0x40,0x90,0x50,0x81,0x48,0x80,0xC4,0x00,0x62,0x00,0x00,0x00,0x00}, /*"系",2*/ {0x04,0x44,0x0C,0xE6,0x35,0x44,0xC6,0x48,0x0C,0x48,0x00,0x01,0x11,0x02,0x13,0x0C, 0x15,0xF0,0x99,0x00,0x71,0x00,0x11,0xFC,0x15,0x02,0x13,0x02,0x11,0x8E,0x00,0x00}, /*"统",3*/ // 当前值 {0x00,0x00,0x02,0x02,0x42,0x22,0x22,0x22,0x1A,0x22,0x02,0x22,0x02,0x22,0xFE,0x22, 0x02,0x22,0x02,0x22,0x0A,0x22,0x12,0x22,0x63,0xFF,0x00,0x00,0x00,0x00,0x00,0x00}, /*"当",4*/ {0x10,0x00,0x10,0x00,0x17,0xFF,0x94,0x90,0x74,0x92,0x14,0x91,0x17,0xFE,0x10,0x00, 0x10,0x00,0x13,0xF0,0x30,0x02,0xD0,0x01,0x17,0xFE,0x10,0x00,0x10,0x00,0x00,0x00}, /*"前",5*/ {0x00,0x80,0x01,0x00,0x06,0x00,0x1F,0xFF,0xE0,0x02,0x20,0x02,0x27,0xFE,0x25,0x52, 0x25,0x52,0xFD,0x52,0x25,0x52,0x25,0x52,0x27,0xFE,0x20,0x02,0x00,0x02,0x00,0x00}, /*"值",6*/ // 阈值 {0x00,0x00,0x1F,0xFF,0x80,0x00,0x44,0x08,0x05,0xC8,0x45,0x50,0x45,0xD2,0x44,0x04, 0x7F,0xE8,0x44,0x30,0x55,0xC8,0x4C,0x1C,0x40,0x01,0x7F,0xFF,0x00,0x00,0x00,0x00}, /*"阈",7*/ // 超声波 {0x02,0x01,0x12,0x06,0x12,0xF8,0x12,0x04,0xFF,0xFE,0x12,0x22,0x12,0x22,0x42,0x02, 0x45,0xF2,0x79,0x12,0x41,0x12,0x45,0x12,0x43,0x12,0x7D,0xF2,0x00,0x02,0x00,0x00}, /*"超",8*/ {0x20,0x01,0x28,0x06,0x2B,0xF8,0x2A,0x40,0x2A,0x40,0x2A,0x40,0x2A,0x40,0xFB,0xC0, 0x2A,0x40,0x2A,0x40,0x2A,0x40,0x2A,0x40,0x2B,0xC0,0x28,0x00,0x20,0x00,0x00,0x00}, /*"声",9*/ {0x08,0x20,0x06,0x20,0x40,0x3E,0x30,0xC0,0x03,0x01,0x00,0x06,0x1F,0xF8,0x11,0x01, 0x11,0xC2,0x11,0x34,0xFF,0x08,0x11,0x14,0x11,0x62,0x15,0x81,0x18,0x01,0x00,0x00}, /*"波",10*/ // 温度 {0x08,0x20,0x06,0x20,0x40,0x7E,0x31,0x80,0x00,0x02,0x00,0x7E,0x7F,0x42,0x49,0x42, 0x49,0x7E,0x49,0x42,0x49,0x7E,0x49,0x42,0x7F,0x42,0x00,0x7E,0x00,0x02,0x00,0x00}, /*"温",11*/ {0x00,0x02,0x00,0x0C,0x3F,0xF1,0x24,0x01,0x24,0x21,0x24,0x32,0x3F,0xAA,0xA4,0xA4, 0x64,0xA4,0x24,0xA4,0x3F,0xAA,0x24,0x32,0x24,0x01,0x24,0x01,0x20,0x01,0x00,0x00}, /*"度",12*/ // 湿度 {0x08,0x20,0x06,0x20,0x40,0x7E,0x31,0x80,0x00,0x22,0x7F,0x12,0x49,0x0A,0x49,0xFE, 0x49,0x02,0x49,0x02,0x49,0xFE,0x49,0x0A,0x7F,0x12,0x00,0x22,0x00,0x02,0x00,0x00}, /*"湿",13*/ // 频率 {0x02,0x09,0x3E,0x71,0x02,0x02,0xFE,0xF2,0x12,0x04,0x12,0x78,0x02,0x01,0x4F,0xF2, 0x48,0x04,0x58,0x18,0x6B,0xE0,0x48,0x08,0x48,0x04,0x4F,0xF2,0x40,0x01,0x00,0x00}, /*"频",14*/ {0x00,0x10,0x28,0x90,0x25,0x10,0x22,0x10,0x24,0x90,0x2C,0x90,0xB5,0x90,0x66,0xBF, 0x24,0x90,0x29,0x90,0x20,0xD0,0x22,0x10,0x25,0x10,0x28,0x90,0x00,0x10,0x00,0x00}, /*"率",15*/ // 按中间键确认 {0x08,0x40,0x08,0x42,0x08,0x81,0xFF,0xFE,0x09,0x00,0x04,0x00,0x19,0x01,0x11,0x21, 0x11,0xD2,0x97,0x14,0x71,0x08,0x11,0x14,0x11,0xE2,0x15,0x01,0x19,0x00,0x00,0x00}, /*"按",16*/ {0x00,0x00,0x00,0x00,0x0F,0xF0,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0xFF,0xFF, 0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00}, /*"中",17*/ {0x00,0x00,0x1F,0xFF,0x80,0x00,0x60,0x00,0x00,0x00,0x0F,0xF8,0x48,0x88,0x48,0x88, 0x48,0x88,0x4F,0xF8,0x40,0x00,0x40,0x02,0x40,0x01,0x7F,0xFE,0x00,0x00,0x00,0x00}, /*"间",18*/ {0x02,0x80,0x0C,0x80,0xF7,0xFE,0x24,0x84,0x24,0x8A,0x01,0x64,0x27,0x18,0x39,0xE4, 0x08,0x22,0x2A,0xA2,0x2A,0xA2,0xFF,0xFA,0x2A,0xA2,0x3E,0xA2,0x08,0x22,0x00,0x00}, /*"键",19*/ {0x20,0x40,0x21,0x80,0x27,0xFE,0x3A,0x08,0x22,0x08,0x23,0xFC,0x04,0x01,0x08,0x06, 0x17,0xF8,0xE4,0x90,0x24,0x90,0x27,0xFC,0x2C,0x92,0x34,0x91,0x07,0xFE,0x00,0x00}, /*"确",20*/ {0x02,0x00,0x02,0x00,0x42,0x00,0x33,0xFC,0x00,0x09,0x00,0x12,0x00,0x04,0x00,0x18, 0x00,0xE0,0xFF,0x00,0x00,0xE0,0x00,0x18,0x00,0x04,0x00,0x02,0x00,0x01,0x00,0x00}, /*"认",21*/ // 左/右:调整 {0x10,0x04,0x10,0x08,0x10,0x12,0x10,0x62,0x11,0x82,0x1E,0x82,0xF0,0x82,0x10,0x82, 0x10,0xFE,0x10,0x82,0x10,0x82,0x10,0x82,0x10,0x82,0x10,0x02,0x10,0x02,0x00,0x00}, /*"左",22*/ {0x10,0x10,0x10,0x20,0x10,0x40,0x10,0x80,0x13,0xFF,0x1C,0x82,0xF0,0x82,0x10,0x82, 0x10,0x82,0x10,0x82,0x10,0x82,0x10,0x82,0x10,0xFF,0x10,0x00,0x10,0x00,0x00,0x00}, /*"右",23*/ {0x02,0x00,0x42,0x00,0x33,0xFC,0x00,0x08,0x00,0x11,0x7F,0xFE,0x41,0x00,0x49,0x78, 0x49,0x48,0x7F,0x48,0x49,0x48,0x49,0x7A,0x41,0x01,0x7F,0xFE,0x00,0x00,0x00,0x00}, /*"调",24*/ {0x20,0x41,0x2E,0x91,0x2B,0x11,0x2A,0x17,0xFF,0xD1,0x2A,0x11,0x2B,0x11,0x2E,0x9F, 0x28,0x15,0x10,0x55,0xEE,0x95,0x21,0x15,0x22,0x95,0x3C,0x51,0x20,0x41,0x00,0x00}, /*"整",25*/ // 中间:编辑 {0x04,0x44,0x0C,0xE6,0x35,0x44,0xC6,0x48,0x0C,0x4A,0x00,0x1C,0x3F,0xE0,0x24,0xFF, 0xA4,0x90,0x64,0xFE,0x24,0x90,0x24,0xFC,0x24,0x91,0x3C,0xFF,0x00,0x00,0x00,0x00}, /*"编",26*/ {0x13,0x10,0x1D,0x18,0xF1,0x10,0x17,0xFF,0x11,0x20,0x13,0x24,0x02,0x04,0x7B,0xFC, 0x4A,0xA4,0x4A,0xA4,0x4A,0xA8,0x4A,0xA8,0x7B,0xFF,0x02,0x08,0x02,0x08,0x00,0x00}, /*"辑",27*/ // 上/下:选择 {0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0xFF,0xFE,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x02,0x00,0x02,0x00,0x00}, /*"上",28*/ {0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0xFF,0x40,0x00, 0x40,0x00,0x42,0x00,0x41,0x00,0x40,0x80,0x40,0x60,0x40,0x00,0x40,0x00,0x00,0x00}, /*"下",29*/ {0x02,0x00,0x02,0x02,0x42,0x04,0x33,0xF8,0x00,0x04,0x0A,0x0A,0x72,0x32,0x13,0xC2, 0x12,0x02,0xFE,0x02,0x13,0xF2,0x12,0x0A,0x12,0x0A,0x02,0x3A,0x00,0x02,0x00,0x00}, /*"选",30*/ {0x08,0x42,0x08,0x41,0xFF,0xFE,0x08,0x80,0x00,0x00,0x41,0x08,0x61,0x48,0x52,0x48, 0x4A,0x48,0x45,0xFF,0x4A,0x48,0x52,0x48,0x61,0x48,0x01,0x08,0x01,0x00,0x00,0x00}, /*"择",31*/ // 阈值已保存 {0x00,0x00,0x00,0x00,0x47,0xFC,0x41,0x02,0x41,0x02,0x41,0x02,0x41,0x02,0x41,0x02, 0x41,0x02,0x41,0x02,0x41,0x02,0x7F,0x02,0x00,0x02,0x00,0x1E,0x00,0x00,0x00,0x00}, /*"已",32*/ {0x00,0x80,0x01,0x00,0x06,0x00,0x1F,0xFF,0xE0,0x04,0x00,0x88,0x7C,0x90,0x44,0xA0, 0x44,0xC0,0x47,0xFF,0x44,0xC0,0x44,0xA0,0x7C,0x90,0x00,0x88,0x00,0x04,0x00,0x00}, /*"保",33*/ {0x20,0x40,0x20,0x80,0x21,0x00,0x27,0xFF,0x38,0x00,0xE0,0x40,0x24,0x40,0x24,0x42, 0x24,0x41,0x24,0xFE,0x25,0x40,0x26,0x40,0x24,0x40,0x20,0x40,0x20,0x40,0x00,0x00}, /*"存",34*/ }; // ASCII 字模(8x16,16 字节/字符,特殊字符 / 和 :) static const uint8_t ascii_fonts[][16] = { {0x00,0x00,0x00,0x06,0x00,0x18,0x00,0xE0,0x03,0x00,0x1C,0x00,0x20,0x00,0x00,0x00}, /*"/"*/ {0x00,0x00,0x00,0x00,0x00,0x6C,0x00,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /*":"*/ }; // 中文字符索引表 static const ChineseFont chinese_fonts[] = { {"监", font_data[0], 16}, {"控", font_data[1], 16}, {"系", font_data[2], 16}, {"统", font_data[3], 16}, {"当", font_data[4], 16}, {"前", font_data[5], 16}, {"值", font_data[6], 16}, {"阈", font_data[7], 16}, {"超", font_data[8], 16}, {"声", font_data[9], 16}, {"波", font_data[10], 16}, {"温", font_data[11], 16}, {"度", font_data[12], 16}, {"湿", font_data[13], 16}, {"频", font_data[14], 16}, {"率", font_data[15], 16}, {"按", font_data[16], 16}, {"中", font_data[17], 16}, {"间", font_data[18], 16}, {"键", font_data[19], 16}, {"确", font_data[20], 16}, {"认", font_data[21], 16}, {"左", font_data[22], 16}, {"右", font_data[23], 16}, {"调", font_data[24], 16}, {"整", font_data[25], 16}, {"编", font_data[26], 16}, {"辑", font_data[27], 16}, {"上", font_data[28], 16}, {"下", font_data[29], 16}, {"选", font_data[30], 16}, {"择", font_data[31], 16}, {"已", font_data[32], 16}, {"保", font_data[33], 16}, {"存", font_data[34], 16}, {NULL, NULL, 0} // 结束标志 }; // 显示中文字符串 void LCD_ShowChinese(uint16_t x, uint16_t y, const char *str, uint8_t size, uint16_t color) { if (size != 16) return; // 仅支持 16x16 字体 uint16_t x0 = x; while (*str) { if (*str == '/') { LCD_ShowChar(x, y, '/', size, color); // 显示 ASCII 字符 / x += 8; // 8 像素宽 str++; } else if (*str == ':') { LCD_ShowChar(x, y, ':', size, color); // 显示 ASCII 字符 : x += 8; // 8 像素宽 str++; } else { // 查找中文字符 for (int i = 0; chinese_fonts[i].ch != NULL; i++) { if (strncmp(str, chinese_fonts[i].ch, strlen(chinese_fonts[i].ch)) == 0) { // 显示 16x16 中文字模 for (int row = 0; row < 16; row++) { uint16_t data = (chinese_fonts[i].data[row * 2] << 8) | chinese_fonts[i].data[row * 2 + 1]; for (int col = 0; col < 16; col++) { if (data & (0x8000 >> col)) { lcd_draw_point(x + col, y + row, color); // lcd_draw_point(y + row,x + col, color); } } } x += 16; // 16 像素宽 str += strlen(chinese_fonts[i].ch); break; } } } } } //void LCD_ShowChinese(uint16_t x, uint16_t y, const char *str, uint8_t size, uint16_t color) //{ // if (size != 16) return; // 仅支持 16x16 字体 // uint16_t x0 = x; // while (*str) { // if (*str == '/') { // // 转换坐标以适应硬件180度翻转和顺时针90度旋转 // uint16_t new_x = y; // x -> y // uint16_t new_y = LCD_WIDTH - (x + 8); // y -> LCD_WIDTH - x - 字符宽度 // LCD_ShowChar(new_x, new_y, '/', size, color); // 显示 ASCII 字符 / // x += 8; // 8 像素宽 // str++; // } else if (*str == ':') { // // 转换坐标 // uint16_t new_x = y; // uint16_t new_y = LCD_WIDTH - (x + 8); // LCD_ShowChar(new_x, new_y, ':', size, color); // 显示 ASCII 字符 : // x += 8; // 8 像素宽 // str++; // } else { // // 查找中文字符 // for (int i = 0; chinese_fonts[i].ch != NULL; i++) { // if (strncmp(str, chinese_fonts[i].ch, strlen(chinese_fonts[i].ch)) == 0) { // // 转换坐标以适应硬件180度翻转和顺时针90度旋转 // uint16_t new_x = y; // x -> y // uint16_t new_y = LCD_WIDTH - (x + 16); // y -> LCD_WIDTH - x - 字符宽度 // // 显示 16x16 中文字模 // for (int row = 0; row < 16; row++) { // uint16_t data = (chinese_fonts[i].data[row * 2] << 8) | chinese_fonts[i].data[row * 2 + 1]; // for (int col = 0; col < 16; col++) { // if (data & (0x8000 >> col)) { // // 映射到顺时针90度旋转后的点阵:(row, col) -> (col, 15-row) // lcd_draw_point(new_x + col, new_y + (15 - row), color); // } // } // } // x += 16; // 16 像素宽 // str += strlen(chinese_fonts[i].ch); // break; // } // } // } // } //} // 显示 ASCII 字符(为 / 和 : 提供支持) void LCD_ShowChar(uint16_t x, uint16_t y, char ch, uint8_t size, uint16_t color) { if (size != 16) return; // 仅支持 16 像素高 int idx = (ch == '/') ? 0 : (ch == ':') ? 1 : -1; if (idx < 0) return; for (int row = 0; row < 16; row++) { uint8_t data = ascii_fonts[idx][row]; for (int col = 0; col < 8; col++) { if (data & (0x80 >> col)) { lcd_draw_point(x + col, y + row, color); } } } }

#include "stm32f4xx.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" // ?????? #define SEG_PORT GPIOB #define SEG_PINS (GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | \ GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_12) // PB4-10,12 #define DIGIT_SEL_PORT GPIOB #define DIGIT_SEL_PINS (GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15) // 138 ABC?? #define MATRIX_COL_PORT GPIOC #define MATRIX_COL_PINS 0x00FF // PC0-PC7 #define MATRIX_ROW_PORT GPIOC #define MATRIX_ROW_PINS (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10) // 138 ABC?? // ???????(??????) const uint16_t SEGMENT_CODES[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, // 0-4 0x92, 0x82, 0xF8, 0x80, 0x90 // 5-9 }; const uint8_t DISPLAY_DIGITS[] = {2,3,4,1,7,0,1,0}; // ????:23417010 // "?"?????(???,??) const uint8_t GONG_MATRIX[8] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF // ???? }; void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; // ??GPIO?? RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE); // ??????? GPIO_InitStruct.GPIO_Pin = SEG_PINS | DIGIT_SEL_PINS; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStruct); // ?????? GPIO_InitStruct.GPIO_Pin = MATRIX_COL_PINS | MATRIX_ROW_PINS; GPIO_Init(GPIOC, &GPIO_InitStruct); // ????? GPIOB->ODR |= SEG_PINS; // ????? GPIOC->ODR = 0xFF; // ????? } // ??????(??SysTick???) void Delay(uint32_t ms) { uint32_t ticks = ms * 16800; // 168MHz?? SysTick->LOAD = ticks - 1; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); SysTick->CTRL = 0; } // ???????(?????) void ShowDigit(uint8_t num, uint8_t pos) { // ???? GPIOB->ODR |= SEG_PINS;

static uint8_t oled_buffer[OLED_HEIGHT/8][OLED_WIDTH]; void OLED_Init(void) { // ???? HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_SET); HAL_Delay(20); // SSD1315??????? OLED_WriteCmd(0xAE); // ???? OLED_WriteCmd(0xD5); // ???????? OLED_WriteCmd(0x80); // ????? OLED_WriteCmd(0xA8); // ?????(Multiplex Ratio) OLED_WriteCmd(0x3F); // 1/64 Duty (128x64) OLED_WriteCmd(0xD3); // ??????(Display Offset) OLED_WriteCmd(0x00); // ??? OLED_WriteCmd(0x40); // ?????(Start Line) OLED_WriteCmd(0x8D); // ?????(Charge Pump) OLED_WriteCmd(0x14); // ???????(??!) OLED_WriteCmd(0x20); // ??????(Memory Mode) OLED_WriteCmd(0x00); // ??????(Horizontal Addressing) OLED_WriteCmd(0xA1); // ????(SEG0 -> SEG127) OLED_WriteCmd(0xC8); // COM????(Reverse COM Scan) OLED_WriteCmd(0xDA); // COM?????? OLED_WriteCmd(0x12); // Sequential COM Pin Config OLED_WriteCmd(0x81); // ?????(Contrast) OLED_WriteCmd(0xCF); // ????(0-255) OLED_WriteCmd(0xD9); // ?????(Pre-charge Period) OLED_WriteCmd(0xF1); // Phase1=15, Phase2=1 OLED_WriteCmd(0xDB); // VCOMH??(VCOMH Deselect Level) OLED_WriteCmd(0x40); // ?0.77xVCC OLED_WriteCmd(0xA4); // ???RAM??(Disable Entire Display On) OLED_WriteCmd(0xA6); // ????(???) OLED_WriteCmd(0xAF); // ????(Display On) // ??????? memset(oled_buffer, 0, sizeof(oled_buffer)); OLED_Refresh(); } void OLED_WriteCmd(uint8_t cmd) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &cmd, 1, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } void OLED_WriteData(uint8_t dat) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &dat, 1, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } void OLED_Refresh(void) { for (uint8_t page = 0; page < 8; page++) { OLED_WriteCmd(0xB0 + page); // ?????(Page 0~7) OLED_WriteCmd(0x00); // ????4?(0x00) OLED_WriteCmd(0x10); // ????4?(0x10) for (uint8_t col = 0; col < 128; col++) { OLED_WriteData(oled_buffer[page][col]); } } } void OLED_ShowChar(uint8_t x, uint8_t y, char chr, uint8_t size) { if (chr < 32 || chr > 126) return; if (x >= OLED_WIDTH || y >= 8) return; // SSD1317????8(0~7) const uint8_t *glyph = font8x8[chr - 32]; for (uint8_t col = 0; col < 8; col++) { oled_buffer[y][x + col] = glyph[col]; } } void OLED_ShowString(uint8_t x, uint8_t y, char *str, uint8_t size) { while(*str) { OLED_ShowChar(x, y, *str++, size); x += 8; if(x >= OLED_WIDTH-8) { x = 0; y += 8; } } } void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size) { char str[10]; snprintf(str, sizeof(str), "%*u", len, num); OLED_ShowString(x, y, str, size); } void OLED_ShowFloat(uint8_t x, uint8_t y, float num, uint8_t precision, uint8_t size) { char str[10]; snprintf(str, sizeof(str), "%.*f", precision, num); OLED_ShowString(x, y, str, size); }修改上面的代码让oled屏幕显示旋转90°

/* * sp0821.c sp0821 yuv module * * Author: Bruce <[email protected]> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sp_sp0821_yuv.h" #include "../../../aw37004/aw37004.h" #include <soc/oplus/system/oplus_project.h> #include #include #include #include extern void ISP_MCLK3_EN (bool En); extern int aw37004_camera_power_up(int out_iotype, unsigned int out_val); #define kal_uint16 unsigned short #define kal_uint32 unsigned int /***************************************************************** * sp0821 marco ******************************************************************/ #define SP0821_DRIVER_VERSION "V2.0" #define SP0821_PRODUCT_NUM 4 #define SP0821_PRODUCT_NAME_LEN 8 #define SP0821_SENSOR_ID 0x3a6c #define SP0821_MCLK_ON "sp0821_mclk_on" #define SP0821_MCLK_OFF "sp0821_mclk_off" /***************************************************************** * sp0821 global global variable ******************************************************************/ static unsigned char read_reg_id = 0; static unsigned char read_reg_value = 0; static int read_reg_flag = 0; static int driver_flag = 0; struct sp0821 *g_sp0821 = NULL; /********************************************************** * i2c write and read **********************************************************/ static void sp0821_i2c_write(struct sp0821 *sp0821, int address, int data) { u8 i2c_buf[8]; struct i2c_client *client = sp0821->i2c_client; struct i2c_msg msg[1]; msg[0].flags = !I2C_M_RD; msg[0].addr = client->addr; msg[0].len = 3; msg[0].buf = i2c_buf; i2c_buf[0] = (address & 0xff00)>>8; i2c_buf[1] = (address & 0xff); i2c_buf[2] = data; i2c_transfer(client->adapter, msg, 1); //printk("write sp0821 addr: 0x%4X val:0x%4X\n", address, data); } static unsigned char sp0821_i2c_read(struct sp0821 *sp0821, int address) { unsigned char rxdata = 0x00; unsigned char i2c_buf[4]; int ret = 0; int retry = 2; u8 i2c_addr[2]; struct i2c_client *client = sp0821->i2c_client; struct i2c_msg msgs[2]; i2c_addr[0] = (address & 0xff00)>>8; i2c_addr[1] = (address & 0xff); msgs[0].flags = 0; msgs[0].addr = (client->addr); msgs[0].len = 2; msgs[0].buf = i2c_addr; msgs[1].flags = I2C_M_RD; msgs[1].addr = (client->addr); msgs[1].len = 1; msgs[1].buf = i2c_buf; while (retry > 0) { ret = i2c_transfer(client->adapter, msgs, 2); //qvga_dev_err(&client->dev, "%s: read step1 ret:%d msgs[1].addr=%x\n", __func__, ret, msgs[1].addr); if (retry >0) { mdelay(20); if (ret == 0) { return 0; } } retry--; mdelay(2); } rxdata = i2c_buf[0]; return rxdata; } static struct sp0821 *sp0821_malloc_init(struct i2c_client *client) { struct sp0821 *sp0821 = devm_kzalloc(&client->dev, sizeof(struct sp0821), GFP_KERNEL); if (sp0821 == NULL) { qvga_dev_err(&client->dev, "%s: devm_kzalloc failed.\n", __func__); return NULL; } sp0821->i2c_client = client; pr_info("%s enter , client_addr = 0x%02x\n", __func__, sp0821->i2c_client->addr); return sp0821; } #if 1 void sp0821_Init(struct sp0821 *sp0821) { /*SYS MCLK=24MHZ*/ sp0821_i2c_write(sp0821, 0x0103,0x01); sp0821_i2c_write(sp0821, 0x0100,0x00); sp0821_i2c_write(sp0821, 0x309b,0xf0); sp0821_i2c_write(sp0821, 0x30b0,0x0a); sp0821_i2c_write(sp0821, 0x30b8,0x21); sp0821_i2c_write(sp0821, 0x320c,0x01); sp0821_i2c_write(sp0821, 0x320d,0x6a); sp0821_i2c_write(sp0821, 0x320e,0x01); sp0821_i2c_write(sp0821, 0x320f,0xba); sp0821_i2c_write(sp0821, 0x3301,0x04); sp0821_i2c_write(sp0821, 0x3304,0x0c); sp0821_i2c_write(sp0821, 0x3305,0x00); sp0821_i2c_write(sp0821, 0x3306,0x10); sp0821_i2c_write(sp0821, 0x3307,0x02); sp0821_i2c_write(sp0821, 0x3308,0x04); sp0821_i2c_write(sp0821, 0x330a,0x00); sp0821_i2c_write(sp0821, 0x330b,0x30); sp0821_i2c_write(sp0821, 0x330e,0x01); sp0821_i2c_write(sp0821, 0x330f,0x01); sp0821_i2c_write(sp0821, 0x3310,0x01); sp0821_i2c_write(sp0821, 0x331e,0x09); sp0821_i2c_write(sp0821, 0x3333,0x10); sp0821_i2c_write(sp0821, 0x3334,0x40); sp0821_i2c_write(sp0821, 0x334c,0x01); sp0821_i2c_write(sp0821, 0x33b3,0x3e); sp0821_i2c_write(sp0821, 0x349f,0x02); sp0821_i2c_write(sp0821, 0x34a6,0x01); sp0821_i2c_write(sp0821, 0x34a7,0x07); sp0821_i2c_write(sp0821, 0x34a8,0x3a); sp0821_i2c_write(sp0821, 0x34a9,0x38); sp0821_i2c_write(sp0821, 0x34e9,0x38); sp0821_i2c_write(sp0821, 0x34f8,0x07); sp0821_i2c_write(sp0821, 0x3630,0x65); sp0821_i2c_write(sp0821, 0x3637,0x47); sp0821_i2c_write(sp0821, 0x363a,0xe0); sp0821_i2c_write(sp0821, 0x3670,0x03); sp0821_i2c_write(sp0821, 0x3674,0x75); sp0821_i2c_write(sp0821, 0x3675,0x65); sp0821_i2c_write(sp0821, 0x3676,0x65); sp0821_i2c_write(sp0821, 0x367c,0x01); sp0821_i2c_write(sp0821, 0x367d,0x03); sp0821_i2c_write(sp0821, 0x3690,0xe0); sp0821_i2c_write(sp0821, 0x3691,0xe1); sp0821_i2c_write(sp0821, 0x3692,0xe1); sp0821_i2c_write(sp0821, 0x3693,0xe1); sp0821_i2c_write(sp0821, 0x3694,0x03); sp0821_i2c_write(sp0821, 0x3695,0x07); sp0821_i2c_write(sp0821, 0x3696,0x07); sp0821_i2c_write(sp0821, 0x37f9,0x29); sp0821_i2c_write(sp0821, 0x3900,0x91); sp0821_i2c_write(sp0821, 0x3904,0x0f); sp0821_i2c_write(sp0821, 0x3908,0x00); sp0821_i2c_write(sp0821, 0x391b,0x07); sp0821_i2c_write(sp0821, 0x391c,0x0a); sp0821_i2c_write(sp0821, 0x391d,0x15); sp0821_i2c_write(sp0821, 0x391e,0x28); sp0821_i2c_write(sp0821, 0x391f,0x41); sp0821_i2c_write(sp0821, 0x3948,0x00);//blc sp0821_i2c_write(sp0821, 0x4509,0x10); sp0821_i2c_write(sp0821, 0x470b,0x0a); sp0821_i2c_write(sp0821, 0x470d,0x06); sp0821_i2c_write(sp0821, 0x5000,0xc2); sp0821_i2c_write(sp0821, 0x5001,0x01); sp0821_i2c_write(sp0821, 0x5170,0x2c); sp0821_i2c_write(sp0821, 0x5172,0xc1); sp0821_i2c_write(sp0821, 0x518b,0x00);//again sp0821_i2c_write(sp0821, 0x518c,0x20); sp0821_i2c_write(sp0821, 0x518d,0x01);//shutter sp0821_i2c_write(sp0821, 0x518e,0x7c); sp0821_i2c_write(sp0821, 0x518f,0x00); sp0821_i2c_write(sp0821, 0x519e,0x10); sp0821_i2c_write(sp0821, 0x300a,0x00);//SIP input sp0821_i2c_write(sp0821, 0x0100,0x01); /*shutter gain must write after stream on */ sp0821_i2c_write(sp0821, 0x518b,0x03);//again=4x sp0821_i2c_write(sp0821, 0x518c,0x20); sp0821_i2c_write(sp0821, 0x518d,0x01);//shutter=20ms sp0821_i2c_write(sp0821, 0x518e,0xb0); sp0821_i2c_write(sp0821, 0x518f,0x00); sp0821_i2c_write(sp0821, 0x519e,0x10); } /* sensor_init */ #endif int sp0821_GetSensorID(struct sp0821 *sp0821) { int retry = 2; unsigned char reg_data = 0x00; //check if sensor ID correct do { reg_data = sp0821_i2c_read(sp0821, 0x3107)<<8|sp0821_i2c_read(sp0821, 0x3108); qvga_dev_err(sp0821->dev, "drv-%s: Read MSB Sensor ID = 0x%02x\n", __func__, reg_data); // if (reg_data == SP0821_SENSOR_ID) { if (1) { qvga_dev_err(sp0821->dev, "drv-%s: Read Sensor ID sucess = 0x%02x\n", __func__, reg_data); driver_flag = 1; return 0; } else { qvga_dev_err(sp0821->dev, "rv-%s: Read Sensor ID Fail = 0x%02x\n", __func__, reg_data); driver_flag = 0; } mdelay(10); retry--; } while (retry > 0); return -1; } static void sp0821_vcam_control(struct sp0821 *sp0821, bool flag) { // struct regulator *vcama; struct regulator *vcamio; struct regulator *vcamd; int ret; int ret1; qvga_dev_info(sp0821->dev, "%s enter\n", __func__); vcamd = regulator_get(sp0821->dev,"vcamd"); if (IS_ERR(vcamd)) { qvga_dev_err(sp0821->dev, "%s get regulator vcamd failed\n", __func__); regulator_put(vcamd); return; } else { qvga_dev_err(sp0821->dev, "%s get regulator vcamd success\n", __func__); } if (flag) { regulator_set_voltage(vcamd, 1200000, 1200000); ret = regulator_enable(vcamd); } else { regulator_disable(vcamd); } // vcama = regulator_get(sp0821->dev,"vcama"); // if (IS_ERR(vcama)) { // qvga_dev_err(sp0821->dev, "%s get regulator vcama failed\n", __func__); // regulator_put(vcama); // return; // } // if (flag) { // regulator_set_voltage(vcama, 2800000, 2800000); // ret = regulator_enable(vcama); // } else { // regulator_disable(vcama); // } if (flag) { ret1 = aw37004_camera_power_up(2, 2800); if (ret1 == 0) { qvga_dev_err(sp0821->dev, "%s get regulator vcama success\n", __func__); } else { qvga_dev_err(sp0821->dev, "%s get regulator vcama failed\n", __func__); } } vcamio = regulator_get(sp0821->dev,"vcamio"); if (IS_ERR(vcamio)) { qvga_dev_err(sp0821->dev, "%s get regulator vcamio failed\n", __func__); regulator_put(vcamio); return; } else { qvga_dev_err(sp0821->dev, "%s get regulator vcamio success\n", __func__); } if (flag) { regulator_set_voltage(vcamio, 1800000, 1800000); ret = regulator_enable(vcamio); } else { regulator_disable(vcamio); } return; } static void sp0821_hw_on_reset(struct sp0821 *sp0821) { qvga_dev_info(sp0821->dev, "%s enter\n", __func__); if (gpio_is_valid(sp0821->reset_gpio)) { gpio_set_value_cansleep(sp0821->reset_gpio, 1); } } static void sp0821_hw_on_reset1(struct sp0821 *sp0821) { qvga_dev_info(sp0821->dev, "%s enter\n", __func__); if (gpio_is_valid(sp0821->reset_gpio1)) { gpio_set_value_cansleep(sp0821->reset_gpio1, 1); } } static void sp0821_hw_off_reset(struct sp0821 *sp0821) { qvga_dev_info(sp0821->dev, "%s enter\n", __func__); if (gpio_is_valid(sp0821->reset_gpio)) { gpio_set_value_cansleep(sp0821->reset_gpio, 0); udelay(50); gpio_set_value_cansleep(sp0821->reset_gpio, 1); udelay(50); gpio_set_value_cansleep(sp0821->reset_gpio, 0); } } static void sp0821_hw_off_reset1(struct sp0821 *sp0821) { qvga_dev_info(sp0821->dev, "%s enter\n", __func__); if (gpio_is_valid(sp0821->reset_gpio1)) { gpio_set_value_cansleep(sp0821->reset_gpio1, 0); } } static void sp0821_hw_on(struct sp0821 *sp0821) { sp0821_hw_on_reset1(sp0821); sp0821_hw_on_reset(sp0821); sp0821_Init(sp0821); sp0821->hwen_flag = 1; } static void sp0821_hw_off(struct sp0821 *sp0821) { sp0821_hw_off_reset(sp0821); sp0821->hwen_flag = 0; } static ssize_t sp0821_get_reg(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; if (read_reg_flag) { len += snprintf(buf + len, PAGE_SIZE - len, "The reg 0x%02X value is 0x%02X\n", read_reg_id, read_reg_value); read_reg_flag = 0; read_reg_id = 0; read_reg_value = 0; } else { len += snprintf(buf + len, PAGE_SIZE - len, "Please echo reg id into reg\n"); } return len; } static ssize_t sp0821_set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { unsigned int databuf[2] = { 0 }; unsigned char reg_data = 0x00; if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { sp0821_i2c_write(g_sp0821, databuf[0], databuf[1]); } else if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 1) { reg_data = sp0821_i2c_read(g_sp0821, databuf[0]); read_reg_id = databuf[0]; read_reg_value = reg_data; read_reg_flag = 1; } return len; } static ssize_t sp0821_get_name(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; if (driver_flag) { len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", "sp_sp0821_yuv"); } else { len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", "none"); } return len; } static ssize_t sp0821_get_light(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; unsigned char reg_data1 = 0x00; //unsigned char reg_data2 = 0x00; u16 light = 0; reg_data1 = sp0821_i2c_read(g_sp0821, 0x5160); //reg_data2 = sp0821_i2c_read(g_sp0821, 0x516b); //light = (reg_data1<<8) + reg_data2; light = reg_data1; qvga_dev_err(g_sp0821->dev, "%s: sp0821 light=%d, %d\n", __func__, light, reg_data1); len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", light); return len; } static ssize_t sp0821_set_light(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { ssize_t ret; unsigned int state; ret = kstrtouint(buf, 10, &state); if (ret) { qvga_dev_err(g_sp0821->dev, "%s: fail to change str to int\n", __func__); return ret; } if (state == 0) sp0821_hw_off(g_sp0821); /*OFF*/ else sp0821_hw_on(g_sp0821); /*ON*/ return len; } static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, sp0821_get_reg, sp0821_set_reg); static DEVICE_ATTR(cam_name, S_IWUSR | S_IRUGO, sp0821_get_name, NULL); static DEVICE_ATTR(light, S_IWUSR | S_IRUGO, sp0821_get_light, sp0821_set_light); static struct attribute *sp0821_attributes[] = { &dev_attr_reg.attr, &dev_attr_cam_name.attr, &dev_attr_light.attr, NULL }; static struct attribute_group sp0821_attribute_group = { .attrs = sp0821_attributes }; static void sp0821_parse_gpio_dt(struct sp0821 *sp0821, struct device_node *np) { qvga_dev_info(sp0821->dev, "%s enter, dev_i2c%d@0x%02X\n", __func__, sp0821->i2c_seq, sp0821->i2c_addr); sp0821->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); if (sp0821->reset_gpio < 0) { qvga_dev_err(sp0821->dev, "%s: no reset gpio provided, hardware reset unavailable\n", __func__); sp0821->reset_gpio = -1; } else { qvga_dev_info(sp0821->dev, "%s: reset gpio provided ok\n", __func__); } sp0821->reset_gpio1 = of_get_named_gpio(np, "reset-gpio1", 0); if (sp0821->reset_gpio1 < 0) { qvga_dev_err(sp0821->dev, "%s: no reset gpio1 provided, hardware reset unavailable\n", __func__); sp0821->reset_gpio1 = -1; } else { qvga_dev_info(sp0821->dev, "%s: reset gpio1 provided ok\n", __func__); } } static void sp0821_parse_dt(struct sp0821 *sp0821, struct device_node *np) { qvga_dev_info(sp0821->dev, "%s enter, dev_i2c%d@0x%02X\n", __func__, sp0821->i2c_seq, sp0821->i2c_addr); sp0821_parse_gpio_dt(sp0821, np); } /**************************************************************************** * sp0821 i2c driver *****************************************************************************/ static int sp0821_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *np = client->dev.of_node; struct pinctrl *sp0821_pinctrl = NULL; struct pinctrl_state *set_state = NULL; struct pinctrl_state *sp0821_mclk_on = NULL; struct pinctrl_state *sp0821_mclk_off = NULL; struct sp0821 *sp0821 = NULL; struct class *qvga_class; struct device *dev; int ret = -1; pr_err("scw %s enter , i2c%d@0x%02x\n", __func__, client->adapter->nr, client->addr); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { qvga_dev_err(&client->dev, "%s: check_functionality failed\n", __func__); ret = -ENODEV; goto exit_check_functionality_failed; } sp0821 = sp0821_malloc_init(client); g_sp0821 = sp0821; sp0821->i2c_seq = sp0821->i2c_client->adapter->nr; sp0821->i2c_addr = sp0821->i2c_client->addr; if (sp0821 == NULL) { dev_err(&client->dev, "%s: failed to parse device tree node\n", __func__); ret = -ENOMEM; goto exit_devm_kzalloc_failed; } sp0821->dev = &client->dev; i2c_set_clientdata(client, sp0821); sp0821_parse_dt(sp0821, np); if (gpio_is_valid(sp0821->reset_gpio)) { ret = devm_gpio_request_one(&client->dev, sp0821->reset_gpio, GPIOF_OUT_INIT_LOW, "sp0821_rst"); if (ret) { qvga_dev_err(&client->dev, "%s: rst request failed\n", __func__); goto exit_gpio_request_failed; } } if (gpio_is_valid(sp0821->reset_gpio1)) { ret = devm_gpio_request_one(&client->dev, sp0821->reset_gpio1, GPIOF_OUT_INIT_LOW, "sp0821_rst1"); if (ret) { qvga_dev_err(&client->dev, "%s: rst1 request failed\n", __func__); goto exit_gpio_request_failed; } } sp0821_pinctrl = devm_pinctrl_get(&client->dev); if (IS_ERR_OR_NULL(sp0821_pinctrl)) { qvga_dev_err(&client->dev, "%s: sp0821_pinctrl not defined\n", __func__); } else { set_state = pinctrl_lookup_state(sp0821_pinctrl, SP0821_MCLK_ON); if (IS_ERR_OR_NULL(set_state)) { qvga_dev_err(&client->dev, "%s: sp0821_pinctrl lookup failed for mclk on\n", __func__); } else { sp0821_mclk_on = set_state; } set_state = pinctrl_lookup_state(sp0821_pinctrl, SP0821_MCLK_OFF); if (IS_ERR_OR_NULL(set_state)) { qvga_dev_err(&client->dev, "%s: sp0821_pinctrl lookup failed for mclk off\n", __func__); } else { sp0821_mclk_off = set_state; } ret = pinctrl_select_state(sp0821_pinctrl, sp0821_mclk_off); if (ret < 0) { qvga_dev_err(&client->dev, "%s: sp0821_pinctrl select failed for mclk off\n", __func__); } } //power on camera sp0821_hw_off_reset1(sp0821); mdelay(5); sp0821_vcam_control(sp0821, true); mdelay(1); ret = pinctrl_select_state(sp0821_pinctrl, sp0821_mclk_on); if (ret < 0) { qvga_dev_err(&client->dev, "%s: sp0821_pinctrl select failed for mclk on\n", __func__); } sp0821_hw_on_reset1(sp0821); sp0821_hw_on_reset(sp0821); mdelay(5); // sp0821->hwen_flag = 1; /* sp0821 sensor id */ ret = sp0821_GetSensorID(sp0821); if (ret < 0) { qvga_dev_err(&client->dev, "%s: sp0821read_sensorid failed ret=%d\n", __func__, ret); goto exit_i2c_check_id_failed; } //power off camera sp0821_vcam_control(sp0821, false); sp0821_hw_off_reset1(sp0821); // sp0821_Init(sp0821); qvga_class = class_create(THIS_MODULE, "qvga_cam"); dev = device_create(qvga_class, NULL, client->dev.devt, NULL, "qvga_depth"); ret = sysfs_create_group(&dev->kobj, &sp0821_attribute_group); if (ret < 0) { qvga_dev_err(&client->dev, "%s failed to create sysfs nodes\n", __func__); } return 0; exit_i2c_check_id_failed: sp0821_vcam_control(sp0821, false); sp0821_hw_off_reset1(sp0821); if (gpio_is_valid(sp0821->reset_gpio)) devm_gpio_free(&client->dev, sp0821->reset_gpio); exit_gpio_request_failed: devm_kfree(&client->dev, sp0821); sp0821 = NULL; exit_devm_kzalloc_failed: exit_check_functionality_failed: return ret; } static int sp0821_i2c_remove(struct i2c_client *client) { struct sp0821 *sp0821 = i2c_get_clientdata(client); if (gpio_is_valid(sp0821->reset_gpio)) devm_gpio_free(&client->dev, sp0821->reset_gpio); if (gpio_is_valid(sp0821->reset_gpio1)) devm_gpio_free(&client->dev, sp0821->reset_gpio1); devm_kfree(&client->dev, sp0821); sp0821 = NULL; return 0; } static const struct of_device_id sp0821_of_match[] = { {.compatible = "sc,sp_sp0821_yuv"}, {}, }; static struct i2c_driver sp0821_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "sp_sp0821_yuv", .of_match_table = sp0821_of_match, }, .probe = sp0821_i2c_probe, .remove = sp0821_i2c_remove, }; static int __init sp0821_yuv_init(void) { int ret; pr_info("%s: driver version: %s\n", __func__, SP0821_DRIVER_VERSION); ret = i2c_add_driver(&sp0821_i2c_driver); if (ret) { pr_info("****[%s] Unable to register driver (%d)\n", __func__, ret); return ret; } return 0; } static void __exit sp0821_yuv_exit(void) { pr_info("%s enter\n", __func__); i2c_del_driver(&sp0821_i2c_driver); } module_init(sp0821_yuv_init); module_exit(sp0821_yuv_exit); MODULE_AUTHOR("[email protected]>"); MODULE_DESCRIPTION("sp0821 yuv driver"); MODULE_LICENSE("GPL v2"); 这个代码哪里有问题可能会导致编译报错的?

/*********************************** 本驱动文件仅适配HAL库版本 ***********************************/ // 在 oled.c 文件顶部添加 // 声明外部定义的句柄 #include "font.h" //字库文件 #include "oled.h" //声明 extern I2C_HandleTypeDef hi2c1; /* 控制宏 */ #define LEFT 0x27 #define RIGHT 0x26 #define UP 0X29 #define DOWM 0x2A #define ON 0xA7 #define OFF 0xA6 /* IIC接口选择 */ #define IICx hi2c1 //oled显示尺寸 uint16_t const displayWidth = 128; uint16_t const displayHeight = 64; /* OLED显存 [0]0 1 2 3 ... 127 [1]0 1 2 3 ... 127 [2]0 1 2 3 ... 127 [3]0 1 2 3 ... 127 [4]0 1 2 3 ... 127 [5]0 1 2 3 ... 127 [6]0 1 2 3 ... 127 [7]0 1 2 3 ... 127 */ static uint8_t OLED_RAM[8][128];//定义GDDRAM缓存区 void HAL_I2C_WriteByte(uint8_t addr,uint8_t data) { uint8_t TxData[2] = {addr,data}; HAL_I2C_Master_Transmit(&IICx,0X78,(uint8_t*)TxData,2,10); } /************************************************************** Prototype : void WriteCmd(uint8_t IIC_Command) Parameters : IIC_Command return : none Description : 写命令 ***************************************************************/ void WriteCmd(uint8_t IIC_Command) { HAL_I2C_WriteByte(0x00, IIC_Command); } /************************************************************** Prototype : void WriteDat(uint8_t IIC_Data) Parameters : IIC_Data return : none Description : 写数据 ***************************************************************/ void WriteDat(uint8_t IIC_Data) { HAL_I2C_WriteByte(0x40, IIC_Data); } /************************************************************** Prototype : void OLED_Init(void) Parameters : none return : none Description : 初始化OLED模块 ***************************************************************/ void OLED_Init(void) { HAL_Delay(500); WriteCmd(0xAE); //开显示 WriteCmd(0x20); //设置内存寻址模式 WriteCmd(0x10); //00,水平寻址模式;01,垂直寻址模式;10,页面寻址模式(重置);11,无效 WriteCmd(0xb0); //为页面寻址模式设置页面开始地址,0-7 WriteCmd(0x00); //---设置低列地址 WriteCmd(0x10); //---设置高列地址 WriteCmd(0xc8); //设置COM输出扫描方向 WriteCmd(0x40); //--设置起始行地址 WriteCmd(0x81); //--set contrast control register WriteCmd(0xff); //亮度调节 0x00~0xff WriteCmd(0xa1); //--设置段重新映射0到127 WriteCmd(0xa6); //--设置正常显示 WriteCmd(0xa8); //--设置复用比(1 ~ 64) WriteCmd(0x3F); // WriteCmd(0xa4); //0xa4,输出遵循RAM内容;0xa5,Output忽略RAM内容 WriteCmd(0xd3); //-设置显示抵消 WriteCmd(0x00); //-not offset WriteCmd(0xd5); //--设置显示时钟分频/振荡器频率 WriteCmd(0xf0); //--设置分率 WriteCmd(0xd9); //--设置pre-charge时期 WriteCmd(0x22); // WriteCmd(0xda); //--设置com大头针硬件配置 WriteCmd(0x12); WriteCmd(0xdb); //--设置vcomh WriteCmd(0x20); //0x20,0.77xVcc WriteCmd(0x8d); //--设置DC-DC WriteCmd(0x14); // WriteCmd(0xaf); //--打开oled面板 OLED_FullyClear();//清屏 } /************************************************************** Prototype : void OLED_ON(void) Parameters : none return : none Description : 将OLED从休眠中唤醒 ***************************************************************/ void OLED_ON(void) { WriteCmd(0X8D); //设置电荷泵 WriteCmd(0X14); //开启电荷泵 WriteCmd(0XAF); //OLED唤醒 } /************************************************************** Prototype : void OLED_OFF(void) Parameters : none return : none Description : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA ***************************************************************/ void OLED_OFF(void) { WriteCmd(0X8D); //设置电荷泵 WriteCmd(0X10); //关闭电荷泵 WriteCmd(0XAE); //OLED休眠 } /************************************************************** Prototype : void OLED_RefreshRAM(void) Parameters : none return : none Description : 全屏填充 ***************************************************************/ void OLED_RefreshRAM(void) { // 页寻址模式填充 for(uint16_t m = 0; m < displayHeight/8; m++) { WriteCmd(0xb0+m); //设置页地址b0~b7 WriteCmd(0x00); //设置显示位置—列低地址00-0f WriteCmd(0x10); //设置显示位置—列高地址10-1f for(uint16_t n = 0; n < displayWidth; n++) { WriteDat(OLED_RAM[m][n]); } } } /************************************************************** Prototype : void OLED_ClearRAM(void) Parameters : none return : none Description : 清除数据缓冲区 ***************************************************************/ void OLED_ClearRAM(void) { for(uint16_t m = 0; m < displayHeight/8; m++) { for(uint16_t n = 0; n < displayWidth; n++) { OLED_RAM[m][n] = 0x00; } } } /************************************************************** Prototype : void OLED_Fill(uint8_t fill_Data) Parameters : fill_Data 填充的1字节数据 return : none Description : 全屏填充 0x00~0xff ***************************************************************/ void OLED_FullyFill(uint8_t fill_Data) { for(uint16_t m = 0; m < displayHeight/8; m++) { for(uint16_t n = 0; n < displayWidth; n++) { OLED_RAM[m][n] = fill_Data; } } OLED_RefreshRAM(); } /************************************************************** Prototype : void OLED_FullyClear(void) Parameters : none return : none Description : 全屏清除 ***************************************************************/ void OLED_FullyClear(void) { OLED_FullyFill(RESET_PIXEL); } /************************************************************** Prototype : void OLED_SetPixel(int16_t x, int16_t y, uint8_t set_pixel) Parameters : x,y -- 起始点坐标(x:0~127, y:0~63); set_pixel 该点的数据 SET_PIXEL = 1, RESET_PIXEL = 0 return : none Description : 设置坐标像素点数据 ***************************************************************/ void OLED_SetPixel(int16_t x, int16_t y, uint8_t set_pixel) { if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight) { if(set_pixel){ OLED_RAM[y/8][x] |= (0x01 << (y%8)); } else{ OLED_RAM[y/8][x] &= ~(0x01 << (y%8)); } } } /************************************************************** Prototype : void OLED_GetPixel(int16_t x, int16_t y) Parameters : x,y -- 起始点坐标(x:0~127, y:0~63); return : PixelStatus 像素点状态 SET_PIXEL = 1, RESET_PIXEL = 0 Description : 获得坐标像素点数据 ***************************************************************/ PixelStatus OLED_GetPixel(int16_t x, int16_t y) { if(OLED_RAM[y/8][x] >> (y%8) & 0x01) return SET_PIXEL; return RESET_PIXEL; } /************************************************************** Prototype : void OLED_ShowStr(int16_t x, int16_t y, uint8_t ch[], uint8_t TextSize) Parameters : x,y -- 起始点坐标(x:0~127, y:0~63); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16) return : none Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择 ***************************************************************/ void OLED_ShowStr(int16_t x, int16_t y, uint8_t ch[], uint8_t TextSize) { if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight) { int32_t c = 0; uint8_t j = 0; switch(TextSize) { case 1: { while(ch[j] != '\0') { c = ch[j] - 32; if(c < 0) //无效字符 break; if(x >= 125 || (127-x < 6))//一行最大显示字符数:21字节显示,多出两列,不显示 || 剩余列小于6不能显示完整字符,换行显示 { x = 0; y += 8;//换行显示 if(63 - y < 8) // 不足以显示一行时不显示 break; } for(uint8_t m = 0; m < 6; m++) { for(uint8_t n = 0; n < 8; n++) { OLED_SetPixel(x+m, y+n, (F6x8[c][m] >> n) & 0x01); } } x += 6; j++; } }break; case 2: { while(ch[j] != '\0') { c = ch[j] - 32; if(c < 0) //无效字符 break; if(x >= 127 || (127-x < 8))//16字节显示 || 剩余列小于8不能显示完整字符,换行显示 { x = 0; y += 16;//换行显示 if(63 - y < 16) // 不足以显示一行时不显示 break; } for(uint8_t m = 0; m < 2; m++) { for(uint8_t n = 0; n < 8; n++) { for(uint8_t i = 0; i < 8; i++) { OLED_SetPixel(x+n, y+i+m*8, (F8X16[c][n+m*8] >> i) & 0x01); } } } x += 8; j++; } }break; } } OLED_RefreshRAM(); } /************************************************************** Prototype : void OLED_ShowCN(int16_t x, int16_t y, uint8_t* ch) Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); CN[]:汉字在codetab.h中的索引 return : none Description : 显示codetab.h中的汉字,16*16点阵 ***************************************************************/ void OLED_ShowCN(int16_t x, int16_t y, uint8_t* ch) { if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight) { int32_t len = 0,offset = sizeof(F16x16_CN[0].index); while(ch[len] != '\0') { if(x >= 127 || (127-x < 16))//8个汉字显示||剩余列小于16不能显示完整字符,换行显示 { x = 0; y += 16; if(63 - y < 16) // 不足以显示一行时不显示 break; } //需要处理输入数据大于显示数据的问题 for(uint8_t i = 0; i < sizeof(F16x16_CN)/sizeof(GB2312_CN); i++) { if(((F16x16_CN[i].index[0] == ch[len]) && (F16x16_CN[i].index[1] == ch[len+1]))){ for(uint8_t m = 0; m < 2; m++) //页 { for(uint8_t n = 0; n < 16; n++) // 列 { for(uint8_t j = 0; j < 8; j++) // 行 { OLED_SetPixel(x+n, y+j+m*8, (F16x16_CN[i].encoder[n+m*16] >> j) & 0x01); } } } x += 16; len += offset; break; } else if(F16x16_CN[i].index[0] == ch[len] && ch[len] == 0x20){ for(uint8_t m = 0; m < 2; m++) { for(uint8_t n = 0; n < 16; n++) { for(uint8_t j = 0; j < 8; j++) { OLED_SetPixel(x+n, y+j+m*8, (F16x16_CN[i].encoder[n+m*16] >> j) & 0x01); } } } x += 16; len++; break; } } } } OLED_RefreshRAM(); } /************************************************************** Prototype : void OLED_Show_MixedCH(int16_t x, int16_t y, uint8_t* ch) Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); CN[]:汉字在codetab.h中的索引 return : none Description : 显示codetab.h中的汉字,16*16点阵,英文,8*16点阵 ***************************************************************/ void OLED_ShowMixedCH(int16_t x, int16_t y, uint8_t* ch) { if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight) { int32_t len = 0, c,offset = sizeof(F16x16_CN[0].index); while(ch[len] != '\0') { if(ch[len] >= 0xa1)//GB2312从0xA1A0开始 { for(uint8_t i = 0; i < sizeof(F16x16_CN)/sizeof(GB2312_CN); i++) { if(((F16x16_CN[i].index[0] == ch[len]) && (F16x16_CN[i].index[1] == ch[len+1]))) { if(x >= 127|| (127-x < 16))//8个汉字显示||剩余列小于16不能显示完整字符,换行显示 { x = 0; y += 16; if(63 - y < 16) // 不足以显示一行时不显示 break; } for(uint8_t m = 0; m < 2; m++) //页 { for(uint8_t n = 0; n < 16; n++) //列 { for(uint8_t j = 0; j < 8; j++) //行 { OLED_SetPixel(x+n, y+j+m*8, (F16x16_CN[i].encoder[n+m*16] >> j) & 0x01); } } } x += 16; len += offset; break; } } } else if(ch[len] <= 127)//ASCII编码范围0-127 { c = ch[len] - 32; if(c < 0) // 无效字符 break; if(x >= 127 || (127-x < 8))//16字节显示 || 剩余列小于8不能显示完整字符,换行显示 { x = 0; y += 16; if(63 - y < 16) // 不足以显示一行时不显示 break; } for(uint8_t m = 0; m < 2; m++) { for(uint8_t n = 0; n < 8; n++) { for(uint8_t i = 0; i < 8; i++) { OLED_SetPixel(x+n, y+i+m*8, (F8X16[c][n+m*8] >> i) & 0x01); } } } x += 8; len++; } } } OLED_RefreshRAM(); } /*************************************************************** Prototype : void OLED_DrawBMP(int16_t x0,int16_t y0,int16_t L,int16_t H,const uint8_t BMP[]) Parameters : (x0,y0)坐标长L宽H区域绘制图像BMP 0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 图像取模 纵向取模,字节倒序 return : none Description : 区域图像绘制,显示BMP位图,格式使用二维数组存储 ***************************************************************/ void OLED_DrawBMP(int16_t x0,int16_t y0,int16_t L,int16_t H,const uint8_t BMP[]) { if (x0 >= 0 && x0 < displayWidth && x0+L <= displayWidth &&\ y0 >= 0 && y0 < displayHeight && y0+H <= displayHeight) { uint8_t *p = (uint8_t *)BMP; for(int16_t y = y0; y < y0+H; y+=8) { for(int16_t x = x0; x < x0+L; x++) { for(int16_t i = 0; i < 8; i++) { // OLED_SetPixel(x, y+i, ((*((uint8_t *)BMP+(x-x0)+L*((y-y0)/8))) >> i) & 0x01); OLED_SetPixel(x, y+i, ((*p) >> i) & 0x01); } p++; } } } OLED_RefreshRAM(); } /*************************************************************** Prototype : void OLED_AreaFill(int16_t x0,int16_t y0,int16_t L,int16_t H) Parameters : 区域内容清除,(x0,y0)坐标长L宽H区域 0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 图像取模 纵向取模,字节倒序 return : none Description : 规定区域内容填充 ***************************************************************/ void OLED_AreaFill(int16_t x0,int16_t y0,int16_t L,int16_t H, uint8_t fill_data) { if (x0 >= 0 && x0 < displayWidth && x0+L <= displayWidth &&\ y0 >= 0 && y0 < displayHeight && y0+H <= displayHeight) { for(int16_t y = y0; y < y0+H; y++) { for(int16_t x = x0; x < x0+L; x++) { for(int16_t i = 0; i < 8; i++) { OLED_SetPixel(x, y+i, (fill_data >> i) & SET_PIXEL); } } } OLED_RefreshRAM(); } } /*************************************************************** Prototype : void OLED_AreaCLR(int16_t x0,int16_t y0,int16_t L,int16_t H) Parameters : (x0,y0)坐标长L宽H区域 0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 图像取模 纵向取模,字节倒序 return : none Description : 规定区域内容清除 ***************************************************************/ void OLED_AreaClear(int16_t x0,int16_t y0,int16_t L,int16_t H) { if (x0 >= 0 && x0 < displayWidth && x0+L <= displayWidth &&\ y0 >= 0 && y0 < displayHeight && y0+H <= displayHeight) { for(int16_t y = y0; y < y0+H; y+=8) { for(int16_t x = x0; x < x0+L; x++) { for(int16_t i = 0; i < 8; i++) { OLED_SetPixel(x, y+i, RESET_PIXEL); } } } OLED_RefreshRAM(); } } /*************************************************************** Prototype : void OLED_FullyToggle(void) Parameters : none return : none Description : 缓冲区数据取反后刷新到GDDRAM ***************************************************************/ void OLED_FullyToggle(void) { for(uint16_t m = 0; m < displayHeight/8; m++) { for(uint16_t n = 0; n < displayWidth; n++) { OLED_RAM[m][n] = ~OLED_RAM[m][n]; } } OLED_RefreshRAM(); } /*************************************************************** Prototype : void OLED_AreaToggle(int16_t x0,int16_t y0,int16_t L,int16_t H) Parameters : (x0,y0)坐标长L宽H区域 0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 图像取模 纵向取模,字节倒序 return : none Description : 规定区域内容取反 ***************************************************************/ void OLED_AreaToggle(int16_t x0,int16_t y0,int16_t L,int16_t H) { if (x0 >= 0 && x0 < displayWidth && x0+L <= displayWidth &&\ y0 >= 0 && y0 < displayHeight && y0+H <= displayHeight) { for(int16_t y = y0; y < y0+H; y+=8) { for(int16_t x = x0; x < x0+L; x++) { for(int16_t i = 0; i < 8; i++) { OLED_SetPixel(x, y+i, !OLED_GetPixel(x, y+i)); } } } OLED_RefreshRAM(); } } /**************************************************************** 全屏垂直偏移,0->63方向 方向垂直向上,范围0-63 方向垂直向下,范围63-0 ****************************************************************/ void OLED_VerticalShift(void) { for(uint8_t i = 0; i < displayHeight; i++) { WriteCmd(0xd3);//设置显示偏移,0->63方向 WriteCmd(i);//偏移量 HAL_Delay(40);//延时时间 } } /**************************************************************** 屏幕内容水平全屏滚动播放 左 LEFT 0x27 右 RIGHT 0x26 ****************************************************************/ void OLED_HorizontalShift(uint8_t direction) { WriteCmd(direction);//设置滚动方向 WriteCmd(0x00);//虚拟字节设置,默认为0x00 WriteCmd(0x00);//设置开始页地址 WriteCmd(0x05);//设置每个滚动步骤之间的时间间隔的帧频 WriteCmd(0x07);//设置结束页地址 WriteCmd(0x00);//虚拟字节设置,默认为0x00 WriteCmd(0xff);//虚拟字节设置,默认为0xff WriteCmd(0x2f);//开启滚动-0x2f,禁用滚动-0x2e,禁用需要重写数据 } /**************************************************************** 屏幕内容垂直水平全屏滚动播放 上 UP 0x29 下 DOWN 0x2A ****************************************************************/ void OLED_VerticalAndHorizontalShift(uint8_t direction) { WriteCmd(direction);//设置滚动方向 WriteCmd(0x00);//虚拟字节设置,默认为0x00 WriteCmd(0x00);//设置开始页地址 WriteCmd(0x05);//设置每个滚动步骤之间的时间间隔的帧频 WriteCmd(0x07);//设置结束页地址 WriteCmd(0x01);//垂直滚动偏移量 WriteCmd(0x2f);//开启滚动-0x2f,禁用滚动-0x2e,禁用需要重写数据 } /**************************************************************** 屏幕内容取反显示 开 ON 0xA7 关 OFF 0xA6 默认此模式,设置像素点亮 ****************************************************************/ void OLED_DisplayMode(uint8_t mode) { WriteCmd(mode); } /**************************************************************** 屏幕亮度调节 intensity 0-255 默认为0x7f ****************************************************************/ void OLED_IntensityControl(uint8_t intensity) { WriteCmd(0x81); WriteCmd(intensity); }这些代码怎么在main.c中调用使用

大家在看

recommend-type

adlink 凌华IO卡 PCI-Dask.dll说明资料 功能参考手册

关于 PCI-Dask.dll 方法函数的说明文件,ADLINK的多款IO板卡的编程说明和支持文件。 PCI-6202/PCI-6208A/cPCI-6208A/PCI-6208V/16V/cPCI-6208V/PCI-6308A/PCI-6308V/PCI-7200/c/PCI-7200/PCI-7230/cPCI-7230/PCI-7233/PCI-7233H/PCI-7234/PCI-7224/PCI-7248/cPCI-7248/cPCI-7249R/PCI-7250/cPCI-7252/PCI-7256/PCI-7258/PCI-7260/PCI-7296/PCI-7300A/cPCI-7300A/PCI-7348/PCI-7350/PCI-7396/PCI-7432/cPCI-7432/PCI-7433/cPCI-7433/PCI-7434/cPCI-7434/cPCI-7432R/cPCI-7433R/cPCI-7434R/PCI-7442/PCI-744
recommend-type

基于YOLO网络的行驶车辆目标检测matlab仿真+操作视频

1.领域:matlab,YOLO网络的行驶车辆目标检测算法 2.内容:基于YOLO网络的行驶车辆目标检测matlab仿真+操作视频 3.用处:用于YOLO网络的行驶车辆目标检测算法编程学习 4.指向人群:本硕博等教研学习使用 5.运行注意事项: 使用matlab2021a或者更高版本测试,运行里面的Runme_.m文件,不要直接运行子函数文件。运行时注意matlab左侧的当前文件夹窗口必须是当前工程所在路径。 具体可观看提供的操作录像视频跟着操作。
recommend-type

JSON,VC++简单交互纯源码!

VC实现json向服务端发送数据,基本可以完成数据发送和解析工作了。具体相应功能请测试后,资源来自网络!
recommend-type

matlab对excel数据批处理实战案例二.rar

matlab对excel数据批处理实战案例二
recommend-type

MarkdownEditor精简绿色版

MarkdownEditor精简绿色版

最新推荐

recommend-type

基于PLC的电机控制系统设计.doc

基于PLC的电机控制系统设计.doc
recommend-type

高中生物《基因工程的原理》教案.docx

高中生物《基因工程的原理》教案.docx
recommend-type

基于密度的聚类算法能够在含有噪声的数据集中识别出任意形状和大小的簇附Matlab代码.rar

1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
recommend-type

2023年新版java题库.doc

2023年新版java题库.doc
recommend-type

飞思OA数据库文件下载指南

根据给定的文件信息,我们可以推断出以下知识点: 首先,从标题“飞思OA源代码[数据库文件]”可以看出,这里涉及的是一个名为“飞思OA”的办公自动化(Office Automation,简称OA)系统的源代码,并且特别提到了数据库文件。OA系统是用于企事业单位内部办公流程自动化的软件系统,它旨在提高工作效率、减少不必要的工作重复,以及增强信息交流与共享。 对于“飞思OA源代码”,这部分信息指出我们正在讨论的是OA系统的源代码部分,这通常意味着软件开发者或维护者拥有访问和修改软件底层代码的权限。源代码对于开发人员来说非常重要,因为它是软件功能实现的直接体现,而数据库文件则是其中的一个关键组成部分,用来存储和管理用户数据、业务数据等信息。 从描述“飞思OA源代码[数据库文件],以上代码没有数据库文件,请从这里下”可以分析出以下信息:虽然文件列表中提到了“DB”,但实际在当前上下文中,并没有提供包含完整数据库文件的下载链接或直接说明,这意味着如果用户需要获取完整的飞思OA系统的数据库文件,可能需要通过其他途径或者联系提供者获取。 文件的标签为“飞思OA源代码[数据库文件]”,这与标题保持一致,表明这是一个与飞思OA系统源代码相关的标签,而附加的“[数据库文件]”特别强调了数据库内容的重要性。在软件开发中,标签常用于帮助分类和检索信息,所以这个标签在这里是为了解释文件内容的属性和类型。 文件名称列表中的“DB”很可能指向的是数据库文件。在一般情况下,数据库文件的扩展名可能包括“.db”、“.sql”、“.mdb”、“.dbf”等,具体要看数据库的类型和使用的数据库管理系统(如MySQL、SQLite、Access等)。如果“DB”是指数据库文件,那么它很可能是以某种形式的压缩文件或包存在,这从“压缩包子文件的文件名称列表”可以推测。 针对这些知识点,以下是一些详细的解释和补充: 1. 办公自动化(OA)系统的构成: - OA系统由多个模块组成,比如工作流管理、文档管理、会议管理、邮件系统、报表系统等。 - 系统内部的流程自动化能够实现任务的自动分配、状态跟踪、结果反馈等。 - 通常,OA系统会提供用户界面来与用户交互,如网页形式的管理界面。 2. 数据库文件的作用: - 数据库文件用于存储数据,是实现业务逻辑和数据管理的基础设施。 - 数据库通常具有数据的CRUD(创建、读取、更新、删除)功能,是信息检索和管理的核心组件。 - 数据库文件的结构和设计直接关系到系统的性能和可扩展性。 3. 数据库文件类型: - 根据数据库管理系统不同,数据库文件可以有不同格式。 - 例如,MySQL数据库的文件通常是“.frm”文件存储表结构,“.MYD”存储数据,“.MYI”存储索引。 - 对于SQLite,数据库就是一个单独的“.sqlite”文件。 4. 数据库设计和管理: - 数据库设计需要遵循一定的规范和最佳实践,如范式化以减少数据冗余。 - 管理数据库包括数据备份、恢复、性能调优、安全性管理等。 5. OA系统开发及源代码维护: - 开发一个OA系统涉及需求分析、系统设计、编码实现、测试、部署和维护等多个阶段。 - OA系统源代码的维护工作包括代码审查、重构、版本控制和更新等。 综上所述,我们可以知道“飞思OA源代码[数据库文件]”所提供的信息包括了对飞思OA系统及其数据库文件的基础介绍和可能的获取途径。对于开发者而言,需要了解如何管理和操作这些数据库文件以确保系统的正常运行和数据的完整性。此外,对于那些希望进行系统定制或二次开发的用户,源代码的可访问性是非常重要的,这可以允许用户根据自己的需求来修改和增强系统功能。
recommend-type

Qt信号与槽优化:提升系统性能与响应速度的实战技巧

# 摘要 本文系统地探讨了Qt框架中信号与槽机制的原理、高级特性、性能优化以及在大型项目中的应用。首先,概述了信号与槽的基本概念和工作原理,包括信号的发射、槽函数的绑定与调用过程。随后,本文深入分析了信号与槽的内部机制,如数据类型兼容性和连接类型,并探讨了优化信
recommend-type

D8流向算法

<think>我们被要求解释或实现D8流向算法。D8流向算法是一种用于水文分析的数字高程模型(DEM)处理方法,用于确定地表水流的方向。每个像元的水流方向被指定为指向周围8个相邻像元中坡度最陡的那个方向。 ### 算法原理 在D8算法中,每个像元的水流方向被定义为指向其8个相邻像元(包括对角线方向)中坡度最大的方向。坡度由高程差除以距离计算,其中相邻像元的距离为1(水平和垂直方向)或√2(对角线方向)。具体步骤如下: 1. 对于中心像元,计算其与8个相邻像元的高程差(中心像元高程减去相邻像元高程,得到正值表示下坡)。 2. 计算每个相邻方向的坡度:坡度 = 高程差 / 距离(水平/垂直方向
recommend-type

精选36个精美ICO图标免费打包下载

在当今的软件开发和应用程序设计中,图标作为图形用户界面(GUI)的一个重要组成部分,承担着向用户传达信息、增加美观性和提高用户体验的重要角色。图标不仅仅是一个应用程序或文件的象征,它还是品牌形象在数字世界中的延伸。因此,开发人员和设计师往往会对默认生成的图标感到不满意,从而寻找更加精美和个性化的图标资源。 【标题】中提到的“精美ICO图标打包下载”,指向用户提供的是一组精选的图标文件,这些文件格式为ICO。ICO文件是一种图标文件格式,主要被用于Windows操作系统中的各种文件和应用程序的图标。由于Windows系统的普及,ICO格式的图标在软件开发中有着广泛的应用。 【描述】中提到的“VB、VC编写应用的自带图标很难看,换这些试试”,提示我们这个ICO图标包是专门为使用Visual Basic(VB)和Visual C++(VC)编写的应用程序准备的。VB和VC是Microsoft公司推出的两款编程语言,其中VB是一种主要面向初学者的面向对象编程语言,而VC则是更加专业化的C++开发环境。在这些开发环境中,用户可以选择自定义应用程序的图标,以提升应用的视觉效果和用户体验。 【标签】中的“.ico 图标”直接告诉我们,这些打包的图标是ICO格式的。在设计ICO图标时,需要注意其独特的尺寸要求,因为ICO格式支持多种尺寸的图标,例如16x16、32x32、48x48、64x64、128x128等像素尺寸,甚至可以包含高DPI版本以适应不同显示需求。此外,ICO文件通常包含多种颜色深度的图标,以便在不同的背景下提供最佳的显示效果。 【压缩包子文件的文件名称列表】显示了这些精美ICO图标的数量,即“精美ICO图标36个打包”。这意味着该压缩包内包含36个不同的ICO图标资源。对于软件开发者和设计师来说,这意味着他们可以从这36个图标中挑选适合其应用程序或项目的图标,以替代默认的、可能看起来不太吸引人的图标。 在实际应用中,将这些图标应用到VB或VC编写的程序中,通常需要编辑程序的资源文件或使用相应的开发环境提供的工具进行图标更换。例如,在VB中,可以通过资源编辑器选择并替换程序的图标;而在VC中,则可能需要通过设置项目属性来更改图标。由于Windows系统支持在编译应用程序时将图标嵌入到可执行文件(EXE)中,因此一旦图标更换完成并重新编译程序,新图标就会在程序运行时显示出来。 此外,当谈及图标资源时,还应当了解图标制作的基本原则和技巧,例如:图标设计应简洁明了,以传达清晰的信息;色彩运用需考虑色彩搭配的美观性和辨识度;图标风格要与应用程序的整体设计风格保持一致,等等。这些原则和技巧在选择和设计图标时都非常重要。 总结来说,【标题】、【描述】、【标签】和【压缩包子文件的文件名称列表】共同勾勒出了一个为VB和VC编程语言用户准备的ICO图标资源包。开发者通过下载和使用这些图标,能够有效地提升应用程序的外观和用户体验。在这一过程中,了解和应用图标设计与应用的基本知识至关重要。
recommend-type

【Qt数据库融合指南】:MySQL与Qt无缝集成的技巧

# 摘要 本文全面探讨了Qt数据库集成的基础知识与进阶应用,从Qt与MySQL的基础操作讲起,深入到Qt数据库编程接口的配置与使用,并详细介绍了数据模型和视图的实现。随着章节的深入,内容逐渐从基础的数据操作界面构建过渡到高级数据库操作实践,涵盖了性能优化、安全性策略和事务管理。本文还特别针对移动设备上的数据库集成进行了讨
recommend-type

Looking in links: https://shi-labs.com/natten/wheels/ WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='shi-labs.com', port=443): Read timed out. (read timeout=15)")': /natten/wheels/ ERROR: Ignored the following yanked versions: 0.14.1 ERROR: Could not find a version that satisfies the requirement natten==0.17.4+torch250cu121 (from versions: 0.14.2.post4, 0.14.4, 0.14.5, 0.14.6, 0.15.0, 0.15.1, 0.17.0, 0.17.1, 0.17.3, 0.17.4, 0.17.5, 0.20.0, 0.20.1) ERROR: No matching distribution found for natten==0.17.4+torch250cu121

<think>我们正在解决用户安装特定版本的natten包(0.17.4+torch250cu121)时遇到的ReadTimeoutError和版本未找到错误。 根据经验,这两个错误通常与网络问题和版本匹配问题有关。 步骤1: 分析问题 - ReadTimeoutError: 通常是由于网络连接不稳定或PyPI服务器响应慢导致下载超时。 - Version not found: 可能的原因包括: a) 指定的版本号在PyPI上不存在。 b) 指定的版本号与当前环境的Python版本或CUDA版本不兼容。 步骤2: 验证版本是否存在 我们可以通过访问PyP