From dae0fbcbdaa0b0bd9212348c323dfa8683b46ce4 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Thu, 16 Apr 2026 13:09:24 +0200 Subject: [PATCH] i2c test and display --- .gitmodules | 3 + fw/Makefile | 17 +++- fw/display.c | 174 +++++++++++++++++++++++++++++++++++++ fw/display.h | 12 +++ fw/funconfig.h | 4 +- fw/lib_i2c.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++ fw/lib_i2c.h | 53 +++++++++++ fw/main.c | 70 ++++++++++++--- fw/u8g2 | 1 + 9 files changed, 552 insertions(+), 14 deletions(-) create mode 100644 fw/display.c create mode 100644 fw/display.h create mode 100644 fw/lib_i2c.c create mode 100644 fw/lib_i2c.h create mode 160000 fw/u8g2 diff --git a/.gitmodules b/.gitmodules index 87e284e..4a07115 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "fw/ch32fun"] path = fw/ch32fun url = https://github.com/cnlohr/ch32fun +[submodule "fw/u8g2"] + path = fw/u8g2 + url = https://github.com/olikraus/u8g2 diff --git a/fw/Makefile b/fw/Makefile index c55619e..58e37a6 100644 --- a/fw/Makefile +++ b/fw/Makefile @@ -1,8 +1,19 @@ all : build -TARGET:=main -TARGET_MCU:=CH32X035 -TARGET_MCU_PACKAGE:=CH32X035F8U6 +TARGET := main +TARGET_MCU := CH32X035 +TARGET_MCU_PACKAGE := CH32X035F8U6 + +# include u8g2 +U8G2_DIR:=u8g2/csrc +# U8G2_SRC:=u8g2/csrc/u8x8_d_ssd1312.c $(filter-out $(U8G2_DIR)/u8x8_d_%.c, $(wildcard $(U8G2_DIR)/*.c)) +U8G2_SRC:=u8g2/csrc/u8x8_d_ssd1306_128x32.c $(filter-out $(U8G2_DIR)/u8x8_d_%.c, $(wildcard $(U8G2_DIR)/*.c)) + +EXTRA_CFLAGS += -I$(U8G2_DIR) + +ADDITIONAL_C_FILES += $(U8G2_SRC) +ADDITIONAL_C_FILES += lib_i2c.c +ADDITIONAL_C_FILES += display.c include ch32fun/ch32fun/ch32fun.mk diff --git a/fw/display.c b/fw/display.c new file mode 100644 index 0000000..c40fae8 --- /dev/null +++ b/fw/display.c @@ -0,0 +1,174 @@ +#include +#include +#include + +#include "display.h" +#include "lib_i2c.h" + + +/* + * GPIO and delay callback for u8g2/u8x8 display driver + */ + + +static u8g2_t u8g2; + + +uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + switch(msg) { + case U8X8_MSG_GPIO_AND_DELAY_INIT: + // called once during init phase of u8g2/u8x8 + // can be used to setup pins + printf("TODO: U8X8_MSG_GPIO_AND_DELAY_INIT\n"); + break; + case U8X8_MSG_DELAY_NANO: + // delay arg_int * 1 nano second + // at 48MHz 1 cycle = 20ns, 1 nop = 1 cycle + // the for lopp takes about 2 cycles per iteration + for (int i = 0; i < (arg_int/(20*3)); i++) { + asm("nop"); + } + break; + case U8X8_MSG_DELAY_100NANO: + // delay arg_int * 100 nano seconds + // at 48MHz 1 cycle = 20ns, 1 nop = 1 cycle + // the for lopp takes about 2 cycles per iteration + for (int i = 0; i < ((arg_int*100)/(20*3)); i++) { + asm("nop"); + } + break; + case U8X8_MSG_DELAY_10MICRO: + // delay arg_int * 10 micro seconds + Delay_Us(arg_int*10); + break; + case U8X8_MSG_DELAY_MILLI: + // delay arg_int * 1 milli second + Delay_Ms(arg_int); + break; + case U8X8_MSG_DELAY_I2C: + // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz + // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us + printf("TODO: U8X8_MSG_DELAY_I2C\n"); + break; + case U8X8_MSG_GPIO_D0: + // D0 or SPI clock pin: Output level in arg_int + //case U8X8_MSG_GPIO_SPI_CLOCK: + break; + case U8X8_MSG_GPIO_D1: + // D1 or SPI data pin: Output level in arg_int + //case U8X8_MSG_GPIO_SPI_DATA: + break; + case U8X8_MSG_GPIO_D2: + // D2 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_D3: + // D3 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_D4: + // D4 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_D5: + // D5 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_D6: + // D6 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_D7: + // D7 pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_E: + // E/WR pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_CS: + // CS (chip select) pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_DC: + // DC (data/cmd, A0, register select) pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_RESET: + funDigitalWrite(PIN_DISP_RST, arg_int); + // Reset pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_CS1: + // CS1 (chip select) pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_CS2: + // CS2 (chip select) pin: Output level in arg_int + break; + case U8X8_MSG_GPIO_I2C_CLOCK: + // arg_int=0: Output low at I2C clock pin + // arg_int=1: Input dir with pullup high for I2C clock pin + break; + case U8X8_MSG_GPIO_I2C_DATA: + // arg_int=0: Output low at I2C data pin + // arg_int=1: Input dir with pullup high for I2C data pin + break; + case U8X8_MSG_GPIO_MENU_SELECT: + // get menu select pin state + u8x8_SetGPIOResult(u8x8, 0); + break; + case U8X8_MSG_GPIO_MENU_NEXT: + // get menu next pin state + u8x8_SetGPIOResult(u8x8, 0); + break; + case U8X8_MSG_GPIO_MENU_PREV: + // get menu prev pin state + u8x8_SetGPIOResult(u8x8, 0); + break; + case U8X8_MSG_GPIO_MENU_HOME: + // get menu home pin state + u8x8_SetGPIOResult(u8x8, 0); + break; + default: + // default return value + u8x8_SetGPIOResult(u8x8, 1); + break; + } + return 1; +} + + +uint8_t u8x8_byte_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + static uint8_t buffer[32]; + static uint8_t buffer_idx; + uint8_t *data; + + switch(msg) { + case U8X8_MSG_BYTE_INIT: + break; + case U8X8_MSG_BYTE_SEND: + data = (uint8_t*)arg_ptr; + while (arg_int > 0) { + buffer[buffer_idx++] = *data; + data++; + arg_int--; + } + break; + case U8X8_MSG_BYTE_START_TRANSFER: + buffer_idx = 0; + break; + case U8X8_MSG_BYTE_END_TRANSFER: + i2c_sendBytes(I2C_TARGET, u8x8_GetI2CAddress(u8x8) >> 1, buffer, buffer_idx); + break; + default: + return 0; + } + return 1; +} + + +u8g2_t* display_init(void) +{ +#if SSD1306_128X32 && SSD1306_128X32 == 1 + u8g2_Setup_ssd1306_i2c_128x32_univision_f(&u8g2, U8G2_R0, u8x8_byte_i2c, u8x8_gpio_and_delay); +#elif SSD1312_96X16 && SSD1312_96X16 == 1 + // NOTE: display size is wrong, hardware is 96x16, but driver is configured for 120x28 + u8g2_Setup_ssd1312_i2c_120x28_f(&u8g2, U8G2_R0, u8x8_byte_i2c, u8x8_gpio_and_delay); +#endif + // TODO: log errors and return NULL on failure + u8g2_InitDisplay(&u8g2); + u8g2_SetPowerSave(&u8g2, 0); + return &u8g2; +} diff --git a/fw/display.h b/fw/display.h new file mode 100644 index 0000000..1e74fb7 --- /dev/null +++ b/fw/display.h @@ -0,0 +1,12 @@ +#ifndef _DISPLAY_H +#define _DISPLAY_H + +#include + +#define SSD1306_128X32 1 +// #define SSD1312_96X16 1 +#define PIN_DISP_RST PA7 // display reset + +u8g2_t* display_init(void); + +#endif // _DISPLAY_H diff --git a/fw/funconfig.h b/fw/funconfig.h index d182602..3d2effa 100644 --- a/fw/funconfig.h +++ b/fw/funconfig.h @@ -5,5 +5,7 @@ #define FUNCONF_USE_USBPRINTF 1 #define FUNCONF_DEBUG_HARDFAULT 0 #define CH32X035 1 +#define I2C_TARGET I2C1 -#endif + +#endif // _FUNCONFIG_H diff --git a/fw/lib_i2c.c b/fw/lib_i2c.c new file mode 100644 index 0000000..521225b --- /dev/null +++ b/fw/lib_i2c.c @@ -0,0 +1,232 @@ +// MIT License +// Copyright (c) 2025 UniTheCat +// Tested with Ch32X03x and CH32V30x + +#include "lib_i2c.h" + +#define I2C_DEFAULT_TIMEOUT 100000 + +//! #################################### +//! I2C INIT FUNCTIONS +//! #################################### + +void i2c_init(I2C_TypeDef* I2Cx, u32 PCLK, u32 i2cSpeed_Hz) { + // Enable I2C clock + if (I2Cx == I2C1) { + RCC->APB1PCENR |= RCC_APB1Periph_I2C1; + } + #ifdef I2C2 + else if (I2Cx == I2C2) { + RCC->APB1PCENR |= RCC_APB1Periph_I2C2; + } + #endif + + // Disable I2C before configuration + I2Cx->CTLR1 &= ~I2C_CTLR1_PE; + + // configure I2C clock + I2Cx->CTLR2 = (PCLK / 1000000); + I2Cx->CKCFGR = PCLK / (i2cSpeed_Hz << 1); // PeripheralClock / (100KHz * 2) + + // Enable I2C + I2Cx->CTLR1 |= I2C_CTLR1_PE; + + // Enable ACK + I2Cx->CTLR1 |= I2C_CTLR1_ACK; +} + +u8 i2c_start(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 isRead) { + //# Wait while BUSY, when BUSY is set to 0 then continue + u32 timeout = I2C_DEFAULT_TIMEOUT; + while((I2Cx->STAR2 & I2C_STAR2_BUSY) && --timeout); + // if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x11; } + + //# Generate START condition + I2Cx->CTLR1 |= I2C_CTLR1_START; + + //# Wait while SB is 0, when SB is set to 1 then continue + timeout = I2C_DEFAULT_TIMEOUT; + while(!(I2Cx->STAR1 & I2C_STAR1_SB) && --timeout); + if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x12; } + // printf("timeoutB: %d\n", I2C_DEFAULT_TIMEOUT - timeout); + + //# Send address + read/write. Write = 0, Read = 1 + I2Cx->DATAR = (i2cAddress << 1) | isRead; + + //# Wait while ADDR is 0, if ADDR is set to 1 then continue + timeout = I2C_DEFAULT_TIMEOUT; + while(!(I2Cx->STAR1 & I2C_STAR1_ADDR) && --timeout); + if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x13; } + // printf("timeoutC: %d\n", I2C_DEFAULT_TIMEOUT - timeout); + + //! REQUIRED. Clear ADDR by reading STAR1 then STAR2 + (void)I2Cx->STAR1; + (void)I2Cx->STAR2; + return 0; +} + +void i2c_stop(I2C_TypeDef* I2Cx) +{ + //# Generate STOP condition + I2Cx->CTLR1 |= I2C_CTLR1_STOP; +} + +void i2c_scan(I2C_TypeDef* I2Cx, void (*onPingFound)(u8 address)) { + // mininum 0x08 to 0x77 (0b1110111) + for (int i = 0x08; i < 0x77; i++) { + u8 ping = i2c_start(I2Cx, i, 1); + + //# Generate STOP condition + I2Cx->CTLR1 |= I2C_CTLR1_STOP; + if (ping == 0) onPingFound(i); + } +} + +//! #################################### +//! I2C SEND FUNCTION +//! #################################### + +u8 i2c_sendBytes_noStop(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len) { + u8 err = i2c_start(I2Cx, i2cAddress, 0); // Write mode + if (err) return err; + u32 timeout; + + for(u8 i = 0; i < len; i++) { + //# Wait for register empty + timeout = I2C_DEFAULT_TIMEOUT; + while(!(I2Cx->STAR1 & I2C_STAR1_TXE) && --timeout); + if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x21; } + I2Cx->DATAR = buffer[i]; // Send data + } + + //# Wait for transmission complete. Wait while BTF is 0, when set to 1 continue + timeout = I2C_DEFAULT_TIMEOUT; + while(!(I2Cx->STAR1 & I2C_STAR1_BTF) && --timeout); + if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x22; } + + return 0; +} + +u8 i2c_sendBytes(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len) { + u8 err = i2c_sendBytes_noStop(I2Cx, i2cAddress, buffer, len); + //# Generate STOP condition + I2Cx->CTLR1 |= I2C_CTLR1_STOP; + return err; +} + +u8 i2c_sendByte(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 data) { + return i2c_sendBytes(I2Cx, i2cAddress, &data, 1); +} + + +//! #################################### +//! I2C RECEIVE FUNCTIONS +//! #################################### + +u8 i2c_readBytes(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len) { + u8 err = i2c_start(I2Cx, i2cAddress, 1); // Read mode + if (err) return err; + + //# Enable ACK at the beginning + I2Cx->CTLR1 |= I2C_CTLR1_ACK; + + for(u8 i = 0; i < len; i++) { + //# Before reading the last bytes, disable ACK to signal the slave to stop sending + if(i == len-1) I2Cx->CTLR1 &= ~I2C_CTLR1_ACK; + + //# Wait for data. Wait while RxNE is 0, when set to 1 continue + u32 timeout = I2C_DEFAULT_TIMEOUT; + while(!(I2Cx->STAR1 & I2C_STAR1_RXNE) && --timeout); + if (timeout == 0) { I2Cx->CTLR1 |= I2C_CTLR1_STOP; return 0x31; } + + //# Read data + buffer[i] = I2Cx->DATAR; + } + + //# Generate STOP condition + I2Cx->CTLR1 |= I2C_CTLR1_STOP; + return 0; +} + +// Write to register and then do read data, no stop inbetween +u8 i2c_readRegTx_buffer(I2C_TypeDef* I2Cx, u8 i2cAddress, + u8 *tx_buf, u8 tx_len, u8 *rx_buf, u8 rx_len +) { + u8 err = i2c_sendBytes_noStop(I2Cx, i2cAddress, tx_buf, tx_len); // Send register address + if (err) return err; + err = i2c_readBytes(I2Cx, i2cAddress, rx_buf, rx_len); // Read data + return err; +} + + +//! #################################### +//! I2C SLAVE FUNCTIONS +//! #################################### + +void i2c_slave_init(I2C_TypeDef* I2Cx, u16 self_addr, u32 PCLK, u32 i2cSpeed_Hz) { + i2c_init(I2Cx, PCLK, i2cSpeed_Hz); + + // Configure the CH32 I2C slave address to make it an I2C slave + I2Cx->OADDR1 = (self_addr << 1); + I2Cx->OADDR2 = 0; + + I2Cx->CTLR2 |= I2C_CTLR2_ITEVTEN | I2C_CTLR2_ITERREN | I2C_CTLR2_ITBUFEN; + + // Enable Event and Error Interrupts + if (I2Cx == I2C1) { + NVIC_EnableIRQ(I2C1_EV_IRQn); // I2C Event interrupt + NVIC_EnableIRQ(I2C1_ER_IRQn); // I2C Error interrupt + } + + #ifdef I2C2 + else if (I2Cx == I2C2) { + NVIC_EnableIRQ(I2C2_EV_IRQn); // I2C Event interrupt + NVIC_EnableIRQ(I2C2_ER_IRQn); // I2C Error interrupt + } + #endif +} + +void I2C1_ER_IRQHandler(void) __attribute__((interrupt)); +void I2C1_ER_IRQHandler(void) { + // get I2C status + uint16_t STAR1 = I2C1->STAR1; + + // Obtain and clear Bus error + if (STAR1 & I2C_STAR1_BERR) { + I2C1->STAR1 &= ~(I2C_STAR1_BERR); + } + + // Obtain and clear Arbitration lost error + if (STAR1 & I2C_STAR1_ARLO) { + I2C1->STAR1 &= ~(I2C_STAR1_ARLO); + } + + // Obtain and clear Acknowledge failure error + if (STAR1 & I2C_STAR1_AF) { + I2C1->STAR1 &= ~(I2C_STAR1_AF); + } +} + + +#ifdef I2C2 + void I2C2_ER_IRQHandler(void) __attribute__((interrupt)); + void I2C2_ER_IRQHandler(void) { + // get I2C status + uint16_t STAR1 = I2C2->STAR1; + + // Obtain and clear Bus error + if (STAR1 & I2C_STAR1_BERR) { + I2C2->STAR1 &= ~(I2C_STAR1_BERR); + } + + // Obtain and clear Arbitration lost error + if (STAR1 & I2C_STAR1_ARLO) { + I2C2->STAR1 &= ~(I2C_STAR1_ARLO); + } + + // Obtain and clear Acknowledge failure error + if (STAR1 & I2C_STAR1_AF) { + I2C2->STAR1 &= ~(I2C_STAR1_AF); + } + } +#endif diff --git a/fw/lib_i2c.h b/fw/lib_i2c.h new file mode 100644 index 0000000..9f542f8 --- /dev/null +++ b/fw/lib_i2c.h @@ -0,0 +1,53 @@ +#ifndef _LIB_I2C_H +#define _LIB_I2C_H + +// MIT License +// Copyright (c) 2025 UniTheCat +// Tested with Ch32X03x and CH32V30x + +#include + + +#define I2C_DEFAULT_TIMEOUT 100000 + +//! #################################### +//! I2C INIT FUNCTIONS +//! #################################### + +void i2c_init(I2C_TypeDef* I2Cx, u32 PCLK, u32 i2cSpeed_Hz); +u8 i2c_start(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 isRead); +void i2c_stop(I2C_TypeDef* I2Cx); +u8 i2c_write(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 reg, u8 data); +u8 i2c_read(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 reg, u8* buffer, u8 len); +void i2c_scan(I2C_TypeDef* I2Cx, void (*onPingFound)(u8 address)); + +//! #################################### +//! I2C SEND FUNCTION +//! #################################### + +u8 i2c_sendBytes_noStop(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len); +u8 i2c_sendBytes(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len); +u8 i2c_sendByte(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 data); + +//! #################################### +//! I2C RECEIVE FUNCTIONS +//! #################################### + +u8 i2c_readBytes(I2C_TypeDef* I2Cx, u8 i2cAddress, u8* buffer, u8 len); +u8 i2c_readByte(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 reg, u8* buffer); + +// Write to register and then do read data, no stop inbetween +u8 i2c_readRegTx_buffer(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 *tx_buf, u8 tx_len, u8 *rx_buf, u8 rx_len); +inline u8 i2c_readReg_buffer(I2C_TypeDef* I2Cx, u8 i2cAddress, u8 reg, u8 *rx_buf, u8 rx_len) +{ + return i2c_readRegTx_buffer(I2Cx, i2cAddress, ®, 1, rx_buf, rx_len); +} + +//! #################################### +//! I2C SLAVE FUNCTIONS +//! #################################### + +void i2c_slave_init(I2C_TypeDef* I2Cx, u16 self_addr, u32 PCLK, u32 i2cSpeed_Hz); + + +#endif // _LIB_I2C_H diff --git a/fw/main.c b/fw/main.c index aa3d9d4..40f75de 100644 --- a/fw/main.c +++ b/fw/main.c @@ -3,17 +3,21 @@ #include #include +#include "lib_i2c.h" +#include "display.h" + + // Pin definitions -#define PIN_LED PA4 // debug led -#define PIN_VBUS PA0 // vbus voltage feedback -#define PIN_CURRENT PA1 // current feedback -#define PIN_NTC PA2 // ntc temperature sensor -#define PIN_TEMP PA3 // thermocouple amplifier -#define PIN_12V PA5 // 12V regulator enable -#define PIN_HEATER PA6 // power mosfet gate control -#define PIN_ENC_A PB3 // rotary encoder A -#define PIN_ENC_B PB11 // rotary encoder B -#define PIN_BTN PB1 // rotary encoder button +#define PIN_LED PA4 // debug led +#define PIN_VBUS PA0 // vbus voltage feedback +#define PIN_CURRENT PA1 // current feedback +#define PIN_NTC PA2 // ntc temperature sensor +#define PIN_TEMP PA3 // thermocouple amplifier +#define PIN_12V PA5 // 12V regulator enable +#define PIN_HEATER PA6 // power mosfet gate control +#define PIN_ENC_A PB3 // rotary encoder A +#define PIN_ENC_B PB11 // rotary encoder B +#define PIN_BTN PB1 // rotary encoder button // Analog channel definitions #define VBUS_ADC_CHANNEL ANALOG_0 // PA0 @@ -32,6 +36,7 @@ const int16_t ntc_table[64] = { }; +u8g2_t *u8g2; uint8_t pin = 0; int16_t encoder = 0; // rotary encoder counter uint32_t last_interrupt = 0; // last time the encoder interrupt was triggered @@ -58,6 +63,12 @@ static inline int16_t get_tip_temp_k(uint16_t adc_reading, int16_t cold_temp_k) } +void print_i2c_device(uint8_t addr) +{ + printf("Device found at 0x%02X\n", addr); +} + + // this callback is mandatory when FUNCONF_USE_USBPRINTF is defined, // can be empty though void handle_usbfs_input(int numbytes, uint8_t *data) @@ -81,8 +92,21 @@ void handle_usbfs_input(int numbytes, uint8_t *data) printf( "Available commands:\n" "\tr : toggle the 12V regulator\n" + "\td : init display\n" + "\ts : scan I2C bus\n" ); break; + case 'd': + printf("Initializing display...\n"); + u8g2 = display_init(); + u8g2_ClearBuffer(u8g2); + u8g2_DrawBox(u8g2, 0, 0, 20, 10); + u8g2_SendBuffer(u8g2); + break; + case 's': + printf("Scanning I2C bus...\n"); + i2c_scan(I2C1, print_i2c_device); + break; default: printf("Unknown command '%c'\n", data[0]); break; @@ -129,6 +153,8 @@ __attribute__((noreturn)) int main(void) funDigitalWrite(PIN_12V, 0); funPinMode(PIN_HEATER, GPIO_CFGLR_OUT_10Mhz_PP); funDigitalWrite(PIN_HEATER, 0); + funPinMode(PIN_DISP_RST, GPIO_CFGLR_OUT_10Mhz_PP); + funDigitalWrite(PIN_DISP_RST, 1); // start with display disabled funPinMode(PIN_ENC_A, GPIO_CFGLR_IN_PUPD); // enable pull-up/down funDigitalWrite(PIN_ENC_A, 1); // specify pull-up @@ -136,6 +162,30 @@ __attribute__((noreturn)) int main(void) funDigitalWrite(PIN_ENC_B, 1); // specify pull-up funPinMode(PIN_BTN, GPIO_CFGLR_IN_FLOAT); + Delay_Ms(5000); + + /* -------- Code to get hardware I2C working on the CH32X035F8U6 -------- */ + // Order here matters, first initialize the AFIO and I2C subsystems then + // change register values, do that the other way around and the configuration + // wont take effect + // Enable AFIO (Alternate Function IO) + RCC->APB2PCENR |= RCC_AFIOEN; + // Init I2C + i2c_init(I2C_TARGET, FUNCONF_SYSTEM_CORE_CLOCK, 100000); + + // To utilize the I2C bus we need to disable SWD first, since the pins overlap + AFIO->PCFR1 &= ~(0b0111 << 24); + AFIO->PCFR1 |= 0b0100 << 24; + + // Map SCL to PC18 and SDA to PC19 + AFIO->PCFR1 |= 0b0101 << 2; + // Manually set the I2C pins to Alternate Function IO, CNF=10b, MODE=10b + GPIOC->CFGXR &= ~((0xF << 8) | (0xF << 12)); // first clear the bits + // then set them + GPIOC->CFGXR |= 0b1010 << 8; // PC18 + GPIOC->CFGXR |= 0b1010 << 12; // PC19 + + // Configure the IO as an interrupt. // PIN_ENC_B is on port B, channel 11 AFIO->EXTICR1 = AFIO_EXTICR1_EXTI11_PB; // Port B channel (pin) 11 diff --git a/fw/u8g2 b/fw/u8g2 new file mode 160000 index 0000000..3b8939c --- /dev/null +++ b/fw/u8g2 @@ -0,0 +1 @@ +Subproject commit 3b8939cd015d6aca6a19f761df84859916f1f7d9