i2c test and display
This commit is contained in:
parent
9e254e0fb1
commit
dae0fbcbda
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||||
|
|||||||
17
fw/Makefile
17
fw/Makefile
@ -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
174
fw/display.c
Normal 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
12
fw/display.h
Normal 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
|
||||||
@ -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
232
fw/lib_i2c.c
Normal 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
53
fw/lib_i2c.h
Normal 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, ®, 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
|
||||||
50
fw/main.c
50
fw/main.c
@ -3,6 +3,10 @@
|
|||||||
#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
|
||||||
@ -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
1
fw/u8g2
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 3b8939cd015d6aca6a19f761df84859916f1f7d9
|
||||||
Loading…
x
Reference in New Issue
Block a user