initial commit of work-in-progress code

this code builds, but is fully untested, incomplete, and guaranteed to not work.
This commit is contained in:
true
2024-08-03 03:21:08 -07:00
commit 27cbc558ce
92 changed files with 27801 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_conf.h
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/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 __CH32V20x_CONF_H
#define __CH32V20x_CONF_H
#include "ch32v20x_adc.h"
#include "ch32v20x_bkp.h"
#include "ch32v20x_can.h"
#include "ch32v20x_crc.h"
#include "ch32v20x_dbgmcu.h"
#include "ch32v20x_dma.h"
#include "ch32v20x_exti.h"
#include "ch32v20x_flash.h"
#include "ch32v20x_gpio.h"
#include "ch32v20x_i2c.h"
#include "ch32v20x_iwdg.h"
#include "ch32v20x_pwr.h"
#include "ch32v20x_rcc.h"
#include "ch32v20x_rtc.h"
#include "ch32v20x_spi.h"
#include "ch32v20x_tim.h"
#include "ch32v20x_usart.h"
#include "ch32v20x_wwdg.h"
#include "ch32v20x_it.h"
#include "ch32v20x_misc.h"
#endif /* __CH32V20x_CONF_H */

View File

@@ -0,0 +1,95 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_it.c
* Author : WCH
* Version : V1.0.0
* Date : 2023/12/29
* 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 <stdint.h>
#include <ch32v20x.h>
#include "ch32v20x_it.h"
#include "src/adc.h"
#include "src/btn.h"
#include "src/led.h"
#include "src/ui.h"
void NMI_Handler(void); //__attribute__((interrupt("WCH-Interrupt-fast")));
void HardFault_Handler(void); //__attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_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)
{
while (1);
}
volatile uint16_t ticnt;
volatile uint32_t uptime;
void SysTick_Handler(void)
{
if (++ticnt > 0x1ff) {
ticnt = 0;
uptime++;
}
// all processes update at 1/128 duty
switch (ticnt & 0x3) {
case 0: { // send new LEDs
led_rgb_update();
led_matrix_send();
break;
}
case 1: { // process / initialize light sensor
adc_process_lsens(0);
break;
}
case 2: { // convert light sensor
adc_process_lsens(1);
break;
}
case 3: { // process buttons
btn_poll();
}
}
// clear comparison flag
SysTick->SR = 0;
return;
}
// accelerometer interrupt handler
void EXTI15_10_IRQHandler(void)
{
// now what the fuck triggered this shit
// oh right something on the accelerometer. but what though?
}

View File

@@ -0,0 +1,28 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_it.h
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/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 __CH32V20x_IT_H
#define __CH32V20x_IT_H
#include <stdint.h>
extern volatile uint16_t ticnt;
extern volatile uint32_t uptime;
#endif /* __CH32V20x_IT_H */

152
firmware/user/main.c Normal file
View File

@@ -0,0 +1,152 @@
/*
* Peppercon9
* GAT Addon Firmware
* by true
*
* code was made for different random addons I designed for dc32,
* then adapted to each one. so things might be a mess.
*
* I had only a few hours on and off over a week to get the code done.
* lots of shit was copied from demo code and adapted to work. certainly
* isn't the best way to get this done.
*
* I know I could save some power, but seeing as all the badges burn it in
* LEDs and almost nobody else power optimizes their badge code, who cares?
*
* sorry. wasn't procrastination I swear. all the ideas were last minute.
*
* notes:
*
* - last 2K of flash memory is reserved for configuration storage.
*/
#include <ch32v20x.h>
#include <stdint.h>
#include "src/adc.h"
#include "src/btn.h"
#include "src/config.h"
#include "src/i2c.h"
#include "src/led.h"
#include "src/lis2dw.h"
#include "src/rand.h"
#include "src/touch.h"
#include "src/ui.h"
void systick_init(void)
{
SysTick->CMP = (SystemCoreClock / 512) - 1; // we want a 512Hz interrupt
SysTick->CNT = 0; // clear counter
SysTick->CTLR = 0xF; // start counter in /1 mode, enable interrupts, auto-reset counter
SysTick->SR = 0; // clear count comparison flag
NVIC_EnableIRQ(SysTicK_IRQn); // enable interrupt
}
void gpio_init()
{
GPIO_InitTypeDef gpio = {0};
gpio.GPIO_Speed = GPIO_Speed_2MHz;
// per the datasheet, PB8 is an output-only pin on this specific submodel.
// however, the datasheet later conflicts and says this is A/I/O pin.
// so we'll try to use it as an input anyway.
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &gpio);
// unused osc pins
gpio.GPIO_Mode = GPIO_Mode_IPD;
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_7;
GPIO_Init(GPIOD, &gpio);
// unused PORTA pins (0=none, 1=none, 5=SWI2C_SCL, 9=none)
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5 | GPIO_Pin_9;
GPIO_Init(GPIOA, &gpio);
// unused PORTB pins (5=SWI2C_SDA)
gpio.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOB, &gpio);
// lightsense LED anode and cathode (2=anode, 3=cathode)
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOA, &gpio);
// TBTN1 front touch button, right side (A4)
gpio.GPIO_Mode = GPIO_Mode_AIN;
gpio.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOA, &gpio);
// TBTN2 front touch button, left side (A9)
gpio.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOB, &gpio);
// rear RGBLED (6=T3C1 blue, 7=T3C2 green)
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOA, &gpio);
// rear RGBLED (1=T3C3 red)
gpio.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &gpio);
// accelerometer interrupt
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &gpio);
// I2C on-board comms
gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOB, &gpio);
}
int main(void)
{
// configure core
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
SystemCoreClockUpdate();
// enable peripheral clocks
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3 | RCC_APB1Periph_I2C1 |
RCC_APB1Periph_PWR, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA |
RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD |
RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
// configure gpio pins
gpio_init();
// get saved settings
i2c_init();
userconf_load();
// configure hardware
adc_init(); // configure ADC1 for lightsense, also configures ADC clock divider
btn_init(); // configures ADC2 touch buttons on the front
lis2dw_init(); // looking at badge: Y+ up, X+ right, Z+ in front of badge
led_init(); // configure matrix IC as well as RGB PWM
// configure random
tinymt32_init(&tinymt32_s, DBGMCU_GetCHIPID() | userconf.checksum);
// configure UI
ui_init();
// configure systick interrupt
systick_init();
// do system shit
while(1) {
__WFI();
// after sending LEDs, run our program
// to update the LED buffer for next time
if ((ticnt & 0x3) == 0) {
ui_render();
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Created on: Jul 27, 2024
*
* uses directly polled i2c code. ensure i2c is configured before using.
*
* todo: use dma or interrupt based i2c.
*/
#include "31fl3729.h"
#include "i2c.h"
/* sends the IS31FL3729 general config.
* does not configure per-channel current sink limits; do that separately.
* does not configure other sane defaults; do that separately.
*/
void is31fl3729_init(uint8_t i2c_addr, uint8_t config, uint8_t global_current)
{
uint8_t buf;
// enable device
#ifdef FL3729_SDB_PORT
FL3729_SDB_PORT->BSHR = FL3729_SDB_PIN;
#endif
// reset config registers
buf = FL3729_RESET_VALUE;
i2c_write_addr1b(i2c_addr, FL3729_REG_RESET, &buf, 1);
// write initial config
i2c_write_addr1b(i2c_addr, FL3729_REG_CONFIG, &config, 1);
i2c_write_addr1b(i2c_addr, FL3729_REG_G_CURRENT, &global_current, 1);
}
uint8_t is31fl3729_get_addr(uint8_t adpin)
{
return FL3729_BASE_ADDR | (adpin & 0x3);
}
void is31fl3729_set_global_current(uint8_t i2c_addr, uint8_t current)
{
i2c_write_addr1b(i2c_addr, FL3729_REG_G_CURRENT, &current, 1);
}
void is31fl3729_set_scaling_current(uint8_t i2c_addr, uint8_t cs, uint8_t current)
{
if (cs > 0xf) return;
i2c_write_addr1b(i2c_addr, FL3729_REG_SCALING + cs, &current, 1);
}
/*
* sets current limit of multiple outputs simultaneously, starting from CS1.
* useful for initial register configuration.
*/
void is31fl3729_set_scaling_current_multi(uint8_t i2c_addr, uint8_t *current, uint8_t count)
{
if (!count) return;
count &= 0xf;
i2c_write_addr1b(i2c_addr, FL3729_REG_SCALING, current, count);
}
/* updates LEDs. data is arranged as 16 bytes per switch output.
* i2c_addr: address of endpoint. get with `is31fl3729_get_addr(FL3729_ADPIN_xxx)`
* sw: first switch segment to update. usually set this to 1 for the first switch.
* if set to 0, will default to 1.
* *out: output data buffer
* len: length of buffer
*/
void is31fl3729_set_outputs(uint8_t i2c_addr, uint8_t sw, uint8_t *out, uint8_t len)
{
if (!sw) sw = 1;
// calculate start address
sw--;
sw <<= 4;
sw++;
i2c_write_addr1b(i2c_addr, sw, out, len);
}

View File

@@ -0,0 +1,93 @@
/*
* Created on: Jul 27, 2024
*/
#ifndef USER_SRC_31FL3729_H_
#define USER_SRC_31FL3729_H_
#include <ch32v20x.h>
#include <stdint.h>
#define FL3729_SDB_PORT GPIOC
#define FL3729_SDB_PIN GPIO_Pin_3
#define FL3729_BASE_ADDR 0x68
#define FL3729_ADPIN_GND 0
#define FL3729_ADPIN_SCL 1
#define FL3729_ADPIN_SDA 2
#define FL3729_ADPIN_VCC 3
#define FL3729_REG_OUTPUT 0x01
#define FL3729_REG_SCALING 0x90
#define FL3729_REG_CONFIG 0xA0
#define FL3729_REG_G_CURRENT 0xA1 // global current
#define FL3729_REG_PUPD 0xB0 // pull up / pull down
#define FL3729_REG_SS 0xB1 // spread spectrum
#define FL3729_REG_PWM 0xB2
#define FL3729_REG_RESET 0xCF
#define FL3729_GCC_MAX 64 // maximum global current level
#define FL3729_CONF_SHDN_ENA 0
#define FL3729_CONF_SHDN_DIS ((1 << 0) << 0)
#define FL3729_CONF_OSDE_OFF 0
#define FL3729_CONF_OSDE_OPEN ((1 << 0) << 1)
#define FL3729_CONF_OSDE_SHORT ((1 << 1) << 1)
#define FL3729_CONF_LOGIC_LOLEV 0
#define FL3729_CONF_LOGIC_HILEV ((1 << 0) << 3)
#define FL3729_CONF_MATRIX_9x15 0x00
#define FL3729_CONF_MATRIX_8x16 0x10
#define FL3729_CONF_MATRIX_7x16 0x20
#define FL3729_CONF_MATRIX_6x16 0x30
#define FL3729_CONF_MATRIX_5x16 0x40
#define FL3729_CONF_MATRIX_4x16 0x50
#define FL3729_CONF_MATRIX_3x16 0x60
#define FL3729_CONF_MATRIX_2x16 0x70
#define FL3729_CONF_MATRIX_1x16 0x80 // note: all of SW1-SW9 are ALWAYS ON in this mode
#define FL3729_SS_CLT_1980us 0
#define FL3729_SS_CLT_1200us (1 << 0)
#define FL3729_SS_CLT_820us (2 << 0)
#define FL3729_SS_CLT_660us (3 << 0)
#define FL3729_SS_RNG_5pct 0
#define FL3729_SS_RNG_15pct (1 << 2)
#define FL3729_SS_RNG_24pct (2 << 2)
#define FL3729_SS_RNG_34pct (3 << 2)
#define FL3729_RESET_VALUE 0xAE
/*
typedef struct IS31FL3729_Conf {
uint8_t ad_pin;
uint8_t config;
uint8_t global_brightness;
} IS31FL3729_Conf;
*/
void is31fl3729_init(uint8_t i2c_addr, uint8_t config, uint8_t global_current);
uint8_t is31fl3729_get_addr(uint8_t adpin);
void is31fl3729_set_global_current(uint8_t i2c_addr, uint8_t current);
void is31fl3729_set_scaling_current(uint8_t i2c_addr, uint8_t cs, uint8_t current);
void is31fl3729_set_scaling_current_multi(uint8_t i2c_addr, uint8_t *current, uint8_t count);
void is31fl3729_set_outputs(uint8_t i2c_addr, uint8_t sw, uint8_t *out, uint8_t len);
#endif /* USER_SRC_31FL3729_H_ */

29
firmware/user/src/accel.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* Created on: Jul 28, 2024
*
* basic accelerometer interface stuff.
*/
#include <stdint.h>
#include <ch32v20x.h>
#include "accel.h"
#include "lis2dw.h"
uint8_t accel_tap;
void accel_init()
{
lis2dw_init();
// configure pin external interrupt
// enable interrupt
NVIC_EnableIRQ(ACCEL_INT1_IRQn);
}

26
firmware/user/src/accel.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* Created on: Jul 29, 2024
*/
#ifndef USER_SRC_ACCEL_H_
#define USER_SRC_ACCEL_H_
#include <stdint.h>
#define ACCEL_INT1_PORT GPIOA
#define ACCEL_INT1_PIN GPIO_Pin_15
#define ACCEL_INT1_IRQn EXTI15_10_IRQn
#define ACCEL_TAP_SINGLE (1 << 0)
#define ACCEL_TAP_DOUBLE (1 << 1)
extern uint8_t accel_tap;
#endif /* USER_SRC_ACCEL_H_ */

107
firmware/user/src/adc.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* Created on: Jul 29, 2024
*
* not sure how well the ambient light sensor will work, being surrounded by other LEDs.
* I guess I could make the programs black out and read then. maybe I'll do that.
*/
#include <ch32v20x.h>
#include "adc.h"
static GPIO_InitTypeDef lsens_gpio = {
.GPIO_Mode = GPIO_Mode_Out_PP,
.GPIO_Pin = LSENS_A_PIN | LSENS_K_PIN,
.GPIO_Speed = GPIO_Speed_2MHz
};
uint16_t lsens_val;
static uint8_t lsens_mode = LSENS_READING_IDLE;
void adc_init()
{
ADC_InitTypeDef adc = {0};
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
ADC_DeInit(ADC1);
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_RegularChannelConfig(ADC1, LSENS_ADC_CH, 1, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);
}
void adc_convert()
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
void adc_read()
{
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
lsens_val = ADC_GetConversionValue(ADC1);
}
void adc_set_mode_lsens(uint8_t mode)
{
lsens_mode = mode;
// always reconfigure the pins as outputs when calling this
lsens_gpio.GPIO_Pin = LSENS_A_PIN | LSENS_K_PIN;
lsens_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LSENS_PORT, &lsens_gpio);
}
uint8_t adc_get_mode_lsens()
{
return lsens_mode;
}
static void lsens_start()
{
// set anode and cathode low
LSENS_PORT->BCR = LSENS_A_PIN | LSENS_K_PIN;
adc_set_mode_lsens(LSENS_READING_START);
// set cathode high, let it charge
LSENS_PORT->BSHR = LSENS_K_PIN;
// set anode floating
lsens_gpio.GPIO_Pin = LSENS_A_PIN;
lsens_gpio.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(LSENS_PORT, &lsens_gpio);
// set cathode as analog input
lsens_gpio.GPIO_Pin = LSENS_K_PIN;
lsens_gpio.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(LSENS_PORT, &lsens_gpio);
}
static void lsens_end()
{
adc_set_mode_lsens(LSENS_READING_IDLE);
}
void adc_process_lsens(uint8_t convert)
{
if (lsens_mode != LSENS_OUTPUT) {
// do what needs to be done by me to defeat the light enemys
}
}
uint16_t adc_get_lsens()
{
return lsens_val;
}

41
firmware/user/src/adc.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* adc.h
*
* Created on: Jul 27, 2024
* Author: true
*/
#ifndef USER_SRC_ADC_H_
#define USER_SRC_ADC_H_
#define LSENS_DARK_THRESHOLD 0x7ff // baseline minimum value reading achieved in darkness
#define LSENS_PORT GPIOA
#define LSENS_A_PIN GPIO_Pin_2
#define LSENS_K_PIN GPIO_Pin_3
#define LSENS_ADC_CH ADC_Channel_3
enum {
LSENS_READING_IDLE = 0,
LSENS_READING_START,
LSENS_READING_WAIT,
LSENS_OUTPUT = 0xff
};
void adc_init();
void adc_convert();
void adc_read();
void adc_set_mode_lsens(uint8_t mode);
uint8_t adc_get_mode_lsens();
void adc_process_lsens(uint8_t convert);
#endif /* USER_SRC_ADC_H_ */

74
firmware/user/src/btn.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* Created on: Jul 27, 2024
*
* generic button handler like I do on most of my projects
*/
#include <ch32v20x.h>
#include "btn.h"
#include "touch.h"
struct Btn btn[BTN_COUNT] = {0};
void btn_init()
{
touch_init();
}
void btn_poll()
{
uint8_t i;
uint8_t ignore;
for (i = 0; i < BTN_COUNT; i++) {
ignore = btn[i]._mask & BTN_IGNORE;
if (touch_read_pushed(i)) {
// is pushed
if (btn[i]._count < BTN_DEBOUNCE) continue;
// hold counter
if (btn[i]._count < 0xffff) btn[i]._count++;
// first push?
if (!btn[i]._mask & BTN_PUSH) {
btn[i]._mask = BTN_PUSH;
if (btn[i].cb_push) {
btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4);
}
} else if (btn[i]._count >= btn[i].hold) {
// held to count limit
// if button is not repeatable, do not retrigger
if ((btn[i]._mask && BTN_HOLD) && !btn[i].repeat) continue;
btn[i]._mask |= BTN_HOLD;
// call callback only if not in ignore state
if (btn[i].cb_hold && !ignore) {
btn[i].cb_hold(i);
btn[i]._mask |= (BTN_HOLD << 4);
}
// apply repeat rate to count
btn[i]._count -= btn[i].repeat;
}
} else {
// is not pushed
if (!btn[i]._mask & BTN_RELEASE) {
btn[i]._mask = BTN_RELEASE;
btn[i]._count = 0;
// call callback only if not in ignore state
if (btn[i].cb_release && !ignore) {
btn[i].cb_release(i);
btn[i]._mask |= (BTN_RELEASE << 4);
}
}
}
}
}

45
firmware/user/src/btn.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* btn.h
*
* Created on: Jul 27, 2024
* Author: true
*/
#ifndef USER_SRC_BTN_H_
#define USER_SRC_BTN_H_
#define BTN_COUNT 2
#define BTN_DEBOUNCE 3 // debounce time in ~8ms increments
#define BTN_PUSH (1 << 0)
#define BTN_HOLD (1 << 1)
#define BTN_RELEASE (1 << 2)
#define BTN_IGNORE (1 << 3)
typedef struct Btn {
uint8_t _mask;
uint8_t _pintype;
uint16_t _count; // held counts
uint16_t hold; // initial hold
uint16_t repeat; // repeated hold
void (*cb_push)(uint8_t);
void (*cb_hold)(uint8_t);
void (*cb_release)(uint8_t);
} Btn;
extern struct Btn btn[BTN_COUNT];
void btn_init();
void btn_poll();
#endif /* USER_SRC_BTN_H_ */

134
firmware/user/src/config.c Normal file
View File

@@ -0,0 +1,134 @@
/*
* config.c
*
* Created on: Jul 27, 2024
* Author: true
*/
#include <ch32v20x.h>
#include "config.h"
#include "flash.h"
struct UserConf userconf;
static uint8_t active_page;
uint32_t chip_get_flash_size()
{
uint32_t w;
w = DBGMCU_GetCHIPID();
w >>= 16;
w &= 0xff;
if (w >= 0x80) return 131072; // can be 3 different sizes, but I don't care right now;
// no applicable MCU for us has this size anyway
switch (w) {
case 0x33:
case 0x36:
case 0x37:
case 0x38:
case 0x39: {
return 32768;
}
default: {
return 65536;
}
}
}
static uint32_t * calc_address()
{
return (uint32_t *)(FLASH_BASE + chip_get_flash_size()) - (CONF_FLASH_PAGE_SIZE * CONF_FLASH_PAGES);
}
static void read_page_from_flash(uint8_t page, uint32_t *data, uint16_t len)
{
uint32_t *addr = calc_address();
// no more than 256 bytes at a time necessary
if (len > 64) len = 64;
// set address of page
addr += page * CONF_FLASH_PAGE_SIZE;
// read the data
flash_read(addr, data, len);
}
static void write_page_to_flash(uint8_t page, uint32_t *data, uint16_t len)
{
len = (uint16_t)len;
uint32_t *addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
// write the data
// note we don't pass any length. we'll just read whatever garbage
// is in RAM after our config and write it to flash. lol
flash_write256(addr, data);
}
static uint16_t checksum()
{
uint16_t i;
uint16_t sum = 0;
uint8_t *uc = (uint8_t *)&userconf;
// calculate checksum
for (i = 0; i < sizeof(userconf) - 6; i++) {
sum += *uc++;
}
return sum;
}
void userconf_load()
{
uint8_t valid;
uint8_t page = CONF_FLASH_PAGES;
// read pages backward until we get non-empty flash
while (page) {
read_page_from_flash(--page, (uint32_t *)&userconf, sizeof(userconf)/4);
if (userconf.checkval == CHECKVAL) {
if (userconf.checksum == checksum()) {
// data appears to be valid... use it
active_page = page;
valid = 1;
}
}
}
if (!valid) {
// config is invalid; reset to default
userconf.version = 0;
userconf.checksum = checksum();
userconf.checkval = CHECKVAL;
}
}
void userconf_save()
{
userconf.version++;
userconf.checksum = checksum();
userconf.checkval = CHECKVAL;
// determine page to write
active_page++;
active_page %= CONF_FLASH_PAGES;
// this MCU writes and erases in full page sized blocks.
// nothing is mentioned in the datasheet nor reference manual
// about repeated writes. so to be safe, each page gets
// written to once per erase.
write_page_to_flash(active_page, (uint32_t *)&userconf, sizeof(userconf)/4);
}

View File

@@ -0,0 +1,54 @@
/*
* config.h
*
* Created on: Jul 27, 2024
* Author: true
*/
#ifndef USER_SRC_CONFIG_H_
#define USER_SRC_CONFIG_H_
#include <stdint.h>
#include <ch32v20x.h>
#define CONF_FLASH_ADDR_BASE FLASH_BASE
#define CONF_FLASH_PAGE_SIZE 256 // CH32V20x is 256-byte page in fast mode
#define CONF_FLASH_PAGES 8 // wear leveling pages
#define CONF_CURSOR_WHITE 0
#define CONF_CURSOR_GREEN 1
#define CONF_CURSOR_ORANGE 2
#define CONF_CURSOR_OFF 3
#define CONF_CURSOR_SELECT_MASK 0x03
#define CONF_CURSOR_FLASH_MASK 0x70
#define CONF_CURSOR_FLASH_SHIFT 4
#define CHECKVAL 0x2024dc32
typedef struct UserConf {
uint32_t version;
uint8_t pep_conf[8][4];
uint8_t rgb_conf[4][4];
uint8_t pep_prog_ena_map;
uint8_t rgb_prog_ena_map;
uint16_t checksum;
uint32_t checkval;
} UserConf; // 60 bytes
extern struct UserConf userconf;
void userconf_load();
void userconf_save();
#endif /* USER_SRC_CONFIG_H_ */

42
firmware/user/src/flash.c Normal file
View File

@@ -0,0 +1,42 @@
/*
* Created on: Jul 29, 2024
*
* routines for fucking around with built in flash memory.
*/
#include <stdint.h>
#include <ch32v20x.h>
// reads from flash in 32-bit mode
uint8_t flash_read(uint32_t *flash_addr, uint32_t *data, uint32_t len)
{
uint32_t *addr = (uint32_t *)flash_addr;
while (len >= 4) {
*data++ = *addr++;
len -= 4;
}
return 0;
}
// erases flash page, then writes 256-byte data buffer to flash page
// flash page must be at 256-byte boundary
uint8_t flash_write256(uint32_t *flash_addr, uint32_t *data)
{
FLASH_Status s;
// erase flash page
s = FLASH_ROM_ERASE((uint32_t)flash_addr, 256);
if (s != FLASH_COMPLETE) {
return s;
}
s = FLASH_ROM_WRITE((uint32_t)flash_addr, data, 256);
if (s != FLASH_COMPLETE) {
return s;
}
return 0;
}

18
firmware/user/src/flash.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* flash.h
*
* Created on: Jul 29, 2024
* Author: true
*/
#ifndef USER_SRC_FLASH_H_
#define USER_SRC_FLASH_H_
int8_t flash_read(uint32_t *flash_addr, uint32_t *data, uint32_t len);
int8_t flash_write256(uint32_t *flash_addr, uint32_t *data);
#endif /* USER_SRC_FLASH_H_ */

View File

@@ -0,0 +1,94 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 B. Stultiens
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hsv2rgb.h"
void hsv2rgb_8b(int16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b)
{
uint8_t sextant;
uint8_t bb;
uint16_t ww;
uint8_t h_fraction;
if (!(s)) {
*(r) = *(g) = *(b) = (v);
return;
}
sextant = h >> 8;
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
*g = v; // Top level
// Perform actual calculations
/*
* Bottom level: v * (1.0 - s)
* --> (v * (255 - s) + error_corr) / 256
*/
bb = ~s;
ww = v * bb;
ww += 1; // Error correction
ww += ww >> 8; // Error correction
*b = ww >> 8;
h_fraction = h & 0xff; // 0...255
if(!(sextant & 1)) {
// *r = ...slope_up...;
/*
* Slope up: v * (1.0 - s * (1.0 - h))
* --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256
*/
ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction));
ww += ww >> 8; // Error correction 1
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
ww += v >> 1; // Error correction 2
*r = ww >> 8;
} else {
// *r = ...slope_down...;
/*
* Slope down: v * (1.0 - s * h)
* --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256
*/
ww = s * h_fraction;
ww += ww >> 8; // Error correction 1
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
ww += v >> 1; // Error correction 2
*r = ww >> 8;
/*
* A perfect match for h_fraction == 0 implies:
* *r = (ww >> 8) + (h_fraction ? 0 : 1)
* However, this is an extra calculation that may not be required.
*/
}
}

View File

@@ -0,0 +1,98 @@
#ifndef _INC_HSV2RGB_H
#define _INC_HSV2RGB_H
#include <stdint.h>
typedef struct color_rgb {
uint8_t r;
uint8_t g;
uint8_t b;
} color_rgb;
typedef struct color_hsv {
int16_t h;
uint8_t s;
uint8_t v;
} color_hsv;
#define HSV_HUE_SEXTANT 256
#define HSV_HUE_STEPS (6 * HSV_HUE_SEXTANT)
#define HSV_HUE_MIN 0
#define HSV_HUE_MAX (HSV_HUE_STEPS - 1)
#define HSV_SAT_MIN 0
#define HSV_SAT_MAX 255
#define HSV_VAL_MIN 0
#define HSV_VAL_MAX 255
/* Options: */
#define HSV_USE_SEXTANT_TEST /* Limit the hue to 0...360 degrees */
void hsv2rgb_8b(int16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b);
/*
* Macros that are common to all implementations
*/
#ifdef HSV_USE_SEXTANT_TEST
#define HSV_SEXTANT_TEST(sextant) \
if((sextant) > 5) { \
(sextant) = 5; \
}
#else
#define HSV_SEXTANT_TEST(sextant)
#endif
/*
* Pointer swapping:
* sext. r g b r<>b g<>b r <> g result
* 0 0 0 v u c !u v c u v c
* 0 0 1 d v c d v c
* 0 1 0 c v u u v c u v c
* 0 1 1 c d v v d c d v c d v c
* 1 0 0 u c v u v c u v c
* 1 0 1 v c d v d c d v c d v c
*
* if(sextant & 2)
* r <-> b
*
* if(sextant & 4)
* g <-> b
*
* if(!(sextant & 6) {
* if(!(sextant & 1))
* r <-> g
* } else {
* if(sextant & 1)
* r <-> g
* }
*/
#define HSV_SWAPPTR(a,b) do { uint8_t *tmp = (a); (a) = (b); (b) = tmp; } while(0)
#define HSV_POINTER_SWAP(sextant,r,g,b) \
do { \
if((sextant) & 2) { \
HSV_SWAPPTR((r), (b)); \
} \
if((sextant) & 4) { \
HSV_SWAPPTR((g), (b)); \
} \
if(!((sextant) & 6)) { \
if(!((sextant) & 1)) { \
HSV_SWAPPTR((r), (g)); \
} \
} else { \
if((sextant) & 1) { \
HSV_SWAPPTR((r), (g)); \
} \
} \
} while(0)
#endif /* _INC_HSV2RGB_H */

165
firmware/user/src/i2c.c Normal file
View File

@@ -0,0 +1,165 @@
/*
* i2c.c
*
* routines more or less copied from WCH example code, then fucked around with to work.
*
* these routines have serious issues.
* - any i2c issue may lock up the machine.
* - timeout handlers are hastily added and may have problems.
* - there's no error handling of any kind
* - the library code makes some serious assumptions re: flags
* - it's a shitpile of polling
*/
#include <ch32v20x.h>
#define I2C_TIMEOUT 0xffff
#define I2C_TIMEOUT_ACK_POLL 0x180
static uint32_t timeout;
void i2c_init()
{
GPIO_InitTypeDef gpio = {0};
I2C_InitTypeDef i2c = {0};
gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &gpio);
i2c.I2C_ClockSpeed = 666666;
i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_DutyCycle = I2C_DutyCycle_16_9;
i2c.I2C_OwnAddress1 = 0x7f;
i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &i2c);
I2C_Cmd(I2C1, ENABLE);
}
/*
* reads data from devices which use a single-byte address register
*/
int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
{
timeout = I2C_TIMEOUT;
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
if (!timeout) return -1;
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -2;
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--);
if (!timeout) return -3;
I2C_SendData(I2C1, reg);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
if (!timeout) return -4;
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -5;
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
if (!timeout) return -6;
while (len) {
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET) {
I2C_AcknowledgeConfig(I2C1, len);
}
*data++ = I2C_ReceiveData(I2C1);
len--;
if (!len) {
I2C_GenerateSTOP(I2C1, ENABLE);
}
}
return 0;
}
uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg)
{
uint8_t dat;
i2c_read_addr1b(addr, reg, &dat, 1);
return dat;
}
int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
{
timeout = I2C_TIMEOUT;
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
if (!timeout) return -1;
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -2;
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--);
if (!timeout) return -3;
I2C_SendData(I2C1, reg);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
if (!timeout) return -4;
while (len) {
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET) {
I2C_SendData(I2C1, *data++);
len--;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
}
I2C_GenerateSTOP(I2C1, ENABLE);
return 0;
}
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat)
{
i2c_write_addr1b(addr, reg, &dat, 1);
}
int8_t i2c_ack_poll(uint8_t addr)
{
int8_t addr_match = 0;
timeout = I2C_TIMEOUT;
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
if (!timeout) return -1;
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -2;
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
timeout = I2C_TIMEOUT_ACK_POLL;
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
if (!timeout) {
addr_match = -128;
}
I2C_GenerateSTOP(I2C1, ENABLE);
return addr_match;
}

29
firmware/user/src/i2c.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* i2c.h
*
* Created on: Jul 27, 2024
* Author: true
*/
#ifndef USER_SRC_I2C_H_
#define USER_SRC_I2C_H_
#include <stdint.h>
void i2c_init();
int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg);
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat);
int8_t i2c_ack_poll(uint8_t devaddr);
#endif /* USER_SRC_I2C_H_ */

121
firmware/user/src/led.c Normal file
View File

@@ -0,0 +1,121 @@
/*
* Created on: Jul 28, 2024
*/
#include "led.h"
#include "ledprog_pep.h"
#include "ledprog_rgb.h"
#define FL3729_SW_COUNT 4 // switches utilized
#define FL3729_CS_COUNT 16 // current sink outputs used by chip
#define FL3729_G_CURRENT 16 // initial global current setting
const uint8_t cs_currents[FL3729_CS_COUNT] = {
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};
const uint16_t pwm_cie_256in_1024out[] = {
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15,
15, 16, 17, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44,
45, 47, 48, 50, 51, 52, 54, 55, 57, 58, 60, 61, 63, 65, 66, 68,
70, 71, 73, 75, 77, 79, 81, 83, 84, 86, 88, 90, 93, 95, 97, 99,
101, 103, 106, 108, 110, 113, 115, 118, 120, 123, 125, 128, 130, 133, 136, 138,
141, 144, 147, 149, 152, 155, 158, 161, 164, 167, 171, 174, 177, 180, 183, 187,
190, 194, 197, 200, 204, 208, 211, 215, 218, 222, 226, 230, 234, 237, 241, 245,
249, 254, 258, 262, 266, 270, 275, 279, 283, 288, 292, 297, 301, 306, 311, 315,
320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 376, 381, 386, 392, 397,
403, 408, 414, 420, 425, 431, 437, 443, 449, 455, 461, 467, 473, 480, 486, 492,
499, 505, 512, 518, 525, 532, 538, 545, 552, 559, 566, 573, 580, 587, 594, 601,
609, 616, 624, 631, 639, 646, 654, 662, 669, 677, 685, 693, 701, 709, 717, 726,
734, 742, 751, 759, 768, 776, 785, 794, 802, 811, 820, 829, 838, 847, 857, 866,
875, 885, 894, 903, 913, 923, 932, 942, 952, 962, 972, 982, 992, 1002, 1013, 1023,
};
struct LedMatrix led;
uint8_t rgb[3];
static uint8_t led_matrix_updated = 0;
// helper for LED programs
void use_brightest(uint8_t *dest, uint8_t *compare, uint8_t count)
{
while (count--) {
if (*dest < *compare) *dest = *compare;
dest++;
compare++;
}
}
static uint16_t rgb_pwm_gamma(uint8_t in)
{
return pwm_cie_256in_1024out[in];
}
void led_init()
{
TIM_TimeBaseInitTypeDef timer ={0};
TIM_OCInitTypeDef pwm = {0};
// configure matrix
is31fl3729_init(FL3729_ADDR,
FL3729_CONF_SHDN_DIS | FL3729_CONF_OSDE_OFF | FL3729_CONF_MATRIX_4x16,
FL3729_G_CURRENT);
is31fl3729_set_scaling_current_multi(FL3729_ADDR, (uint8_t *)cs_currents, sizeof(cs_currents));
// configure rear RGBLED
timer.TIM_Period = (1 << 10) - 1; // 10-bit
timer.TIM_Prescaler = 0;
timer.TIM_ClockDivision = TIM_CKD_DIV1;
timer.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(RGB_TIM, &timer);
pwm.TIM_OCMode = TIM_OCMode_PWM1;
pwm.TIM_OutputState = TIM_OutputState_Enable;
pwm.TIM_Pulse = 0;
pwm.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(RGB_TIM, &pwm);
TIM_OC2Init(RGB_TIM, &pwm);
TIM_OC3Init(RGB_TIM, &pwm);
TIM_CtrlPWMOutputs(RGB_TIM, ENABLE);
TIM_OC1PreloadConfig(RGB_TIM, TIM_OCPreload_Disable);
TIM_ARRPreloadConfig(RGB_TIM, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
void led_matrix_is_updated()
{
led_matrix_updated = 1;
}
void led_matrix_send()
{
// only render when there's something to render
if (led_matrix_updated) {
is31fl3729_set_outputs(FL3729_ADDR, 1, (uint8_t *)&led, FL3729_SW_COUNT * FL3729_CS_COUNT);
}
}
void led_rgb_update()
{
// this isn't a matrix so we can just update whenever
TIM_CtrlPWMOutputs(RGB_TIM, DISABLE);
RGB_TIM->CH1CVR = rgb_pwm_gamma(rgb[2]);
RGB_TIM->CH2CVR = rgb_pwm_gamma(rgb[1]);
RGB_TIM->CH3CVR = rgb_pwm_gamma(rgb[0]);
TIM_CtrlPWMOutputs(RGB_TIM, ENABLE);
}

51
firmware/user/src/led.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* Created on: Jul 28, 2024
*/
#ifndef USER_SRC_LED_H_
#define USER_SRC_LED_H_
#include <stdint.h>
#include "31fl3729.h"
#define FL3729_ADDR (FL3729_BASE_ADDR | FL3729_ADPIN_GND)
#define LED_PEP_COUNT 36 // all pepper body LEDs
#define LED_PEP_NOTOP 31 // pepper body LEDs not including the part under the hat
#define LED_HAT_COUNT 28 // all pepper hat LEDs
#define LED_ALL_COUNT (LED_PEP_COUNT + LED_HAT_COUNT)
typedef struct LedMatrix {
union {
uint8_t pep[LED_PEP_COUNT];
uint8_t hat[LED_HAT_COUNT];
};
uint8_t all[LED_ALL_COUNT];
} LedMatrix;
extern struct LedMatrix led;
extern uint8_t rgb[3];
void use_brightest(uint8_t *dest, uint8_t *compare, uint8_t count);
void led_init();
void led_matrix_is_updated();
void led_matrix_send();
void led_rgb_update();
#endif /* USER_SRC_LED_H_ */

View File

@@ -0,0 +1,385 @@
/*
* Created on: Jul 29, 2024
*/
#include <stdint.h>
#include "config.h"
#include "led.h"
#include "lis2dw.h"
#include "rand.h"
enum {
PEP_0, PEP_1, PEP_2, PEP_3,
PEP_4, PEP_5, PEP_6, PEP_7
};
static uint16_t pep_rnd;
static uint8_t pep_work[4];
/*
* my little pepper is flashing you
* can be instant (all on/off) or fading in/out
* todo: implement fading mode
* if flash rate is 0, then just stay solid
*
* work global:
* 0: flash timeout
* 1: flash state
* 2: fade timeout
* 3: fade state
*/
#define PEP_0_CONF_BRIGHTNESS 0
#define PEP_0_CONF_FLASH_RATE 1
#define PEP_0_CONF_FADE_ENA 2
#define PEP_0_CONF_FADE_RATE 3
static void pep_0_flash(uint8_t tick)
{
uint8_t i;
uint16_t out = userconf.pep_conf[PEP_0][PEP_0_CONF_BRIGHTNESS] ^ 0xff;
uint8_t flash = userconf.pep_conf[PEP_0][PEP_0_CONF_FLASH_RATE];
if (!flash) {
// just force solid on
pep_work[0] = pep_work[1] = 0;
flash = 1;
}
if (!pep_work[0]) {
pep_work[1] ^= 0x1;
if (pep_work[1] & 1) {
for (i = 0; i < LED_PEP_COUNT; i++) {
led.pep[i] = out;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = out;
}
} else {
for (i = 0; i < LED_PEP_COUNT; i++) {
led.pep[i] = 0;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = 0;
}
}
led_matrix_is_updated();
pep_work[0] = flash;
}
pep_work[0]--;
}
/*
* pepper is lit with random slightly varying intensity
*/
static void pep_1_sizzle(uint8_t tick)
{
uint8_t i;
uint8_t trig;
uint16_t rnd;
uint32_t rnd2;
trig = prng_get8();
rnd = prng_get8();
rnd2 = prng_get32();
// are we going to spike an LED?
trig = (trig > 0xf2) ? 1 : 0;
if (trig) {
// which LED?
rnd *= LED_PEP_NOTOP;
rnd >>= 8;
}
// do some sizzles
for (i = 0; i < LED_PEP_NOTOP; i++) {
if (trig && (rnd == i)) {
// go bright on this LED
led.pep[i] = 200;
} else {
if (led.pep[i] < 48) led.pep[i]++;
else if (led.pep[i] > 64) led.pep[i]--;
else {
if (rnd2 & 1) led.pep[i]++;
else led.pep[i]--;
}
rnd2 >>= 1;
}
}
// the hat just gets lit normally
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = 200;
}
}
/*
* trail chase around the pepper
* pepper is on slightly dim at all times
*
* no user config at this time
*
* work global:
* 0: loop state
*/
static void pep_2_loops(uint8_t tick)
{
uint8_t i;
uint16_t top, bot;
pep_work[0]++;
top = pep_work[0] * LED_PEP_COUNT;
top >>= 8;
bot = pep_work[0] * LED_HAT_COUNT;
bot >>= 8;
for (i = 0; i < LED_PEP_COUNT; i++) {
if (i == bot) led.pep[i] = 255;
else if (led.pep[i]) led.pep[i]--;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
if (i == top) led.hat[i] = 255;
else if (led.hat[i]) led.hat[i]--;
}
}
/*
* pepper is a "sign" that flickers on and off
* sometimes the entire sign goes off, then each
* segment, upper and lower, turns on
* after both segments are on the separator goes off
*/
static void pep_3_neon_sign(uint8_t tick)
{
uint8_t i;
// todo: implement flickering on/off
for (i = 0; i < LED_PEP_COUNT; i++) {
led.pep[i] = (tick & 1) ? 255 : 200;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = (tick & 1) ? 255 : 200;
}
led_matrix_is_updated();
}
/*
* pepper hat and pepper alternate
* can be a nice fade or can be immediate
*/
#define PEP_4_CONF_BRIGHTNESS 0
#define PEP_4_CONF_FLASH_RATE 1
#define PEP_4_CONF_LOW_BRITE 2
static void pep_4_alternate(uint8_t tick)
{
uint8_t i;
uint8_t hi = userconf.pep_conf[PEP_4][PEP_4_CONF_BRIGHTNESS];
uint8_t lo = userconf.pep_conf[PEP_4][PEP_4_CONF_LOW_BRITE];
if (!pep_work[0]) {
for (i = 0; i < LED_PEP_COUNT; i++) {
led.pep[i] = (pep_work[1] & 1) ? hi : lo;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = (pep_work[1] & 1) ? lo : hi;
}
pep_work[0] = userconf.pep_conf[PEP_4][PEP_4_CONF_FLASH_RATE];
} else {
pep_work[0]--;
}
}
/*
* pepper slowly gets eateded
* but it regenerates because it is pepper
*
* work global:
* 0: state index
* 1: eat / regen step
* 2: delay step
*/
static const uint8_t nom_map[5][2] = {
{15, 21},
{13, 23},
{ 6, 24},
{ 5, 27},
{ 2, 29}
};
static void pep_5_nom(uint8_t tick)
{
uint8_t i;
i = nom_map[0][0];
i = (uint8_t)i;
uint8_t start, end;
switch (pep_work[0]) {
case 0:
case 2: { // wait a while
if (!pep_work[2]) {
// just got here; set a new random timeout
i = prng_get8() >> 2;
pep_work[2] = (0xff - 64) + i;
} else {
// wait around for a little while
pep_work[2]--;
if (!pep_work[2]) {
// done here
pep_work[1] = 0;
pep_work[0]++;
}
}
break;
}
case 1: { // eat the pepper
// eat at about one bite per second
if ((tick & 0x7f) != 0) break;
start = 0;
if (!pep_work[1]) {
end = LED_PEP_NOTOP;
} else if (pep_work[1] < 6) {
start = nom_map[pep_work[i] - 1][0];
end = nom_map[pep_work[i] - 1][1];
} else {
end = 0;
}
// set pepper body LEDs to initial on state (or off if pepper is fully eated)
for (i = 0; i < LED_PEP_NOTOP; i++) {
led.pep[i] = end ? 255 : 0;
}
// in this mode we need to light up the top pepper line
for (i = LED_PEP_NOTOP; i < LED_PEP_COUNT; i++) {
led.pep[i] = 200;
}
// clear eated pepper portions, gonna cry
if (end) {
for (i = start; i < end; i++) {
led.pep[i] = 0;
}
}
led_matrix_is_updated();
pep_work[1]++;
if (pep_work[1] > 6) pep_work[0]++;
break;
}
case 3: { // regen the pepper
if (led.pep[pep_work[1]] >= 0xfe) {
// next segment
pep_work[1]++;
if (pep_work[i] >= (LED_PEP_NOTOP - pep_work[i])) {
// we're done regenerating
pep_work[0] = 0;
}
} else {
led.pep[pep_work[1]] += 2;
if (pep_work[1]) {
led.pep[LED_PEP_NOTOP - pep_work[1]] = led.pep[pep_work[1]];
}
led_matrix_is_updated();
}
break;
}
}
}
/*
* make a sharp point at whatever direction is the ground
* note: probably won't be done before con
*/
static void pep_6_accelerometer(uint8_t tick)
{
// not done in time for con
}
/*
* adjust pepper level based on ambient temperature
* ~70F or below: minimum
* ~94F or above: spicy
*/
static void pep_7_heat(uint8_t tick)
{
// not done in time for con
}
/*
const void (*ledprog_pep[8])(uint8_t) = {
(const void (*)(uint8_t))pep_0_flash,
(const void (*)(uint8_t))pep_1_sizzle,
(const void (*)(uint8_t))pep_2_loops,
(const void (*)(uint8_t))pep_3_neon_sign,
(const void (*)(uint8_t))pep_4_alternate,
(const void (*)(uint8_t))pep_5_nom,
(const void (*)(uint8_t))pep_6_accelerometer,
(const void (*)(uint8_t))pep_7_heat
};
*/
void (*ledprog_pep[8])(uint8_t) = {
pep_0_flash,
pep_1_sizzle,
pep_2_loops,
pep_3_neon_sign,
pep_4_alternate,
pep_5_nom,
pep_6_accelerometer,
pep_7_heat
};
void ledprog_pep_init()
{
uint8_t i;
pep_rnd = prng_get16();
// reset LEDs
for (i = 0; i < LED_PEP_COUNT; i++) {
led.pep[i] = 0;
}
for (i = 0; i < LED_HAT_COUNT; i++) {
led.hat[i] = 0;
}
// global program initialization
for (i = 0; i < 4; i++) {
pep_work[i] = 0;
}
// per-program initialization
// there is none on this badge
}

View File

@@ -0,0 +1,21 @@
/*
* ledprog_pep.h
*
* Created on: Jul 29, 2024
* Author: true
*/
#ifndef USER_SRC_LEDPROG_PEP_H_
#define USER_SRC_LEDPROG_PEP_H_
extern void (*ledprog_pep[8])(uint8_t);
void ledprog_pep_init();
#endif /* USER_SRC_LEDPROG_PEP_H_ */

View File

@@ -0,0 +1,63 @@
/*
* Created on: Jul 31, 2024
*/
#include <stdint.h>
#include "hsv2rgb.h"
#include "led.h"
#include "rand.h"
static uint16_t rgb_rnd;
static uint16_t rgb_work[4];
/*
* rainbow puke
*/
static void rgb_0_rainbow(uint8_t tick)
{
}
/*
* static color with bright flickers
*/
static void rgb_1_flicker(uint8_t tick)
{
}
/*
* alternate between two colors
*/
static void rgb_2_alternate(uint8_t tick)
{
}
const void (*ledprog_rgb[4])(uint8_t) = {
(const void (*)(uint8_t))rgb_0_rainbow,
(const void (*)(uint8_t))rgb_1_flicker,
(const void (*)(uint8_t))rgb_2_alternate
};
void ledprog_rgb_init()
{
uint8_t i;
rgb_rnd = prng_get16();
// global program initialization
for (i = 0; i < 4; i++) {
rgb_work[i] = 0;
}
}

View File

@@ -0,0 +1,25 @@
/*
* ledprog_rgb.h
*
* Created on: Jul 29, 2024
* Author: true
*/
#ifndef USER_SRC_LEDPROG_RGB_H_
#define USER_SRC_LEDPROG_RGB_H_
#define RGB_TIM TIM3
extern void (*ledprog_rgb[8])(uint8_t, uint8_t);
void ledprog_rgb_init();
#endif /* USER_SRC_LEDPROG_RGB_H_ */

View File

@@ -0,0 +1,30 @@
/*
* Created on: Jul 28, 2024
*/
#include <stdint.h>
#include "i2c.h"
#include "lis2dw.h"
void lis2dw_init()
{
}
void lis2dw_set_operating_mode(uint8_t mode)
{
}
uint8_t lis2dw_get_temp_8b()
{
return i2c_read_reg_8b(LIS2DW_ADDR, LIS2DW_REG_OUT_T);
}
void lis2dw_get_axes(int16_t *x, int16_t *y, int16_t *z)
{
}

276
firmware/user/src/lis2dw.h Normal file
View File

@@ -0,0 +1,276 @@
/*
* lis2dw.h
*
* Created on: Jul 28, 2024
* Author: true
*/
#ifndef USER_SRC_LIS2DW_H_
#define USER_SRC_LIS2DW_H_
#define LIS2DW_ADDR 0x30
#define LIS2DW_ADDR_SDO_LOW 0x31
// registers
#define LIS2DW_REG_OUT_T_L 0x0d
#define LIS2DW_REG_OUT_T_H 0x0e
#define LIS2DW_REG_WHO_AM_I 0x0f
#define LIS2DW_REG_CTRL1 0x20
#define LIS2DW_REG_CTRL2 0x21
#define LIS2DW_REG_CTRL3 0x22
#define LIS2DW_REG_CTRL4 0x23
#define LIS2DW_REG_INT1_PAD 0x23 // default: 0x00
#define LIS2DW_REG_CTRL5 0x24
#define LIS2DW_REG_INT2_PAD 0x24 // default: 0x00
#define LIS2DW_REG_CTRL6 0x25
#define LIS2DW_REG_OUT_T 0x26
#define LIS2DW_REG_STATUS 0x27
#define LIS2DW_REG_OUT_X_L 0x28
#define LIS2DW_REG_OUT_X_H 0x29
#define LIS2DW_REG_OUT_Y_L 0x2a
#define LIS2DW_REG_OUT_Y_H 0x2b
#define LIS2DW_REG_OUT_Z_L 0x2c
#define LIS2DW_REG_OUT_Z_H 0x2d
#define LIS2DW_REG_FIFO_CTRL 0x2e
#define LIS2DW_REG_FIFO_SAMPLES 0x2f
#define LIS2DW_REG_TAP_THS_X 0x30
#define LIS2DW_REG_TAP_THS_Y 0x31
#define LIS2DW_REG_TAP_THS_Z 0x32
#define LIS2DW_REG_INT_DUR 0x33
#define LIS2DW_REG_WAKE_UP_THS 0x34
#define LIS2DW_REG_WAKE_UP_DUR 0x35
#define LIS2DW_REG_FREE_FALL 0x36
#define LIS2DW_REG_STATUS_DUP 0x37
#define LIS2DW_REG_WAKE_UP_SRC 0x38
#define LIS2DW_REG_TAP_SRC 0x39
#define LIS2DW_REG_SIXD_SRC 0x3a
#define LIS2DW_REG_ALL_INT_SRC 0x3b // read this register to clear all INT pins
#define LIS2DW_REG_X_OFS_USR 0x3c
#define LIS2DW_REG_Y_OFS_USR 0x3d
#define LIS2DW_REG_Z_OFS_USR 0x3e
#define LIS2DW_REG_CTRL_REG7 0x3f
#define LIS2DW_REG_CTRL7 0x3f
// register properties
#define LIS2DW_WHO_AM_I 0x44
#define LIS2DW_CTRL1_LP_MODE1 (0 << 0)
#define LIS2DW_CTRL1_LP_MODE2 (1 << 0)
#define LIS2DW_CTRL1_LP_MODE3 (2 << 0)
#define LIS2DW_CTRL1_LP_MODE4 (3 << 0)
#define LIS2DW_CTRL1_MODE_LO_POWR (0 << 2)
#define LIS2DW_CTRL1_MODE_HI_PERF (1 << 2)
#define LIS2DW_CTRL1_MODE_SNGL_DATA (2 << 2)
#define LIS2DW_CTRL1_DATA_RATE_PWR_DOWN (0 << 4)
#define LIS2DW_CTRL1_DATA_RATE_1_6 (1 << 4) // 12.5 in high power mode
#define LIS2DW_CTRL1_DATA_RATE_12_5 (2 << 4)
#define LIS2DW_CTRL1_DATA_RATE_25 (3 << 4)
#define LIS2DW_CTRL1_DATA_RATE_50 (4 << 4)
#define LIS2DW_CTRL1_DATA_RATE_100 (5 << 4)
#define LIS2DW_CTRL1_DATA_RATE_200 (6 << 4)
#define LIS2DW_CTRL1_DATA_RATE_400 (7 << 4) // 200 in low power mode
#define LIS2DW_CTRL1_DATA_RATE_800 (8 << 4) // 200 in low power mode
#define LIS2DW_CTRL1_DATA_RATE_1600 (9 << 4) // 200 in low power mode
#define LIS2DW_CTRL2_SPI_4_WIRE (0 << 0)
#define LIS2DW_CTRL2_SPI_3_WIRE (1 << 0)
#define LIS2DW_CTRL2_I2C_ENABLE (0 << 1)
#define LIS2DW_CTRL2_I2C_DISABLE (1 << 1)
#define LIS2DW_CTRL2_IF_ADDR_INC (1 << 2) // automatically increment register; roll back from 0x2d->0x28
#define LIS2DW_CTRL2_BLOCK_DATA_UPDATE (1 << 3) // only update data after both MSB+LSB read
#define LIS2DW_CTRL2_CS_PU_DISC (1 << 4)
#define LIS2DW_CTRL2_SOFT_RESET (1 << 6) // resets all registers
#define LIS2DW_CTRL2_BOOT (1 << 7) // re-loads calibration constants
#define LIS2DW_CTRL3_SLP_MODE_1 (1 << 0) // single data conversion on demand trigger
#define LIS2DW_CTRL3_SLP_MODE_SEL (1 << 1) // single data conversion on demand (0=INT2, 1=SLP_MODE_1)
#define LIS2DW_CTRL3_H_L_ACTIVE (1 << 3) // interrupt level. 0=active high, 1=active low
#define LIS2DW_CTRL3_LATCHED_IR (1 << 4) // signals latched. 0=not latched (pulse), 1=latched
#define LIS2DW_CTRL3_PP_OD (1 << 5) // push pull or open drain on interrupts. 0=pushpull, 1=opendrain
#define LIS2DW_CTRL3_SELFTEST_NONE (0 << 6)
#define LIS2DW_CTRL3_SELFTEST_POS (1 << 6)
#define LIS2DW_CTRL3_SELFTEST_NEG (2 << 6)
#define LIS2DW_INT1_DRDY (1 << 0) // data ready
#define LIS2DW_INT1_FTH (1 << 1) // fifo threshold
#define LIS2DW_INT1_DIFF5 (1 << 2) // fifo full
#define LIS2DW_INT1_TAP (1 << 3) // double tap
#define LIS2DW_INT1_FF (1 << 4) // free fall
#define LIS2DW_INT1_WU (1 << 5) // wakeup
#define LIS2DW_INT1_SINGLE_TAP (1 << 6) // single tap
#define LIS2DW_INT1_6D (1 << 7) // 6D
#define LIS2DW_INT2_DRDY (1 << 0) // data ready
#define LIS2DW_INT2_FTH (1 << 1) // fifo threshold
#define LIS2DW_INT2_DIFF5 (1 << 2) // fifo full
#define LIS2DW_INT1_OVR (1 << 3) // fifo overrun
#define LIS2DW_INT1_DRDY_TEMP (1 << 4) // temperature data ready
#define LIS2DW_INT1_BOOT (1 << 5) // boot
#define LIS2DW_INT1_SLEEP_CHG (1 << 6) // sleep change status
#define LIS2DW_INT1_SLEEP_STATE (1 << 7) // sleep state
#define LIS2DW_CTRL6_LOW_NOISE (1 << 2)
#define LIS2DW_CTRL6_FILTER_DATA_SELECT (1 << 3) // 0=low pass, 1=high pass
#define LIS2DW_CTRL6_FULLSCALE_2G (0 << 4)
#define LIS2DW_CTRL6_FULLSCALE_4G (1 << 4)
#define LIS2DW_CTRL6_FULLSCALE_8G (2 << 4)
#define LIS2DW_CTRL6_FULLSCALE_16G (3 << 4)
#define LIS2DW_CTRL6_BW_FILT_ODR_2 (0 << 6)
#define LIS2DW_CTRL6_BW_FILT_ODR_4 (1 << 6)
#define LIS2DW_CTRL6_BW_FILT_ODR_10 (2 << 6)
#define LIS2DW_CTRL6_BW_FILT_ODR_20 (3 << 6)
#define LIS2DW_STATUS_DRDY (1 << 0) // 0=nrdy, 1=x/y/z new data
#define LIS2DW_STATUS_FF_IA (1 << 1) // free fall event
#define LIS2DW_STATUS_6D_IA (1 << 2) // 6D change in pos detected
#define LIS2DW_STATUS_SINGLE_TAP (1 << 3)
#define LIS2DW_STATUS_DOUBLE_TAP (1 << 4)
#define LIS2DW_STATUS_SLEEP_STATE (1 << 5) // sleep event detected
#define LIS2DW_STATUS_WU_IA (1 << 6) // wakeup event detected
#define LIS2DW_STATUS_FIFO_THS (1 << 7) // fifo thresh; 1=equal or higher than threshold
#define LIS2DW_FIFO_THRESHOLD_MASK 0x1f
#define LIS2DW_FIFO_MODE_BYPASS (0 << 4)
#define LIS2DW_FIFO_MODE_FIFO (1 << 4)
#define LIS2DW_FIFO_MODE_CONT_TO_FIFO (3 << 4)
#define LIS2DW_FIFO_MODE_BYPASS_TO_CONT (4 << 4)
#define LIS2DW_FIFO_MODE_CONT (6 << 4)
#define LIS2DW_FIFO_SAMPLES_COUNT_MASK 0x3f
#define LIS2DW_FIFO_SAMPLES_OVERRUN (1 << 6) // 0=not filled, 1=filled and sample overwritten
#define LIS2DW_FIFO_SAMPLES_THRESHOLD (1 << 7) // 0=lower, 1=equal or higher
#define LIS2DW_TAP_THSX_THRESHOLD_MASK 0x1f
#define LIS2DW_TAP_6D_THRESHOLD_80DEG (0 << 5)
#define LIS2DW_TAP_6D_THRESHOLD_70DEG (1 << 5)
#define LIS2DW_TAP_6D_THRESHOLD_60DEG (2 << 5)
#define LIS2DW_TAP_6D_THRESHOLD_50DEG (3 << 5)
#define LIS2DW_TAP_4D_EN (1 << 7)
#define LIS2DW_TAP_THSY_THRESHOLD_MASK 0x1f
#define LIS2DW_TAP_PRIO_XYZ (0 << 5)
#define LIS2DW_TAP_PRIO_YXZ (1 << 5)
#define LIS2DW_TAP_PRIO_XZY (2 << 5)
#define LIS2DW_TAP_PRIO_ZYX (3 << 5)
#define LIS2DW_TAP_PRIO_YZX (5 << 5)
#define LIS2DW_TAP_PRIO_ZXY (6 << 5)
#define LIS2DW_TAP_THSZ_THRESHOLD_MASK 0x1f
#define LIS2DW_TAP_Z_EN (1 << 5)
#define LIS2DW_TAP_Y_EN (1 << 6)
#define LIS2DW_TAP_X_EN (1 << 7)
#define LIS2DW_INT_DUR_SHOCK_4xODR (0 << 0) // tap event min signal threshold
#define LIS2DW_INT_DUR_SHOCK_8xODR (1 << 0) // data sheet isn't clear on durations
#define LIS2DW_INT_DUR_SHOCK_12xODR (2 << 0)
#define LIS2DW_INT_DUR_SHOCK_16xODR (3 << 0)
#define LIS2DW_INT_DUR_QUIET_4xODR (0 << 2) // post tap event quiet time threshold
#define LIS2DW_INT_DUR_QUIET_8xODR (1 << 2)
#define LIS2DW_INT_DUR_QUIET_12xODR (2 << 2)
#define LIS2DW_INT_DUR_QUIET_16xODR (3 << 2)
#define LIS2DW_INT_DUR_LATENCY_16xODR (0 << 4) // double tap gap maximum time between events
#define LIS2DW_INT_DUR_LATENCY_32xODR (1 << 4)
#define LIS2DW_INT_DUR_LATENCY_48xODR (2 << 4)
#define LIS2DW_INT_DUR_LATENCY_64xODR (3 << 4)
#define LIS2DW_INT_DUR_LATENCY_80xODR (4 << 4)
#define LIS2DW_INT_DUR_LATENCY_96xODR (5 << 4)
#define LIS2DW_INT_DUR_LATENCY_112xODR (6 << 4)
#define LIS2DW_INT_DUR_LATENCY_128xODR (7 << 4)
#define LIS2DW_WAKE_UP_THRESHOLD_MASK 0x3f // 1 LSB = 1/64 of FS
#define LIS2DW_WAKE_UP_SLEEP_ON (1 << 6) // sleep enable / disable
#define LIS2DW_WAKE_UP_SNGL_DUBL_TAP (1 << 7) // enable single/double tap event, 0=single only, 1=both
#define LIS2DW_WAKE_UP_SLEEP_DUR_MASK 0x0f // duration in sleep. 0=16*1/ODR, LSB=512*1/ODR
#define LIS2DW_WAKE_UP_DUR_STATIONARY (1 << 4) // stationary detection ena/dis
#define LIS2DW_WAKE_UP_DURATION_0 (0 << 5)
#define LIS2DW_WAKE_UP_DURATION_1 (1 << 5)
#define LIS2DW_WAKE_UP_DURATION_2 (2 << 5)
#define LIS2DW_WAKE_UP_DURATION_3 (3 << 5)
#define LIS2DW_WAKE_UP_FF_DUR5 (1 << 7) // free fall duration, highest bit. see below.
#define LIS2DW_FREE_FALL_THRESHOLD_5 (0 << 0) // freefall threshold @ +-2G FS in LSB counts
#define LIS2DW_FREE_FALL_THRESHOLD_7 (1 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_8 (2 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_10 (3 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_11 (4 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_13 (5 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_15 (6 << 0)
#define LIS2DW_FREE_FALL_THRESHOLD_16 (7 << 0)
#define LIS2DW_FREE_FALL_DUR14_MASK 0xf8 // free fall duration, lowest bits. 1 LSB = 1 * 1/ODR
#define LIS2DW_STATUS_DUP_DRDY (1 << 0) // 0=nrdy, 1=x/y/z new data
#define LIS2DW_STATUS_DUP_FF_IA (1 << 1) // free fall event
#define LIS2DW_STATUS_DUP_6D_IA (1 << 2) // 6D change in pos detected
#define LIS2DW_STATUS_DUP_SINGLE_TAP (1 << 3)
#define LIS2DW_STATUS_DUP_DOUBLE_TAP (1 << 4)
#define LIS2DW_STATUS_DUP_SLEEP_STATE (1 << 5) // sleep event detected
#define LIS2DW_STATUS_DUP_DRDY_TEMP (1 << 6) // new temperature data available
#define LIS2DW_STATUS_FIFO_OVERRUN (1 << 7) // fifo overrun. 1=filled completely and data overwritten
#define LIS2DW_WAKE_UP_SRC_Z (1 << 0)
#define LIS2DW_WAKE_UP_SRC_Y (1 << 1)
#define LIS2DW_WAKE_UP_SRC_X (1 << 2)
#define LIS2DW_WAKE_UP_SRC_WU (1 << 3)
#define LIS2DW_WAKE_UP_SRC_SLEEP_STATE (1 << 4)
#define LIS2DW_WAKE_UP_SRC_FF (1 << 5)
#define LIS2DW_TAP_SRC_Z_TAP (1 << 0)
#define LIS2DW_TAP_SRC_Y_TAP (1 << 1)
#define LIS2DW_TAP_SRC_X_TAP (1 << 2)
#define LIS2DW_TAP_SRC_TAP_SIGN (1 << 3) // sign of detected tap acceleration
#define LIS2DW_TAP_SRC_DOUBLE_TAP (1 << 4) // double tap detected
#define LIS2DW_TAP_SRC_SINGLE_TAP (1 << 5) // single tap detected
#define LIS2DW_TAP_SRC_TAP_ANY (1 << 6) // tap of any kind detected
#define LIS2DW_6D_SRC_XL (1 << 0) // XL over threshold
#define LIS2DW_6D_SRC_XH (1 << 1) // XH over threshold
#define LIS2DW_6D_SRC_YL (1 << 2) // YL over threshold
#define LIS2DW_6D_SRC_YH (1 << 3) // YH over threshold
#define LIS2DW_6D_SRC_ZL (1 << 4) // ZL over threshold
#define LIS2DW_6D_SRC_ZH (1 << 5) // ZH over threshold
#define LIS2DW_6D_6D_POS_CHANGED (1 << 6) // position changed (portrait/landscape/faceup/facedown)
#define LIS2DW_ALL_INT_SRC_FF (1 << 0) // free fall event detected
#define LIS2DW_ALL_INT_SRC_WU (1 << 0) // wakeup event detected
#define LIS2DW_ALL_INT_SRC_SINGLE_TAP (1 << 0) // single tap status
#define LIS2DW_ALL_INT_SRC_DOUBLE_TAP (1 << 0) // double tap status
#define LIS2DW_ALL_INT_SRC_6D (1 << 0) // change in position
#define LIS2DW_ALL_INT_SRC_SLEEP_CHANGE (1 << 0) // sleep change detected
#define LIS2DW_CTRL7_LPASS_ON6D (1 << 0) // 0=ODR/2 low pass data sent to 6D interrupt, 1=LPF2 sent to 6D interrupt
#define LIS2DW_CTRL7_HP_REF_MODE (1 << 1) // 1=high pass filter reference mode eanbled
#define LIS2DW_CTRL7_USR_OFF_W (1 << 2) // 0=977ug/LSB, 1=15.6mg/LSB. weight of user offset words
#define LIS2DW_CTRL7_USR_OFF_ON_WU (1 << 3) // user offset value on XL data for wakeup only
#define LIS2DW_CTRL7_USR_OFF_ON_OUT (1 << 4) // user offset value on XL output data registers (CTRL6_FDS must be set to 0)
#define LIS2DW_CTRL7_INTERRUPTS_ENABLE (1 << 5)
#define LIS2DW_CTRL7_INT2_ON_INT1 (1 << 6) // 1=INT2 signals are routed to INT1
#define LIS2DW_CTRL7_DRDY_PUSLED (1 << 7) // 0=latched mode, 1=pulsed mode for DRDY
// API
enum {
LIS2DW_OP_MODE_HI_PERF = 0x00, // 14-bit, 90<39><30>A@200Hz, 12.5-1600Hz, 110<31><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_4 = 0x01, // 14-bit, 63<36><33>A@200Hz, 1.6 - 200Hz, 160<36><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_3 = 0x02, // 14-bit, 34<33><34>A@200Hz, 1.6 - 200Hz, 210<31><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_2 = 0x03, // 14-bit, 20<32><30>A@200Hz, 1.6 - 200Hz, 300<30><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_1 = 0x04, // 12-bit, 10<31><30>A@200Hz, 1.6 - 200Hz, 550<35><30>g/<2F><>Hz
LIS2DW_OP_MODE_HI_PERF_LN = 0x10, // 14-bit, 120<32><30>A@200, 12.5-1600Hz, 110<31><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_4_LN = 0x11, // 14-bit, 77<37><37>A@200Hz, 1.6 - 200Hz, 160<36><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_3_LN = 0x12, // 14-bit, 42<34><32>A@200Hz, 1.6 - 200Hz, 210<31><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_2_LN = 0x13, // 14-bit, 25<32><35>A@200Hz, 1.6 - 200Hz, 300<30><30>g/<2F><>Hz
LIS2DW_OP_MODE_LO_PWR_1_LN = 0x14, // 12-bit, 12<31><32>A@200Hz, 1.6 - 200Hz, 550<35><30>g/<2F><>Hz
};
void lis2dw_init();
#endif /* USER_SRC_LIS2DW_H_ */

162
firmware/user/src/rand.c Normal file
View File

@@ -0,0 +1,162 @@
/**
* Tiny Mersenne Twister: only 127-bit internal state.
* Derived from the reference implementation version 1.1 (2015/04/24)
* by Mutsuo Saito (Hiroshima University) and Makoto Matsumoto
* (Hiroshima University).
*/
#include <stdint.h>
#include "rand.h"
static void tinymt32_next_state(tinymt32_t *s);
static uint32_t tinymt32_temper(tinymt32_t *s);
tinymt32_t tinymt32_s;
/**
* Parameter set to use for this IETF specification. Don't change.
* This parameter set is the first entry of the precalculated
* parameter sets in tinymt32dc/tinymt32dc.0.1048576.txt by
* Kenji Rikitake, available at:
* https://github.com/jj1bdx/tinymtdc-longbatch/.
* It is also the parameter set used in:
* Rikitake, K., "TinyMT pseudo random number generator for
* Erlang", Proceedings of the 11th ACM SIGPLAN Erlang Workshop,
* September 2012.
*/
const uint32_t TINYMT32_MAT1_PARAM = UINT32_C(0x8f7011ee);
const uint32_t TINYMT32_MAT2_PARAM = UINT32_C(0xfc78ff1f);
const uint32_t TINYMT32_TMAT_PARAM = UINT32_C(0x3793fdff);
/**
* This function initializes the internal state array with a
* 32-bit unsigned integer seed.
* @param s pointer to tinymt internal state.
* @param seed a 32-bit unsigned integer used as a seed.
*/
void tinymt32_init (tinymt32_t* s, uint32_t seed)
{
const uint32_t MIN_LOOP = 8;
const uint32_t PRE_LOOP = 8;
s->status[0] = seed;
s->status[1] = s->mat1 = TINYMT32_MAT1_PARAM;
s->status[2] = s->mat2 = TINYMT32_MAT2_PARAM;
s->status[3] = s->tmat = TINYMT32_TMAT_PARAM;
for (int i = 1; i < MIN_LOOP; i++) {
s->status[i & 3] ^= i + UINT32_C(1812433253)
* (s->status[(i - 1) & 3]
^ (s->status[(i - 1) & 3] >> 30));
}
/*
* NB: The parameter set of this specification warrants
* that none of the possible 2^^32 seeds leads to an
* all-zero 127-bit internal state. Therefore, the
* period_certification() function of the original
* TinyMT32 source code has been safely removed. If
* another parameter set is used, this function will
* have to be reintroduced here.
*/
for (int i = 0; i < PRE_LOOP; i++) {
tinymt32_next_state(s);
}
}
/**
* This function outputs a 32-bit unsigned integer from
* the internal state.
* @param s pointer to tinymt internal state.
* @return 32-bit unsigned integer r (0 <= r < 2^32).
*/
uint32_t tinymt32_get_uint32(tinymt32_t* s)
{
tinymt32_next_state(s);
return tinymt32_temper(s);
}
/**
* Internal tinymt32 constants and functions.
* Users should not call these functions directly.
*/
const uint32_t TINYMT32_SH0 = 1;
const uint32_t TINYMT32_SH1 = 10;
const uint32_t TINYMT32_SH8 = 8;
const uint32_t TINYMT32_MASK = UINT32_C(0x7fffffff);
/**
* This function changes the internal state of tinymt32.
* @param s pointer to tinymt internal state.
*/
static void tinymt32_next_state (tinymt32_t* s)
{
uint32_t x;
uint32_t y;
y = s->status[3];
x = (s->status[0] & TINYMT32_MASK)
^ s->status[1]
^ s->status[2];
x ^= (x << TINYMT32_SH0);
y ^= (y >> TINYMT32_SH0) ^ x;
s->status[0] = s->status[1];
s->status[1] = s->status[2];
s->status[2] = x ^ (y << TINYMT32_SH1);
s->status[3] = y;
/*
* The if (y & 1) {...} block below replaces:
* s->status[1] ^= -((int32_t)(y & 1)) & s->mat1;
* s->status[2] ^= -((int32_t)(y & 1)) & s->mat2;
* The adopted code is equivalent to the original code
* but does not depend on the representation of negative
* integers by 2's complements. It is therefore more
* portable but includes an if branch, which may slow
* down the generation speed.
*/
if (y & 1) {
s->status[1] ^= s->mat1;
s->status[2] ^= s->mat2;
}
}
/**
* This function outputs a 32-bit unsigned integer from
* the internal state.
* @param s pointer to tinymt internal state.
* @return 32-bit unsigned pseudorandom number.
*/
static uint32_t tinymt32_temper (tinymt32_t* s)
{
uint32_t t0, t1;
t0 = s->status[3];
t1 = s->status[0] + (s->status[2] >> TINYMT32_SH8);
t0 ^= t1;
/*
* The if (t1 & 1) {...} block below replaces:
* t0 ^= -((int32_t)(t1 & 1)) & s->tmat;
* The adopted code is equivalent to the original code
* but does not depend on the representation of negative
* integers by 2's complements. It is therefore more
* portable but includes an if branch, which may slow
* down the generation speed.
*/
if (t1 & 1) {
t0 ^= s->tmat;
}
return t0;
}
uint16_t prng_scale16(uint16_t min, uint16_t max)
{
uint16_t rnd;
rnd = prng_get16();
rnd *= (max - min);
rnd >>= 16;
rnd += min;
return rnd;
}

39
firmware/user/src/rand.h Normal file
View File

@@ -0,0 +1,39 @@
/**
* Tiny Mersenne Twister
*/
#ifndef CODE_INC_RAND_H_
#define CODE_INC_RAND_H_
/**
* tinymt32 internal state vector and parameters
*/
typedef struct {
uint32_t status[4];
uint32_t mat1;
uint32_t mat2;
uint32_t tmat;
} tinymt32_t;
extern tinymt32_t tinymt32_s;
void tinymt32_init(tinymt32_t *s, uint32_t seed);
uint32_t tinymt32_get_uint32(tinymt32_t* s);
#define prng_get8() (tinymt32_get_uint32(&tinymt32_s) & 0xff)
#define prng_get16() (tinymt32_get_uint32(&tinymt32_s) & 0xffff)
#define prng_get32() tinymt32_get_uint32(&tinymt32_s)
uint16_t prng_scale16(uint16_t min, uint16_t max);
#endif /* CODE_INC_RAND_H */

88
firmware/user/src/touch.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* Created on: Jul 29, 2024
*
* touch my pepper
*/
#include <stdint.h>
#include <ch32v20x.h>
#include "touch.h"
const uint16_t tbtn_adc_ch[2] = {TBTN2, TBTN1};
uint16_t tbtn_idle_cal[2];
uint16_t touch_read_adc(u8 adc_ch)
{
ADC_RegularChannelConfig(TBTN_ADC, adc_ch, 1, ADC_SampleTime_7Cycles5);
TBTN_ADC->IDATAR1 = 0x10; // charging time
TBTN_ADC->RDATAR = 0x08; // discharging time
while(!ADC_GetFlagStatus(TBTN_ADC, ADC_FLAG_EOC));
return (uint16_t)TBTN_ADC->RDATAR;
}
uint16_t touch_read(u8 btn_ch)
{
if (btn_ch < 2) return touch_read_adc(tbtn_adc_ch[btn_ch]);
else return 0xffff;
}
uint8_t touch_read_pushed(uint8_t btn_ch)
{
if (touch_read_adc(tbtn_adc_ch[btn_ch]) < tbtn_idle_cal[btn_ch] - TBTN_PUSHED_COUNTS)
return 1;
return 0;
}
void touch_cal()
{
uint8_t i, j;
uint16_t val;
for (i = 0; i < 2; i++) {
val = 0xffff;
while (val > TBTN_IDLE_WINDOW_HI) {
// be warned,
// we can hang here until the buttons can be calibrated
val = touch_read(tbtn_adc_ch[i]);
}
// get average of 8 readings
val = 0;
for (j = 0; j < 8; j++) {
val += touch_read(tbtn_adc_ch[i]);
}
val >>= 3;
tbtn_idle_cal[i] = val;
}
}
void touch_init()
{
ADC_InitTypeDef adc = {0};
// make sure ADC peripheral is clocked and
// pins are configured as analog inputs before calling this function.
// configure ADC for touchkey use
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(TBTN_ADC, &adc);
// enable ADC, then enable touchkey
ADC_Cmd(TBTN_ADC, ENABLE);
TBTN_ADC->CTLR1 |= ADC_CTLR1_BUFEN | ADC_CTLR1_TKENABLE;
// calibrate idle touch
touch_cal();
}

37
firmware/user/src/touch.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* touch.h
*
* Created on: Jul 29, 2024
* Author: true
*/
#ifndef USER_SRC_TOUCH_H_
#define USER_SRC_TOUCH_H_
#define TBTN_ADC ADC2
#define TBTN1 ADC_Channel_4 // right button
#define TBTN2 ADC_Channel_9 // left button
#define TBTN_IDLE_WINDOW_LO 300
#define TBTN_IDLE_WINDOW_HI 500
#define TBTN_PUSHED_COUNTS 100
#define ADC_CTLR1_TKENABLE (1 << 24)
#define ADC_CTLR1_BUFEN (1 << 26)
void touch_init();
uint16_t touch_read_adc(u8 adc_ch);
uint16_t touch_read(u8 btn_ch);
uint8_t touch_read_pushed(uint8_t btn_ch);
#endif /* USER_SRC_TOUCH_H_ */

67
firmware/user/src/ui.c Normal file
View File

@@ -0,0 +1,67 @@
/*
* Created on: Jul 28, 2024
*/
#include <stdint.h>
#include "btn.h"
#include "led.h"
#include "ledprog_pep.h"
#include "ledprog_rgb.h"
#define MODE_RUN 0
#define MODE_PROGRAM 1
#define MODE_PARAMETER 2
#define UI_CONF_SAVE_TIMEOUT 160
#define UI_PROG_RUNTIME_MIN (128*15) // 15 seconds
#define UI_PROG_RUNTIME_MAX (128*120) // 120 seconds
static uint8_t mode = MODE_RUN;
static uint8_t tick = 0;
void ui_btn_push_cb(uint8_t idx)
{
}
void ui_btn_hold_cb(uint8_t idx)
{
}
void ui_btn_release_cb(uint8_t idx)
{
}
void ui_init()
{
btn[0].hold = 330 >> 1;
btn[0].repeat = 0; // (1000 / 20) >> 1;
btn[0].cb_push = ui_btn_push_cb;
btn[0].cb_hold = ui_btn_hold_cb;
btn[0].cb_release = ui_btn_release_cb;
btn[1].hold = 330 >> 1;
btn[1].repeat = 0;
btn[1].cb_push = ui_btn_push_cb;
btn[1].cb_hold = ui_btn_hold_cb;
btn[1].cb_release = ui_btn_release_cb;
}
void ui_render()
{
tick++;
ledprog_pep[0](tick);
}

15
firmware/user/src/ui.h Normal file
View File

@@ -0,0 +1,15 @@
/*
* Created on: Jul 28, 2024
*/
#ifndef USER_SRC_UI_H_
#define USER_SRC_UI_H_
void ui_init();
void ui_render();
#endif /* USER_SRC_UI_H_ */

View File

@@ -0,0 +1,987 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32v20x.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : CH32V20x Device Peripheral Access Layer System Source File.
* For HSE = 32Mhz (CH32V208x/CH32V203RBT6)
* For HSE = 8Mhz (other CH32V203x)
*********************************************************************************
* 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 "ch32v20x.h"
/*
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
* reset the HSI is used as SYSCLK source).
* If none of the define below is enabled, the HSI is used as System clock source.
*/
//#define SYSCLK_FREQ_HSE HSE_VALUE
//#define SYSCLK_FREQ_48MHz_HSE 48000000
//#define SYSCLK_FREQ_56MHz_HSE 56000000
//#define SYSCLK_FREQ_72MHz_HSE 72000000
//#define SYSCLK_FREQ_96MHz_HSE 96000000
//#define SYSCLK_FREQ_120MHz_HSE 120000000
//#define SYSCLK_FREQ_144MHz_HSE 144000000
//#define SYSCLK_FREQ_HSI HSI_VALUE
#define SYSCLK_FREQ_48MHz_HSI 48000000
//#define SYSCLK_FREQ_56MHz_HSI 56000000
//#define SYSCLK_FREQ_72MHz_HSI 72000000
//#define SYSCLK_FREQ_96MHz_HSI 96000000
//#define SYSCLK_FREQ_120MHz_HSI 120000000
//#define SYSCLK_FREQ_144MHz_HSI 144000000
/* Clock Definitions */
#ifdef SYSCLK_FREQ_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_96MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_120MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_144MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_96MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_120MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_144MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSI; /* System Clock Frequency (Core Clock) */
#else
uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */
#endif
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
/* system_private_function_proto_types */
static void SetSysClock(void);
#ifdef SYSCLK_FREQ_HSE
static void SetSysClockToHSE( void );
#elif defined SYSCLK_FREQ_48MHz_HSE
static void SetSysClockTo48_HSE( void );
#elif defined SYSCLK_FREQ_56MHz_HSE
static void SetSysClockTo56_HSE( void );
#elif defined SYSCLK_FREQ_72MHz_HSE
static void SetSysClockTo72_HSE( void );
#elif defined SYSCLK_FREQ_96MHz_HSE
static void SetSysClockTo96_HSE( void );
#elif defined SYSCLK_FREQ_120MHz_HSE
static void SetSysClockTo120_HSE( void );
#elif defined SYSCLK_FREQ_144MHz_HSE
static void SetSysClockTo144_HSE( void );
#elif defined SYSCLK_FREQ_48MHz_HSI
static void SetSysClockTo48_HSI( void );
#elif defined SYSCLK_FREQ_56MHz_HSI
static void SetSysClockTo56_HSI( void );
#elif defined SYSCLK_FREQ_72MHz_HSI
static void SetSysClockTo72_HSI( void );
#elif defined SYSCLK_FREQ_96MHz_HSI
static void SetSysClockTo96_HSI( void );
#elif defined SYSCLK_FREQ_120MHz_HSI
static void SetSysClockTo120_HSI( void );
#elif defined SYSCLK_FREQ_144MHz_HSI
static void SetSysClockTo144_HSI( void );
#endif
/*********************************************************************
* @fn SystemInit
*
* @brief Setup the microcontroller system Initialize the Embedded Flash Interface,
* the PLL and update the SystemCoreClock variable.
*
* @return none
*/
void SystemInit (void)
{
RCC->CTLR |= (uint32_t)0x00000001;
RCC->CFGR0 &= (uint32_t)0xF0FF0000;
RCC->CTLR &= (uint32_t)0xFEF6FFFF;
RCC->CTLR &= (uint32_t)0xFFFBFFFF;
RCC->CFGR0 &= (uint32_t)0xFF00FFFF;
RCC->INTR = 0x009F0000;
SetSysClock();
}
/*********************************************************************
* @fn SystemCoreClockUpdate
*
* @brief Update SystemCoreClock variable according to Clock Register Values.
*
* @return none
*/
void SystemCoreClockUpdate (void)
{
uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0;
tmp = RCC->CFGR0 & RCC_SWS;
switch (tmp)
{
case 0x00:
SystemCoreClock = HSI_VALUE;
break;
case 0x04:
SystemCoreClock = HSE_VALUE;
break;
case 0x08:
pllmull = RCC->CFGR0 & RCC_PLLMULL;
pllsource = RCC->CFGR0 & RCC_PLLSRC;
pllmull = ( pllmull >> 18) + 2;
if(pllmull == 17) pllmull = 18;
if (pllsource == 0x00)
{
if(EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE){
SystemCoreClock = HSI_VALUE * pllmull;
}
else{
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
}
}
else
{
#if defined (CH32V20x_D8W)
if((RCC->CFGR0 & (3<<22)) == (3<<22))
{
SystemCoreClock = ((HSE_VALUE>>1)) * pllmull;
}
else
#endif
if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
{
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
SystemCoreClock = ((HSE_VALUE>>2) >> 1) * pllmull;
#else
SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
#endif
}
else
{
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
SystemCoreClock = (HSE_VALUE>>2) * pllmull;
#else
SystemCoreClock = HSE_VALUE * pllmull;
#endif
}
}
if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2);
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
SystemCoreClock >>= tmp;
}
/*********************************************************************
* @fn SetSysClock
*
* @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClock(void)
{
//GPIO_IPD_Unused();
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_48MHz_HSE
SetSysClockTo48_HSE();
#elif defined SYSCLK_FREQ_56MHz_HSE
SetSysClockTo56_HSE();
#elif defined SYSCLK_FREQ_72MHz_HSE
SetSysClockTo72_HSE();
#elif defined SYSCLK_FREQ_96MHz_HSE
SetSysClockTo96_HSE();
#elif defined SYSCLK_FREQ_120MHz_HSE
SetSysClockTo120_HSE();
#elif defined SYSCLK_FREQ_144MHz_HSE
SetSysClockTo144_HSE();
#elif defined SYSCLK_FREQ_48MHz_HSI
SetSysClockTo48_HSI();
#elif defined SYSCLK_FREQ_56MHz_HSI
SetSysClockTo56_HSI();
#elif defined SYSCLK_FREQ_72MHz_HSI
SetSysClockTo72_HSI();
#elif defined SYSCLK_FREQ_96MHz_HSI
SetSysClockTo96_HSI();
#elif defined SYSCLK_FREQ_120MHz_HSI
SetSysClockTo120_HSI();
#elif defined SYSCLK_FREQ_144MHz_HSI
SetSysClockTo144_HSI();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
* source (default after reset)
*/
}
#ifdef SYSCLK_FREQ_HSE
/*********************************************************************
* @fn SetSysClockToHSE
*
* @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockToHSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
/* Select HSE as system clock source
* CH32V20x_D6 (HSE=8MHZ)
* CH32V20x_D8 (HSE=32MHZ)
* CH32V20x_D8W (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_HSE;
/* Wait till HSE is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04)
{
}
}
else
{
/* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_48MHz_HSE
/*********************************************************************
* @fn SetSysClockTo48_HSE
*
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo48_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 6 = 48 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_56MHz_HSE
/*********************************************************************
* @fn SetSysClockTo56_HSE
*
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo56_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 7 = 56 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_72MHz_HSE
/*********************************************************************
* @fn SetSysClockTo72_HSE
*
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo72_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 9 = 72 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_96MHz_HSE
/*********************************************************************
* @fn SetSysClockTo96_HSE
*
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo96_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 12 = 96 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_120MHz_HSE
/*********************************************************************
* @fn SetSysClockTo120_HSE
*
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo120_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if(HSEStatus == (uint32_t)0x01)
{
#if defined (CH32V20x_D8W)
RCC->CFGR0 |= (uint32_t)(3<<22);
/* HCLK = SYSCLK/2 */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2;
#else
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
#endif
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 15 = 120 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 15 = 120 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/2 * 15 = 240 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_144MHz_HSE
/*********************************************************************
* @fn SetSysClockTo144_HSE
*
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo144_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 18 = 144 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_48MHz_HSI
/*********************************************************************
* @fn SetSysClockTo48_HSI
*
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo48_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
/* PLL configuration: PLLCLK = HSI * 6 = 48 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_56MHz_HSI
/*********************************************************************
* @fn SetSysClockTo56_HSI
*
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo56_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 7 = 48 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_72MHz_HSI
/*********************************************************************
* @fn SetSysClockTo72_HSI
*
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo72_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 9 = 72 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_96MHz_HSI
/*********************************************************************
* @fn SetSysClockTo96_HSI
*
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo96_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 12 = 96 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL12);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_120MHz_HSI
/*********************************************************************
* @fn SetSysClockTo120_HSI
*
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo120_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 15 = 120 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL15);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_144MHz_HSI
/*********************************************************************
* @fn SetSysClockTo144_HSI
*
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo144_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 18 = 144 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL18);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#endif

View File

@@ -0,0 +1,32 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32v20x.h
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : CH32V20x 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_ch32v20x_H
#define __SYSTEM_ch32v20x_H
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
/* System_Exported_Functions */
extern void SystemInit(void);
extern void SystemCoreClockUpdate(void);
#ifdef __cplusplus
}
#endif
#endif /*__CH32V20x_SYSTEM_H */