initial WIP

lots of code copied over, things filled in to hopefully get the LED matrix lighting up. untested.
This commit is contained in:
true
2026-05-08 11:54:12 -07:00
commit d95af918fa
77 changed files with 24395 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32x035_conf.h
* Author : WCH
* Version : V1.0.0
* Date : 2023/04/06
* Description : Library configuration file.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __CH32X035_CONF_H
#define __CH32X035_CONF_H
#include <stdint.h>
#include "ch32x035_adc.h"
#include "ch32x035_awu.h"
#include "ch32x035_dbgmcu.h"
#include "ch32x035_dma.h"
#include "ch32x035_exti.h"
#include "ch32x035_flash.h"
#include "ch32x035_gpio.h"
#include "ch32x035_i2c.h"
#include "ch32x035_iwdg.h"
#include "ch32x035_pwr.h"
#include "ch32x035_rcc.h"
#include "ch32x035_spi.h"
#include "ch32x035_tim.h"
#include "ch32x035_usart.h"
#include "ch32x035_wwdg.h"
#include "ch32x035_it.h"
#include "ch32x035_misc.h"
#endif

View File

@@ -0,0 +1,56 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32x035_it.c
* Author : WCH
* Version : V1.0.0
* Date : 2024/10/28
* Description : Main Interrupt Service Routines.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "ch32x035_it.h"
#include "driver/adc.h"
void NMI_Handler(void) __attribute__((interrupt)); //("WCH-Interrupt-fast")));
void HardFault_Handler(void) __attribute__((interrupt)); //("WCH-Interrupt-fast")));
/*********************************************************************
* @fn NMI_Handler
*
* @brief This function handles NMI exception.
*
* @return none
*/
void NMI_Handler(void)
{
while (1)
{
}
}
/*********************************************************************
* @fn HardFault_Handler
*
* @brief This function handles Hard Fault exception.
*
* @return none
*/
void HardFault_Handler(void)
{
NVIC_SystemReset();
while (1)
{
}
}
void ADC1_IRQHandler() __attribute__((interrupt("WCH-Interrupt-fast")));
void ADC1_IRQHandler()
{
adc_isr();
}

View File

@@ -0,0 +1,20 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32x035_it.h
* Author : WCH
* Version : V1.0.0
* Date : 2023/04/06
* Description : This file contains the headers of the interrupt handlers.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __CH32X035_IT_H
#define __CH32X035_IT_H
#include "debug.h"
#endif

92
firmware/app/comms/i2c.h Normal file
View 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_ */

View 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();
}

View 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_ */

View 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(&reg, 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(&reg, 1);
spim_read_raw(data, len);
}

View 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 */

168
firmware/app/driver/accel.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* accel.c
*
* Created on: Oct 13, 2024
* Author: true
*/
#include "ch32x035_conf.h"
#include "accel.h"
#include "lis2hh12_reg.h"
#include "comms/spi_master.h"
#include "misc/i8atan2.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// user data
AccelData accel;
uint8_t accel_found = 0;
uint8_t movement_idx; // index into "read" register
int16_t movement_read[4]; // last read movement value
int16_t movement; // last calculated movement value
uint16_t movement_worst; // worst seen movement value
// hardware
static stmdev_ctx_t dev_ctx;
static int32_t accel_write(void *handle, uint8_t reg, const uint8_t *bufp, uint16_t len)
{
(void)(handle);
// i2c_write_reg8(LIS2_I2C_ADDR_SDO_LOW, reg, bufp, len);
LIS2_CS_PORT->BCR = LIS2_CS_PIN;
spim_write_reg8(reg, bufp, len);
LIS2_CS_PORT->BSHR = LIS2_CS_PIN;
return 0;
}
static int32_t accel_read(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len)
{
(void)(handle);
// i2c_read_reg8(LIS2DW_I2C_ADDR_SDO_LOW, reg, bufp, len);
LIS2_CS_PORT->BCR = LIS2_CS_PIN;
spim_read_reg8(reg, bufp, len);
LIS2_CS_PORT->BSHR = LIS2_CS_PIN;
return 0;
}
void accel_init()
{
uint8_t devid;
uint8_t reset;
uint16_t timeout = 1000;
dev_ctx.write_reg = accel_write;
dev_ctx.read_reg = accel_read;
// make sure we've got the right device
lis2hh12_dev_id_get(&dev_ctx, &devid);
if (devid == LIS2HH12_ID) {
accel_found = 1;
}
// reset accelerometer
if (accel_found) {
lis2hh12_dev_reset_set(&dev_ctx, PROPERTY_ENABLE);
do {
lis2hh12_dev_reset_get(&dev_ctx, &reset);
if (!reset) {
timeout = 0;
accel_found = 1;
}
} while (timeout--);
}
if (accel_found) {
// disable block update
// data in output registers is updated immediately; FIFO is disabled
lis2hh12_block_data_update_set(&dev_ctx, PROPERTY_DISABLE);
// configure scale, power mode
lis2hh12_xl_full_scale_set(&dev_ctx, LIS2HH12_2g);
// TODO: check this
// lis2hh12_power_mode_set(&dev_ctx, LIS2DW12_CONT_LOW_PWR_LOW_NOISE_4);
// configure filter chain
// low pass filter enabled for 6D (not currently used)
lis2hh12_xl_filter_out_path_set(&dev_ctx, LIS2HH12_FILT_LP);
// digital LPF2 filter of output data
lis2hh12_xl_filter_low_bandwidth_set(&dev_ctx, LIS2HH12_LP_ODR_DIV_9);
// configure output data rate
lis2hh12_xl_data_rate_set(&dev_ctx, LIS2HH12_XL_ODR_200Hz);
}
}
void accel_poll()
{
uint8_t reg = 1;
int16_t xyz[3];
if (!accel_found) return;
while (reg) {
// read output only if new value is available
lis2hh12_xl_flag_data_ready_get(&dev_ctx, &reg);
if (reg) {
// read acceleration data
memset(xyz, 0x00, 3 * sizeof(int16_t));
lis2hh12_acceleration_raw_get(&dev_ctx, xyz);
accel.x = xyz[0] >>= 8;
accel.y = xyz[1] >>= 8;
accel.z = xyz[2] >>= 8;
// compute our shitty "movement" thing
// awful way to detect being still
reg = sizeof(movement_read) / sizeof(movement_read[0]);
movement_idx++;
if (movement_idx == reg) {
movement_idx = 0;
movement = abs(movement_read[3] - movement_read[0]);
if (movement > movement_worst) {
movement_worst = movement;
}
}
movement_read[movement_idx] = abs(accel.x) + abs(accel.y);
}
}
}
int8_t accel_get_rotation(struct AccelData *a)
{
int8_t nx, ny, ret;
nx = -a->x;
ny = a->y;
ret = i8atan2(nx, ny) >> 1;
if (ret < 0) {
ret += 128;
}
return ret;
}
int16_t accel_get_movement()
{
return movement;
}

View File

@@ -0,0 +1,48 @@
/*
* accel.h
*
* Created on: Oct 13, 2024
* Author: true
*/
#ifndef USER_MISC_ACCEL_H_
#define USER_MISC_ACCEL_H_
#include "ch32x035_conf.h"
#include <stdint.h>
#define LIS2_CS_PORT GPIOB // CH32X035-specific
#define LIS2_CS_PIN GPIO_Pin_6
#define LIS2_I2C_ADDR 0x30
#define LIS2_I2C_ADDR_SDO_LOW 0x32
typedef struct AccelData {
int16_t x;
int16_t y;
int16_t z;
} AccelData;
extern AccelData accel;
extern uint8_t accel_found;
extern uint16_t movement_worst;
void accel_init();
void accel_poll();
int8_t accel_get_rotation(struct AccelData *a);
int16_t accel_get_movement();
#endif /* USER_MISC_ACCEL_H_ */

186
firmware/app/driver/adc.c Normal file
View File

@@ -0,0 +1,186 @@
/*
* adc.c
*
* todo:
* - use DMA
*/
#include "ch32x035_conf.h"
#define ADC_CHANNELS 8
#define CHAN_ENABLED 0x80
#define CHAN_MASK 0x7f
#define CHAN_IS_NORMAL 0
#define CHAN_IS_TOUCH 1
#define CHAN_IS_LIGHTSENSE 2
#define TOUCH_DEFAULT_CHARGE 0x88
#define TOUCH_DEFAULT_THRESH 0x800
typedef struct AdcChan {
uint8_t chan; // ADC channel number
uint8_t type; // ADC channel type (use above defines)
uint8_t timing; // sample rate for normal channels, charge values for touch-type channels
uint8_t idx; // rawval index
uint16_t avg; // averaged output
uint16_t thresh; // touch threshold
uint16_t rawval[8]; // raw counts
} AdcChan;
static uint8_t adc_idx = 0;
static AdcChan adc_chan[ADC_CHANNELS] = {0};
void adc_init()
{
ADC_InitTypeDef adc = {0};
// configure pin struct
for (uint8_t i = 0; i < ADC_CHANNELS; i++) {
adc_chan[i].type = CHAN_ENABLED | CHAN_IS_TOUCH;
adc_chan[i].timing = TOUCH_DEFAULT_CHARGE;
adc_chan[i].thresh = TOUCH_DEFAULT_THRESH;
}
adc_chan[0].chan = ADC_Channel_0;
adc_chan[1].chan = ADC_Channel_1;
adc_chan[2].chan = ADC_Channel_2;
adc_chan[3].chan = ADC_Channel_3;
adc_chan[4].chan = ADC_Channel_4;
adc_chan[5].chan = ADC_Channel_8;
adc_chan[6].chan = ADC_Channel_13;
adc_chan[7].chan = ADC_Channel_9;
adc_chan[7].type = CHAN_IS_LIGHTSENSE;
adc_chan[7].timing = ADC_SampleTime_7Cycles;
// configure actual peripheral
ADC_DeInit(ADC1);
ADC_CLKConfig(ADC1, ADC_CLK_Div6);
adc.ADC_Mode = ADC_Mode_Independent;
adc.ADC_ScanConvMode = DISABLE;
adc.ADC_ContinuousConvMode = DISABLE;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &adc);
ADC_Cmd(ADC1, ENABLE);
}
int8_t adc_get_tkey(uint8_t key_idx)
{
if (key_idx > ADC_CHANNELS)
return 0;
if (adc_chan[key_idx].avg < adc_chan[key_idx].thresh) {
return 1;
}
return 0;
}
void adc_next()
{
AdcChan *chan;
uint8_t timeout = 0;
// todo:
// lightsense is timing-sensitive. will need to have this as an overridden channel?
// get next index
adc_idx++;
if (adc_idx > (sizeof(adc_chan) / sizeof(adc_chan[0]))) {
adc_idx = 0;
}
// so long as the channel is enabled
while (!(adc_chan[adc_idx].type & CHAN_ENABLED) && (timeout++ < ADC_CHANNELS)) {
adc_idx++;
}
// if nothing is enabled, break out
if (timeout == ADC_CHANNELS)
return;
chan = &adc_chan[adc_idx];
// configure sampling
switch (chan->type) {
case CHAN_IS_NORMAL: {
TKey1->CTLR1 &= ~ADC_TKENABLE; // disable TouchKey
// we don't have any
break;
}
case CHAN_IS_TOUCH: {
TKey1->CTLR1 |= ADC_TKENABLE; // enable TouchKey
ADC_RegularChannelConfig(ADC1, chan->chan, 1, ADC_SampleTime_11Cycles);
TKey1->IDATAR1 = chan->timing & 0xf0; // R32_TKEY1_CHGOFFSET Charging Time
TKey1->RDATAR = chan->timing & 0x0f; // R32_TKEY1_ACT_DCG Discharging Time
// per the RM, setting RDATAR (ACT_DCG) starts the conversion
break;
}
case CHAN_IS_LIGHTSENSE: {
TKey1->CTLR1 &= ~ADC_TKENABLE; // disable TouchKey
// todo: use thresh as a "timeout" value maybe?
// this way the other channels can be sampled, and each time we pass by
// the lightsensor, if it isn't time to sample it, it is skipped.
ADC_RegularChannelConfig(ADC1, chan->chan, 1, chan->timing & 0xf);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
break;
}
}
}
void adc_isr()
{
AdcChan *chan;
uint32_t v = 0;
uint8_t amax, ashift;
chan = &adc_chan[adc_idx];
// conversion done
if (ADC1->STATR & ADC_EOC) {
chan->rawval[chan->idx] = ADC1->RDATAR;
// how many averages should we do?
switch (chan->type & CHAN_MASK) {
case CHAN_IS_TOUCH: {
amax = 4;
ashift = 2;
break;
}
default: {
amax = 8;
ashift = 3;
break;
}
}
chan->idx++;
if (chan->idx >= amax) {
chan->idx = 0;
for (int i = 0; i < amax; i++) {
v += chan->rawval[i];
}
chan->avg = v >> ashift;
}
}
// flags cleared by software
ADC1->STATR = 0;
}

22
firmware/app/driver/adc.h Normal file
View File

@@ -0,0 +1,22 @@
/*
*
*/
#ifndef __APP_DRIVER_ADC_H
#define __APP_DRIVER_ADC_H
#include <stdint.h>
void adc_init();
void adc_next();
int8_t adc_get_tkey(uint8_t key_idx);
void adc_isr();
#endif /* __APP_DRIVER_ADC_H */

View File

@@ -0,0 +1,247 @@
/*
* awinic AW20108 / AW20072 / AW20054 / AW20036 LED Matrix Driver
*
* originally written by true in 2022
* while sleep deprived for a super constrained mcu
*
* some bugs fixed for defcon 32 on aug 6-7, 2024
* - don't remember what was fixed
*
* some bugs fixed for supercon on oct 15, 2024
* - fixed support for AW20054, which only supports 9 rows
* did this by respecting aw->rows
*
*
* driver assumptions:
* - rows and columns are as is ordered on the chip, lowest to highest
* (if any are skipped, just skip this data in your buffer)
* - buffer size does not need to encompass all possible LEDs, only those
* which you have specified you are using.
* ensure sizeof(aw->fade) == aw->rows + aw->cols
* - duty cycle will be set automatically according to aw->cols
* - all AW20xxx chips will operate on the same i2c bus
* - a static data buffer is used
*
* driver notices:
* - updates only happen one column at a time, and are blocking
* (future version may implement a callback when each column is done)
* - this driver has not yet implemented the pattern controller
* - this driver has not yet implemented the GAIN register, except as
* a global configuration, so dimming only operates 8-bit
* (will be implemented later to allow for beyond-8-bit operation)
* - this driver has not yet implemented FADEDIM mode
*
* if you need anything different, write it yourself
*/
#include "aw20xxx.h"
#define AW20X_THIS_PAGE (aw->state & AW20X_STATE_PAGE_MASK)
#define AW20X_SET_PAGE(x) if (AW20X_THIS_PAGE != (x)) { \
aw20x_page(aw, x); \
aw->state &= ~AW20X_STATE_PAGE_MASK; \
aw->state |= x; }
static uint8_t aw_buf[25]; // enough bytes for register and single column FADEDIM update (1 + (12*2))
void aw20x_page(struct AW20x *aw, uint8_t page)
{
// the datasheet isn't clear on this. the default is zero, and only the lower
// three bits are specified. yet the DS says to send 0xCy where y is the page bits.
// we'll just do what the DS says even though it contradicts itself.
aw_buf[0] = 0xc0 | (page & AW20X_PAGE_MASK);
AW20X_I2C_writereg(aw->addr, AW20X_REG_PAGE, aw_buf, 1);
}
void aw20x_init(struct AW20x *aw, uint8_t addr, uint8_t cols, uint8_t rows, uint8_t imax)
{
// set config register as specified
aw->addr = addr;
aw->cols = cols;
aw->rows = rows;
aw->config = imax & AW20X_CONF_IMAX_MASK;
// wake up (and set page to config page)
aw20x_set_sleep(aw, 0);
// enabled columns
aw_buf[0] = cols - 1;
while (AW20X_I2C_busy()) {};
AW20X_I2C_writereg(aw->addr, AW20X_REG_SIZE, aw_buf, 1);
// general config
aw_buf[0] = imax & AW20X_CONF_IMAX_MASK;
while (AW20X_I2C_busy()) {};
AW20X_I2C_writereg(aw->addr, AW20X_REG_GCCR, aw_buf, 1);
}
void aw20x_set_sleep(struct AW20x *aw, uint8_t sleep)
{
// make sure we're on the config page
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
aw_buf[0] = sleep ? AW20X_SLPCR_SLEEP : 0;
// send sleep bit
while (AW20X_I2C_busy()) {};
AW20X_I2C_writereg(aw->addr, AW20X_REG_SLPCR, aw_buf, 1);
// set state
if (sleep) aw->state |= AW20X_STATE_SLEEP_MASK;
else aw->state &= ~AW20X_STATE_SLEEP_MASK;
// burn some cycles if we woke up
if (!sleep) AW20X_INIT_DELAY();
}
void aw20x_set_imax(struct AW20x *aw, uint8_t imax)
{
// make sure we're on the config page
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
aw_buf[0] = imax & AW20X_CONF_IMAX_MASK;
// send imax
while (AW20X_I2C_busy()) {};
AW20X_I2C_writereg(aw->addr, AW20X_REG_GCCR, aw_buf, 1);
aw->config = imax & AW20X_CONF_IMAX_MASK;
}
/*
* sends LED values to the chip
*/
void aw20x_set_fade(struct AW20x *aw)
{
uint8_t c;
uint8_t row;
uint8_t offset;
// make sure we're on the fade page
AW20X_SET_PAGE(AW20X_PAGE2_FADE);
// don't touch the buffer until we are allowed
while (AW20X_I2C_busy()) {};
row = offset = 0;
for (c = 0; c < aw->cols; c++) {
// write to chip
AW20X_I2C_writereg(aw->addr, offset, aw->fade + row, aw->rows);
while (AW20X_I2C_busy());
row += aw->rows;
offset += AW20X_MAX_ROWS;
}
}
void aw20x_set_dim(struct AW20x *aw)
{
// todo: implement
}
/*
* sets all LEDs to the specified 6-bit DIM value.
* used when just using FADE and 8-bit mode
* to set initial and fine tune from IMAX the output current.
*/
void aw20x_set_dim_global(struct AW20x *aw, uint8_t dim)
{
uint8_t i;
uint8_t row;
uint8_t offset;
// ceil
if (dim > 0x3f) dim = 0x3f;
// make sure we're on the dim page
AW20X_SET_PAGE(AW20X_PAGE1_DIM);
// don't touch the buffer until we are allowed
while (AW20X_I2C_busy()) {};
// clear buffer
for (i = 0; i <= aw->rows; i++) aw_buf[i] = dim;
// send buffer for each column
row = offset = 0;
for (i = 0; i < aw->cols; i++) {
AW20X_I2C_writereg(aw->addr, offset, aw_buf, aw->rows);
while (AW20X_I2C_busy());
row += aw->rows;
offset += AW20X_MAX_ROWS;
}
}
void aw20x_commit_fadedim(struct AW20x *aw)
{
}
void aw20x_led_on(struct AW20x *aw, uint8_t first, uint8_t last, uint8_t on_bit)
{
}
/*
* enables LEDs based on user LED count, zero-indexed
* assumes AW20X_MAX_LEDON_BITS per row but will only turn on active defined rows
* AW20036 would be 0-35, but AW20054 would be 0-71 and so on
* for example, LEDs 8-12 on AW20054 with 9 roww enabled
* would enable C0R8, C1R0, C1R1, C1R2. all other LEDs are disabled
*
* todo:
* - read current state, and apply bitfields to the currently active state
* - allow bypassing the readback for faster operation (such as setting all LEDs on at startup)
* - make this more efficient (36 LEDs takes ~0.3ms on a 48MHz PIC!)
*/
void aw20x_led_enable_range(struct AW20x *aw, uint8_t first, uint8_t last)
{
uint8_t c, r;
uint8_t idx;
uint8_t boff;
uint8_t *buf;
// make sure we're on the config page
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
// don't touch the buffer until we are allowed
while (AW20X_I2C_busy());
// bits are stored 6 bits per byte, 2 bytes per column, one bit for each row
// for a maximum of the AW20X_MAX_ROWS of LED on bits
// we only want to touch bits that exist on the chip and in the correct order
idx = 0;
for (c = 0; c < aw->cols; c++) {
buf = &aw_buf[c*2];
buf[0] = buf[1] = 0;
boff = 0;
for (r = 0; r < AW20X_MAX_LEDON_BITS*2; r++) {
if (r + idx < first) continue; // only start at the first led
if (r + idx > last) break; // and stop at the last
if (r >= aw->rows) continue; // max bits to process per row
if (r == AW20X_MAX_LEDON_BITS) { // only this many bits per byte
boff += AW20X_MAX_LEDON_BITS;
buf++;
}
*buf |= (1 << (r - boff));
}
idx += AW20X_MAX_LEDON_BITS*2;
}
AW20X_I2C_writereg(aw->addr, AW20X_REG_LEDON0, aw_buf, c*2);
}
/*
* disables LEDs based on user LED count, zero-indexed
* (for example, LEDs 8-12 on AW20054 would enable C0R8, C0R9, C1R0, C1R1)
*/
void aw20x_led_disable(struct AW20x *aw, uint8_t first, uint8_t last)
{
}

View File

@@ -0,0 +1,194 @@
/*
* awinic AW20108 / AW20072 / AW20054 / AW20036 LED Matrix Driver
*/
#ifndef AW20X_LED_MATRIX_H
#define AW20X_LED_MATRIX_H
#include <stdint.h>
#include "../comms/i2c.h"
#define AW20X_INIT_DELAY() { uint16_t zz = 1000; while(zz--); }
// burn cycles for ~200us
#define AW20X_MAX_COLS 9
#define AW20X_MAX_ROWS 12
#define AW20X_MAX_LEDON_BITS 6
#define AW20X_ADDR_SCL 0x38 // AD pin tied to SCL
#define AW20X_ADDR_SDA 0x39 // AD pin tied to SDA
#define AW20X_ADDR_GND 0x3A // AD pin tied to GND
#define AW20X_ADDR_VDD 0x3B // AD pin tied to VDD
#define AW20X_CONF_IMAX_MASK 0xf0 // 7:4
#define AW20X_CONF_IMAX_OFFSET 4 // 7:4
#define AW20X_CONF_ADDR_MASK 0x03 // 1:0
#define AW20X_CONF_ADDR_OFFSET 0 // 1:0
#define AW20X_CONF_USE_FADEDIM 0x08 // aw20x.fade now becomes fadedim; updates are done with FADEDIM page
// this mode uses 2 bytes per LED; dim as byte 0, fade as byte 1
#define AW20X_CONF_USE_EXPEN 0x04 // sets GCCR.EXPEN=1; fade is now only 6 bits (not yet implemented)
#define AW20X_PAGE_MASK 0x07
#define AW20X_STATE_PAGE_MASK AW20X_PAGE_MASK
#define AW20X_STATE_SLEEP_MASK 0x80
#define AW20X_PAGE0_CONFIG 0x00 // function register
#define AW20X_PAGE1_DIM 0x01 // 5:0 dim; 8 bits per LED
#define AW20X_PAGE2_FADE 0x02 // 7:0 fade; 8 bits per LED
#define AW20X_PAGE3_PATTERN 0x03 // 1:0 pattern; 8 bits per LED
#define AW20X_PAGE4_DIMFADE 0x04 // 13:8 dim, 7:0 fade; 16 bits per LED
#define AW20X_PAGE5_DFP 0x05 // 15:14 pat, 13:8 dim, 7:0 fade; 16 bits per LED
#define AW20X_REG_IDR 0x00 // R 7:0 chip ID {0x18}
#define AW20X_REG_SLPCR 0x01 // RW 7 sleep {0x80}
#define AW20X_REG_RSTR 0x02 // W 7:0 SW_RSTN (?)
#define AW20X_REG_GCCR 0x03 // RW 7:3 IMAX(7:4), ALLON, -, -, EXPEN {0x10}
#define AW20X_REG_FCD 0x04 // W 0 FCDE (fast clear display enable)
#define AW20X_REG_CLKSYS 0x05 // RW 1:0 CLK_IO, CLK_SEL
#define AW20X_REG_FLTCFG1 0x09 // RW 5:0 UVLOPE, OTPE, UVIE, OTIE, UVLOE, OTE
#define AW20X_REG_FLTCFG2 0x0a // RW 3:2 UVTH
#define AW20X_REG_ISRFLT 0x0b // RW 5:0 PAT2IS, PAT1IS, PAT0IS, -, -, UVLOIS, OTIS
#define AW20X_REG_LEDON0 0x31 // W 5:0 ON0:ON5, same pattern through to LEDON17 (0x42)
#define AW20X_REG_PATCR 0x43 // RW 6:0 PAT2IE, PAT1IE, PAT0IE, -, PAT2EN, PAT1EN, PAT0EN
#define AW20X_REG_FADEH0 0x44 // RW 7:0 FADEH0
#define AW20X_REG_FADEH1 0x45
#define AW20X_REG_FADEH2 0x46
#define AW20X_REG_FADEL0 0x47
#define AW20X_REG_FADEL1 0x48
#define AW20X_REG_FADEL2 0x49 // RW 7:0 FADEL2
#define AW20X_REG_PAT0T0 0x4a // RW 7:0 T1[4], T2[4]
#define AW20X_REG_PAT0T1 0x4b // RW 7:0 T3[4], T4[4]
#define AW20X_REG_PAT0T2 0x4c // RW 7:0 LE[2], LB[2], LT(11:8)[4]
#define AW20X_REG_PAT0T3 0x4d // RW 7:0 LT(7:0)
#define AW20X_REG_PAT1T0 0x4e
#define AW20X_REG_PAT1T1 0x4f
#define AW20X_REG_PAT1T2 0x50
#define AW20X_REG_PAT1T3 0x51
#define AW20X_REG_PAT2T0 0x52
#define AW20X_REG_PAT2T1 0x53
#define AW20X_REG_PAT2T2 0x54
#define AW20X_REG_PAT2T3 0x55
#define AW20X_REG_PAT0CFG 0x56 // RW 2:0 SWITCH, RAMPE, PATMD
#define AW20X_REG_PAT1CFG 0x57
#define AW20X_REG_PAT2CFG 0x58
#define AW20X_REG_PATGO 0x59 // RW 6:0 PAT2ST, PAT1ST, PAT0ST, -, RUN2, RUN1, RUN0
#define AW20X_REG_SIZE 0x80 // RW 3:0 SWSEL
#define AW20X_REG_PAGE 0xf0 // RW 2:0 page select, 0-5; available from all pages
#define AW20X_IDR_ID 0x18 // value for all chips in this series
#define AW20X_SLPCR_SLEEP 0x01 // sleep mode (default is HIGH / asleep)
#define AW20X_RSTR_SW_RSTN 0x01 // write value to soft reset the chip
#define AW20X_GCCR_IMAX 0xf0 // global current setting (default 20mA)
#define AW20X_GCCR_ALLON 0x08 // 0=normal, 1=force all on
#define AW20X_GCCR_EXPEN 0x01 // 0=fade is linear 8-bit, 1=fade is exponential 6-bit
#define AW20X_FCD_FCDE 0x01 // write value to clear display (DS doesn't specify; is this a blanker?)
#define AW20X_CLKSYS_CLK_IO 0x02 // 0=no clk output, 1=clk output on (output) CLKIO pin
#define AW20X_CLKSYS_CLK_SEL 0x01 // 0=internal 4MHz, 1=use clk on (input) CLKIO pin
#define AW20X_FLTCFG1_UVLOPE 0x20 // 1=enable UVLO protection; chip sets SLPCR.SLEEP when ISRFLT.UVLOIS=1
#define AW20X_FLTCFG1_OTPE 0x10 // 1=enable overtemp protection; chip sets SLPCR.SLEEP when ISRFLT.UVLOIS=1
#define AW20X_FLTCFG1_UVIE 0x08 // 1=UVLO interrupt enable
#define AW20X_FLTCFG1_OTIE 0x04 // 1=overtemp interrupt enable
#define AW20X_FLTCFG1_UVLOE 0x02 // 1=enable UVLO detect
#define AW20X_FLTCFG1_OTE 0x01 // 1=enable overtemp detect
#define AW20X_FLTCFG1_UVTH_2V0 (0x00 << 2) // UVLO threshold voltage
#define AW20X_FLTCFG1_UVTH_2V1 (0x01 << 2) // UVLO threshold voltage
#define AW20X_FLTCFG1_UVTH_2V2 (0x02 << 2) // UVLO threshold voltage
#define AW20X_FLTCFG1_UVTH_2V3 (0x03 << 2) // UVLO threshold voltage
#define AW20X_ISRFLT_PAT2IS 0x40 // pattern controller 2 interrupt (finished breath loop)
#define AW20X_ISRFLT_PAT1IS 0x20 // pattern controller 1 interrupt (finished breath loop)
#define AW20X_ISRFLT_PAT0IS 0x10 // pattern controller 0 interrupt (finished breath loop)
#define AW20X_ISRFLT_UVLOIS 0x02 // 0=normal, 1=UVLO detected
#define AW20X_ISRFLT_OTIS 0x01 // 0=normal, 1=overtemp detected
// todo: fill in all values from PATCR onward
#define AW20X_SOFT_RESET AW20X_RSTR_SW_RSTN
enum aw20x_imax {
AW20X_IMAX_10MA = 0x00,
AW20X_IMAX_20MA = 0x10,
AW20X_IMAX_30MA = 0x20,
AW20X_IMAX_40MA = 0x30,
AW20X_IMAX_60MA = 0x40,
AW20X_IMAX_80MA = 0x50,
AW20X_IMAX_120MA = 0x60,
AW20X_IMAX_160MA = 0x70,
AW20X_IMAX_3_3MA = 0x80,
AW20X_IMAX_6_7MA = 0x90,
AW20X_IMAX_10MA_2 = 0xa0,
AW20X_IMAX_13_3MA = 0xb0,
AW20X_IMAX_20MA_2 = 0xc0,
AW20X_IMAX_26_7MA = 0xd0,
AW20X_IMAX_40MA_2 = 0xe0,
AW20X_IMAX_53_3MA = 0xf0
};
enum aw20x_size {
AW20X_SIZE_1COL = 0,
AW20X_SIZE_2COL,
AW20X_SIZE_3COL,
AW20X_SIZE_4COL,
AW20X_SIZE_5COL,
AW20X_SIZE_6COL,
AW20X_SIZE_7COL,
AW20X_SIZE_8COL,
AW20X_SIZE_9COL
};
#ifdef SOFT_I2C_MASTER
#define AW20X_I2C_busy() (0)
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_write_reg8(adr, reg, buf, siz)
#else
#define AW20X_I2C_busy() (0)
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_write_addr1b(adr, reg, buf, siz);
#endif
typedef struct AW20x {
uint8_t addr;
uint8_t config; // settings for the chip
uint8_t state; // keeps track of active page, and high bit is set if asleep
uint8_t cols; // highest column used by application, 1-9
uint8_t rows; // highest row used by application, 1-12
uint8_t pad[3];
uint8_t *fade; // led buffer location for FADE (required), of size cols+rows
uint8_t *gain; // led buffer location for GAIN (optional), of size cols+rows
} AW20x;
void aw20x_init(struct AW20x *aw, uint8_t addr, uint8_t cols, uint8_t rows, uint8_t imax);
void aw20x_set_sleep(struct AW20x *aw, uint8_t sleep);
void aw20x_set_imax(struct AW20x *aw, uint8_t imax);
void aw20x_set_fade(struct AW20x *aw);
void aw20x_set_dim_global(struct AW20x *aw, uint8_t dim);
void aw20x_led_enable_range(struct AW20x *aw, uint8_t first, uint8_t last);
#endif /* AW02X_LED_MATRIX_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,992 @@
/**
******************************************************************************
* @file lis2hh12_reg.h
* @author Sensors Software Solution Team
* @brief This file contains all the functions prototypes for the
* lis2hh12_reg.c driver.
******************************************************************************
* @attention
*
* Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef LIS2HH12_REGS_H
#define LIS2HH12_REGS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stddef.h>
#include <math.h>
/** @addtogroup LIS2HH12
* @{
*
*/
/** @defgroup Endianness definitions
* @{
*
*/
#ifndef DRV_BYTE_ORDER
#ifndef __BYTE_ORDER__
#define DRV_LITTLE_ENDIAN 1234
#define DRV_BIG_ENDIAN 4321
/** if _BYTE_ORDER is not defined, choose the endianness of your architecture
* by uncommenting the define which fits your platform endianness
*/
//#define DRV_BYTE_ORDER DRV_BIG_ENDIAN
#define DRV_BYTE_ORDER DRV_LITTLE_ENDIAN
#else /* defined __BYTE_ORDER__ */
#define DRV_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#define DRV_BIG_ENDIAN __ORDER_BIG_ENDIAN__
#define DRV_BYTE_ORDER __BYTE_ORDER__
#endif /* __BYTE_ORDER__*/
#endif /* DRV_BYTE_ORDER */
/**
* @}
*
*/
/** @defgroup STMicroelectronics sensors common types
* @{
*
*/
#ifndef MEMS_SHARED_TYPES
#define MEMS_SHARED_TYPES
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t bit0 : 1;
uint8_t bit1 : 1;
uint8_t bit2 : 1;
uint8_t bit3 : 1;
uint8_t bit4 : 1;
uint8_t bit5 : 1;
uint8_t bit6 : 1;
uint8_t bit7 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t bit7 : 1;
uint8_t bit6 : 1;
uint8_t bit5 : 1;
uint8_t bit4 : 1;
uint8_t bit3 : 1;
uint8_t bit2 : 1;
uint8_t bit1 : 1;
uint8_t bit0 : 1;
#endif /* DRV_BYTE_ORDER */
} bitwise_t;
#define PROPERTY_DISABLE (0U)
#define PROPERTY_ENABLE (1U)
/** @addtogroup Interfaces_Functions
* @brief This section provide a set of functions used to read and
* write a generic register of the device.
* MANDATORY: return 0 -> no Error.
* @{
*
*/
typedef int32_t (*stmdev_write_ptr)(
void *handle,
uint8_t reg,
const uint8_t *buf,
uint16_t len);
typedef int32_t (*stmdev_read_ptr)(
void *handle,
uint8_t reg,
uint8_t *buf,
uint16_t len);
typedef void (*stmdev_mdelay_ptr)(uint32_t millisec);
typedef struct
{
/** Component mandatory fields **/
stmdev_write_ptr write_reg;
stmdev_read_ptr read_reg;
/** Component optional fields **/
stmdev_mdelay_ptr mdelay;
/** Customizable optional pointer **/
void *handle;
/** private data **/
void *priv_data;
} stmdev_ctx_t;
/**
* @}
*
*/
#endif /* MEMS_SHARED_TYPES */
#ifndef MEMS_UCF_SHARED_TYPES
#define MEMS_UCF_SHARED_TYPES
/** @defgroup Generic address-data structure definition
* @brief This structure is useful to load a predefined configuration
* of a sensor.
* You can create a sensor configuration by your own or using
* Unico / Unicleo tools available on STMicroelectronics
* web site.
*
* @{
*
*/
typedef struct
{
uint8_t address;
uint8_t data;
} ucf_line_t;
/**
* @}
*
*/
#endif /* MEMS_UCF_SHARED_TYPES */
/**
* @}
*
*/
/** @defgroup LIS2HH12_Infos
* @{
*
*/
/** I2C Device Address 8 bit format if SA0=0 -> 0x3D if SA0=1 -> 0x3B **/
#define LIS2HH12_I2C_ADD_L 0x3DU
#define LIS2HH12_I2C_ADD_H 0x3BU
/** Device Identification (Who am I) **/
#define LIS2HH12_ID 0x41U
/**
* @}
*
*/
#define LIS2HH12_TEMP_L 0x0BU
#define LIS2HH12_TEMP_H 0x0CU
#define LIS2HH12_WHO_AM_I 0x0FU
#define LIS2HH12_ACT_THS 0x1EU
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t ths : 7;
uint8_t not_used_01 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t not_used_01 : 1;
uint8_t ths : 7;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_act_ths_t;
#define LIS2HH12_ACT_DUR 0x1FU
typedef struct
{
uint8_t dur : 8;
} lis2hh12_act_dur_t;
#define LIS2HH12_CTRL1 0x20U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xen : 1;
uint8_t yen : 1;
uint8_t zen : 1;
uint8_t bdu : 1;
uint8_t odr : 3;
uint8_t hr : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t hr : 1;
uint8_t odr : 3;
uint8_t bdu : 1;
uint8_t zen : 1;
uint8_t yen : 1;
uint8_t xen : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl1_t;
#define LIS2HH12_CTRL2 0x21U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t hpis : 2;
uint8_t fds : 1;
uint8_t hpm : 2;
uint8_t dfc : 2;
uint8_t not_used_01 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t not_used_01 : 1;
uint8_t dfc : 2;
uint8_t hpm : 2;
uint8_t fds : 1;
uint8_t hpis : 2;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl2_t;
#define LIS2HH12_CTRL3 0x22U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t int1_drdy : 1;
uint8_t int1_fth : 1;
uint8_t int1_ovr : 1;
uint8_t int1_ig1 : 1;
uint8_t int1_ig2 : 1;
uint8_t int1_inact : 1;
uint8_t stop_fth : 1;
uint8_t fifo_en : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t fifo_en : 1;
uint8_t stop_fth : 1;
uint8_t int1_inact : 1;
uint8_t int1_ig2 : 1;
uint8_t int1_ig1 : 1;
uint8_t int1_ovr : 1;
uint8_t int1_fth : 1;
uint8_t int1_drdy : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl3_t;
#define LIS2HH12_CTRL4 0x23U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t sim : 1;
uint8_t i2c_disable : 1;
uint8_t if_add_inc : 1;
uint8_t bw_scale_odr : 1;
uint8_t fs : 2;
uint8_t bw : 2;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t bw : 2;
uint8_t fs : 2;
uint8_t bw_scale_odr : 1;
uint8_t if_add_inc : 1;
uint8_t i2c_disable : 1;
uint8_t sim : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl4_t;
#define LIS2HH12_CTRL5 0x24U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t pp_od : 1;
uint8_t h_lactive : 1;
uint8_t st : 2;
uint8_t dec : 2;
uint8_t soft_reset : 1;
uint8_t debug : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t debug : 1;
uint8_t soft_reset : 1;
uint8_t dec : 2;
uint8_t st : 2;
uint8_t h_lactive : 1;
uint8_t pp_od : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl5_t;
#define LIS2HH12_CTRL6 0x25U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t int2_drdy : 1;
uint8_t int2_fth : 1;
uint8_t int2_empty : 1;
uint8_t int2_ig1 : 1;
uint8_t int2_ig2 : 1;
uint8_t int2_boot : 1;
uint8_t not_used_01 : 1;
uint8_t boot : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t boot : 1;
uint8_t not_used_01 : 1;
uint8_t int2_boot : 1;
uint8_t int2_ig2 : 1;
uint8_t int2_ig1 : 1;
uint8_t int2_empty : 1;
uint8_t int2_fth : 1;
uint8_t int2_drdy : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl6_t;
#define LIS2HH12_CTRL7 0x26U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t _4d_ig : 2;
uint8_t lir : 2;
uint8_t dcrm : 2;
uint8_t not_used_01 : 2;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t not_used_01 : 2;
uint8_t dcrm : 2;
uint8_t lir : 2;
uint8_t _4d_ig : 2;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ctrl7_t;
#define LIS2HH12_STATUS 0x27U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xda : 1;
uint8_t yda : 1;
uint8_t zda : 1;
uint8_t zyxda : 1;
uint8_t _xor : 1;
uint8_t yor : 1;
uint8_t zor : 1;
uint8_t zyxor : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t zyxor : 1;
uint8_t zor : 1;
uint8_t yor : 1;
uint8_t _xor : 1;
uint8_t zyxda : 1;
uint8_t zda : 1;
uint8_t yda : 1;
uint8_t xda : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_status_t;
#define LIS2HH12_OUT_X_L 0x28U
#define LIS2HH12_OUT_X_H 0x29U
#define LIS2HH12_OUT_Y_L 0x2AU
#define LIS2HH12_OUT_Y_H 0x2BU
#define LIS2HH12_OUT_Z_L 0x2CU
#define LIS2HH12_OUT_Z_H 0x2DU
#define LIS2HH12_FIFO_CTRL 0x2EU
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t fth : 5;
uint8_t fmode : 3;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t fmode : 3;
uint8_t fth : 5;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_fifo_ctrl_t;
#define LIS2HH12_FIFO_SRC 0x2FU
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t fss : 5;
uint8_t empty : 1;
uint8_t ovr : 1;
uint8_t fth : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t fth : 1;
uint8_t ovr : 1;
uint8_t empty : 1;
uint8_t fss : 5;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_fifo_src_t;
#define LIS2HH12_IG_CFG1 0x30U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xlie : 1;
uint8_t xhie : 1;
uint8_t ylie : 1;
uint8_t yhie : 1;
uint8_t zlie : 1;
uint8_t zhie : 1;
uint8_t _6d : 1;
uint8_t aoi : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t aoi : 1;
uint8_t _6d : 1;
uint8_t zhie : 1;
uint8_t zlie : 1;
uint8_t yhie : 1;
uint8_t ylie : 1;
uint8_t xhie : 1;
uint8_t xlie : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_cfg1_t;
#define LIS2HH12_IG_SRC1 0x31U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xl : 1;
uint8_t xh : 1;
uint8_t yl : 1;
uint8_t yh : 1;
uint8_t zl : 1;
uint8_t zh : 1;
uint8_t ia : 1;
uint8_t not_used_01 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t not_used_01 : 1;
uint8_t ia : 1;
uint8_t zh : 1;
uint8_t zl : 1;
uint8_t yh : 1;
uint8_t yl : 1;
uint8_t xh : 1;
uint8_t xl : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_src1_t;
#define LIS2HH12_IG_THS_X1 0x32U
#define LIS2HH12_IG_THS_Y1 0x33U
#define LIS2HH12_IG_THS_Z1 0x34U
#define LIS2HH12_IG_DUR1 0x35U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t dur1 : 7;
uint8_t wait1 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t wait1 : 1;
uint8_t dur1 : 7;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_dur1_t;
#define LIS2HH12_IG_CFG2 0x36U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xlie : 1;
uint8_t xhie : 1;
uint8_t ylie : 1;
uint8_t yhie : 1;
uint8_t zlie : 1;
uint8_t zhie : 1;
uint8_t _6d : 1;
uint8_t aoi : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t aoi : 1;
uint8_t _6d : 1;
uint8_t zhie : 1;
uint8_t zlie : 1;
uint8_t yhie : 1;
uint8_t ylie : 1;
uint8_t xhie : 1;
uint8_t xlie : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_cfg2_t;
#define LIS2HH12_IG_SRC2 0x37U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t xl : 1;
uint8_t xh : 1;
uint8_t yl : 1;
uint8_t yh : 1;
uint8_t zl : 1;
uint8_t zh : 1;
uint8_t ia : 1;
uint8_t not_used_01 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t not_used_01 : 1;
uint8_t ia : 1;
uint8_t zh : 1;
uint8_t zl : 1;
uint8_t yh : 1;
uint8_t yl : 1;
uint8_t xh : 1;
uint8_t xl : 1;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_src2_t;
#define LIS2HH12_IG_THS2 0x38U
#define LIS2HH12_IG_DUR2 0x39U
typedef struct
{
#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN
uint8_t dur2 : 7;
uint8_t wait2 : 1;
#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN
uint8_t wait2 : 1;
uint8_t dur2 : 7;
#endif /* DRV_BYTE_ORDER */
} lis2hh12_ig_dur2_t;
#define LIS2HH12_XL_REFERENCE 0x3AU
#define LIS2HH12_XH_REFERENCE 0x3BU
#define LIS2HH12_YL_REFERENCE 0x3CU
#define LIS2HH12_YH_REFERENCE 0x3DU
#define LIS2HH12_ZL_REFERENCE 0x3EU
#define LIS2HH12_ZH_REFERENCE 0x3FU
/**
* @defgroup LIS2HH12_Register_Union
* @brief This union group all the registers having a bit-field
* description.
* This union is useful but it's not needed by the driver.
*
* REMOVING this union you are compliant with:
* MISRA-C 2012 [Rule 19.2] -> " Union are not allowed "
*
* @{
*
*/
typedef union
{
lis2hh12_act_ths_t act_ths;
lis2hh12_act_dur_t act_dur;
lis2hh12_ctrl1_t ctrl1;
lis2hh12_ctrl2_t ctrl2;
lis2hh12_ctrl3_t ctrl3;
lis2hh12_ctrl4_t ctrl4;
lis2hh12_ctrl5_t ctrl5;
lis2hh12_ctrl6_t ctrl6;
lis2hh12_ctrl7_t ctrl7;
lis2hh12_status_t status;
lis2hh12_fifo_ctrl_t fifo_ctrl;
lis2hh12_fifo_src_t fifo_src;
lis2hh12_ig_cfg1_t ig_cfg1;
lis2hh12_ig_src1_t ig_src1;
lis2hh12_ig_dur1_t ig_dur1;
lis2hh12_ig_cfg2_t ig_cfg2;
lis2hh12_ig_src2_t ig_src2;
lis2hh12_ig_dur2_t ig_dur2;
bitwise_t bitwise;
uint8_t byte;
} lis2hh12_reg_t;
/**
* @}
*
*/
#ifndef __weak
#define __weak __attribute__((weak))
#endif /* __weak */
/*
* These are the basic platform dependent I/O routines to read
* and write device registers connected on a standard bus.
* The driver keeps offering a default implementation based on function
* pointers to read/write routines for backward compatibility.
* The __weak directive allows the final application to overwrite
* them with a custom implementation.
*/
int32_t lis2hh12_read_reg(const stmdev_ctx_t *ctx, uint8_t reg,
uint8_t *data,
uint16_t len);
int32_t lis2hh12_write_reg(const stmdev_ctx_t *ctx, uint8_t reg,
uint8_t *data,
uint16_t len);
float_t lis2hh12_from_fs2g_to_mg(int16_t lsb);
float_t lis2hh12_from_fs4g_to_mg(int16_t lsb);
float_t lis2hh12_from_fs8g_to_mg(int16_t lsb);
float_t lis2hh12_from_lsb_to_celsius(int16_t lsb);
typedef struct
{
uint8_t xen : 1;
uint8_t yen : 1;
uint8_t zen : 1;
} lis2hh12_xl_axis_t;
int32_t lis2hh12_xl_axis_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_axis_t val);
int32_t lis2hh12_xl_axis_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_axis_t *val);
int32_t lis2hh12_block_data_update_set(const stmdev_ctx_t *ctx,
uint8_t val);
int32_t lis2hh12_block_data_update_get(const stmdev_ctx_t *ctx,
uint8_t *val);
typedef enum
{
LIS2HH12_XL_ODR_OFF = 0x00,
LIS2HH12_XL_ODR_10Hz = 0x01,
LIS2HH12_XL_ODR_50Hz = 0x02,
LIS2HH12_XL_ODR_100Hz = 0x03,
LIS2HH12_XL_ODR_200Hz = 0x04,
LIS2HH12_XL_ODR_400Hz = 0x05,
LIS2HH12_XL_ODR_800Hz = 0x06,
} lis2hh12_xl_data_rate_t;
int32_t lis2hh12_xl_data_rate_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_data_rate_t val);
int32_t lis2hh12_xl_data_rate_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_data_rate_t *val);
typedef enum
{
LIS2HH12_2g = 0x00,
LIS2HH12_4g = 0x02,
LIS2HH12_8g = 0x03,
} lis2hh12_xl_fs_t;
int32_t lis2hh12_xl_full_scale_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_fs_t val);
int32_t lis2hh12_xl_full_scale_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_fs_t *val);
typedef enum
{
LIS2HH12_NO_DECIMATION = 0x00,
LIS2HH12_EVERY_2_SAMPLES = 0x01,
LIS2HH12_EVERY_4_SAMPLES = 0x02,
LIS2HH12_EVERY_8_SAMPLES = 0x03,
} lis2hh12_dec_t;
int32_t lis2hh12_xl_decimation_set(const stmdev_ctx_t *ctx,
lis2hh12_dec_t val);
int32_t lis2hh12_xl_decimation_get(const stmdev_ctx_t *ctx,
lis2hh12_dec_t *val);
int32_t lis2hh12_xl_flag_data_ready_get(const stmdev_ctx_t *ctx,
uint8_t *val);
int32_t lis2hh12_temperature_raw_get(const stmdev_ctx_t *ctx, int16_t *val);
int32_t lis2hh12_acceleration_raw_get(const stmdev_ctx_t *ctx,
int16_t *val);
int32_t lis2hh12_dev_id_get(const stmdev_ctx_t *ctx, uint8_t *buff);
int32_t lis2hh12_dev_reset_set(const stmdev_ctx_t *ctx, uint8_t val);
int32_t lis2hh12_dev_reset_get(const stmdev_ctx_t *ctx, uint8_t *val);
int32_t lis2hh12_dev_boot_set(const stmdev_ctx_t *ctx, uint8_t val);
int32_t lis2hh12_dev_boot_get(const stmdev_ctx_t *ctx, uint8_t *val);
typedef struct
{
uint8_t xda : 1;
uint8_t yda : 1;
uint8_t zda : 1;
uint8_t zyxda : 1;
uint8_t _xor : 1;
uint8_t yor : 1;
uint8_t zor : 1;
uint8_t zyxor : 1;
} lis2hh12_status_reg_t;
int32_t lis2hh12_dev_status_get(const stmdev_ctx_t *ctx,
lis2hh12_status_reg_t *val);
typedef enum
{
LIS2HH12_HP_DISABLE = 0x00,
LIS2HH12_HP_ON_INT_GEN_1 = 0x02,
LIS2HH12_HP_ON_INT_GEN_2 = 0x01,
LIS2HH12_HP_ON_BOTH_GEN = 0x03,
} lis2hh12_xl_hp_path_t;
int32_t lis2hh12_xl_filter_int_path_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_hp_path_t val);
int32_t lis2hh12_xl_filter_int_path_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_hp_path_t *val);
typedef enum
{
LIS2HH12_BYPASSED = 0x00,
LIS2HH12_FILT_HP = 0x02,
LIS2HH12_FILT_LP = 0x01,
} lis2hh12_xl_out_path_t;
int32_t lis2hh12_xl_filter_out_path_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_out_path_t val);
int32_t lis2hh12_xl_filter_out_path_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_out_path_t *val);
typedef enum
{
LIS2HH12_HP_ODR_DIV_50 = 0x00,
LIS2HH12_HP_ODR_DIV_100 = 0x10,
LIS2HH12_HP_ODR_DIV_9 = 0x20,
LIS2HH12_HP_ODR_DIV_400 = 0x30,
LIS2HH12_HP_ODR_DIV_50_REF_MD = 0x01,
LIS2HH12_HP_ODR_DIV_100_REF_MD = 0x11,
LIS2HH12_HP_ODR_DIV_9_REF_MD = 0x21,
LIS2HH12_HP_ODR_DIV_400_REF_MD = 0x31,
} lis2hh12_xl_hp_bw_t;
int32_t lis2hh12_xl_filter_hp_bandwidth_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_hp_bw_t val);
int32_t lis2hh12_xl_filter_hp_bandwidth_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_hp_bw_t *val);
typedef enum
{
LIS2HH12_LP_ODR_DIV_50 = 0,
LIS2HH12_LP_ODR_DIV_100 = 1,
LIS2HH12_LP_ODR_DIV_9 = 2,
LIS2HH12_LP_ODR_DIV_400 = 3,
} lis2hh12_xl_lp_bw_t;
int32_t lis2hh12_xl_filter_low_bandwidth_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_lp_bw_t val);
int32_t lis2hh12_xl_filter_low_bandwidth_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_lp_bw_t *val);
typedef enum
{
LIS2HH12_AUTO = 0x00,
LIS2HH12_408Hz = 0x10,
LIS2HH12_211Hz = 0x11,
LIS2HH12_105Hz = 0x12,
LIS2HH12_50Hz = 0x13,
} lis2hh12_xl_filt_aa_bw_t;
int32_t lis2hh12_xl_filter_aalias_bandwidth_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_filt_aa_bw_t val);
int32_t lis2hh12_xl_filter_aalias_bandwidth_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_filt_aa_bw_t *val);
int32_t lis2hh12_xl_filter_reference_set(const stmdev_ctx_t *ctx,
int16_t *val);
int32_t lis2hh12_xl_filter_reference_get(const stmdev_ctx_t *ctx,
int16_t *val);
typedef enum
{
LIS2HH12_SPI_4_WIRE = 0x00,
LIS2HH12_SPI_3_WIRE = 0x01,
} lis2hh12_sim_t;
int32_t lis2hh12_spi_mode_set(const stmdev_ctx_t *ctx, lis2hh12_sim_t val);
int32_t lis2hh12_spi_mode_get(const stmdev_ctx_t *ctx, lis2hh12_sim_t *val);
typedef enum
{
LIS2HH12_I2C_ENABLE = 0x00,
LIS2HH12_I2C_DISABLE = 0x01,
} lis2hh12_i2c_dis_t;
int32_t lis2hh12_i2c_interface_set(const stmdev_ctx_t *ctx,
lis2hh12_i2c_dis_t val);
int32_t lis2hh12_i2c_interface_get(const stmdev_ctx_t *ctx,
lis2hh12_i2c_dis_t *val);
typedef enum
{
LIS2HH12_DISABLE = 0x00,
LIS2HH12_ENABLE = 0x01,
} lis2hh12_auto_inc_t;
int32_t lis2hh12_auto_increment_set(const stmdev_ctx_t *ctx,
lis2hh12_auto_inc_t val);
int32_t lis2hh12_auto_increment_get(const stmdev_ctx_t *ctx,
lis2hh12_auto_inc_t *val);
typedef struct
{
uint8_t int1_drdy : 1;
uint8_t int1_fth : 1;
uint8_t int1_ovr : 1;
uint8_t int1_ig1 : 1;
uint8_t int1_ig2 : 1;
uint8_t int1_inact : 1;
} lis2hh12_pin_int1_route_t;
int32_t lis2hh12_pin_int1_route_set(const stmdev_ctx_t *ctx,
lis2hh12_pin_int1_route_t val);
int32_t lis2hh12_pin_int1_route_get(const stmdev_ctx_t *ctx,
lis2hh12_pin_int1_route_t *val);
typedef enum
{
LIS2HH12_PUSH_PULL = 0x00,
LIS2HH12_OPEN_DRAIN = 0x01,
} lis2hh12_pp_od_t;
int32_t lis2hh12_pin_mode_set(const stmdev_ctx_t *ctx,
lis2hh12_pp_od_t val);
int32_t lis2hh12_pin_mode_get(const stmdev_ctx_t *ctx,
lis2hh12_pp_od_t *val);
typedef enum
{
LIS2HH12_ACTIVE_HIGH = 0x00,
LIS2HH12_ACTIVE_LOW = 0x01,
} lis2hh12_pin_pol_t;
int32_t lis2hh12_pin_polarity_set(const stmdev_ctx_t *ctx,
lis2hh12_pin_pol_t val);
int32_t lis2hh12_pin_polarity_get(const stmdev_ctx_t *ctx,
lis2hh12_pin_pol_t *val);
typedef struct
{
uint8_t int2_drdy : 1;
uint8_t int2_fth : 1;
uint8_t int2_empty : 1;
uint8_t int2_ig1 : 1;
uint8_t int2_ig2 : 1;
uint8_t int2_boot : 1;
} lis2hh12_pin_int2_route_t;
int32_t lis2hh12_pin_int2_route_set(const stmdev_ctx_t *ctx,
lis2hh12_pin_int2_route_t val);
int32_t lis2hh12_pin_int2_route_get(const stmdev_ctx_t *ctx,
lis2hh12_pin_int2_route_t *val);
typedef enum
{
LIS2HH12_INT_PULSED = 0x00,
LIS2HH12_INT_LATCHED = 0x01,
} lis2hh12_lir_t;
int32_t lis2hh12_pin_notification_set(const stmdev_ctx_t *ctx,
lis2hh12_lir_t val);
int32_t lis2hh12_pin_notification_get(const stmdev_ctx_t *ctx,
lis2hh12_lir_t *val);
typedef enum
{
LIS2HH12_IG1_OR_IG2_OR = 0x00,
LIS2HH12_IG1_AND_IG2_OR = 0x01,
LIS2HH12_IG1_OR_IG2_AND = 0x10,
LIS2HH12_IG1_AND_IG2_AND = 0x11,
} lis2hh12_pin_logic_t;
int32_t lis2hh12_pin_logic_set(const stmdev_ctx_t *ctx,
lis2hh12_pin_logic_t val);
int32_t lis2hh12_pin_logic_get(const stmdev_ctx_t *ctx,
lis2hh12_pin_logic_t *val);
typedef enum
{
LIS2HH12_RESET_MODE = 0x00,
LIS2HH12_DECREMENT_MODE = 0x01,
} lis2hh12_dcrm_t;
int32_t lis2hh12_xl_trshld_mode_set(const stmdev_ctx_t *ctx,
lis2hh12_dcrm_t val);
int32_t lis2hh12_xl_trshld_mode_get(const stmdev_ctx_t *ctx,
lis2hh12_dcrm_t *val);
typedef struct
{
uint16_t ig1_xlie : 1;
uint16_t ig1_xhie : 1;
uint16_t ig1_ylie : 1;
uint16_t ig1_yhie : 1;
uint16_t ig1_zlie : 1;
uint16_t ig1_zhie : 1;
uint16_t ig2_xlie : 1;
uint16_t ig2_xhie : 1;
uint16_t ig2_ylie : 1;
uint16_t ig2_yhie : 1;
uint16_t ig2_zlie : 1;
uint16_t ig2_zhie : 1;
} lis2hh12_xl_trshld_en_t;
int32_t lis2hh12_xl_trshld_axis_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_trshld_en_t val);
int32_t lis2hh12_xl_trshld_axis_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_trshld_en_t *val);
typedef struct
{
uint16_t ig1_xl : 1;
uint16_t ig1_xh : 1;
uint16_t ig1_yl : 1;
uint16_t ig1_yh : 1;
uint16_t ig1_zl : 1;
uint16_t ig1_zh : 1;
uint16_t ig1_ia : 1;
uint16_t ig2_xl : 1;
uint16_t ig2_xh : 1;
uint16_t ig2_yl : 1;
uint16_t ig2_yh : 1;
uint16_t ig2_zl : 1;
uint16_t ig2_zh : 1;
uint16_t ig2_ia : 1;
} lis2hh12_xl_trshld_src_t;
int32_t lis2hh12_xl_trshld_src_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_trshld_src_t *val);
int32_t lis2hh12_xl_trshld_set(const stmdev_ctx_t *ctx, uint8_t ig1_x,
uint8_t ig1_y, uint8_t ig1_z,
uint8_t ig2_xyz);
int32_t lis2hh12_xl_trshld_get(const stmdev_ctx_t *ctx, uint8_t *ig1_x,
uint8_t *ig1_y, uint8_t *ig1_z,
uint8_t *ig2_xyz);
int32_t lis2hh12_xl_trshld_min_sample_set(const stmdev_ctx_t *ctx,
uint8_t ig1_sam, uint8_t ig2_sam);
int32_t lis2hh12_xl_trshld_min_sample_get(const stmdev_ctx_t *ctx,
uint8_t *ig1_sam, uint8_t *ig2_sam);
int32_t lis2hh12_act_threshold_set(const stmdev_ctx_t *ctx, uint8_t val);
int32_t lis2hh12_act_threshold_get(const stmdev_ctx_t *ctx, uint8_t *val);
int32_t lis2hh12_act_duration_set(const stmdev_ctx_t *ctx, uint8_t val);
int32_t lis2hh12_act_duration_get(const stmdev_ctx_t *ctx, uint8_t *val);
typedef enum
{
LIS2HH12_6D_4D_DISABLE = 0x00,
LIS2HH12_ENABLE_ON_IG1_6D = 0x01,
LIS2HH12_ENABLE_ON_IG2_6D = 0x02,
LIS2HH12_ENABLE_ON_IG1_4D = 0x11,
LIS2HH12_ENABLE_ON_IG2_4D = 0x12,
} lis2hh12_6d_mode_t;
int32_t lis2hh12_6d_mode_set(const stmdev_ctx_t *ctx,
lis2hh12_6d_mode_t val);
int32_t lis2hh12_6d_mode_get(const stmdev_ctx_t *ctx,
lis2hh12_6d_mode_t *val);
int32_t lis2hh12_fifo_watermark_set(const stmdev_ctx_t *ctx, uint8_t val);
int32_t lis2hh12_fifo_watermark_get(const stmdev_ctx_t *ctx, uint8_t *val);
typedef enum
{
LIS2HH12_FIFO_OFF = 0x00,
LIS2HH12_BYPASS_MODE = 0x10,
LIS2HH12_FIFO_MODE = 0x11,
LIS2HH12_STREAM_MODE = 0x12,
LIS2HH12_STREAM_TO_FIFO_MODE = 0x13,
LIS2HH12_BYPASS_TO_STREAM_MODE = 0x14,
LIS2HH12_BYPASS_TO_FIFO_MODE = 0x17,
} lis2hh12_fifo_md_t;
int32_t lis2hh12_fifo_mode_set(const stmdev_ctx_t *ctx,
lis2hh12_fifo_md_t val);
int32_t lis2hh12_fifo_mode_get(const stmdev_ctx_t *ctx,
lis2hh12_fifo_md_t *val);
typedef struct
{
uint8_t fss : 5;
uint8_t empty : 1;
uint8_t ovr : 1;
uint8_t fth : 1;
} lis2hh12_fifo_stat_t;
int32_t lis2hh12_fifo_status_get(const stmdev_ctx_t *ctx,
lis2hh12_fifo_stat_t *val);
typedef enum
{
LIS2HH12_ST_DISABLE = 0x00,
LIS2HH12_ST_POSITIVE = 0x01,
LIS2HH12_ST_NEGATIVE = 0x02,
} lis2hh12_xl_st_t;
int32_t lis2hh12_xl_self_test_set(const stmdev_ctx_t *ctx,
lis2hh12_xl_st_t val);
int32_t lis2hh12_xl_self_test_get(const stmdev_ctx_t *ctx,
lis2hh12_xl_st_t *val);
/**
*@}
*
*/
#ifdef __cplusplus
}
#endif
#endif /* LIS2HH12_REGS_H */

View File

@@ -0,0 +1,81 @@
/*
* ledprog.c: led programs
*/
#include "matrix.h"
static uint32_t s[8] = {0};
//static uint32_t t[8] = {0};
//static uint32_t l[8] = {0};
void lp_ribbon_init()
{
uint8_t i;
for (i = 0; i < 8; i++) {
s[i] = 0;
}
}
void lp_ribbon_upward(uint16_t wait, uint16_t rate, uint8_t fade)
{
uint32_t i;
uint32_t x;
/*
0: state
1: timeout
2: index
*/
switch (s[0]) {
case 0: { // timeout
if (!s[1]) {
s[0]++;
s[2] = 0;
return;
}
s[1]--;
break;
}
case 1: { // upward trails
// rate delay
if (!s[1]) {
s[1] = rate;
} else {
s[1]--;
break;
}
x = s[2]++;
// are we done?
if (x > sizeof(led_set.ribbon)) {
s[0] = 0;
s[1] = wait;
break;
}
// fade in and up
led_set.ribbon[x] = 0x40;
if (x) led_set.ribbon[x - 1] = 0x70;
if (x > 1) led_set.ribbon[x - 2] = 0xff;
}
}
// fade out LEDs
for (i = 0; i < sizeof(led_set.ribbon); i++) {
if (led_set.ribbon[i] >= fade) {
led_set.ribbon[i] -= fade;
} else {
led_set.ribbon[i] = 0;
}
}
matrix_flag_update();
}

View File

@@ -0,0 +1,15 @@
/*
* ledprog.h
*/
#ifndef __APP_LED_LEDPROG_H
#define __APP_LED_LEDPROG_H
void lp_ribbon_init();
void lp_ribbon_upward(uint16_t wait, uint16_t rate, uint8_t fade);
#endif /* __APP_LED_LEDPROG_H */

View File

View File

128
firmware/app/led/matrix.c Normal file
View File

@@ -0,0 +1,128 @@
/*
*
*/
#include "ch32x035_conf.h"
#include <string.h>
#include "matrix.h"
#include "driver/aw20xxx.h"
// #include "user_config.h"
#define AW20X_DIM 31 // initial global current setting
#define AW20X_COLS 6
#define AW20X_ROWS 12
#define AW20X_FADE_COUNT (AW20X_ROWS * AW20X_COLS)
#define HWEN_PORT GPIOB
#define HWEN_PIN GPIO_Pin_7
AW20x awled;
static uint8_t awled_fade[AW20X_FADE_COUNT];
static uint8_t led_matrix_needs_update = 0;
uint16_t led_map_size;
const LedMap led_map = {
// map is 1-based to match part numbers on the board
{ // ribbon
1, 25, 13, 49,
37, 14,
28, 2, 38,
50, 3,
27, 15,
39, 51,
4, 16,
28, 40,
52, 5,
17, 29,
41, 53,
6, 18,
30, 42,
54,
0, 0, 0, 0, 0, 0,
7, 0,
19, 0,
31, 0,
43, 0,
55, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 20, 32,
0, 0
},
{ // "A"
9, 21, 33, 0, 0, 0, 45, 57, 10,
22,
34, 46, 58, 11, 23, 35, 47, 59, 12
},
{ // "//" left one
61, 62, 63, 64, 44
},
{ // "//" right one
60, 48, 36, 24, 56
}
};
LedMap led_set;
void matrix_init()
{
volatile uint32_t x;
// make sure the hardware pin is on before calling.
HWEN_PORT->BSHR = HWEN_PIN;
// wait a little while to ensure controller is awake
x = 131072;
while (x--);
// clear fade
memset(awled_fade, 0x00, sizeof(awled_fade));
// set up RGBLED chip
awled.fade = awled_fade;
aw20x_init(&awled, AW20X_ADDR_VDD << 1, AW20X_COLS, AW20X_ROWS, AW20X_IMAX_20MA);
aw20x_set_dim_global(&awled, AW20X_DIM);
aw20x_set_fade(&awled);
aw20x_led_enable_range(&awled, 0, (AW20X_COLS * AW20X_MAX_ROWS) - 1);
led_map_size = sizeof(led_map.ribbon) + sizeof(led_map.a) + sizeof(led_map.ii_lf) + sizeof(led_map.ii_rt);
}
void matrix_flag_update()
{
led_matrix_needs_update = 1;
}
void matrix_send()
{
int8_t i;
uint8_t *fade = awled_fade;
uint8_t *map = (uint8_t *)&led_map;
uint8_t *set = (uint8_t *)&led_set;
if (led_matrix_needs_update) {
led_matrix_needs_update = 0;
// remap data for sending
for (i = 0; i < led_map_size; i++) {
if ((*map) & (*map <= AW20X_FADE_COUNT)) {
fade[*map - 1] = *set;
}
map++;
set++;
}
aw20x_set_fade(&awled);
}
}

35
firmware/app/led/matrix.h Normal file
View File

@@ -0,0 +1,35 @@
/*
*
*/
#ifndef __APP_LED_MATRIX_H
#define __APP_LED_MATRIX_H
#include <stdint.h>
// 0 index = bottom.
// 0 value = no LED here.
typedef struct LedMap {
uint8_t ribbon[80]; // a "linear" bottom to top map, with gaps for the spacing.
uint8_t a[20]; // left to right, bottom to apex to bottom.
uint8_t ii_lf[5]; // top to bottom.
uint8_t ii_rt[5]; // top to bottom.
} LedMap;
extern LedMap led_set;
void matrix_init();
void matrix_send();
void matrix_flag_update();
#endif /* __APP_LED_MATRIX_H */

175
firmware/app/main.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* HSC26 Artemis II
*
* 2026 true
*
* todo:
* - implement SPI master for accelerometer (done, blocking / basic)
* - configure ADC to get touchpads (done)
* - get main tick interrupt working
*
* - get user input working (buttons, touchpads)
* - configure accelerometer for wakeup / sleep
* - configure buttons to wake up in case of sleep
* - write lightshows
* - write userconfig stuff, save to end of FLASH
*/
#include "ch32x035_conf.h"
#include "comms/soft_i2c_master.h"
#include "comms/spi_master.h"
#include "driver/adc.h"
#include "led/matrix.h"
#include "led/ledprog.h"
/*
*@Note
***Only PA0--PA15 and PC16--PC17 support input pull-down.
*/
uint16_t cnt = 0;
volatile uint32_t wake_uptime = 0;
volatile uint8_t lp_render = 0;
void clk_init()
{
SystemCoreClockUpdate();
}
void periphclk_init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO |
RCC_APB2Periph_ADC1 | RCC_APB2Periph_SPI1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 | RCC_AHBPeriph_USBPD, ENABLE);
}
void gpio_init()
{
GPIO_InitTypeDef gpio = {0};
gpio.GPIO_Speed = GPIO_Speed_50MHz;
// PA0-PA4 touch0-touch4
gpio.GPIO_Mode = GPIO_Mode_AIN;
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_Init(GPIOA, &gpio);
// PB0 touch8
gpio.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &gpio);
// PC0 VBAT, PC3 touch13
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;
GPIO_Init(GPIOC, &gpio);
// PA5 SCK, PA7 MOSI
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_Init(GPIOA, &gpio);
// PA6 MISO
gpio.GPIO_Mode = GPIO_Mode_IPU;
gpio.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOA, &gpio);
// PB3 LIS_INT, PB8 soft I2C SCL, PB9 soft I2C SDA, PB11 btn0, PB12 btn1
gpio.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_12;
GPIO_Init(GPIOB, &gpio);
// PC10 USB DP (btn2), PC11 USB DM (unused)
gpio.GPIO_Mode = GPIO_Mode_IPD;
gpio.GPIO_Pin = GPIO_Pin_16 | GPIO_Pin_17;
GPIO_Init(GPIOC, &gpio);
// PB4 LSENS A, PB5 LSENS_K, PB6 SPI softCS, PB7 LED HWEN, PB10 STAT LED
GPIOB->BCR = 0x4f0;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_10;
GPIO_Init(GPIOB, &gpio);
// PC14 CC1, PC15 CC2
// todo later
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
clk_init();
periphclk_init();
gpio_init();
// initialize led programs
lp_ribbon_init();
// set up soft I2C and configure lighting controller
i2cm_init();
matrix_init();
// set up touchsensing and light sensing
adc_init();
// set up accelerometer
spim_init();
// configure AWU to provide ~1440Hz wakeup interrupt for main program
// TODO: confirm if the counter is reset / preloaded
AWU_SetPrescaler(AWU_Prescaler_32);
AWU_SetWindowValue(0x3e);
AutoWakeUpCmd(ENABLE);
while (1) {
// low-priority tasks like
// rendering next LED program output frame
if (lp_render) {
lp_render = 0;
lp_ribbon_upward(800, 3, 2);
}
// stay a while
// __WFI();
}
}
void AWU_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void AWU_IRQHandler(void)
{
cnt++;
if (cnt >= 1440) {
cnt = 0;
wake_uptime++;
}
// handle render new frame at 90Hz
if ((cnt & 0xf) == 0) {
lp_render = 1;
}
// send led matrix data to matrix chip
matrix_send();
// handle ADC
// handle buttons
// do we sleep?
}

View File

@@ -0,0 +1,50 @@
/*
* i8atan2.c
* begin 20190611 true
*
* copied and fixed up from teh internets
*/
#include <stdint.h>
static int8_t iat2(int8_t y, int8_t x)
{
return ((y * 32 + (x / 2)) / x) * 2;
}
int8_t i8atan2(int8_t y, int8_t x)
{
// determine octant
if (y >= 0) { // oct 0,1,2,3
if (x >= 0) { // oct 0,1
if (x > y) {
return iat2(-y, -x) / 2 + (0 * 32);
} else {
if (y == 0) return 0; // (x=0,y=0)
return -iat2(-x, -y) / 2 + (2 * 32);
}
} else { // oct 2,3
if (x >= -y) {
return iat2(x, -y) / 2 + (2 * 32);
} else {
return -iat2(-y, x) / 2 + (4 * 32);
}
}
} else { // oct 4,5,6,7
if (x < 0) { // oct 4,5
if (x < y) {
return iat2(y, x) / 2 + (-4 * 32);
} else {
return -iat2(x, y) / 2 + (-2 * 32);
}
} else { // oct 6,7
if (-x >= y) {
return iat2(-x, y) / 2 + (-2 * 32);
} else {
return -iat2(y, -x) / 2 + (-0 * 32);
}
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* i8atan2.h
* begin 20190611 true
*
* copied and fixed up from teh internets
*/
#ifndef INC_I8ATAN2_H_
#define INC_I8ATAN2_H_
#include <stdint.h>
int8_t i8atan2(int8_t y, int8_t x);
#endif /* INC_I8ATAN2_H_ */

View File

@@ -0,0 +1,101 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32x035.c
* Author : WCH
* Version : V1.0.0
* Date : 2023/04/06
* Description : CH32X035 Device Peripheral Access Layer System Source File.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "ch32x035.h"
/*
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
* reset the HSI is used as SYSCLK source).
*/
//#define SYSCLK_FREQ_8MHz_HSI 8000000
//#define SYSCLK_FREQ_12MHz_HSI 12000000
//#define SYSCLK_FREQ_16MHz_HSI 16000000
//#define SYSCLK_FREQ_24MHz_HSI 24000000
//#define SYSCLK_FREQ_48MHz_HSI HSI_VALUE
/* Clock Definitions */
uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */
__I uint8_t AHBPrescTable[16] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
/*********************************************************************
* @fn SystemInit
*
* @brief Setup the microcontroller system Initialize the Embedded Flash Interface,
* update the SystemCoreClock variable.
*
* @return none
*/
void SystemInit(void)
{
RCC->CTLR |= (uint32_t)0x00000001;
RCC->CFGR0 |= (uint32_t)0x00000050;
RCC->CFGR0 &= (uint32_t)0xF8FFFF5F;
SetSysClock_HSI(HCLK_24MHZ);
}
/*********************************************************************
* @fn SystemCoreClockUpdate
*
* @brief Update SystemCoreClock variable according to Clock Register Values.
*
* @return none
*/
void SystemCoreClockUpdate(void)
{
uint32_t tmp = 0;
SystemCoreClock = HSI_VALUE;
tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
if(((RCC->CFGR0 & RCC_HPRE) >> 4) < 8)
{
SystemCoreClock /= tmp;
}
else
{
SystemCoreClock >>= tmp;
}
}
/*********************************************************************
* @fn SetSysClock_HSI
*
* @brief Sets System clock frequency to specified and configure HCLK prescalers.
*
* @return none
*/
void SetSysClock_HSI(uint32_t divider)
{
uint32_t actlr;
/* Flash 2 wait state */
actlr = FLASH->ACTLR & ((uint32_t)~FLASH_ACTLR_LATENCY);
FLASH->ACTLR = actlr | (uint32_t)FLASH_ACTLR_LATENCY_2;
/* HCLK = SYSCLK = APB1 */
RCC->CFGR0 &= (uint32_t)0xFFFFFF0F;
RCC->CFGR0 |= (uint32_t)divider;
// set wait states depending on speed
if (divider > RCC_HPRE_DIV1) { // 48MHz is already set to 2 cycles
if (divider >= RCC_HPRE_DIV1) { // 24MHz is 1 cycle
actlr |= (uint32_t)FLASH_ACTLR_LATENCY_0;
} else { // all others are 0 cycle
actlr |= (uint32_t)FLASH_ACTLR_LATENCY_1;
}
FLASH->ACTLR = actlr;
}
}

View File

@@ -0,0 +1,51 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32x035.h
* Author : WCH
* Version : V1.0.0
* Date : 2023/04/06
* Description : CH32X035 Device Peripheral Access Layer System Header File.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __SYSTEM_CH32X035_H
#define __SYSTEM_CH32X035_H
#ifdef __cplusplus
extern "C" {
#endif
#define HCLK_48MHZ RCC_HPRE_DIV1
#define HCLK_24MHZ RCC_HPRE_DIV2
#define HCLK_16MHZ RCC_HPRE_DIV3
#define HCLK_12MHZ RCC_HPRE_DIV4
#define HCLK_9_6MHZ RCC_HPRE_DIV5
#define HCLK_8MHZ RCC_HPRE_DIV6
#define HCLK_6_8MHZ RCC_HPRE_DIV7
#define HCLK_6MHZ RCC_HPRE_DIV8
#define HCLK_3MHZ RCC_HPRE_DIV16
#define HCLK_1_5MHZ RCC_HPRE_DIV32
#define HCLK_750KHZ RCC_HPRE_DIV64
#define HCLK_375KHZ RCC_HPRE_DIV128
#define HCLK_187KHZ RCC_HPRE_DIV256
extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
/* System_Exported_Functions */
extern void SystemInit(void);
extern void SystemCoreClockUpdate(void);
void SetSysClock_HSI(uint32_t divider);
#ifdef __cplusplus
}
#endif
#endif

6
firmware/app/ui/btn.c Normal file
View File

@@ -0,0 +1,6 @@
/*
* btn.c: handler for user interface with real or capacitive buttons
*/
#include "ch32x035_conf.h"
#include "driver/adc.h"