From 002c586b1460db30e7ff004580baa3e3f0e2ae19 Mon Sep 17 00:00:00 2001 From: xushenghao Date: Thu, 2 Jan 2025 19:33:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- delay.c | 24 + delay.h | 3 +- driver/ssd1306_oled.c | 1011 +++++++++++++++++++++++++++++++++++++++++ driver/ssd1306_oled.h | 80 ++++ driver/tmc2240.c | 364 +++++++++++++++ driver/tmc2240.h | 372 +++++++++++++++ 6 files changed, 1853 insertions(+), 1 deletion(-) create mode 100644 driver/ssd1306_oled.c create mode 100644 driver/ssd1306_oled.h create mode 100644 driver/tmc2240.c create mode 100644 driver/tmc2240.h diff --git a/delay.c b/delay.c index bd6cd96..2e6bfdf 100644 --- a/delay.c +++ b/delay.c @@ -80,6 +80,14 @@ void delay_us(uint32_t nus) } } +/** + * @brief 对指定的硬件定时器进行微秒级延时 + * + * 使用指定的硬件定时器进行延时操作,延时时间为微秒级。 + * + * @param timer_us 指向硬件定时器的指针 + * @param us 需要延时的微秒数 + */ void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us) { RESET_TIM_COUNT(timer_us); @@ -87,6 +95,22 @@ void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us) ; // 等待计数器达到指定值 } +/** + * @brief 延迟指定微秒数的硬件延迟函数 + * + * 使用指定的定时器实现微秒级的硬件延迟。 + * + * @param timer_us 指向定时器的指针,用于实现延迟功能 + * @param us 需要延迟的微秒数 + */ +void delay_hardware_ms(TIM_TypeDef *timer_us, uint32_t ms) +{ + while (ms--) + { + delay_hardware_us(timer_us, 1000); // 每毫秒延时1000微秒 + } +} + /** * @brief 延时nms * @param nms: 要延时的ms数 (0< nms <= 65535) diff --git a/delay.h b/delay.h index 43d19eb..b08b27d 100644 --- a/delay.h +++ b/delay.h @@ -17,7 +17,8 @@ void delay_init(uint16_t sysclk); /* 初始化延迟函数 */ void delay_ms(uint16_t nms); /* 延时nms */ void delay_us(uint32_t nus); /* 延时nus */ -void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us); /* 硬件延时nus */ +void delay_hardware_us(TIM_TypeDef *timer_us, uint32_t us); /* 硬件延时us */ +void delay_hardware_ms(TIM_TypeDef *timer_us, uint32_t ms); /* 硬件延时ms */ void delay_tick(uint32_t ticks); /* 延时ticks */ #endif diff --git a/driver/ssd1306_oled.c b/driver/ssd1306_oled.c new file mode 100644 index 0000000..a232cae --- /dev/null +++ b/driver/ssd1306_oled.c @@ -0,0 +1,1011 @@ +/** + * @file ssd1306_oled.c + * @author xushenghao + * @brief 0.96 SSD1306 OLED display driver + * @version 0.1 + * @note PB13-SCK PB12-SDA + */ +#include "ssd1306_oled.h" +#include "string.h" + +/************************************6*8的点阵************************************/ +const uint8_t F6x8[][6] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp + 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! + 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " + 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # + 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ + 0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % + 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, 0x00, 0xA0, 0x60, 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, // 0 + 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 + 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 + 0x00, 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, 0x59, 0x51, 0x3E, // @ + 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C + 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F + 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G + 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H + 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I + 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J + 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K + 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L + 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M + 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P + 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q + 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R + 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S + 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T + 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U + 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V + 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W + 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X + 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y + 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ + 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55 + 0x00, 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, // a + 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b + 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c + 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d + 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e + 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f + 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g + 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h + 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i + 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j + 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k + 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l + 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n + 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o + 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p + 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r + 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s + 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t + 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u + 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v + 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w + 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x + 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y + 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines +}; +/****************************************8*16的点阵************************************/ +const uint8_t F8X16[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 + 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1 + 0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2 + 0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3 + 0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4 + 0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5 + 0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6 + 0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7 + 0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8 + 0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9 + 0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10 + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16 + 0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17 + 0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18 + 0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19 + 0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20 + 0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21 + 0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22 + 0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25 + 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26 + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27 + 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28 + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29 + 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30 + 0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31 + 0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32 + 0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40 + 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41 + 0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42 + 0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44 + 0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45 + 0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51 + 0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53 + 0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54 + 0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55 + 0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56 + 0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57 + 0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58 + 0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59 + 0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60 + 0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61 + 0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63 + 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69 + 0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72 + 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73 + 0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74 + 0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75 + 0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81 + 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83 + 0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84 + 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86 + 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87 + 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89 + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92 + 0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93 + 0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94 +}; + +const uint8_t LOGO[] = { + 0x00, 0x03, 0x05, 0x09, 0x11, 0xFF, 0x11, 0x89, 0x05, 0xC3, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0xFF, 0x11, 0xAA, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x01, 0x38, 0x44, 0x82, 0x92, + 0x92, 0x74, 0x01, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x44, 0xC7, 0x01, 0x7D, + 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0x00, 0x00, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0xE6, 0x66, 0x20, 0x00, 0x06, 0x06, 0x86, 0x06, + 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x86, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x86, 0x86, 0x86, 0x86, 0x86, 0x80, 0x80, 0x86, 0x86, 0x06, 0x86, 0x86, 0xC0, 0xC0, 0x86, + 0x86, 0x86, 0x06, 0x06, 0xD0, 0x30, 0x76, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0xFE, 0x00, 0x01, + 0x02, 0x00, 0xC4, 0x18, 0x20, 0x02, 0x9E, 0x63, 0xB2, 0x0E, 0x00, 0xFF, 0x81, 0x81, 0xFF, 0x00, + 0x00, 0x80, 0x40, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x23, 0xEA, 0xAA, 0xBF, 0xAA, + 0xEA, 0x03, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0C, 0x08, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81, 0x80, 0x80, 0x81, 0x80, + 0x81, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x09, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0xC1, 0xFF, + 0xFF, 0xC1, 0xC1, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0xFC, 0xF3, 0xEF, 0xF3, 0xFC, + 0x80, 0xFF, 0x80, 0xEE, 0xEE, 0xEE, 0xF5, 0xFB, 0xFF, 0x9C, 0xBE, 0xB6, 0xB6, 0x88, 0xFF, 0x00, + // end +}; + +static uint8_t _buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 定义屏幕缓冲区 +static uint8_t _buffer_copy[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 屏幕缓冲区副本 + +static void i2c_start(void) +{ + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_stop(void) +{ + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); +} + +static BOOL i2c_wait_ack(void) +{ + uint8_t count = 0; + SDA_IN(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + while (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + count++; + if (count > 250) + { + i2c_stop(); + return FALSE; + } + } + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + return TRUE; +} + +static void i2c_ack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_nack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +uint8_t i2c_read_byte(BOOL ack) +{ + uint8_t i = 0, receive = 0; + SDA_IN(); + for (i = 0; i < 8; i++) + { + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + receive <<= 1; + if (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + receive++; + } + delay_us(1); + } + + if (!ack) + { + i2c_nack(); + } + else + { + i2c_ack(); + } + + return receive; +} + +void i2c_write_byte(uint8_t data) +{ + uint8_t i = 0; + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + for (i = 0; i < 8; i++) + { + // IIC_SDA=(txd&0x80)>>7; + if ((data & 0x80) >> 7) + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + else + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + + data <<= 1; + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + } +} + +static void i2c_write_command(uint8_t command) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_LOW_COLUMN; // Co = 0, D/C# = 0 + dd[1] = command; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); + i2c_stop(); +} + +static void i2c_write_data(uint8_t data) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_START_LINE; // Co = 0, D/C# = 1 + dd[1] = data; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); +} + +/** + * @brief 设置SSD1306 OLED显示屏上的显示位置 + * + * 该函数用于设置SSD1306 OLED显示屏上的显示位置,通过x和y坐标确定显示位置。 + * + * @param x 要设置的横坐标(0-127) + * @param y 要设置的纵坐标(0-7,对应SSD1306 OLED的8个页面) + */ +void set_position(uint8_t x, uint8_t y) +{ + i2c_write_command(0xb0 + y); + i2c_write_command(((x & 0xf0) >> 4) | 0x10); + i2c_write_command((x & 0x0f) | 0x01); +} + +void ssd1306_test(void) +{ + ssd1306_f8x16_string(0, 0, " TEST"); + ssd1306_f8x16_number(40, 0, -15.26, 2); + ssd1306_f6x8_string_number(0, 3, " @ADC1:", 'V', 24); + ssd1306_update_screen(); +} + +void ssd1306_init(void) +{ + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // display off + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // Set Memory Addressing Mode + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid + i2c_write_command(SSD1306_CMD_COM_SCAN_DEC); // Set COM Output Scan Direction + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //---set low column address + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); //---set high column address + i2c_write_command(SSD1306_CMD_SET_START_LINE); //--set start line address + i2c_write_command(SSD1306_CMD_SET_CONTRAST); //--set contrast control register + i2c_write_command(0xff); // 亮度调节 0x00~0xff + i2c_write_command(0xa1); //--set segment re-map 0 to 127 + i2c_write_command(SSD1306_CMD_NORMAL_DISPLAY); //--set normal display + i2c_write_command(SSD1306_CMD_SET_MULTIPLEX); //--set multiplex ratio(1 to 64) + i2c_write_command(0x3f); // + i2c_write_command(SSD1306_CMD_DISPLAY_ALL_ON_RESUME); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content + i2c_write_command(SSD1306_CMD_SET_DISPLAY_OFFSET); //-set display offset + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //-not offset + i2c_write_command(SSD1306_CMD_SET_DISPLAY_CLOCK_DIV); //--set display clock divide ratio/oscillator frequency + i2c_write_command(0xf0); //--set divide ratio + i2c_write_command(SSD1306_CMD_SET_PRECHARGE); //--set pre-charge period + i2c_write_command(SSD1306_CMD_PAGE_ADDR); // + i2c_write_command(SSD1306_CMD_SET_COM_PINS); //--set com pins hardware configuration + i2c_write_command(0x12); + i2c_write_command(SSD1306_CMD_SET_VCOM_DETECT); //--set vcomh + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // 0x20,0.77xVcc + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); //--set DC-DC enable + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // + i2c_write_command(SSD1306_CMD_DISPLAY_ON); //--turn on oled panel + ssd1306_clear(); + + // ssd1306_test(); +} + +void ssd1306_logo(void) +{ + ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2, LOGO); +} + +void ssd1306_display_on(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // 开启电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_ON); // OLED唤醒 +} + +void ssd1306_display_off(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 关闭电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // OLED休眠 +} + +/** + * @brief 更新SSD1306 OLED显示屏的内容 + * + * 此函数将缓冲区中的数据写入SSD1306 OLED显示屏,从而更新显示内容。 + * + * 首先,通过发送命令设置列地址和页地址,然后将缓冲区中的数据逐行写入显示屏。 + * + * @note 在调用此函数之前,需要将需要显示的数据写入缓冲区。 + */ +void ssd1306_update_screen(void) +{ + for (uint8_t i = 0; i < SSD1306_HEIGHT / 8; i++) + { + uint8_t update_needed = 0; + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + if (_buffer[j + i * SSD1306_WIDTH] != _buffer_copy[j + i * SSD1306_WIDTH]) + { + update_needed = 1; + break; + } + } + if (update_needed) + { + i2c_write_command(0xb0 + i); + i2c_write_command(0x01); + i2c_write_command(0x10); + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + i2c_write_data(_buffer[j + i * SSD1306_WIDTH]); + } + } + } + + osel_memcpy(_buffer_copy, _buffer, ARRAY_LEN(_buffer)); +} + +/** + * @brief 填充整个屏幕为指定颜色 + * + * 该函数将 SSD1306 OLED 显示屏的每一个像素点都设置为指定的颜色。 + * + * @param color 颜色值,SSD1306_BLACK 表示关闭像素点(黑色),SSD1306_WHITE 表示打开像素点(白色) + */ +void ssd1306_fill(uint8_t color) +{ + ssd1306_clear_buffer(); + osel_memset(_buffer, color, ARRAY_LEN(_buffer)); + ssd1306_update_screen(); +} + +/** + * @brief 清空SSD1306显示屏 + * + * 该函数通过向SSD1306显示屏发送一系列命令来清空显示内容。 + * + * 首先,通过循环遍历每个页面(SSD1306显示屏有8个页面), + * 对每个页面执行以下操作: + * 1. 发送页面地址命令(0xb0 + y),其中y为当前页面索引。 + * 2. 发送列地址低位命令(0x01)。 + * 3. 发送列地址高位命令(0x10),表示从第一列开始。 + * 4. 循环遍历每一列(SSD1306显示屏的宽度), + * 发送数据0x00以清空该列的内容。 + */ +void ssd1306_clear(void) +{ + ssd1306_clear_buffer(); + ssd1306_update_screen(); +} + +void ssd1306_clear_buffer(void) +{ + osel_memset(_buffer, SSD1306_BLACK, ARRAY_LEN(_buffer)); + osel_memset(_buffer_copy, 0xff, ARRAY_LEN(_buffer_copy)); +} + +/** + * @brief 在指定区域内绘制BMP + * + * 在指定坐标区域内绘制一个BMP,使用SSD1306 OLED显示屏 + * ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2); + * @param x0 BMP绘制的起始X坐标 + * @param y0 BMP绘制的起始Y坐标 + * @param x1 BMP绘制的结束X坐标 + * @param y1 BMP绘制的结束Y坐标 + */ +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp) +{ + uint8_t j = 0; + uint8_t x, y; + ssd1306_clear_buffer(); + if (y1 % 8 == 0) + y = y1 / 8; + else + y = y1 / 8 + 1; + for (y = y0; y < y1; y++) + { + set_position(x0, y); + for (x = x0; x < x1; x++) + { + i2c_write_data(bmp[j++]); + } + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示字符串 + * + * 在SSD1306 OLED显示屏上以6x8像素的字体显示给定的字符串。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} + +/** + * @brief 将浮点数显示在SSD1306显示屏上 + * + * 该函数将给定的浮点数num转换为字符串,并将其显示在SSD1306显示屏的指定位置。 + * + * @param x 显示的起始x坐标 + * @param y 显示的起始y坐标 + * @param num 要显示的浮点数 + * @param dot_num 小数点后的位数,0表示不显示小数部分 + */ +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} +/** + * @brief 在SSD1306 OLED屏幕上显示字符串和数字 + * + * 该函数用于在SSD1306 OLED屏幕上显示字符串和数字。首先显示一个字符串, + * 然后显示一个浮点数。字符串和数字按照指定的位置和单位显示。 + * + * @param x 起始位置的x坐标 + * @param y 起始位置的y坐标 + * @param ch 要显示的字符串 + * @param unit 数字的单位,例如'm'表示米 + * @param num 要显示的浮点数 + */ +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num) +{ + uint8_t c = 0, i = 0, j = 0; + int8_t a, number[7] = {0, 0, 0, -2, 0, 0, 0}; + uint32_t d; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + d = 1000 * num; + for (a = 0; a < 3; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + for (a = 4; a < 7; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + if (num >= 100) + { + a = 0; + } + else if (num >= 10) + { + a = 1; + } + else if (num >= 0) + { + a = 2; + } + for (; a < 7; a++) + { + c = number[a] + 16; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + for (int h = 0; h < 7; h++) + { + c = unit - 32; + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示8x16大小的字符串 + * + * 该函数使用8x16字体在SSD1306 OLED显示屏上显示指定的字符串。字符串的字符位置由x和y参数指定。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上以8x16像素字体显示浮点数 + * + * 在指定的坐标位置 (x, y) 上,使用8x16像素大小的字体显示浮点数 num,并显示指定数量的小数点 dot_num。 + * + * @param x 显示位置的x坐标 + * @param y 显示位置的y坐标 + * @param num 要显示的浮点数 + * @param dot_num 要显示的小数点数量 + */ +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +// 在 SSD1306 显示屏上绘制一个像素 +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color) +{ + if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) + { + // 超出屏幕范围 + return; + } + + // 设置像素颜色 + if (color == SSD1306_WHITE) + { + _buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); + } + else + { + _buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8)); + } +} + +// 在 SSD1306 显示屏上绘制一条线 +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) +{ + int16_t dx, dy, sx, sy, err, e2; + + dx = ABS(x2 - x1); + dy = ABS(y2 - y1); + sx = (x1 < x2) ? 1 : -1; + sy = (y1 < y2) ? 1 : -1; + err = dx - dy; + + while (1) + { + ssd1306_draw_pixel(x1, y1, color); + if (x1 == x2 && y1 == y2) + break; + e2 = err * 2; + if (e2 > -dy) + { + err -= dy; + x1 += sx; + } + if (e2 < dx) + { + err += dx; + y1 += sy; + } + } +} + +// 在 SSD1306 显示屏上绘制一个矩形 +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + // 绘制矩形的四条边 + ssd1306_draw_line(x, y, x + w, y, color); // 上边 + ssd1306_draw_line(x, y + h, x + w, y + h, color); // 下边 + ssd1306_draw_line(x, y, x, y + h, color); // 左边 + ssd1306_draw_line(x + w, y, x + w, y + h, color); // 右边 +} + +// 在 SSD1306 显示屏上绘制一个填充矩形 +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + for (uint8_t i = 0; i < h; i++) + { + ssd1306_draw_line(x, y + i, x + w, y + i, color); + } +} + +// 绘制进度条 +void ssd1306_draw_progress_bar(uint8_t progress) +{ + uint8_t offset_y = 10; + char progress_text[5]; // 用于存储百分比文本 + // 绘制边框 + ssd1306_draw_rect_angle(10, 24 + offset_y, 108, 10, SSD1306_WHITE); + + // 绘制进度条 + ssd1306_fill_rect_angle(12, 26 + offset_y, 4 + progress, 7, SSD1306_WHITE); + + // 显示百分比文本 + snprintf(progress_text, sizeof(progress_text), "%3d%%", progress); + ssd1306_f8x16_string(SSD1306_WIDTH * 0.35, 2, (const uint8_t *)progress_text); + + // 更新显示 + ssd1306_update_screen(); +} + +// 在中间显示文字 +void ssd1306_draw_text_center(uint8_t y, const char *text) +{ + uint8_t x = (SSD1306_WIDTH - strlen(text) * 6) / 2; + ssd1306_f6x8_string(x, y, (const uint8_t *)text); + + // 更新显示 + ssd1306_update_screen(); +} diff --git a/driver/ssd1306_oled.h b/driver/ssd1306_oled.h new file mode 100644 index 0000000..32a3304 --- /dev/null +++ b/driver/ssd1306_oled.h @@ -0,0 +1,80 @@ +#ifndef __SSD1306_OLED_H +#define __SSD1306_OLED_H + +#include "main.h" + +// OLED引脚定义 +#define SSD1306_SDA_PORT OLED_SDA_GPIO_Port +#define SSD1306_SDA_PIN OLED_SDA_Pin +#define SSD1306_SCK_PORT OLDE_SCK_GPIO_Port +#define SSD1306_SCK_PIN OLDE_SCK_Pin + +// I2C地址 +#define SSD1306_I2C_ADDRESS 0x78 +// OLED显示参数 +#define SSD1306_WIDTH 128 +#define SSD1306_HEIGHT 64 +// OLED颜色 +#define SSD1306_WHITE 1 +#define SSD1306_BLACK 0 + +// OLED命令定义 +#define SSD1306_CMD_DISPLAY_OFF 0xAE +#define SSD1306_CMD_DISPLAY_ON 0xAF +#define SSD1306_CMD_SET_CONTRAST 0x81 +#define SSD1306_CMD_DISPLAY_ALL_ON_RESUME 0xA4 +#define SSD1306_CMD_DISPLAY_ALL_ON 0xA5 +#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 +#define SSD1306_CMD_INVERT_DISPLAY 0xA7 +#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3 +#define SSD1306_CMD_SET_COM_PINS 0xDA +#define SSD1306_CMD_SET_VCOM_DETECT 0xDB +#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIV 0xD5 +#define SSD1306_CMD_SET_PRECHARGE 0xD9 +#define SSD1306_CMD_SET_MULTIPLEX 0xA8 +#define SSD1306_CMD_SET_LOW_COLUMN 0x00 +#define SSD1306_CMD_SET_HIGH_COLUMN 0x10 +#define SSD1306_CMD_SET_START_LINE 0x40 +#define SSD1306_CMD_MEMORY_MODE 0x20 +#define SSD1306_CMD_COLUMN_ADDR 0x21 +#define SSD1306_CMD_PAGE_ADDR 0x22 +#define SSD1306_CMD_COM_SCAN_INC 0xC0 +#define SSD1306_CMD_COM_SCAN_DEC 0xC8 +#define SSD1306_CMD_SEG_REMAP 0xA0 +#define SSD1306_CMD_CHARGE_PUMP 0x8D +#define SSD1306_CMD_SET_DC_DC_ENABLE 0x14 + +#define SDA_OUT() \ + { \ + GPIO_SET_OUTPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +#define SDA_IN() \ + { \ + GPIO_SET_INPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +// 函数声明 +void ssd1306_init(void); +void ssd1306_logo(void); +void ssd1306_display_on(void); +void ssd1306_display_off(void); +void ssd1306_update_screen(void); + +void ssd1306_fill(uint8_t color); +void ssd1306_clear(void); +void ssd1306_clear_buffer(void); +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp); +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num); +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); + +void ssd1306_draw_text_center(uint8_t y, const char *text); +void ssd1306_draw_progress_bar(uint8_t progress); +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color); +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color); +#endif // __SSD1306_OLED_H diff --git a/driver/tmc2240.c b/driver/tmc2240.c new file mode 100644 index 0000000..d4e2296 --- /dev/null +++ b/driver/tmc2240.c @@ -0,0 +1,364 @@ +#include "tmc2240.h" + +tmc2240_t _tmc2240[TMC2240_MAX]; + +static void tmc2240_write(tmc2240_index_e index, uint8_t *data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + tmc->spi->interface.u.normal.spi_send(tmc->spi, data[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_read(tmc2240_index_e index, uint8_t *wdata, uint8_t *rdata) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); + + __NOP(); + __NOP(); + __NOP(); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_reg_write(tmc2240_index_e index, uint8_t reg, uint32_t data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + wdata[0] = TMC2240_HIGHT_BIT | reg; + wdata[1] = (data >> 24) & 0xFF; + wdata[2] = (data >> 16) & 0xFF; + wdata[3] = (data >> 8) & 0xFF; + wdata[4] = data & 0xFF; + tmc2240_write(index, wdata); +} + +static uint32_t tmc2240_reg_read(tmc2240_index_e index, uint8_t reg) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + uint8_t rdata[5] = {0}; + wdata[0] = reg; + tmc2240_read(index, wdata, rdata); + return (rdata[1] << 24) | (rdata[2] << 16) | (rdata[3] << 8) | rdata[4]; +} + +/** + * @brief 配置TMC2240步进电机驱动器 + * + * 该函数用于配置TMC2240步进电机驱动器的各种参数,包括步进电机的工作模式、加减速曲线等。 + * + * @param index TMC2240步进电机驱动器的索引,用于指定要配置的驱动器。 + */ +static void tmc2240_config_write(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->config.gconf.data = 0x00000000; + + tmc->config.chopconf.data = 0x00410153; + tmc->config.chopconf.bits.mres = TMC2240_MRES_8; + + tmc->config.drvconf.data = 0x00000021; + tmc->config.global_scaler.data = 0x00000000; + + tmc->config.ihold_irun.bits.ihold = 31; + tmc->config.ihold_irun.bits.irun = 31; + tmc->config.ihold_irun.bits.iholddelay = 0; + tmc->config.ihold_irun.bits.irundelay = 0; + + tmc->config.pwmconf.data = 0xC44C261E; + tmc->config.gstat.data = 0x00000007; + tmc2240_reg_write(index, TMC2240_GCONF, tmc->config.gconf.data); + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + tmc2240_reg_write(index, TMC2240_DRV_CONF, tmc->config.drvconf.data); + tmc2240_reg_write(index, TMC2240_GLOBAL_SCALER, tmc->config.global_scaler.data); + tmc2240_reg_write(index, TMC2240_IHOLD_IRUN, tmc->config.ihold_irun.data); + tmc2240_reg_write(index, TMC2240_PWMCONF, tmc->config.pwmconf.data); + tmc2240_reg_write(index, TMC2240_GSTAT, tmc->config.gstat.data); +} + +static void _tmc2240_motor_update(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + switch (tmc->config.chopconf.bits.mres) + { + case TMC2240_MRES_256: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 256; + break; + case TMC2240_MRES_128: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 128; + break; + case TMC2240_MRES_64: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 64; + break; + case TMC2240_MRES_32: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 32; + break; + case TMC2240_MRES_16: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 16; + break; + case TMC2240_MRES_8: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 8; + break; + case TMC2240_MRES_4: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 4; + break; + case TMC2240_MRES_2: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE / 2; + break; + case TMC2240_MRES_1: + tmc->motor.step_angle = MOTOR_42_STEP_ANGLE; + break; + default: + break; + } + + tmc->motor.circle_pulse = 360 / tmc->motor.step_angle; +} + +static void _tmc2240_enable(tmc2240_index_e index, BOOL enable) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + BOOL state = tmc->en->read(*tmc->en) == 0 ? TRUE : FALSE; + + if (state == enable) + { + return; + } + + if (enable == TRUE) + { + PWM_START(tmc->timer, tmc->time_ch); + tmc->en->reset(*tmc->en); + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + } + else + { + PWM_STOP(tmc->timer, tmc->time_ch); + tmc->en->set(*tmc->en); + chopconf_u chopconf; + osel_memset((uint8_t *)&chopconf, 0, sizeof(chopconf_u)); + chopconf.bits.mres = TMC2240_MRES_1; + tmc2240_reg_write(index, TMC2240_CHOPCONF, chopconf.data); + } +} + +static void _tmc2240_direction(tmc2240_index_e index, tmc2240_direction_e dir) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (dir == TMC2240_FORWARD) + { + tmc->dir->reset(*tmc->dir); + } + else + { + tmc->dir->set(*tmc->dir); + } +} + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + osel_memset((uint8_t *)tmc, 0, sizeof(tmc2240_t)); + + tmc->timer = timer; + tmc->time_ch = time_ch; + tmc->spi = spi_create(SPI_TYPE_NORMAL, *gpios, 0); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + tmc->spi->interface.hardware_enable(tmc->spi, SPIx); + { + tmc->default_tm.sysclk = SystemCoreClock / 1000; + tmc->default_tm.psc = PWM_GET_PSC(tmc->timer); + tmc->default_tm.arr = PWM_GET_ARR(tmc->timer); + tmc->default_tm.freq = PWM_GET_FREQ(tmc->timer); + } + tmc->params.percent = TMC2240_PWM_DUTY_DEFAULT; + tmc->params.arr = 0; + tmc->params.freq = 0; + tmc->params.enable = FALSE; + tmc->params.direction = TMC2240_FORWARD; + tmc2240_config_write(index); + _tmc2240_motor_update(index); +} + +tmc2240_t *tmc2240_get(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + return &_tmc2240[index]; +} + +void tmc2240_percent(tmc2240_index_e index, float32 percent) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + PWM_SET_DUTY(tmc->timer, tmc->time_ch, ABS(percent)); +} + +void tmc2240_config_read(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->read_config.gconf.data = tmc2240_reg_read(index, TMC2240_GCONF); + tmc->read_config.chopconf.data = tmc2240_reg_read(index, TMC2240_CHOPCONF); + tmc->read_config.drvconf.data = tmc2240_reg_read(index, TMC2240_DRV_CONF); + tmc->read_config.global_scaler.data = tmc2240_reg_read(index, TMC2240_GLOBAL_SCALER); + tmc->read_config.ihold_irun.data = tmc2240_reg_read(index, TMC2240_IHOLD_IRUN); + tmc->read_config.pwmconf.data = tmc2240_reg_read(index, TMC2240_PWMCONF); + tmc->read_config.gstat.data = tmc2240_reg_read(index, TMC2240_GSTAT); + tmc->data.tmc2240_adc_temp = tmc2240_reg_read(index, TMC2240_ADC_TEMP); + tmc->data.tmc2240_temperature = (float32)(tmc->data.tmc2240_adc_temp - 2038) / 7.7; +} + +void tmc2240_test(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + _tmc2240_enable(index, tmc->params.enable); + + if (tmc->params.enable == TRUE) + { + _tmc2240_direction(index, tmc->params.direction); + + if (PWM_GET_ARR(tmc->timer) != tmc->params.arr) + { + if (tmc->params.arr == 0) + { + tmc->params.arr = tmc->default_tm.arr; + } + PWM_SET_ARR(tmc->timer, tmc->params.arr); + tmc->params.freq = PWM_GET_FREQ(tmc->timer); + } + + tmc2240_percent(index, tmc->params.percent); + + if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres) + { + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + _tmc2240_motor_update(index); + } + } +} + +void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (angle == 0) + { + return; + } + + if (angle > 0) + { + tmc->params.direction = TMC2240_FORWARD; + } + else + { + tmc->params.direction = TMC2240_BACKWARD; + } + + tmc->motor.pulse_count = ABS(angle) / tmc->motor.step_angle; + tmc->motor.step_angle_count = 0; + + tmc->params.enable = TRUE; + + _tmc2240_direction(index, tmc->params.direction); + if (PWM_GET_ARR(tmc->timer) != tmc->params.arr) + { + if (tmc->params.arr == 0) + { + tmc->params.arr = tmc->default_tm.arr; + } + PWM_SET_ARR(tmc->timer, tmc->params.arr); + tmc->params.freq = PWM_GET_FREQ(tmc->timer); + } + + tmc2240_percent(index, tmc->params.percent); + + if (tmc->config.chopconf.bits.mres != tmc->read_config.chopconf.bits.mres) + { + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + _tmc2240_motor_update(index); + } + _tmc2240_enable(index, tmc->params.enable); +} + +void tmc2240_motor_update(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (tmc->motor.pulse_count > 0) + { + tmc->motor.pulse_count--; // 脉冲数 + tmc->motor.step_angle_count++; // 步距个数 + if (tmc->params.direction == TMC2240_FORWARD) + { + tmc->motor.add_pulse_count++; /* 绝对位置++ */ + } + else + { + tmc->motor.add_pulse_count--; /* 绝对位置-- */ + } + } + + /* 当脉冲数等于0的时候 代表需要发送的脉冲个数已完成,停止输出 */ + + if (tmc->motor.pulse_count <= 0) + { + tmc->params.enable = FALSE; + _tmc2240_enable(index, tmc->params.enable); + } +} diff --git a/driver/tmc2240.h b/driver/tmc2240.h new file mode 100644 index 0000000..1f5379d --- /dev/null +++ b/driver/tmc2240.h @@ -0,0 +1,372 @@ +/** + * @file tmc2240.h + * @author xushenghao + * @brief TMC2240驱动头文件 + * @version 0.1 + * @note + * 1. 芯片VM需要供电,否则SPI无法正常通信 + * 2. 42步进电机每步1.8度,1圈200步。1/8细分,1步=0.225度,电机转一周需要1600个脉冲 + */ + +#ifndef __TMC2240_H +#define __TMC2240_H +#include "main.h" +#include "spis.h" + +#define MOTOR_42_STEP_ANGLE 1.8f // 42步进电机每步1.8度 +#define TMC2240_PWM_DUTY_DEFAULT 50 // PWM默认占空比 + +/* +0x00 = 0x00002108 ;; writing GCONF @ address 0=0x00 with 0x00002108=8456=0.0 +0x03 = 0x00000000 ;; writing SLAVECONF @ address 1=0x03 with 0x00000000=0=0.0 +0x04 = 0x4001682C ;; writing IOIN @ address 2=0x04 with 0x4001682C=1073834028=0.0 +0x0A = 0x00000021 ;; writing DRV_CONF @ address 3=0x0A with 0x00000021=33=0.0 +0x0B = 0x00000000 ;; writing GLOBAL_SCALER @ address 4=0x0B with 0x00000000=0=0.0 +0x10 = 0x00001208 ;; writing IHOLD_IRUN @ address 5=0x10 with 0x00001208=4616=0.0 +0x11 = 0x00000000 ;; writing TPOWERDOWN @ address 6=0x11 with 0x00000000=0=0.0 +0x13 = 0x00000000 ;; writing TPWMTHRS @ address 7=0x13 with 0x00000000=0=0.0 +0x14 = 0x000003BE ;; writing TCOOLTHRS @ address 8=0x14 with 0x000003BE=958=0.0 +0x15 = 0x00000000 ;; writing THIGH @ address 9=0x15 with 0x00000000=0=0.0 +0x2D = 0x00000000 ;; writing DIRECT_MODE @ address 10=0x2D with 0x00000000=0=0.0 +0x38 = 0x00000000 ;; writing ENCMODE @ address 11=0x38 with 0x00000000=0=0.0 +0x39 = 0x00000000 ;; writing X_ENC @ address 12=0x39 with 0x00000000=0=0.0 +0x3A = 0x00010000 ;; writing ENC_CONST @ address 13=0x3A with 0x00010000=65536=0.0 +0x52 = 0x0B920F25 ;; writing OTW_OV_VTH @ address 14=0x52 with 0x0B920F25=194121509=0.0 +0x60 = 0xAAAAB554 ;; writing MSLUT[0] @ address 15=0x60 with 0xAAAAB554=0=0.0 +0x61 = 0x4A9554AA ;; writing MSLUT[1] @ address 16=0x61 with 0x4A9554AA=1251300522=0.0 +0x62 = 0x24492929 ;; writing MSLUT[2] @ address 17=0x62 with 0x24492929=608774441=0.0 +0x63 = 0x10104222 ;; writing MSLUT[3] @ address 18=0x63 with 0x10104222=269500962=0.0 +0x64 = 0xFBFFFFFF ;; writing MSLUT[4] @ address 19=0x64 with 0xFBFFFFFF=0=0.0 +0x65 = 0xB5BB777D ;; writing MSLUT[5] @ address 20=0x65 with 0xB5BB777D=0=0.0 +0x66 = 0x49295556 ;; writing MSLUT[6] @ address 21=0x66 with 0x49295556=1227445590=0.0 +0x67 = 0x00404222 ;; writing MSLUT[7] @ address 22=0x67 with 0x00404222=4211234=0.0 +0x68 = 0xFFFF8056 ;; writing MSLUTSEL @ address 23=0x68 with 0xFFFF8056=0=0.0 +0x69 = 0x00F70000 ;; writing MSLUTSTART @ address 24=0x69 with 0x00F70000=16187392=0.0 +0x6C = 0x00410153 ;; writing CHOPCONF @ address 25=0x6C with 0x00410153=4260179=0.0 +0x6D = 0x00040000 ;; writing COOLCONF @ address 26=0x6D with 0x00040000=262144=0.0 +0x70 = 0xC44C001E ;; writing PWMCONF @ address 27=0x70 with 0xC44C001E=0=0.0 +0x74 = 0x00000000 ;; writing SG4_THRS @ address 28=0x74 with 0x00000000=0=0.0 +*/ + +#define TMC2240_GCONF 0x00 +#define TMC2240_GSTAT 0x01 +#define TMC2240_IFCNT 0x02 +#define TMC2240_SLAVECONF 0x03 +#define TMC2240_IOIN 0x04 +#define TMC2240_DRV_CONF 0x0A +#define TMC2240_GLOBAL_SCALER 0x0B + +#define TMC2240_IHOLD_IRUN 0x10 +#define TMC2240_TPOWERDOWN 0x11 +#define TMC2240_TSTEP 0x12 +#define TMC2240_TPWMTHRS 0x13 +#define TMC2240_TCOOLTHRS 0x14 +#define TMC2240_THIGH 0x15 + +#define TMC2240_DIRECT_MODE 0x2D + +#define TMC2240_ENCMODE 0x38 +#define TMC2240_XENC 0x39 +#define TMC2240_ENC_CONST 0x3A +#define TMC2240_ENC_STATUS 0x3B +#define TMC2240_ENC_LATCH 0x3C + +#define TMC2240_ADC_VSUPPLY_AIN 0x50 +#define TMC2240_ADC_TEMP 0x51 +#define TMC2240_OTW_OV_VTH 0x52 + +#define TMC2240_MSLUT0 0x60 +#define TMC2240_MSLUT1 0x61 +#define TMC2240_MSLUT2 0x62 +#define TMC2240_MSLUT3 0x63 +#define TMC2240_MSLUT4 0x64 +#define TMC2240_MSLUT5 0x65 +#define TMC2240_MSLUT6 0x66 +#define TMC2240_MSLUT7 0x67 +#define TMC2240_MSLUTSEL 0x68 +#define TMC2240_MSLUTSTART 0x69 +#define TMC2240_MSCNT 0x6A +#define TMC2240_MSCURACT 0x6B +#define TMC2240_CHOPCONF 0x6C +#define TMC2240_COOLCONF 0x6D +#define TMC2240_DCCTRL 0x6E +#define TMC2240_DRVSTATUS 0x6F + +#define TMC2240_PWMCONF 0x70 +#define TMC2240_PWMSCALE 0x71 +#define TMC2240_PWM_AUTO 0x72 +#define TMC2240_SG4_THRS 0x74 +#define TMC2240_SG4_RESULT 0x75 +#define TMC2240_SG4_IND 0x76 + +#define TMC2240_HIGHT_BIT 0x80 + +typedef enum +{ + TMC2240_1, + TMC2240_MAX, +} tmc2240_index_e; + +typedef enum +{ + TMC2240_FORWARD, // 正转 + TMC2240_BACKWARD, // 反转 +} tmc2240_direction_e; + +typedef enum +{ + TMC2240_MRES_256, + TMC2240_MRES_128, + TMC2240_MRES_64, + TMC2240_MRES_32, + TMC2240_MRES_16, + TMC2240_MRES_8, + TMC2240_MRES_4, + TMC2240_MRES_2, + TMC2240_MRES_1, // FULL STEP +} tmc2240_mres_e; + +// 0x00 GCONF +typedef union +{ + uint32_t data; + struct + { + + uint32_t reserved1 : 1; + /** + * 停止之前的步骤执行超时检测。 + * 0x0:正常时间:2^20个时钟 + * 0x1:短时间:2^18个时钟 + */ + uint32_t fast_standstill : 1; + /** + * 启用StealthChop2模式。 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能(取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD = 时为开状态 仅限额定电流。 + */ + uint32_t en_pwm_mode : 1; + /** + * 启用StealthChop2的步进输入筛选 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能 + (取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD=时为开状态 + 仅限额定电流。 + */ + uint32_t multistep_filt : 1; + /** + * 更改电机方向/方向标志 + * 0x0:默认电机方向 + * 0x1:电机方向相反 + */ + uint32_t shaft : 1; + uint32_t diag0_error : 1; + uint32_t diag0_otpw : 1; + uint32_t diag0_stall : 1; + uint32_t diag1_stall : 1; + uint32_t diag1_index : 1; + uint32_t diag1_onstate : 1; + uint32_t reserved2 : 1; + uint32_t diag0_pushpull : 1; + uint32_t diag1_pushpull : 1; + uint32_t small_hysteresis : 1; + /** + * 电机硬停止功能启用。 + * 0x0:正常运行 + * 0x1:紧急停止:ENCA停止定序器 + 当绑得很高时(不执行任何步骤 + 定序器、电机进入停顿状态)。 + */ + uint32_t stop_enable : 1; + /** + * 通过以下方式启用直接motpr相电流控制 + * 0x0:正常运行 + * 0x1:电机线圈电流和极性直接 + 通过串口编程:寄存器 + 直接模式(0x2D)指定带符号线圈A + 电流(位8..0)和线圈B电流(位24..16)。在……里面 + 在此模式下,电流按lHOLD设置进行定标。 + 基于速度的StealthChop2电流调节 + 在此模式下不可用。自动的 + StealthChop2电流调节仅适用于 + 步进电机速度低。 + */ + uint32_t direct_mode : 1; + } bits; +} gconf_u; + +// 0x01 GSTAT +typedef union +{ + uint32_t data; + struct + { + uint32_t reset : 1; + uint32_t drv_err : 1; + uint32_t uv_cp : 1; + uint32_t register_reset : 1; + uint32_t vm_uvlo : 1; + } bits; +} gstat_u; + +// 0x0A DRVCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t current_range : 2; // 0-1 + uint32_t reserved1 : 2; // 2-3 + uint32_t slope_control : 2; // 4-5 + } bits; +} drvconf_u; + +// 0x0B GLOBAL_SCALER +typedef union +{ + uint32_t data; + struct + { + uint32_t global_scale : 8; // 0-7 + } bits; +} global_scaler_u; + +// 0x10 IHOLD_IRUN +typedef union +{ + uint32_t data; + struct + { + uint32_t ihold : 5; // 0-4 + uint32_t reserved1 : 3; // 5-7 + uint32_t irun : 5; // 8-12 + uint32_t reserved2 : 3; // 13-15 + uint32_t iholddelay : 4; // 16-19 + uint32_t reserved3 : 4; // 20-23 + uint32_t irundelay : 4; // 24-27 + } bits; +} ihold_irun_u; + +// 0x6C CHOPCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t toff : 4; // 0-3 + uint32_t hstrt : 3; // 4-6 + uint32_t hend : 4; // 7-10 + uint32_t fd3 : 1; // 11 + uint32_t disfdcc : 1; // 12 + uint32_t reserved1 : 1; + uint32_t chm : 1; // 14 + uint32_t tbl : 2; // 15-16 + uint32_t reserved2 : 1; + uint32_t vhighfs : 1; // 18 + uint32_t vhighchm : 1; // 19 + uint32_t tpfd : 4; // 20-23 + uint32_t mres : 4; // 24-27 + uint32_t intpol : 1; // 28 + uint32_t dedge : 1; // 29 + uint32_t diss2g : 1; // 30 + uint32_t diss2vs : 1; // 31 + } bits; +} chopconf_u; + +// 0x70 PWMCONF +typedef union +{ + uint32_t data; + struct + { + /** + * 用户定义的 PWM 幅度偏移(0-255)与静止状态下的全电机电流(CS_ACTUAL=31)相关。 (重置默认值=30) 使用 PWM_OFS 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_OFS 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 PWM_OFS = 0 将禁用将电机电流缩放到低于电机特定的较低测量阈值。此设置应仅在某些条件下使用, + * 例如当电源电压可以上下变化两倍或更多时。它可以防止电机超出调节范围,但也会防止电流降到调节限制以下。 PWM_OFS > 0 允许自动缩放到低 PWM 占空比, + * 甚至低于较低的调节阈值。这允许基于实际(保持)电流比例(寄存器 IHOLD_IRUN)的低(静止)电流设置。 + */ + uint32_t pwm_ofs : 8; + /** + * PWM 幅度的速度依赖梯度: PWM_GRAD x 256 / TSTEP 此值加到 PWM_OFS 以补偿速度依赖的电机反电动势。 使用 PWM_GRAD 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_GRAD 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 提示: 初始调谐后,可以从 PWM_GRAD_AUTO 中读取所需的初始值。 + */ + uint32_t pwm_grad : 8; + uint32_t pwm_freq : 2; + uint32_t pwm_autoscale : 1; + uint32_t pwm_autograd : 1; + } bits; +} pwmconf_u; + +typedef struct +{ + gconf_u gconf; // 0x00 GCONF + gstat_u gstat; // 0x01 GSTAT + drvconf_u drvconf; // 0x0A DRVCONF + global_scaler_u global_scaler; // 0x0B GLOBAL_SCALER + ihold_irun_u ihold_irun; // 0x10 IHOLD_IRUN + chopconf_u chopconf; // 0x6C CHOPCONF + pwmconf_u pwmconf; // 0x70 PWMCONF +} tmc2240_config_t; + +typedef struct +{ + gpio_t *en; ///< EN_PIN + gpio_t *dir; ///< DIR_PIN + TIM_TypeDef *timer; + uint32_t time_ch; + spi_t *spi; + + tmc2240_config_t config; + tmc2240_config_t read_config; + + uint32_t step; + __IO uint32_t step_count; + + // PRIVATE + struct + { + uint32_t sysclk; // 系统时钟 + uint32_t psc; // 预分频系数 + uint16_t arr; // 自动重装值 auto reload value + uint32_t freq; // 频率 + } default_tm; + struct + { + BOOL enable; // 使能 + tmc2240_direction_e direction; // 方向 + float32 percent; // 占空比 + uint16_t arr; // 自动重装值(改变速度) + uint32_t freq; // 频率 + } params; + + struct + { + float32 step_angle; // 步进角度 + uint16_t circle_pulse; // 一圈脉冲数 + + __IO int32_t add_pulse_count; /* 脉冲个数累计 */ + __IO uint32_t pulse_count; /* 脉冲个数记录 */ + __IO uint32_t step_angle_count; /* 步距个数 */ + } motor; + + struct + { + uint16_t tmc2240_adc_temp; // 温度ADC值 + float32 tmc2240_temperature; // 温度 + } data; + +} tmc2240_t; + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios); +tmc2240_t *tmc2240_get(tmc2240_index_e index); + +void tmc2240_motor_set_angle(tmc2240_index_e index, int32_t angle); +void tmc2240_motor_update(tmc2240_index_e index); + +void tmc2240_test(tmc2240_index_e index); +void tmc2240_config_read(tmc2240_index_e index); +#endif // __TMC2240_H