i2c test and display

This commit is contained in:
Alessandro Mauri 2026-04-16 13:09:24 +02:00
parent 9e254e0fb1
commit dae0fbcbda
9 changed files with 552 additions and 14 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "fw/ch32fun"] [submodule "fw/ch32fun"]
path = fw/ch32fun path = fw/ch32fun
url = https://github.com/cnlohr/ch32fun url = https://github.com/cnlohr/ch32fun
[submodule "fw/u8g2"]
path = fw/u8g2
url = https://github.com/olikraus/u8g2

View File

@ -1,8 +1,19 @@
all : build all : build
TARGET:=main TARGET := main
TARGET_MCU:=CH32X035 TARGET_MCU := CH32X035
TARGET_MCU_PACKAGE:=CH32X035F8U6 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 include ch32fun/ch32fun/ch32fun.mk

174
fw/display.c Normal file
View File

@ -0,0 +1,174 @@
#include <u8g2.h>
#include <ch32fun.h>
#include <stdio.h>
#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;
}

12
fw/display.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _DISPLAY_H
#define _DISPLAY_H
#include <u8g2.h>
#define SSD1306_128X32 1
// #define SSD1312_96X16 1
#define PIN_DISP_RST PA7 // display reset
u8g2_t* display_init(void);
#endif // _DISPLAY_H

View File

@ -5,5 +5,7 @@
#define FUNCONF_USE_USBPRINTF 1 #define FUNCONF_USE_USBPRINTF 1
#define FUNCONF_DEBUG_HARDFAULT 0 #define FUNCONF_DEBUG_HARDFAULT 0
#define CH32X035 1 #define CH32X035 1
#define I2C_TARGET I2C1
#endif
#endif // _FUNCONFIG_H

232
fw/lib_i2c.c Normal file
View File

@ -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

53
fw/lib_i2c.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef _LIB_I2C_H
#define _LIB_I2C_H
// MIT License
// Copyright (c) 2025 UniTheCat
// Tested with Ch32X03x and CH32V30x
#include <ch32fun.h>
#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, &reg, 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

View File

@ -3,17 +3,21 @@
#include <stdio.h> #include <stdio.h>
#include <fsusb.h> #include <fsusb.h>
#include "lib_i2c.h"
#include "display.h"
// Pin definitions // Pin definitions
#define PIN_LED PA4 // debug led #define PIN_LED PA4 // debug led
#define PIN_VBUS PA0 // vbus voltage feedback #define PIN_VBUS PA0 // vbus voltage feedback
#define PIN_CURRENT PA1 // current feedback #define PIN_CURRENT PA1 // current feedback
#define PIN_NTC PA2 // ntc temperature sensor #define PIN_NTC PA2 // ntc temperature sensor
#define PIN_TEMP PA3 // thermocouple amplifier #define PIN_TEMP PA3 // thermocouple amplifier
#define PIN_12V PA5 // 12V regulator enable #define PIN_12V PA5 // 12V regulator enable
#define PIN_HEATER PA6 // power mosfet gate control #define PIN_HEATER PA6 // power mosfet gate control
#define PIN_ENC_A PB3 // rotary encoder A #define PIN_ENC_A PB3 // rotary encoder A
#define PIN_ENC_B PB11 // rotary encoder B #define PIN_ENC_B PB11 // rotary encoder B
#define PIN_BTN PB1 // rotary encoder button #define PIN_BTN PB1 // rotary encoder button
// Analog channel definitions // Analog channel definitions
#define VBUS_ADC_CHANNEL ANALOG_0 // PA0 #define VBUS_ADC_CHANNEL ANALOG_0 // PA0
@ -32,6 +36,7 @@ const int16_t ntc_table[64] = {
}; };
u8g2_t *u8g2;
uint8_t pin = 0; uint8_t pin = 0;
int16_t encoder = 0; // rotary encoder counter int16_t encoder = 0; // rotary encoder counter
uint32_t last_interrupt = 0; // last time the encoder interrupt was triggered 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, // this callback is mandatory when FUNCONF_USE_USBPRINTF is defined,
// can be empty though // can be empty though
void handle_usbfs_input(int numbytes, uint8_t *data) void handle_usbfs_input(int numbytes, uint8_t *data)
@ -81,8 +92,21 @@ void handle_usbfs_input(int numbytes, uint8_t *data)
printf( printf(
"Available commands:\n" "Available commands:\n"
"\tr : toggle the 12V regulator\n" "\tr : toggle the 12V regulator\n"
"\td : init display\n"
"\ts : scan I2C bus\n"
); );
break; 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: default:
printf("Unknown command '%c'\n", data[0]); printf("Unknown command '%c'\n", data[0]);
break; break;
@ -129,6 +153,8 @@ __attribute__((noreturn)) int main(void)
funDigitalWrite(PIN_12V, 0); funDigitalWrite(PIN_12V, 0);
funPinMode(PIN_HEATER, GPIO_CFGLR_OUT_10Mhz_PP); funPinMode(PIN_HEATER, GPIO_CFGLR_OUT_10Mhz_PP);
funDigitalWrite(PIN_HEATER, 0); 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 funPinMode(PIN_ENC_A, GPIO_CFGLR_IN_PUPD); // enable pull-up/down
funDigitalWrite(PIN_ENC_A, 1); // specify pull-up 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 funDigitalWrite(PIN_ENC_B, 1); // specify pull-up
funPinMode(PIN_BTN, GPIO_CFGLR_IN_FLOAT); 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. // Configure the IO as an interrupt.
// PIN_ENC_B is on port B, channel 11 // PIN_ENC_B is on port B, channel 11
AFIO->EXTICR1 = AFIO_EXTICR1_EXTI11_PB; // Port B channel (pin) 11 AFIO->EXTICR1 = AFIO_EXTICR1_EXTI11_PB; // Port B channel (pin) 11

1
fw/u8g2 Submodule

@ -0,0 +1 @@
Subproject commit 3b8939cd015d6aca6a19f761df84859916f1f7d9