minor cleanup, add i2c single register read/write functions
This commit is contained in:
41
code/retro_tech_fw/user/ch32v00x_conf.h
Normal file
41
code/retro_tech_fw/user/ch32v00x_conf.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v00x_conf.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2020/08/08
|
||||
* 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 __CH32V00x_CONF_H
|
||||
#define __CH32V00x_CONF_H
|
||||
|
||||
|
||||
#include <ch32v00x_adc.h>
|
||||
#include <ch32v00x_dbgmcu.h>
|
||||
#include <ch32v00x_dma.h>
|
||||
#include <ch32v00x_exti.h>
|
||||
#include <ch32v00x_flash.h>
|
||||
#include <ch32v00x_gpio.h>
|
||||
#include <ch32v00x_i2c.h>
|
||||
#include <ch32v00x_it.h>
|
||||
#include <ch32v00x_iwdg.h>
|
||||
#include <ch32v00x_misc.h>
|
||||
#include <ch32v00x_pwr.h>
|
||||
#include <ch32v00x_rcc.h>
|
||||
#include <ch32v00x_spi.h>
|
||||
#include <ch32v00x_tim.h>
|
||||
#include <ch32v00x_usart.h>
|
||||
#include <ch32v00x_wwdg.h>
|
||||
#include <ch32v00x_opa.h>
|
||||
|
||||
|
||||
|
||||
#endif /* __CH32V00x_CONF_H */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
83
code/retro_tech_fw/user/ch32v00x_it.c
Normal file
83
code/retro_tech_fw/user/ch32v00x_it.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v00x_it.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/08
|
||||
* 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 <ch32v00x_it.h>
|
||||
#include <ch32v00x.h>
|
||||
|
||||
#include "src/adc.h"
|
||||
#include "src/btn.h"
|
||||
#include "src/led.h"
|
||||
|
||||
|
||||
|
||||
void NMI_Handler(void);
|
||||
void HardFault_Handler(void);
|
||||
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn NMI_Handler
|
||||
*
|
||||
* @brief This function handles NMI exception.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void NMI_Handler(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @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 > 0x7ff) {
|
||||
ticnt = 0;
|
||||
uptime++;
|
||||
}
|
||||
|
||||
// send new LEDs (1/128 duty)
|
||||
if ((ticnt & 0xf) == 3) {
|
||||
led_send();
|
||||
}
|
||||
|
||||
// process buttons (1/512 duty)
|
||||
if ((ticnt & 0x3) == 0) {
|
||||
btn_poll();
|
||||
}
|
||||
|
||||
// process ADC (1/256 duty)
|
||||
if ((ticnt & 0x07) == 1) {
|
||||
adc_convert();
|
||||
}
|
||||
if ((ticnt & 0x07) == 2) {
|
||||
adc_read();
|
||||
}
|
||||
|
||||
// clear comparison flag
|
||||
SysTick->SR = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
26
code/retro_tech_fw/user/ch32v00x_it.h
Normal file
26
code/retro_tech_fw/user/ch32v00x_it.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : ch32v00x_it.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/08
|
||||
* 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 __CH32V00x_IT_H
|
||||
#define __CH32V00x_IT_H
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
extern volatile uint16_t ticnt;
|
||||
extern volatile uint32_t uptime;
|
||||
|
||||
|
||||
|
||||
#endif /* __CH32V00x_IT_H */
|
||||
136
code/retro_tech_fw/user/main.c
Normal file
136
code/retro_tech_fw/user/main.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* RETRO TECH COMMUNITY
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <ch32v00x.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/rand.h"
|
||||
#include "src/ui.h"
|
||||
|
||||
|
||||
void systick_init(void)
|
||||
{
|
||||
SysTick->CMP = (SystemCoreClock / 2048) - 1; // we want a 2048Hz 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;
|
||||
|
||||
// Soft I2C, USART TX/RX; currently unused
|
||||
gpio.GPIO_Mode = GPIO_Mode_IPD;
|
||||
gpio.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
|
||||
GPIO_Init(GPIOD, &gpio);
|
||||
|
||||
gpio.GPIO_Pin = GPIO_Pin_1;
|
||||
GPIO_Init(GPIOA, &gpio);
|
||||
|
||||
// lightsense LED anode
|
||||
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
|
||||
gpio.GPIO_Pin = GPIO_Pin_0;
|
||||
GPIO_Init(GPIOD, &gpio);
|
||||
|
||||
// unused pins
|
||||
gpio.GPIO_Mode = GPIO_Mode_IPD;
|
||||
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_7;
|
||||
GPIO_Init(GPIOC, &gpio);
|
||||
|
||||
gpio.GPIO_Pin = GPIO_Pin_2;
|
||||
GPIO_Init(GPIOD, &gpio);
|
||||
|
||||
// I2C will be handled by the driver
|
||||
|
||||
// IS_SDB IS31FL3729 shutdown pin (active low)
|
||||
GPIOC->BCR = GPIO_Pin_3;
|
||||
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
|
||||
gpio.GPIO_Pin = GPIO_Pin_3;
|
||||
GPIO_Init(GPIOC, &gpio);
|
||||
|
||||
// BTN1, BTN2 will be handled by button handler
|
||||
|
||||
// WP for EEPROM
|
||||
GPIOC->BSHR = GPIO_Pin_6;
|
||||
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
|
||||
gpio.GPIO_Pin = GPIO_Pin_6;
|
||||
GPIO_Init(GPIOC, &gpio);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn main
|
||||
*
|
||||
* @brief Main program.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
// configure core
|
||||
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
|
||||
SystemCoreClockUpdate();
|
||||
|
||||
// enable peripheral clocks
|
||||
RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_I2C1, ENABLE);
|
||||
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA |
|
||||
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
|
||||
RCC_APB2Periph_ADC1 | RCC_APB2Periph_TIM1, ENABLE);
|
||||
|
||||
// configure gpio pins
|
||||
gpio_init();
|
||||
|
||||
// get saved settings
|
||||
i2c_init();
|
||||
userconf_load();
|
||||
|
||||
// configure hardware
|
||||
adc_init();
|
||||
btn_init();
|
||||
led_init();
|
||||
|
||||
// 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
|
||||
// we have approx 7ms to render... plenty of time
|
||||
if ((ticnt & 0xf) == 3) {
|
||||
ui_render();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
code/retro_tech_fw/user/src/31fl3729.c
Normal file
82
code/retro_tech_fw/user/src/31fl3729.c
Normal 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, ¤t, 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, ¤t, 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);
|
||||
}
|
||||
93
code/retro_tech_fw/user/src/31fl3729.h
Normal file
93
code/retro_tech_fw/user/src/31fl3729.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_31FL3729_H_
|
||||
#define USER_SRC_31FL3729_H_
|
||||
|
||||
|
||||
|
||||
#include <ch32v00x.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_ */
|
||||
142
code/retro_tech_fw/user/src/adc.c
Normal file
142
code/retro_tech_fw/user/src/adc.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* because the ambient light sensor is facing toward the bottom, and
|
||||
* this badge has an "ADJUST" knob that is intended for brightness,
|
||||
* the light sensor code is incomplete.
|
||||
*
|
||||
* if you want to use it, copy code from another badge, where it's also
|
||||
* likely incomplete lol
|
||||
*/
|
||||
|
||||
#include <ch32v00x.h>
|
||||
|
||||
|
||||
|
||||
// deadzone fudge factors for potentionmeter. test on actual badge
|
||||
// and see if necessary, or if pot hardware + adc periph is good enough
|
||||
#define POT_LO 40
|
||||
#define POT_HI (1024-POT_LO)
|
||||
|
||||
|
||||
|
||||
uint16_t adc_val[16];
|
||||
uint16_t adc_val_idx;
|
||||
uint16_t adc_avg;
|
||||
|
||||
uint16_t lsens_val;
|
||||
|
||||
|
||||
|
||||
void adc_init()
|
||||
{
|
||||
ADC_InitTypeDef adc = {0};
|
||||
GPIO_InitTypeDef gpio = {0};
|
||||
|
||||
|
||||
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
|
||||
|
||||
gpio.GPIO_Pin = GPIO_Pin_3;
|
||||
gpio.GPIO_Mode = GPIO_Mode_AIN;
|
||||
GPIO_Init(GPIOD, &gpio);
|
||||
|
||||
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, ADC_Channel_4, 1, ADC_SampleTime_241Cycles);
|
||||
ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_73Cycles);
|
||||
ADC_Calibration_Vol(ADC1, ADC_CALVOL_50PERCENT);
|
||||
ADC_Cmd(ADC1, ENABLE);
|
||||
|
||||
ADC_ResetCalibration(ADC1);
|
||||
while(ADC_GetResetCalibrationStatus(ADC1));
|
||||
ADC_StartCalibration(ADC1);
|
||||
while(ADC_GetCalibrationStatus(ADC1));
|
||||
}
|
||||
|
||||
void adc_calc_avg()
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t sum = 0;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
sum += adc_val[i];
|
||||
}
|
||||
|
||||
sum >>= 4;
|
||||
|
||||
adc_avg = sum;
|
||||
}
|
||||
|
||||
void adc_convert()
|
||||
{
|
||||
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
|
||||
}
|
||||
|
||||
void adc_read()
|
||||
{
|
||||
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
|
||||
adc_val[adc_val_idx++] = ADC_GetConversionValue(ADC1);
|
||||
adc_val_idx &= 0x0f;
|
||||
if (!adc_val_idx) adc_calc_avg();
|
||||
|
||||
if (ADC1->CTLR1 & ADC_JAUTO) {
|
||||
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC));
|
||||
lsens_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
|
||||
|
||||
// reset LSENS
|
||||
ADC_AutoInjectedConvCmd(ADC1, DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void adc_use_lsens()
|
||||
{
|
||||
GPIO_InitTypeDef gpio = {0};
|
||||
|
||||
// configure GPIO
|
||||
gpio.GPIO_Pin = GPIO_Pin_2;
|
||||
gpio.GPIO_Mode = GPIO_Mode_AIN;
|
||||
GPIO_Init(GPIOA, &gpio);
|
||||
|
||||
// enable injected channel
|
||||
ADC_AutoInjectedConvCmd(ADC1, ENABLE);
|
||||
}
|
||||
|
||||
void adc_stop_lsens()
|
||||
{
|
||||
GPIO_InitTypeDef gpio = {0};
|
||||
|
||||
// configure GPIO
|
||||
gpio.GPIO_Pin = GPIO_Pin_2;
|
||||
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init(GPIOA, &gpio);
|
||||
}
|
||||
|
||||
uint16_t adc_get_pot()
|
||||
{
|
||||
uint32_t pot;
|
||||
|
||||
// set lower bound
|
||||
pot = adc_avg;
|
||||
if (pot < POT_LO) pot = 0; else pot -= POT_LO;
|
||||
|
||||
// scale to upper bound
|
||||
pot <<= 10;
|
||||
pot /= POT_HI;
|
||||
|
||||
// 8 bits is good enough for us
|
||||
pot >>= 2;
|
||||
|
||||
return pot & 0xff;
|
||||
}
|
||||
|
||||
uint16_t adc_get_lsens()
|
||||
{
|
||||
return lsens_val;
|
||||
}
|
||||
28
code/retro_tech_fw/user/src/adc.h
Normal file
28
code/retro_tech_fw/user/src/adc.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* adc.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_ADC_H_
|
||||
#define USER_SRC_ADC_H_
|
||||
|
||||
|
||||
|
||||
void adc_init();
|
||||
|
||||
void adc_config_lsens();
|
||||
|
||||
void adc_convert();
|
||||
void adc_read();
|
||||
|
||||
void adc_use_lsens();
|
||||
void adc_reset_lsens();
|
||||
|
||||
uint16_t adc_get_pot();
|
||||
uint16_t adc_get_lsens();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_ADC_H_ */
|
||||
87
code/retro_tech_fw/user/src/btn.c
Normal file
87
code/retro_tech_fw/user/src/btn.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* generic button handler like I do on most of my projects
|
||||
*/
|
||||
|
||||
#include <ch32v00x.h>
|
||||
#include "btn.h"
|
||||
|
||||
|
||||
|
||||
struct Btn btn[BTN_COUNT] = {0};
|
||||
|
||||
|
||||
|
||||
void btn_init()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
// configure GPIO
|
||||
BTN_PORT->BSHR = (BTN1_PUPD << BTN1_PIN) | (BTN2_PUPD << BTN2_PIN);
|
||||
BTN_PORT->CFGLR &= ~((0xff << (BTN1_PIN*4)) | ((0xff << (BTN2_PIN*4))));
|
||||
BTN_PORT->CFGLR |= (0x80 << (BTN1_PIN*4)) | (0x80 << (BTN2_PIN*4));
|
||||
|
||||
// configure default setup
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
btn[i]._mask = 0;
|
||||
}
|
||||
|
||||
btn[0]._pintype = BTN1_PIN | 0x10;
|
||||
btn[1]._pintype = BTN2_PIN;
|
||||
}
|
||||
|
||||
void btn_poll()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t r;
|
||||
uint8_t ignore;
|
||||
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
ignore = btn[i]._mask & BTN_IGNORE;
|
||||
r = BTN_PORT->INDR & (1 << (btn[i]._pintype & 0xf));
|
||||
|
||||
if ((!r && (btn[i]._pintype & 0x10)) || (r && !(btn[i]._pintype & 0x10))) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
code/retro_tech_fw/user/src/btn.h
Normal file
57
code/retro_tech_fw/user/src/btn.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 (12 >> 1) // debounce time in 2ms
|
||||
|
||||
#define BTN_PORT GPIOC
|
||||
#define BTN_MODE 0x80
|
||||
|
||||
#define BTN_PULL_UP (1 << 0)
|
||||
#define BTN_PULL_DOWN (1 << 16)
|
||||
|
||||
#define BTN1_PIN 5
|
||||
#define BTN1_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN2_PIN 4
|
||||
#define BTN2_PUPD BTN_PULL_DOWN
|
||||
|
||||
#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_ */
|
||||
61
code/retro_tech_fw/user/src/config.c
Normal file
61
code/retro_tech_fw/user/src/config.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* config.c
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
|
||||
|
||||
struct UserConf userconf;
|
||||
|
||||
|
||||
|
||||
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 csum;
|
||||
|
||||
eeprom_read_bytes(0, 0, (uint8_t *)&userconf, sizeof(userconf));
|
||||
csum = checksum();
|
||||
|
||||
if ((userconf.checkval != CHECKVAL) || (userconf.checksum != csum)) {
|
||||
// config is invalid; reset to default
|
||||
userconf.cursor_color = CONF_CURSOR_WHITE;
|
||||
userconf.cursor_flash = 4; // default flash rate
|
||||
userconf.ledprog_ena_mask = 0; // no programs enabled
|
||||
|
||||
userconf.ledprog_setting[0][0] = 2; // rainbow: angle top left to bottom right
|
||||
userconf.ledprog_setting[0][1] = 25; // rainbow: spacing
|
||||
|
||||
userconf.checksum = checksum();
|
||||
userconf.checkval = CHECKVAL;
|
||||
}
|
||||
}
|
||||
|
||||
void userconf_save()
|
||||
{
|
||||
userconf.checksum = checksum();
|
||||
userconf.checkval = CHECKVAL;
|
||||
|
||||
// this eeprom writes in 16-byte pages only, so split up the data
|
||||
eeprom_write_bytes(0, 0x00, (uint8_t *)&userconf + 0x00, 16);
|
||||
eeprom_write_bytes(0, 0x10, (uint8_t *)&userconf + 0x10, 16);
|
||||
}
|
||||
50
code/retro_tech_fw/user/src/config.h
Normal file
50
code/retro_tech_fw/user/src/config.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* config.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_CONFIG_H_
|
||||
#define USER_SRC_CONFIG_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#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 {
|
||||
uint8_t cursor_color;
|
||||
uint8_t cursor_flash;
|
||||
uint8_t padding8;
|
||||
uint8_t ledprog_ena_mask;
|
||||
uint8_t ledprog_setting[5][2];
|
||||
uint16_t checksum;
|
||||
uint32_t checkval;
|
||||
} UserConf; // 20 bytes
|
||||
|
||||
|
||||
|
||||
extern struct UserConf userconf;
|
||||
|
||||
|
||||
|
||||
void userconf_load();
|
||||
void userconf_save();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_CONFIG_H_ */
|
||||
78
code/retro_tech_fw/user/src/eeprom.c
Normal file
78
code/retro_tech_fw/user/src/eeprom.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* code taken from example WCH code, which was likely stolen from somewhere else.
|
||||
* directly uses i2c peripheral in shitty way. no time to make this better.
|
||||
* modified to support multiple "pages" (addresses) for 02/04/08/16 EEPROMs.
|
||||
*
|
||||
* ensure i2c bus has been configured before using these functions.
|
||||
*/
|
||||
|
||||
#define EEPROM_BASE_ADDR (0xa0 >> 1)
|
||||
|
||||
|
||||
#include <ch32v00x.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "eeprom.h"
|
||||
#include "i2c.h"
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn eeprom_read_byte
|
||||
*
|
||||
* @brief Read one data from EEPROM.
|
||||
* @param page - Which 256-byte EEPROM page.
|
||||
* @param addr - First read address.
|
||||
* @param data - Pointer to byte(s) to write.
|
||||
* @param len - Count of data to write.
|
||||
*/
|
||||
void eeprom_read_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
|
||||
{
|
||||
page &= 0x7; // limit page to 3 bits (8 pages)
|
||||
page |= EEPROM_BASE_ADDR; // add address to page
|
||||
page <<= 1; // make i2c address from page
|
||||
|
||||
i2c_read_addr1b(page, addr, data, len);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn eeprom_write_bytes
|
||||
*
|
||||
* @brief Write one data to EEPROM.
|
||||
* Note that some EEPROMs have buffers smaller than the page size.
|
||||
*
|
||||
* @param page - Which 256-byte EEPROM page.
|
||||
* @param addr - Starting write address.
|
||||
* @param data - Pointer to byte(s) to write.
|
||||
* @param len - Count of data to write.
|
||||
*/
|
||||
void eeprom_write_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
|
||||
{
|
||||
page &= 0x7; // limit page to 3 bits (8 pages)
|
||||
page |= EEPROM_BASE_ADDR; // add address to page
|
||||
page <<= 1; // make i2c address from page
|
||||
|
||||
// disable systick interrupt
|
||||
NVIC_DisableIRQ(SysTicK_IRQn);
|
||||
|
||||
// write the data
|
||||
#ifdef EEPROM_WP_PORT
|
||||
EEPROM_WP_PORT->BCR = EEPROM_WP_PIN;
|
||||
#endif
|
||||
|
||||
while (i2c_ack_poll(page));
|
||||
i2c_write_addr1b(page, addr, data, len);
|
||||
while (i2c_ack_poll(page));
|
||||
|
||||
#ifdef EEPROM_WP_PORT
|
||||
EEPROM_WP_PORT->BSHR = EEPROM_WP_PIN;
|
||||
#endif
|
||||
|
||||
// clear systick timer
|
||||
SysTick->CNT = 0;
|
||||
SysTick->SR = 0;
|
||||
|
||||
// re-enable systick interrupt
|
||||
NVIC_EnableIRQ(SysTicK_IRQn);
|
||||
}
|
||||
27
code/retro_tech_fw/user/src/eeprom.h
Normal file
27
code/retro_tech_fw/user/src/eeprom.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* eeprom.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_EEPROM_H_
|
||||
#define USER_SRC_EEPROM_H_
|
||||
|
||||
|
||||
#include <ch32v00x.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#define EEPROM_WP_PORT GPIOC
|
||||
#define EEPROM_WP_PIN GPIO_Pin_7
|
||||
|
||||
|
||||
|
||||
void eeprom_read_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len);
|
||||
void eeprom_write_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_EEPROM_H_ */
|
||||
94
code/retro_tech_fw/user/src/hsv2rgb.c
Normal file
94
code/retro_tech_fw/user/src/hsv2rgb.c
Normal 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.
|
||||
*/
|
||||
}
|
||||
}
|
||||
98
code/retro_tech_fw/user/src/hsv2rgb.h
Normal file
98
code/retro_tech_fw/user/src/hsv2rgb.h
Normal 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
code/retro_tech_fw/user/src/i2c.c
Normal file
165
code/retro_tech_fw/user/src/i2c.c
Normal 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 <ch32v00x.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;
|
||||
}
|
||||
26
code/retro_tech_fw/user/src/i2c.h
Normal file
26
code/retro_tech_fw/user/src/i2c.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
66
code/retro_tech_fw/user/src/led.c
Normal file
66
code/retro_tech_fw/user/src/led.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*/
|
||||
|
||||
#include "led.h"
|
||||
|
||||
|
||||
#define FL3729_CS_OUTPUTS 15
|
||||
|
||||
#define LED_INIT_CURRENT 0
|
||||
#define LED_INIT_SCALING 0xff
|
||||
|
||||
|
||||
|
||||
const uint8_t rgb_map[9] = {0x04, 0x14, 0x01, 0x11, 0x07, 0x0a, 0x1a, 0x0d, 0x1d};
|
||||
const uint8_t cursor_map[3] = {0x17, 0x18, 0x19};
|
||||
|
||||
|
||||
|
||||
uint16_t rgb[9][3] = {0};
|
||||
uint8_t cursor[3] = {0};
|
||||
|
||||
static uint8_t led_updated = 0;
|
||||
|
||||
|
||||
void led_init()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t buf[FL3729_CS_OUTPUTS] = {0xff};
|
||||
|
||||
is31fl3729_init(FL3729_ADDR,
|
||||
FL3729_CONF_SHDN_DIS | FL3729_CONF_OSDE_OFF | FL3729_CONF_MATRIX_2x16,
|
||||
LED_INIT_CURRENT);
|
||||
|
||||
for (i = 0; i < FL3729_CS_OUTPUTS; i++) buf[i] = LED_INIT_SCALING;
|
||||
is31fl3729_set_scaling_current_multi(FL3729_ADDR, buf, FL3729_CS_OUTPUTS);
|
||||
|
||||
}
|
||||
|
||||
void led_is_updated()
|
||||
{
|
||||
led_updated = 1;
|
||||
}
|
||||
|
||||
void led_send()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t out[16*2];
|
||||
|
||||
// only render when there's something to render
|
||||
if (led_updated) {
|
||||
// stuff RGB outputs
|
||||
for (i = 0; i < 9; i++) {
|
||||
out[rgb_map[i] + 0] = rgb[i][0];
|
||||
out[rgb_map[i] + 1] = rgb[i][1];
|
||||
out[rgb_map[i] + 2] = rgb[i][2];
|
||||
}
|
||||
|
||||
// stuff cursor outputs
|
||||
out[cursor_map[0]] = cursor[0];
|
||||
out[cursor_map[1]] = cursor[1];
|
||||
out[cursor_map[2]] = cursor[2];
|
||||
|
||||
is31fl3729_set_outputs(FL3729_ADDR, 1, out, 16+15);
|
||||
}
|
||||
}
|
||||
35
code/retro_tech_fw/user/src/led.h
Normal file
35
code/retro_tech_fw/user/src/led.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* led.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
|
||||
extern uint16_t rgb[9][3];
|
||||
extern uint8_t cursor[3];
|
||||
|
||||
|
||||
|
||||
void led_init();
|
||||
|
||||
void led_is_updated();
|
||||
void led_send();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_LED_H_ */
|
||||
519
code/retro_tech_fw/user/src/led_rgbprog.c
Normal file
519
code/retro_tech_fw/user/src/led_rgbprog.c
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* led_rgbprog.c
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "hsv2rgb.h"
|
||||
#include "led.h"
|
||||
#include "rand.h"
|
||||
|
||||
|
||||
// led programs
|
||||
void led_rgb_0_rainbow(uint8_t preview, uint8_t tick);
|
||||
void led_rgb_1_lite_then_fade(uint8_t preview, uint8_t tick);
|
||||
void led_rgb_2_twinkle(uint8_t preview, uint8_t tick);
|
||||
void led_rgb_3_alternate(uint8_t preview, uint8_t tick);
|
||||
void led_rgb_4_typing(uint8_t preview, uint8_t tick);
|
||||
|
||||
void ((*led_rgbprog[5])(uint8_t, uint8_t)) = {
|
||||
led_rgb_0_rainbow,
|
||||
led_rgb_1_lite_then_fade,
|
||||
led_rgb_2_twinkle,
|
||||
led_rgb_3_alternate,
|
||||
led_rgb_4_typing
|
||||
};
|
||||
|
||||
static uint8_t prog_rgb[3];
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* always have to have unicorn piss
|
||||
*
|
||||
* button: angle (45 degree shifts for each press)
|
||||
* knob: spacing
|
||||
*/
|
||||
static uint16_t rainbow_hue = 0;
|
||||
|
||||
// probably some way to compute this but I can't think of it right now
|
||||
// might take more codespace than this 40-byte LUT anyway
|
||||
// encoding: LED1-LED2 LED3-LED4 LED5-MAX
|
||||
// LED6-LED7 LED8-LED9
|
||||
static const uint8_t rainbow_angles[8][5] = {
|
||||
{0x12, 0x34, 0x55,
|
||||
0x01, 0x23}, // bottom left to top right
|
||||
{0x01, 0x23, 0x44,
|
||||
0x01, 0x23}, // left to right
|
||||
{0x01, 0x23, 0x44,
|
||||
0x12, 0x34}, // top left to bottom right
|
||||
{0x00, 0x00, 0x02,
|
||||
0x22, 0x22}, // top to bottom
|
||||
{0x43, 0x21, 0x04,
|
||||
0x32, 0x10}, // top right to bottom left
|
||||
{0x43, 0x21, 0x04,
|
||||
0x43, 0x21}, // right to left
|
||||
{0x43, 0x21, 0x04,
|
||||
0x32, 0x10}, // bottom right to top left
|
||||
{0x22, 0x22, 0x22,
|
||||
0x00, 0x00} // bottom to top
|
||||
};
|
||||
|
||||
static void rainbow_letter_next()
|
||||
{
|
||||
uint16_t h;
|
||||
|
||||
// add to our hue
|
||||
h = userconf.ledprog_setting[0][1];
|
||||
h <<= 2;
|
||||
|
||||
rainbow_hue += h;
|
||||
rainbow_hue++;
|
||||
rainbow_hue %= 256*6;
|
||||
|
||||
// make a color out of it
|
||||
hsv2rgb_8b(rainbow_hue, 255, 255, &prog_rgb[0], &prog_rgb[1], &prog_rgb[2]);
|
||||
}
|
||||
|
||||
void led_rgb_0_rainbow(uint8_t preview, uint8_t tick)
|
||||
{
|
||||
uint8_t i, j; // iterator
|
||||
uint8_t n; // led index, remapped
|
||||
uint8_t idx; // led index being updated, 0-5
|
||||
uint8_t max; // max number of iterations for this angle
|
||||
uint8_t angle; // fill direction from LUT
|
||||
|
||||
// run at half framerate
|
||||
if (tick & 1) {
|
||||
// no matter what, this puke is getting updated
|
||||
led_is_updated();
|
||||
|
||||
// setup
|
||||
angle = userconf.ledprog_setting[0][0];
|
||||
angle &= 0x7;
|
||||
max = rainbow_angles[angle][2] & 0x7;
|
||||
|
||||
// process outputs
|
||||
for (i = 0; i <= max; i++) {
|
||||
// get the next puke
|
||||
rainbow_letter_next();
|
||||
|
||||
// apply to next set of LEDs
|
||||
n = 0;
|
||||
for (j = 0; j < 9; j++) {
|
||||
n++;
|
||||
if (j == 5) n++; // nybble index
|
||||
|
||||
idx = rainbow_angles[angle][n >> 1]; // get mask byte
|
||||
if ((n & 1) == 0) idx >>= 4; // shift even values over
|
||||
idx &= 0x7; // mask lowest 3 bits
|
||||
|
||||
if (idx == i) { // is this LED part of this pattern?
|
||||
if (preview) { // are we in preview mode?
|
||||
if (j != 0) { // is the first letter not the one selected?
|
||||
if ((preview & 0x7f) == 0) {// is this preview selected?
|
||||
if (j < 5) continue; // pass over any outputs that are not "TECH" letters
|
||||
}
|
||||
} else return; // preview is not selected, so we're done here
|
||||
}
|
||||
rgb[j][0] = prog_rgb[0]; // if so, copy the values
|
||||
rgb[j][1] = prog_rgb[1];
|
||||
rgb[j][2] = prog_rgb[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* LEDs are lit from left to right, which fade out as it passes
|
||||
* then top word then bottom word light, then fade out,
|
||||
* and program repeats.
|
||||
* button: fade rate
|
||||
* knob: hue
|
||||
*/
|
||||
|
||||
#define LITE_INTER_DWELL 20 // time between characters
|
||||
#define LITE_IDLE_DWELL (256+64) // time before changing direction
|
||||
#define LITE_PERSIST 10 // how long to keep each LED at fullbrite
|
||||
|
||||
static uint8_t lite_direction = 0;
|
||||
static uint8_t lite_idx = 0;
|
||||
static uint16_t lite_timeout = 0;
|
||||
static uint8_t lite_persist[9] = {0};
|
||||
|
||||
void led_rgb_1_lite_then_fade(uint8_t preview, uint8_t tick)
|
||||
{
|
||||
uint16_t i, j;
|
||||
uint8_t start, end;
|
||||
|
||||
uint16_t hue = userconf.ledprog_setting[1][1];
|
||||
uint8_t rate = userconf.ledprog_setting[1][0];
|
||||
|
||||
// user sets the color
|
||||
i = 255;
|
||||
if (hue >= 254) {
|
||||
// force white
|
||||
i = 0;
|
||||
}
|
||||
|
||||
hue *= 6;
|
||||
hsv2rgb_8b(hue, i, 255, &prog_rgb[0], &prog_rgb[1], &prog_rgb[2]);
|
||||
|
||||
// make the rate adjustment a bit more intuitive
|
||||
if (rate >= 254) rate = 255;
|
||||
else rate >>= 2;
|
||||
|
||||
// fade out anything that's already lit at user's rate
|
||||
for (i = 0; i < 9; i++) {
|
||||
if (lite_persist[i]) lite_persist[i]--;
|
||||
else for (j = 0; j < 2; j++) {
|
||||
if (rgb[i][j] > rate) rgb[i][j] -= rate; else rgb[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// light it up
|
||||
if (!lite_timeout) {
|
||||
if (!lite_direction) {
|
||||
// left to right
|
||||
// light RETRO
|
||||
if (lite_idx < 5) {
|
||||
rgb[lite_idx][0] = prog_rgb[0];
|
||||
rgb[lite_idx][1] = prog_rgb[1];
|
||||
rgb[lite_idx][2] = prog_rgb[2];
|
||||
lite_persist[lite_idx] = LITE_PERSIST;
|
||||
}
|
||||
|
||||
// light TECH
|
||||
if (lite_idx < 4) {
|
||||
rgb[lite_idx + 5][0] = prog_rgb[0];
|
||||
rgb[lite_idx + 5][1] = prog_rgb[1];
|
||||
rgb[lite_idx + 5][2] = prog_rgb[2];
|
||||
lite_persist[lite_idx + 5] = LITE_PERSIST;
|
||||
}
|
||||
} else {
|
||||
// up to down
|
||||
if (lite_idx < 2) {
|
||||
if (!lite_idx) {
|
||||
start = 0;
|
||||
end = 5;
|
||||
} else {
|
||||
start = 5;
|
||||
end = 9;
|
||||
}
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
rgb[i][0] = prog_rgb[0];
|
||||
lite_persist[i] = LITE_PERSIST;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next light
|
||||
lite_idx++;
|
||||
|
||||
end = lite_direction ? 2 : 5;
|
||||
|
||||
// after idle phase, start over in next direction
|
||||
if (lite_idx > end) {
|
||||
lite_idx = 0;
|
||||
lite_direction++;
|
||||
lite_direction &= 1;
|
||||
}
|
||||
|
||||
lite_timeout = lite_direction ? LITE_INTER_DWELL*2 : LITE_INTER_DWELL;
|
||||
|
||||
// at idle phase?
|
||||
if (lite_idx == end) {
|
||||
lite_timeout = LITE_IDLE_DWELL;
|
||||
}
|
||||
}
|
||||
|
||||
lite_timeout--;
|
||||
}
|
||||
|
||||
/*
|
||||
* LEDs are on at some idle level, and random letters will
|
||||
* twinkle by intensifying and shifting hue slightly by a
|
||||
* random small amount.
|
||||
* button: saturation
|
||||
* knob: flashing intensity
|
||||
*/
|
||||
|
||||
#define TWINKLE_PERSIST 3;
|
||||
#define TWINKLE_RGB_VAL 240
|
||||
uint8_t twinkle_set[9] = {0};
|
||||
|
||||
void led_rgb_2_twinkle(uint8_t preview, uint8_t tick)
|
||||
{
|
||||
uint8_t i, j;
|
||||
uint32_t rnd;
|
||||
uint8_t sat;
|
||||
|
||||
// set
|
||||
rnd = prng_get8();
|
||||
if (rnd >= userconf.ledprog_setting[2][1]) {
|
||||
// yup, we're doing it
|
||||
rnd = prng_get16();
|
||||
rnd *= 1536;
|
||||
rnd <<= 16;
|
||||
|
||||
sat = userconf.ledprog_setting[2][0];
|
||||
if (sat >= 128) sat = 128 - (sat - 128);
|
||||
sat <<= 1;
|
||||
|
||||
hsv2rgb_8b(rnd, sat, TWINKLE_RGB_VAL, &prog_rgb[0], &prog_rgb[1], &prog_rgb[2]);
|
||||
|
||||
rnd = prng_get16() & 0xfff;
|
||||
rnd /= 455;
|
||||
|
||||
rgb[rnd][0] = prog_rgb[0];
|
||||
rgb[rnd][1] = prog_rgb[1];
|
||||
rgb[rnd][2] = prog_rgb[2];
|
||||
twinkle_set[rnd] = TWINKLE_PERSIST;
|
||||
}
|
||||
|
||||
// decay
|
||||
for (i = 0; i < 9; i++) {
|
||||
twinkle_set[i]--;
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (twinkle_set[i]) rgb[i][j]--;
|
||||
else rgb[i][j] >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* alternates flashing the words between two colors.
|
||||
* button: offset degrees (360/16)
|
||||
* knob: hue (alternate hue will be either 90 or 180 degrees offset)
|
||||
*/
|
||||
|
||||
|
||||
void led_rgb_3_alternate(uint8_t preview, uint8_t tick)
|
||||
{
|
||||
uint8_t i, j;
|
||||
|
||||
uint16_t hue = userconf.ledprog_setting[3][1] * 6;
|
||||
uint8_t offset = (userconf.ledprog_setting[3][0] & 0xf) * 16 * 6;
|
||||
|
||||
uint8_t is_flashing = 0;
|
||||
|
||||
if (userconf.cursor_color == CONF_CURSOR_OFF) {
|
||||
// flash at ~2Hz
|
||||
tick >>= 5;
|
||||
if (tick & 1) is_flashing = 1;
|
||||
} else {
|
||||
// match cursor
|
||||
if (cursor[userconf.cursor_color]) {
|
||||
is_flashing = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// set hues
|
||||
for (i = 0; i < 2; i++) {
|
||||
hsv2rgb_8b(hue, 255, 255, &prog_rgb[0], &prog_rgb[1], &prog_rgb[2]);
|
||||
hue += offset;
|
||||
hue %= 1536;
|
||||
|
||||
if (is_flashing) {
|
||||
// set TECH
|
||||
for (j = 5; j < 9; j++) {
|
||||
rgb[j][0] = prog_rgb[0];
|
||||
rgb[j][1] = prog_rgb[1];
|
||||
rgb[j][2] = prog_rgb[2];
|
||||
}
|
||||
} else {
|
||||
// set RETRO
|
||||
for (j = 0; j < 5; j++) {
|
||||
rgb[j][0] = prog_rgb[0];
|
||||
rgb[j][1] = prog_rgb[1];
|
||||
rgb[j][2] = prog_rgb[2];
|
||||
}
|
||||
}
|
||||
|
||||
is_flashing++;
|
||||
is_flashing &= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* a green, orange or white phosphor sort of "typing,"
|
||||
* with quick but not instant fade-in and flashing of
|
||||
* an active character.
|
||||
* the only program that interferes with the cursor.
|
||||
* after "typing" and filling, the characters fade then
|
||||
* the program repeats.
|
||||
*
|
||||
* no user config for this program
|
||||
*/
|
||||
|
||||
#define TYPING_CHAR_DELAY_MIN 12 // about 1/10 of a second
|
||||
#define TYPING_CHAR_DELAY_MAX 500 // just shy of 4 seconds
|
||||
|
||||
static const uint8_t idle_glow[3] = {12, 8, 8};
|
||||
static const uint8_t typing[3][3] = {
|
||||
{255, 240, 240}, // white
|
||||
{ 0, 240, 0}, // green
|
||||
{255, 96, 12}, // orange
|
||||
};
|
||||
|
||||
static uint8_t typing_idx;
|
||||
static uint16_t typing_char_delay;
|
||||
|
||||
static uint8_t typing_flash_state;
|
||||
static uint8_t typing_flash_delay;
|
||||
|
||||
static uint8_t typing_fadeout;
|
||||
|
||||
|
||||
void led_rgb_4_typing(uint8_t preview, uint8_t tick)
|
||||
{
|
||||
uint8_t i, j;
|
||||
uint8_t w;
|
||||
uint16_t s;
|
||||
uint8_t color;
|
||||
|
||||
// match cursor color (well, approximate, or go green)
|
||||
color = userconf.cursor_color;
|
||||
if (color == CONF_CURSOR_OFF) color = CONF_CURSOR_GREEN;
|
||||
|
||||
// set flash
|
||||
if (!typing_flash_delay) {
|
||||
typing_flash_state++;
|
||||
typing_flash_state &= 1;
|
||||
|
||||
typing_flash_delay = userconf.cursor_flash;
|
||||
}
|
||||
typing_flash_delay--;
|
||||
|
||||
// set rgb colors as appropriate
|
||||
for (i = 0; i < 9; i++) {
|
||||
if (i < typing_idx || ((i == typing_idx) && (typing_flash_state))) {
|
||||
// these are on
|
||||
rgb[i][0] = typing[color][0];
|
||||
rgb[i][1] = typing[color][1];
|
||||
rgb[i][2] = typing[color][2];
|
||||
}
|
||||
|
||||
if (i > typing_idx || ((i == typing_idx) && (!typing_flash_state))) {
|
||||
// these are idle
|
||||
rgb[i][0] = idle_glow[0];
|
||||
rgb[i][1] = idle_glow[1];
|
||||
rgb[i][2] = idle_glow[2];
|
||||
}
|
||||
|
||||
// flashing cursor fadeout
|
||||
if (i == typing_idx && typing_flash_state) {
|
||||
if (typing_flash_delay <= 8) {
|
||||
w = 8 - typing_flash_delay;
|
||||
rgb[i][0] >>= w; if (rgb[i][0] < idle_glow[0]) rgb[i][0] = idle_glow[0];
|
||||
rgb[i][1] >>= w; if (rgb[i][1] < idle_glow[1]) rgb[i][1] = idle_glow[1];
|
||||
rgb[i][2] >>= w; if (rgb[i][2] < idle_glow[2]) rgb[i][2] = idle_glow[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set cursor as appropriate
|
||||
for (i = 0; i < 2; i++) {
|
||||
cursor[i] = 0;
|
||||
}
|
||||
|
||||
// at the cursor
|
||||
if (typing_idx >= 9) {
|
||||
cursor[color] = typing_flash_state ? 240 : 12;
|
||||
if (typing_flash_delay <= 8) {
|
||||
w = 8 - typing_flash_delay;
|
||||
cursor[color] >>= w;
|
||||
if (cursor[color] < 12) cursor[color] = 12;
|
||||
}
|
||||
}
|
||||
|
||||
// decay all the characters
|
||||
if (typing_idx == 10) {
|
||||
if (typing_fadeout >= 3) typing_fadeout--;
|
||||
else typing_fadeout = 0;
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
s = rgb[i][j];
|
||||
s *= typing_fadeout;
|
||||
rgb[i][j] = s >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
s = cursor[color];
|
||||
s *= typing_fadeout;
|
||||
cursor[color] = s >> 8;
|
||||
}
|
||||
|
||||
// are we on the next character?
|
||||
if (!(--typing_char_delay)) {
|
||||
// next character
|
||||
typing_idx++;
|
||||
|
||||
if (typing_idx == 9) {
|
||||
// at the cursor, we wait two max periods
|
||||
typing_char_delay = TYPING_CHAR_DELAY_MAX << 1;
|
||||
} else if (typing_idx == 10) {
|
||||
// at the end, we wait half of one max period
|
||||
typing_char_delay = TYPING_CHAR_DELAY_MAX >> 1;
|
||||
} else {
|
||||
typing_char_delay = prng_scale16(TYPING_CHAR_DELAY_MIN, TYPING_CHAR_DELAY_MAX);
|
||||
}
|
||||
|
||||
if (typing_idx > 10) typing_idx = 0;
|
||||
|
||||
// reset cursor fade (used in last step)
|
||||
typing_fadeout = 0xff;
|
||||
|
||||
// reset flash state and delay to reset cursor flash
|
||||
// comment this code to keep persistent cursor rate
|
||||
typing_flash_state = 0;
|
||||
typing_flash_delay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialize settings for programs before they run
|
||||
* this can be run when changing programs to set them
|
||||
* to a known state before starting over
|
||||
*/
|
||||
void led_rgb_firstrun()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
rgb[i][0] = 0;
|
||||
rgb[i][1] = 0;
|
||||
rgb[i][2] = 0;
|
||||
}
|
||||
|
||||
// program 0: rainbow
|
||||
// nothing to set
|
||||
|
||||
// program 1: lite then fade
|
||||
lite_direction = 0;
|
||||
lite_idx = 0;
|
||||
lite_timeout = 0;
|
||||
for (i = 0; i < 9; i++) {
|
||||
lite_persist[i] = 0;
|
||||
}
|
||||
|
||||
// program 2: twinkle
|
||||
for (i = 0; i < 9; i++) twinkle_set[i] = 0;
|
||||
|
||||
// program 3: alternating
|
||||
// nothing to set
|
||||
|
||||
// program 4: typing
|
||||
typing_idx = 0;
|
||||
typing_char_delay = prng_scale16(TYPING_CHAR_DELAY_MIN, TYPING_CHAR_DELAY_MAX);
|
||||
|
||||
typing_flash_state = 0;
|
||||
typing_flash_delay = userconf.cursor_flash;
|
||||
}
|
||||
26
code/retro_tech_fw/user/src/led_rgbprog.h
Normal file
26
code/retro_tech_fw/user/src/led_rgbprog.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* led_rgbprog.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_LED_RGBPROG_H_
|
||||
#define USER_SRC_LED_RGBPROG_H_
|
||||
|
||||
|
||||
|
||||
#define LED_RGBPROG_NORMAL 0
|
||||
#define LED_RGBPROG_PREVIEW 0x80
|
||||
|
||||
|
||||
|
||||
extern void ((*led_rgbprog[5])(uint8_t, uint8_t));
|
||||
|
||||
|
||||
|
||||
void led_rgb_firstrun();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_LED_RGBPROG_H_ */
|
||||
162
code/retro_tech_fw/user/src/rand.c
Normal file
162
code/retro_tech_fw/user/src/rand.c
Normal 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
code/retro_tech_fw/user/src/rand.h
Normal file
39
code/retro_tech_fw/user/src/rand.h
Normal 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 */
|
||||
552
code/retro_tech_fw/user/src/ui.c
Normal file
552
code/retro_tech_fw/user/src/ui.c
Normal file
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* super basic interface for the buttons
|
||||
* renderer for the cursor (always 50% duty cycle)
|
||||
* interface for the rgb programs
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "btn.h"
|
||||
#include "config.h"
|
||||
#include "led.h"
|
||||
#include "led_rgbprog.h"
|
||||
#include "rand.h"
|
||||
|
||||
|
||||
#define MODE_RUN 0
|
||||
#define MODE_PROGRAM 1
|
||||
#define MODE_PARAMETER 2
|
||||
|
||||
#define MIN_GC 4
|
||||
#define MAX_GC 60
|
||||
|
||||
#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;
|
||||
|
||||
static uint16_t editor_timeout_timer = 0;
|
||||
|
||||
static uint8_t target_gc = 0;
|
||||
|
||||
static uint8_t cursor_flash = 0;
|
||||
static uint8_t cursor_state = 0xff;
|
||||
static const uint16_t cursor_flash_rates[] = { // off-to-on flash rates in 1/256 second increments
|
||||
255, // (0.5 on-off/second)
|
||||
191, // (0.75 on-off/second)
|
||||
127, // (1.0 on-off/second)
|
||||
85, // C64 rate (1.5 on-off/second)
|
||||
68, // Windows default (530ms on-off)
|
||||
41, // PC MDA (3.114 on-off/second)
|
||||
34, // PC CGA/EGA (3.745 on-off/second)
|
||||
29, // PC VGA text mode (4.38 on-off/second)
|
||||
};
|
||||
|
||||
static uint8_t rgb_prog_idx = 0; // currently running rgbled program index
|
||||
static uint16_t rgb_prog_timer = 0; // timeout until this program is done and switches
|
||||
static uint8_t rgb_prog_is_editing = 0; // currently editing a program's parameters
|
||||
static uint8_t preview_idx = 0; // currently selected program preview index
|
||||
|
||||
static uint8_t config_save_timer;
|
||||
|
||||
|
||||
|
||||
void ui_btn_push_cb(uint8_t idx)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
// are both buttons pushed?
|
||||
if ((btn[0]._mask & BTN_PUSH) && (btn[1]._mask & BTN_PUSH)) {
|
||||
// are none held?
|
||||
if (!(btn[0]._mask & BTN_HOLD) && !(btn[1]._mask & BTN_HOLD)) {
|
||||
// can only enter programming mode if the cursor is flashing
|
||||
if (userconf.cursor_color != CONF_CURSOR_OFF) {
|
||||
// put both buttons into ignore mode, so hold and release events
|
||||
// do not fire
|
||||
btn[0]._mask |= BTN_IGNORE;
|
||||
btn[1]._mask |= BTN_IGNORE;
|
||||
|
||||
// both buttons are pushed at the same time quickly, and not held
|
||||
// this will toggle modes
|
||||
mode++;
|
||||
if (mode > MODE_PARAMETER) {
|
||||
mode = MODE_RUN;
|
||||
}
|
||||
|
||||
// reset any LED program
|
||||
led_rgb_firstrun();
|
||||
|
||||
// configure the preview index to be the first available
|
||||
// depending on the mode
|
||||
if (mode == MODE_PROGRAM) preview_idx = 0;
|
||||
if (mode == MODE_PARAMETER) {
|
||||
preview_idx = 0;
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (userconf.ledprog_ena_mask & (1 << preview_idx)) {
|
||||
preview_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do normal button actions
|
||||
switch (mode) {
|
||||
/* button logic flow:
|
||||
*
|
||||
*
|
||||
* mode changes happen by pressing both buttons.
|
||||
*
|
||||
*
|
||||
* NORMAL MODE: cursor is flashing.
|
||||
*
|
||||
* cursor changes are committed to EEPROM a couple of seconds after changing is done.
|
||||
*
|
||||
* if cursor is off, programming mode cannot be entered.
|
||||
*
|
||||
* - top button: changes cursor color (white, green, orange, off)
|
||||
* - bot button: changes cursor flash rate. press and hold while not off to
|
||||
* enter program change mode.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* PROGRAM CHANGE MODE: cursor is off.
|
||||
*
|
||||
*
|
||||
* letters in RETRO show enabled programs. by default, none are enabled.
|
||||
* enabled programs are bright. disabled ones are dim.
|
||||
* selected program will flash. if on, will flash brightly. if off, it will flash dimly.
|
||||
*
|
||||
* any program which is enabled can run. the active program randomly changes after random delays.
|
||||
*
|
||||
* letters in TECH preview the currently selected program.
|
||||
* letters in RETRO will each preview their own program (any flash / twinkle will always have idle light)
|
||||
*
|
||||
* - top button: tap enables or disables the program. push and hold does nothing.
|
||||
* - bot button: tap selects the next program index. push and hold enters parameter change mode.
|
||||
*
|
||||
*
|
||||
* PARAMETER CHANGE MODE: cursor is on.
|
||||
*
|
||||
* letters in RETRO flash to show currently selected program.
|
||||
* selected program will flash. if a program has been disabled, nothing will light for that letter.
|
||||
*
|
||||
* letters in TECH preview the current program, at some fixed brightness.
|
||||
*
|
||||
* - top button: increment some parameter, with loop (speed, etc)
|
||||
* - bot button: selects the next program
|
||||
* - knob: changes some parameter (color/hue, etc)
|
||||
*
|
||||
* settings are saved when the cursor returns to flashing from any other mode.
|
||||
*
|
||||
* settings are restored to default if the top button is held while powering on.
|
||||
* restored settings are not committed until entering programming mode
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void ui_btn_hold_cb(uint8_t idx)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_btn_release_cb(uint8_t idx)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t w;
|
||||
|
||||
switch (mode) {
|
||||
/* button logic flow:
|
||||
*
|
||||
*
|
||||
* mode changes happen by pressing both buttons.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NORMAL MODE: cursor is flashing (or off if disabled).
|
||||
*
|
||||
* cursor changes are committed to EEPROM about a second after changing is done.
|
||||
*
|
||||
* if cursor is off, programming mode cannot be entered.
|
||||
*
|
||||
* any program which is enabled will run. the active program randomly changes after random delays.
|
||||
*/
|
||||
case MODE_RUN: {
|
||||
switch (idx) {
|
||||
case 0: { // - top button: changes cursor color (white, green, orange, off)
|
||||
userconf.cursor_color++;
|
||||
userconf.cursor_color &= 0x3;
|
||||
|
||||
config_save_timer = UI_CONF_SAVE_TIMEOUT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: { // - bot button: changes cursor flash rate. press and hold
|
||||
// while cursor is not off to enter program change mode.
|
||||
userconf.cursor_flash++;
|
||||
userconf.cursor_flash &= 0x7;
|
||||
|
||||
config_save_timer = UI_CONF_SAVE_TIMEOUT;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PROGRAM CHANGE MODE: cursor is on.
|
||||
*
|
||||
* letters in RETRO show programs. by default, all are disabled.
|
||||
* enabled programs are bright. disabled programs are dim.
|
||||
* selected program will flash. if program is on, will flash brightly. if off, it will flash dimly.
|
||||
*
|
||||
* letters in TECH preview the currently selected program.
|
||||
* letters in RETRO will each preview their own program (any flash / twinkle will always have idle light)
|
||||
*/
|
||||
case MODE_PROGRAM: {
|
||||
switch (idx) {
|
||||
case 0: { // - top button: tap enables or disables the program. push and hold does nothing.
|
||||
w = userconf.ledprog_ena_mask;
|
||||
if (userconf.ledprog_ena_mask & (1 << preview_idx)) {
|
||||
userconf.ledprog_ena_mask &= ~(1 << preview_idx);
|
||||
} else {
|
||||
userconf.ledprog_ena_mask |= (1 << preview_idx);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: { // - bot button: tap selects the next program index. push and hold does nothing.
|
||||
preview_idx++;
|
||||
if (preview_idx >= 5) preview_idx = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PARAMETER CHANGE MODE: cursor is off (or rather, very dimly on).
|
||||
*
|
||||
* letters in RETRO only show enabled programs. dark for disabled programs.
|
||||
* RETRO letters will flash to indicate currently selected program.
|
||||
*
|
||||
* letters in TECH preview the current program, at some fixed brightness.
|
||||
*
|
||||
*
|
||||
*
|
||||
* - knob: changes some parameter (color/hue, etc)
|
||||
*/
|
||||
case MODE_PARAMETER: {
|
||||
switch (idx) {
|
||||
case 0: { // - top button: tapped or held: increment some parameter, with loop (speed, etc)
|
||||
userconf.ledprog_setting[preview_idx][0]++;
|
||||
break;
|
||||
}
|
||||
case 1: { // - bot button: when editing disabled: selects the next program
|
||||
// when editing enabled: decrements some parameter, with loop (speed, etc)
|
||||
// held: enables / disables editing the program (done in other callback)
|
||||
if (rgb_prog_is_editing) {
|
||||
if (userconf.ledprog_setting[preview_idx][0]) {
|
||||
userconf.ledprog_setting[preview_idx][0]--;
|
||||
} else {
|
||||
userconf.ledprog_setting[preview_idx][0] = 0xff;
|
||||
}
|
||||
} else {
|
||||
// select new program, except the last program, since that doesn't have a user editable config
|
||||
w = preview_idx;
|
||||
for (i = 0; i < 4; i++) {
|
||||
w++;
|
||||
if (w > 4) w = 0;
|
||||
|
||||
if (userconf.ledprog_ena_mask & (1 << w)) {
|
||||
preview_idx = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_cursor_flash()
|
||||
{
|
||||
uint8_t color, flash;
|
||||
uint8_t level = 0;
|
||||
|
||||
color = userconf.cursor_color;
|
||||
flash = userconf.cursor_flash;
|
||||
|
||||
switch (mode) {
|
||||
case MODE_RUN: {
|
||||
if (rgb_prog_idx != 4) {
|
||||
// initial setup
|
||||
if (cursor_state == 0xff) {
|
||||
cursor_state = 0;
|
||||
goto ui_cursor_flash_setup;
|
||||
}
|
||||
|
||||
// wind down counter
|
||||
cursor_flash--;
|
||||
|
||||
// set brightness of led, if we're in an on state
|
||||
if (cursor_state && (color < CONF_CURSOR_OFF)) {
|
||||
// first frame of on = not quite full brightness
|
||||
level = (cursor_flash == cursor_flash_rates[flash] - 1) ? 160 : 255;
|
||||
|
||||
// at final frames, dim
|
||||
if (cursor_flash < 4) {
|
||||
level >>= (cursor_flash << 1);
|
||||
}
|
||||
|
||||
// set the level on the cursor
|
||||
if (cursor[color] != level) {
|
||||
cursor[color] = level;
|
||||
led_is_updated();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cursor_flash) {
|
||||
// toggle on/off
|
||||
cursor_state++;
|
||||
cursor_state &= 1;
|
||||
|
||||
ui_cursor_flash_setup:
|
||||
// set new cursor rate
|
||||
cursor_flash = cursor_flash_rates[flash] >> 1;
|
||||
|
||||
// set all colors off
|
||||
cursor[0] = cursor[1] = cursor[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MODE_PROGRAM: {
|
||||
// cursor is always on bright
|
||||
cursor[color] = 255;
|
||||
|
||||
break;
|
||||
}
|
||||
case MODE_PARAMETER: {
|
||||
// cursor is always on dim
|
||||
cursor[color] = 16;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
uint32_t t;
|
||||
|
||||
uint8_t i;
|
||||
uint8_t w;
|
||||
uint8_t flash;
|
||||
|
||||
|
||||
// deal with eeprom
|
||||
if (config_save_timer) {
|
||||
config_save_timer--;
|
||||
if (config_save_timer) {
|
||||
userconf_save();
|
||||
}
|
||||
}
|
||||
|
||||
// flash rate at 2 times/second
|
||||
flash = tick >> 5;
|
||||
flash &= 0x3;
|
||||
|
||||
if (editor_timeout_timer) {
|
||||
editor_timeout_timer--;
|
||||
if (!editor_timeout_timer) {
|
||||
mode = MODE_RUN;
|
||||
|
||||
// save settings to eeprom
|
||||
userconf_save();
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case MODE_RUN: { // render an existing program
|
||||
tick++;
|
||||
|
||||
// set brightness from knob 64 times/second
|
||||
// (the value actually updates less frequently, but we need to fade nicely)
|
||||
if (tick & 3 == 3) {
|
||||
w = adc_get_pot() >> 2;
|
||||
if (!w) {
|
||||
if (target_gc) target_gc--; // full off = full off.
|
||||
} else if (w < target_gc) {
|
||||
if (target_gc > MIN_GC) target_gc--; // dim to min possible
|
||||
} else if (w > target_gc) {
|
||||
if (target_gc < MAX_GC) target_gc++; // dim to max possible
|
||||
}
|
||||
|
||||
is31fl3729_set_global_current(FL3729_ADDR, target_gc);
|
||||
}
|
||||
|
||||
led_rgbprog[rgb_prog_idx](LED_RGBPROG_NORMAL, tick);
|
||||
|
||||
// fade and change programs depending on the timer
|
||||
rgb_prog_timer--;
|
||||
if (rgb_prog_timer <= 17) {
|
||||
if (rgb_prog_timer > 9) {
|
||||
// fade out current program
|
||||
w = (rgb_prog_timer & 0x1f) - 10;
|
||||
for (i = 0; i < 9; i++) {
|
||||
rgb[i][0] >>= w;
|
||||
rgb[i][1] >>= w;
|
||||
rgb[i][2] >>= w;
|
||||
}
|
||||
} else if (rgb_prog_timer > 7) {
|
||||
// select a new random program
|
||||
if (rgb_prog_timer == 8) {
|
||||
w = prng_get8();
|
||||
w &= 0x3;
|
||||
if (w == rgb_prog_idx) w++;
|
||||
rgb_prog_idx = w;
|
||||
|
||||
led_rgb_firstrun();
|
||||
}
|
||||
|
||||
// clear out for now, since new program hasn't actually been run
|
||||
for (i = 0; i < 9; i++) {
|
||||
rgb[i][0] = 0;
|
||||
rgb[i][1] = 0;
|
||||
rgb[i][2] = 0;
|
||||
}
|
||||
} else if (rgb_prog_timer >= 1) {
|
||||
// fade in new program
|
||||
w = 8 - (rgb_prog_timer & 0x1f);
|
||||
for (i = 0; i < 9; i++) {
|
||||
rgb[i][0] >>= w;
|
||||
rgb[i][1] >>= w;
|
||||
rgb[i][2] >>= w;
|
||||
}
|
||||
} else { // 0
|
||||
// randomize next program timing
|
||||
t = prng_get16();
|
||||
t *= (UI_PROG_RUNTIME_MAX - UI_PROG_RUNTIME_MIN);
|
||||
t >>= 16;
|
||||
|
||||
rgb_prog_timer = t + UI_PROG_RUNTIME_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MODE_PROGRAM: {
|
||||
// always postpone config saving
|
||||
config_save_timer = UI_CONF_SAVE_TIMEOUT;
|
||||
|
||||
// always force rendering
|
||||
led_is_updated();
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
// render
|
||||
led_rgbprog[i](LED_RGBPROG_PREVIEW | preview_idx, tick);
|
||||
|
||||
if (preview_idx == i) {
|
||||
// flash the selected output
|
||||
if (flash & 1) {
|
||||
rgb[i][0] >>= 5;
|
||||
rgb[i][1] >>= 5;
|
||||
rgb[i][2] >>= 5;
|
||||
}
|
||||
} else {
|
||||
// dim inactive outputs
|
||||
if (!(userconf.ledprog_ena_mask & (1 << i))) {
|
||||
rgb[i][0] >>= 3;
|
||||
rgb[i][1] >>= 3;
|
||||
rgb[i][2] >>= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MODE_PARAMETER: {
|
||||
// always postpone config saving
|
||||
config_save_timer = UI_CONF_SAVE_TIMEOUT;
|
||||
|
||||
// always force rendering
|
||||
led_is_updated();
|
||||
|
||||
// deal with knob if editing is active
|
||||
if (rgb_prog_is_editing) {
|
||||
userconf.ledprog_setting[preview_idx][1] = adc_get_pot();
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
// render
|
||||
if (userconf.ledprog_ena_mask & (1 << i)) {
|
||||
led_rgbprog[i](LED_RGBPROG_PREVIEW | preview_idx, tick);
|
||||
|
||||
if (preview_idx == i) {
|
||||
// flash the selected output
|
||||
if (flash & 1) {
|
||||
rgb[i][0] >>= 5;
|
||||
rgb[i][1] >>= 5;
|
||||
rgb[i][2] >>= 5;
|
||||
}
|
||||
} else {
|
||||
// dim the other outputs
|
||||
rgb[i][0] >>= 2;
|
||||
rgb[i][1] >>= 2;
|
||||
rgb[i][2] >>= 2;
|
||||
}
|
||||
} else {
|
||||
// clear the output if it isn't enabled
|
||||
rgb[i][0] = 0;
|
||||
rgb[i][1] = 0;
|
||||
rgb[i][2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ui_cursor_flash();
|
||||
}
|
||||
18
code/retro_tech_fw/user/src/ui.h
Normal file
18
code/retro_tech_fw/user/src/ui.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* ui.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_UI_H_
|
||||
#define USER_SRC_UI_H_
|
||||
|
||||
|
||||
|
||||
void ui_init();
|
||||
void ui_render();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_UI_H_ */
|
||||
449
code/retro_tech_fw/user/system_ch32v00x.c
Normal file
449
code/retro_tech_fw/user/system_ch32v00x.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : system_ch32v00x.c
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/08
|
||||
* Description : CH32V00x Device Peripheral Access Layer System Source File.
|
||||
*********************************************************************************
|
||||
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||
* Attention: This software (modified or not) and binary are used for
|
||||
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||
*******************************************************************************/
|
||||
#include <ch32v00x.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_8MHz_HSI 8000000
|
||||
#define SYSCLK_FREQ_24MHZ_HSI HSI_VALUE
|
||||
//#define SYSCLK_FREQ_48MHZ_HSI 48000000
|
||||
//#define SYSCLK_FREQ_8MHz_HSE 8000000
|
||||
//#define SYSCLK_FREQ_24MHz_HSE HSE_VALUE
|
||||
//#define SYSCLK_FREQ_48MHz_HSE 48000000
|
||||
|
||||
/* Clock Definitions */
|
||||
#ifdef SYSCLK_FREQ_8MHz_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_8MHz_HSI; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_24MHZ_HSI
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_24MHZ_HSI; /* 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_8MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_8MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_24MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSE; /* System Clock Frequency (Core Clock) */
|
||||
#else
|
||||
uint32_t SystemCoreClock = HSI_VALUE;
|
||||
#endif
|
||||
|
||||
__I uint8_t AHBPrescTable[16] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
|
||||
/* system_private_function_proto_types */
|
||||
static void SetSysClock(void);
|
||||
|
||||
#ifdef SYSCLK_FREQ_8MHz_HSI
|
||||
static void SetSysClockTo_8MHz_HSI(void);
|
||||
#elif defined SYSCLK_FREQ_24MHZ_HSI
|
||||
static void SetSysClockTo_24MHZ_HSI(void);
|
||||
#elif defined SYSCLK_FREQ_48MHZ_HSI
|
||||
static void SetSysClockTo_48MHZ_HSI(void);
|
||||
#elif defined SYSCLK_FREQ_8MHz_HSE
|
||||
static void SetSysClockTo_8MHz_HSE(void);
|
||||
#elif defined SYSCLK_FREQ_24MHz_HSE
|
||||
static void SetSysClockTo_24MHz_HSE(void);
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
static void SetSysClockTo_48MHz_HSE(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)0xFCFF0000;
|
||||
RCC->CTLR &= (uint32_t)0xFEF6FFFF;
|
||||
RCC->CTLR &= (uint32_t)0xFFFBFFFF;
|
||||
RCC->CFGR0 &= (uint32_t)0xFFFEFFFF;
|
||||
RCC->INTR = 0x009F0000;
|
||||
|
||||
SetSysClock();
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SystemCoreClockUpdate
|
||||
*
|
||||
* @brief Update SystemCoreClock variable according to Clock Register Values.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void SystemCoreClockUpdate (void)
|
||||
{
|
||||
uint32_t tmp = 0, pllsource = 0;
|
||||
|
||||
tmp = RCC->CFGR0 & RCC_SWS;
|
||||
|
||||
switch (tmp)
|
||||
{
|
||||
case 0x00:
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
case 0x04:
|
||||
SystemCoreClock = HSE_VALUE;
|
||||
break;
|
||||
case 0x08:
|
||||
pllsource = RCC->CFGR0 & RCC_PLLSRC;
|
||||
if (pllsource == 0x00)
|
||||
{
|
||||
SystemCoreClock = HSI_VALUE * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemCoreClock = HSE_VALUE * 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
|
||||
|
||||
if(((RCC->CFGR0 & RCC_HPRE) >> 4) < 8)
|
||||
{
|
||||
SystemCoreClock /= tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemCoreClock >>= tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClock
|
||||
*
|
||||
* @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClock(void)
|
||||
{
|
||||
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
|
||||
GPIOD->CFGLR&=(~0xF0);
|
||||
GPIOD->CFGLR|=0x80;
|
||||
GPIOD->BSHR =0x2;
|
||||
//GPIO_IPD_Unused();
|
||||
|
||||
#ifdef SYSCLK_FREQ_8MHz_HSI
|
||||
SetSysClockTo_8MHz_HSI();
|
||||
#elif defined SYSCLK_FREQ_24MHZ_HSI
|
||||
SetSysClockTo_24MHZ_HSI();
|
||||
#elif defined SYSCLK_FREQ_48MHZ_HSI
|
||||
SetSysClockTo_48MHZ_HSI();
|
||||
#elif defined SYSCLK_FREQ_8MHz_HSE
|
||||
SetSysClockTo_8MHz_HSE();
|
||||
#elif defined SYSCLK_FREQ_24MHz_HSE
|
||||
SetSysClockTo_24MHz_HSE();
|
||||
#elif defined SYSCLK_FREQ_48MHz_HSE
|
||||
SetSysClockTo_48MHz_HSE();
|
||||
#endif
|
||||
|
||||
/* If none of the define above is enabled, the HSI is used as System clock.
|
||||
* source (default after reset)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
#ifdef SYSCLK_FREQ_8MHz_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo_8MHz_HSI
|
||||
*
|
||||
* @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_8MHz_HSI(void)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV3;
|
||||
}
|
||||
|
||||
#elif defined SYSCLK_FREQ_24MHZ_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo_24MHZ_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 24MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_24MHZ_HSI(void)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
}
|
||||
|
||||
|
||||
#elif defined SYSCLK_FREQ_48MHZ_HSI
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo_48MHZ_HSI
|
||||
*
|
||||
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_48MHZ_HSI(void)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSI * 2 = 48 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC));
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Mul2);
|
||||
|
||||
/* 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_8MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo_8MHz_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_8MHz_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
/* Close PA0-PA1 GPIO function */
|
||||
RCC->APB2PCENR |= RCC_AFIOEN;
|
||||
AFIO->PCFR1 |= (1<<15);
|
||||
|
||||
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));
|
||||
|
||||
RCC->APB2PCENR |= RCC_AFIOEN;
|
||||
AFIO->PCFR1 |= (1<<15);
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV3;
|
||||
|
||||
/* Select HSE as system clock source */
|
||||
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_24MHz_HSE
|
||||
|
||||
/*********************************************************************
|
||||
* @fn SetSysClockTo_24MHz_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_24MHz_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
/* Close PA0-PA1 GPIO function */
|
||||
RCC->APB2PCENR |= RCC_AFIOEN;
|
||||
AFIO->PCFR1 |= (1<<15);
|
||||
|
||||
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));
|
||||
|
||||
RCC->APB2PCENR |= RCC_AFIOEN;
|
||||
AFIO->PCFR1 |= (1<<15);
|
||||
|
||||
if ((RCC->CTLR & RCC_HSERDY) != RESET)
|
||||
{
|
||||
HSEStatus = (uint32_t)0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSEStatus = (uint32_t)0x00;
|
||||
}
|
||||
|
||||
if (HSEStatus == (uint32_t)0x01)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
|
||||
/* Select HSE as system clock source */
|
||||
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 SetSysClockTo_48MHz_HSE
|
||||
*
|
||||
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
static void SetSysClockTo_48MHz_HSE(void)
|
||||
{
|
||||
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
|
||||
|
||||
/* Close PA0-PA1 GPIO function */
|
||||
RCC->APB2PCENR |= RCC_AFIOEN;
|
||||
AFIO->PCFR1 |= (1<<15);
|
||||
|
||||
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)
|
||||
{
|
||||
/* Flash 0 wait state */
|
||||
FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
|
||||
FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
|
||||
|
||||
/* HCLK = SYSCLK = APB1 */
|
||||
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
|
||||
|
||||
/* PLL configuration: PLLCLK = HSE * 2 = 48 MHz */
|
||||
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC));
|
||||
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE_Mul2);
|
||||
|
||||
/* 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
|
||||
*/
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
32
code/retro_tech_fw/user/system_ch32v00x.h
Normal file
32
code/retro_tech_fw/user/system_ch32v00x.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/********************************** (C) COPYRIGHT *******************************
|
||||
* File Name : system_ch32v00x.h
|
||||
* Author : WCH
|
||||
* Version : V1.0.0
|
||||
* Date : 2022/08/08
|
||||
* Description : CH32V00x 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_CH32V00x_H
|
||||
#define __SYSTEM_CH32V00x_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 /*__CH32V00x_SYSTEM_H */
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user