initial WIP
lots of code copied over, things filled in to hopefully get the LED matrix lighting up. untested.
This commit is contained in:
92
firmware/app/comms/i2c.h
Normal file
92
firmware/app/comms/i2c.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* i2c.h
|
||||
*
|
||||
* Created on: Oct 13, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_COMMS_I2C_H_
|
||||
#define USER_COMMS_I2C_H_
|
||||
|
||||
|
||||
#include "ch32x035_conf.h"
|
||||
|
||||
|
||||
|
||||
#define SOFT_I2C_MASTER
|
||||
|
||||
|
||||
|
||||
#ifdef SOFT_I2C_MASTER
|
||||
|
||||
|
||||
#include "soft_i2c_master.h"
|
||||
|
||||
/* known bugs / limitations:
|
||||
* - there is no ack on address. code always assumes slave is responding.
|
||||
*/
|
||||
#define i2c_init() i2cm_init()
|
||||
#define i2c_start() { SetSysClock_HSI(HCLK_24MHZ); i2cm_start(); }
|
||||
#define i2c_restart() i2cm_restart()
|
||||
#define i2c_stop() { i2cm_stop(); SetSysClock_HSI(HCLK_8MHZ); }
|
||||
#define i2c_rd(ack) i2cm_rd(ack)
|
||||
#define i2c_wr(dat) i2cm_wr(dat)
|
||||
#define i2c_addr(a, w) i2c_start(); i2cm_addr(a, w)
|
||||
|
||||
#define i2c_rdbuf(d, s) i2cm_rdbuf(d, s); i2c_stop()
|
||||
#define i2c_wrbuf(d, s) i2cm_wrbuf(d, s); i2c_stop()
|
||||
|
||||
|
||||
#define i2c_read(a, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_read_reg8(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r); \
|
||||
i2cm_restart(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_read_reg16(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r >> 8); \
|
||||
i2cm_wr(r & 0xff); \
|
||||
i2cm_restart(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
|
||||
#define i2c_write(a, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_write_reg8(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_write_reg16(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r >> 8); \
|
||||
i2cm_wr(r & 0xff); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#error HW I2C NOT YET IMPLEMENTED!
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* USER_COMMS_I2C_H_ */
|
||||
222
firmware/app/comms/soft_i2c_master.c
Normal file
222
firmware/app/comms/soft_i2c_master.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* soft_i2c.c
|
||||
*
|
||||
* looked at random i2c code, used as inspiration, and wrote some
|
||||
* garbage for CH59x. then ported to CH32X035.
|
||||
*
|
||||
* i2c hardware peripheral on CH59x is shared with debug pins or.
|
||||
* USB pins. using both? touch shit, no I2C master periph for you.
|
||||
*
|
||||
* quirks, features and limitations:
|
||||
* - this implementation is blocking.
|
||||
* - clock stretching is supported, and somewhat tested
|
||||
* - any slave stuck clock stretching will lock everything until released;
|
||||
* there is no timeout or cancel operation implemented
|
||||
*
|
||||
* known good settings at 32MHz on CH59x:
|
||||
* CYCLES_TO_HI = 5, CYCLES_TO_LOW = 2: ~400-500kHz
|
||||
* CYCLES_TO_HI = 2, CYCLES_TO_LOW = 0: ~650KHz
|
||||
* CYCLES_TO_HI = 1, bit_delay_lo = __nop: ~900KHz, but sub MCU fails to respond
|
||||
*
|
||||
* speed settings chosen are what the lowest speed device (the sub MCU) will
|
||||
* handle without faults.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ch32x035_conf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#define CYCLES_TO_HI 2
|
||||
#define CYCLES_TO_LO 0
|
||||
|
||||
//#define CYCLES_RD 2 // cycles spent in read routine
|
||||
//#define CYCLES_WR_HI 2 // extra cycles spent in write routine
|
||||
//#define CYCLES_WR_LO 4
|
||||
|
||||
#define bit_delay_hi() { spin = delay_hi; while(spin--); }
|
||||
#define bit_delay_lo() { spin = delay_lo; while(spin--); }
|
||||
|
||||
#define rd_delay() bit_delay_hi()
|
||||
|
||||
#define wr_delay_hi() bit_delay_hi()
|
||||
#define wr_delay_lo() bit_delay_hi() // spin = delay_hi - CYCLES_EXTRA_WR_HI; while(spin--)
|
||||
|
||||
|
||||
#define SDA_PIN 9
|
||||
#define SDA_BIT (1 << SDA_PIN)
|
||||
#define SDA_CONFOFF ((SDA_PIN % 8) * 4)
|
||||
#define SDA_GPIO GPIOB
|
||||
#define SDA_CFGR GPIOB->CFGHR
|
||||
|
||||
#define SCL_PIN 8
|
||||
#define SCL_BIT (1 << SCL_PIN)
|
||||
#define SCL_CONFOFF ((SCL_PIN % 8) * 4)
|
||||
#define SCL_GPIO GPIOB
|
||||
#define SCL_CFGR GPIOB->CFGHR
|
||||
|
||||
#define GPIO_CFG_LO 0x1
|
||||
#define GPIO_CFG_HI 0x8
|
||||
|
||||
|
||||
#define SDA_IN_HI() { SDA_CFGR &= ~(0xf << SDA_CONFOFF); SDA_CFGR |= (GPIO_CFG_HI << SDA_CONFOFF); }
|
||||
#define SDA_OUTLO() { SDA_CFGR &= ~(0xf << SDA_CONFOFF); SDA_CFGR |= (GPIO_CFG_LO << SDA_CONFOFF); }
|
||||
#define SDA_SET_LO() SDA_GPIO->BCR = SDA_BIT
|
||||
#define SDA_GET() ( SDA_GPIO->INDR & SDA_BIT )
|
||||
|
||||
#define SCL_IN_HI() { SCL_CFGR &= ~(0xf << SCL_CONFOFF); SCL_CFGR |= (GPIO_CFG_HI << SCL_CONFOFF); }
|
||||
#define SCL_OUTLO() { SCL_CFGR &= ~(0xf << SCL_CONFOFF); SCL_CFGR |= (GPIO_CFG_LO << SCL_CONFOFF); }
|
||||
#define SCL_SET_LO() SCL_GPIO->BCR = SCL_BIT
|
||||
#define SCL_GET() ( SCL_GPIO->INDR & SCL_BIT )
|
||||
|
||||
|
||||
|
||||
static uint16_t delay_hi, delay_lo;
|
||||
static volatile uint16_t spin;
|
||||
|
||||
|
||||
|
||||
// re-run init any time the clock speed changes to recalculate delays.
|
||||
void i2cm_init()
|
||||
{
|
||||
//uint32_t sysclk;
|
||||
//uint32_t cycles;
|
||||
|
||||
// configure GPIO
|
||||
SCL_IN_HI();
|
||||
SCL_SET_LO();
|
||||
|
||||
SDA_IN_HI();
|
||||
SDA_SET_LO();
|
||||
|
||||
// configure timer
|
||||
//sysclk = GetSysClock();
|
||||
//cycles = sysclk / 500000;
|
||||
|
||||
delay_hi = CYCLES_TO_HI;
|
||||
delay_lo = CYCLES_TO_LO;
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc")))
|
||||
void i2cm_start()
|
||||
{
|
||||
SDA_IN_HI(); bit_delay_hi();
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
while (!SCL_GET()) {}; // clock stretch
|
||||
SDA_OUTLO(); bit_delay_lo();
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc")))
|
||||
void i2cm_restart()
|
||||
{
|
||||
SDA_IN_HI(); bit_delay_hi();
|
||||
SCL_IN_HI();
|
||||
while (!SCL_GET()) {}; // clock stretch
|
||||
bit_delay_hi(); // attempt to fix corruption; unnecessary?
|
||||
i2cm_start();
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc")))
|
||||
void i2cm_stop()
|
||||
{
|
||||
SDA_OUTLO(); bit_delay_lo();
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
while (!SCL_GET()) {}; // clock stretch
|
||||
SDA_IN_HI(); bit_delay_hi();
|
||||
bit_delay_hi();
|
||||
bit_delay_hi();
|
||||
bit_delay_hi();
|
||||
}
|
||||
|
||||
// returns: data byte
|
||||
__attribute__((section(".ramfunc")))
|
||||
uint8_t i2cm_rd(uint8_t ack)
|
||||
{
|
||||
int8_t x;
|
||||
uint8_t in = 0;
|
||||
|
||||
SDA_IN_HI();
|
||||
|
||||
for (x = 8; x; x--) {
|
||||
in <<= 1; // clock next bit
|
||||
|
||||
SCL_IN_HI();
|
||||
while (!SCL_GET()) {}; // clock stretch
|
||||
|
||||
rd_delay();
|
||||
|
||||
if (SDA_GET()) in |= 1;
|
||||
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
}
|
||||
|
||||
if (ack) { SDA_OUTLO(); } // ack
|
||||
else { SDA_IN_HI(); } // nack
|
||||
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
// returns: possible ack from target
|
||||
__attribute__((section(".ramfunc")))
|
||||
uint8_t i2cm_wr(uint8_t dat)
|
||||
{
|
||||
int8_t x;
|
||||
uint8_t ack;
|
||||
|
||||
SCL_OUTLO();
|
||||
|
||||
for (x = 8; x; x--) {
|
||||
if (dat & 0x80) { SDA_IN_HI(); }
|
||||
else { SDA_OUTLO(); }
|
||||
|
||||
SCL_IN_HI();
|
||||
while (!SCL_GET()) {}; // clock stretch
|
||||
wr_delay_hi();
|
||||
|
||||
dat <<= 1;
|
||||
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
}
|
||||
|
||||
SDA_IN_HI(); __asm__ volatile("nop"); // slave will now try to ack
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
while (!SCL_GET()); // nothing should stretch here, but...
|
||||
|
||||
ack = SDA_GET();
|
||||
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
// use a left-aligned address with this implementation.
|
||||
__attribute__((section(".ramfunc")))
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit)
|
||||
{
|
||||
addr &= 0xfe;
|
||||
addr |= reading_bit ? 1 : 0;
|
||||
return i2cm_wr(addr);
|
||||
}
|
||||
|
||||
|
||||
void i2cm_rdbuf(uint8_t *dat, uint16_t len)
|
||||
{
|
||||
while (len--) *dat++ = i2cm_rd(len > 0);
|
||||
// i2cm_stop();
|
||||
}
|
||||
|
||||
void i2cm_wrbuf(const uint8_t *dat, uint16_t len)
|
||||
{
|
||||
uint8_t nack;
|
||||
|
||||
while (len--) {
|
||||
nack = i2cm_wr(*dat++);
|
||||
if (nack) break;
|
||||
}
|
||||
// i2cm_stop();
|
||||
}
|
||||
28
firmware/app/comms/soft_i2c_master.h
Normal file
28
firmware/app/comms/soft_i2c_master.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* soft_i2c.h
|
||||
*/
|
||||
|
||||
#ifndef USER_COMM_SOFT_I2C_MASTER_H_
|
||||
#define USER_COMM_SOFT_I2C_MASTER_H_
|
||||
|
||||
#include <ch32x035.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
void i2cm_init();
|
||||
|
||||
void i2cm_start();
|
||||
void i2cm_restart();
|
||||
void i2cm_stop();
|
||||
|
||||
uint8_t i2cm_rd(uint8_t ack);
|
||||
uint8_t i2cm_wr(uint8_t dat);
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit);
|
||||
|
||||
void i2cm_rdbuf(uint8_t *dat, uint16_t len);
|
||||
void i2cm_wrbuf(const uint8_t *dat, uint16_t len);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_COMM_SOFT_I2C_MASTER_H_ */
|
||||
67
firmware/app/comms/spi_master.c
Normal file
67
firmware/app/comms/spi_master.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* spi_master.c
|
||||
*
|
||||
* manage chip select externally.
|
||||
*
|
||||
* current version is BLOCKING. update later to use interrupt or DMA.
|
||||
*/
|
||||
|
||||
#include "spi_master.h"
|
||||
|
||||
|
||||
|
||||
void spim_init()
|
||||
{
|
||||
SPI_InitTypeDef spi = {0};
|
||||
|
||||
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
spi.SPI_Mode = SPI_Mode_Master;
|
||||
spi.SPI_DataSize = SPI_DataSize_8b;
|
||||
spi.SPI_CPOL = SPI_CPOL_High;
|
||||
spi.SPI_CPHA = SPI_CPHA_2Edge;
|
||||
spi.SPI_NSS = SPI_NSS_Soft;
|
||||
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
|
||||
spi.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
|
||||
SPI_Init(SPI1, &spi);
|
||||
|
||||
SPI_Cmd(SPI1, ENABLE);
|
||||
}
|
||||
|
||||
void spim_write_raw(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
// todo: make non-blocking, timeout and error checking
|
||||
while (len) {
|
||||
if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)) {
|
||||
SPI_I2S_SendData(SPI1, *data);
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spim_write_reg8(uint8_t reg, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
spim_write_raw(®, 1);
|
||||
spim_write_raw(data, len);
|
||||
}
|
||||
|
||||
void spim_read_raw(uint8_t *data, uint16_t len)
|
||||
{
|
||||
// todo: non-blocking, timeout and error checking
|
||||
while (len) {
|
||||
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY)) {};
|
||||
|
||||
if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)) {
|
||||
*data++ = SPI_I2S_ReceiveData(SPI1);
|
||||
len--;
|
||||
}
|
||||
SPI_I2S_SendData(SPI1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void spim_read_reg8(uint8_t reg, uint8_t *data, uint16_t len)
|
||||
{
|
||||
spim_write_raw(®, 1);
|
||||
spim_read_raw(data, len);
|
||||
}
|
||||
20
firmware/app/comms/spi_master.h
Normal file
20
firmware/app/comms/spi_master.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __APP_COMMS_SPI_MASTER_H
|
||||
#define __APP_COMMS_SPI_MASTER_H
|
||||
|
||||
|
||||
#include "ch32x035_conf.h"
|
||||
|
||||
|
||||
|
||||
void spim_init();
|
||||
|
||||
void spim_write_reg8(uint8_t reg, const uint8_t *data, uint16_t len);
|
||||
void spim_read_reg8(uint8_t reg, uint8_t *data, uint16_t len);
|
||||
|
||||
|
||||
|
||||
#endif /* __APP_COMMS_SPI_MASTER_H */
|
||||
Reference in New Issue
Block a user