Compare commits

...

2 Commits

Author SHA1 Message Date
true b1bca1012f initial soft i2c routines written; AW20xxx code copied in
completely untested. i2c delay cycles are at best a guess.
2024-10-13 02:53:18 -07:00
true b6582a599a Englishify some of the CH592 StdPeriphDriver 2024-10-12 22:53:04 -07:00
18 changed files with 919 additions and 347 deletions

BIN
datasheets/CH592DS1_EN.PDF Normal file

Binary file not shown.

View File

@ -15,9 +15,11 @@
/*********************************************************************
* @fn LClk32K_Select
*
* @brief 32K
* @brief 32K Low Frequency clock source select
*
* @param hc - 32K使用内部还是外部
* @param hc - Use internal or external 32K oscillator
* Clk32K_LSI - Internal
* Clk32K_LSE - External
*
* @return none
*/
@ -43,10 +45,10 @@ void LClk32K_Select(LClk32KTypeDef hc)
/*********************************************************************
* @fn LClk32K_Cfg
*
* @brief 32K
* @brief 32K Low Frequency clock power
*
* @param hc - 32K还是外部32K
* @param s -
* @param hc - Clock Selection
* @param s - Power ENABLE/DISABLE
*
* @return none
*/
@ -85,7 +87,7 @@ void LClk32K_Cfg(LClk32KTypeDef hc, FunctionalState s)
/*********************************************************************
* @fn HSECFG_Current
*
* @brief HSE晶体
* @brief HSE Bias Current configuration
*
* @param c - 75%,100%,125%,150%
*
@ -106,7 +108,7 @@ void HSECFG_Current(HSECurrentTypeDef c)
/*********************************************************************
* @fn HSECFG_Capacitance
*
* @brief HSE晶体
* @brief HSE Load Capacitor configuration
*
* @param c - refer to HSECapTypeDef
*
@ -127,7 +129,7 @@ void HSECFG_Capacitance(HSECapTypeDef c)
/*********************************************************************
* @fn LSECFG_Current
*
* @brief LSE晶体
* @brief LSE Bias Current configuration
*
* @param c - 70%,100%,140%,200%
*
@ -148,7 +150,7 @@ void LSECFG_Current(LSECurrentTypeDef c)
/*********************************************************************
* @fn LSECFG_Capacitance
*
* @brief LSE晶体
* @brief LSE Load Capacitor configuration
*
* @param c - refer to LSECapTypeDef
*
@ -169,11 +171,12 @@ void LSECFG_Capacitance(LSECapTypeDef c)
/*********************************************************************
* @fn Calibration_LSI
*
* @brief 32K时钟
* @brief Calibrate 32K LSI clock from HSE
*
* @param cali_Lv - Level_32 2.4ms 1000ppm (32M ) 1100ppm (60M )
* Level_64 4.4ms 800ppm (32M ) 1000ppm (60M )
* Level_128 8.4ms 600ppm (32M ) 800ppm (60M )
* @param cali_Lv - Calibration Level Selection
* Level_32 £º2.4ms 1000ppm (32M HSE), 1100ppm (60M HSE)
* Level_64 £º4.4ms 800ppm (32M HSE), 1000ppm (60M HSE)
* Level_128 £º8.4ms 600ppm (32M HSE), 800ppm (60M HSE)
*
* @return none
*/
@ -197,7 +200,7 @@ void Calibration_LSI(Cali_LevelTypeDef cali_Lv)
while(1)
{
// 粗调
// coarse adjustment
sys_safe_access_enable();
R8_OSC_CAL_CTRL &= ~RB_OSC_CNT_TOTAL;
R8_OSC_CAL_CTRL |= 1;
@ -253,7 +256,7 @@ void Calibration_LSI(Cali_LevelTypeDef cali_Lv)
sys_safe_access_disable();
}
// 细调
// fine adjustment
// 配置细调参数后丢弃2次捕获值软件行为上判断已有一次这里只留一次
sys_safe_access_enable();
R8_OSC_CAL_CTRL &= ~RB_OSC_CNT_TOTAL;
@ -354,14 +357,14 @@ void Calibration_LSI(Cali_LevelTypeDef cali_Lv)
/*********************************************************************
* @fn RTCInitTime
*
* @brief RTC时钟初始化当前时间
* @brief RTC Clock initialize with time and date
*
* @param y - MAX_Y = BEGYEAR + 44
* @param mon - MAX_MON = 12
* @param d - MAX_D = 31
* @param h - MAX_H = 23
* @param m - MAX_M = 59
* @param s - MAX_S = 59
* @param y - year, MAX_Y = BEGYEAR + 44
* @param mon - month, MAX_MON = 12
* @param d - day, MAX_D = 31
* @param h - hour, MAX_H = 23
* @param m - minute, MAX_M = 59
* @param s - second, MAX_S = 59
*
* @return none
*/
@ -420,14 +423,14 @@ void RTC_InitTime(uint16_t y, uint16_t mon, uint16_t d, uint16_t h, uint16_t m,
/*********************************************************************
* @fn RTC_GetTime
*
* @brief
* @brief Get the current time and date from RTC
*
* @param py - MAX_Y = BEGYEAR + 44
* @param pmon - MAX_MON = 12
* @param pd - MAX_D = 31
* @param ph - MAX_H = 23
* @param pm - MAX_M = 59
* @param ps - MAX_S = 59
* @param py - year, MAX_Y = BEGYEAR + 44
* @param pmon - month, MAX_MON = 12
* @param pd - day, MAX_D = 31
* @param ph - hour, MAX_H = 23
* @param pm - minute, MAX_M = 59
* @param ps - second, MAX_S = 59
*
* @return none
*/
@ -465,9 +468,9 @@ void RTC_GetTime(uint16_t *py, uint16_t *pmon, uint16_t *pd, uint16_t *ph, uint1
/*********************************************************************
* @fn RTC_SetCycle32k
*
* @brief LSE/LSI时钟RTC
* @brief Based on LSI/LSE clock, configure the number of cycles for RTC
*
* @param cyc - MAX_CYC = 0xA8BFFFFF = 2831155199
* @param cyc - cycle count, MAX_CYC = 0xA8BFFFFF = 2831155199
*
* @return none
*/
@ -489,11 +492,11 @@ void RTC_SetCycle32k(uint32_t cyc)
/*********************************************************************
* @fn RTC_GetCycle32k
*
* @brief LSE/LSI时钟RTC
* @brief Get the current number of cycles for RTC based on LSI/LSE clock
*
* @param none
*
* @return MAX_CYC = 0xA8BFFFFF = 2831155199
* @return cycle count, MAX_CYC = 0xA8BFFFFF = 2831155199
*/
uint32_t RTC_GetCycle32k(void)
{
@ -509,7 +512,7 @@ uint32_t RTC_GetCycle32k(void)
/*********************************************************************
* @fn RTC_TMRFunCfg
*
* @brief RTC定时模式配置32768Hz
* @brief RTC Timer configuration (timebase is fixed to 32768Hz)
*
* @param t - refer to RTC_TMRCycTypeDef
*
@ -528,7 +531,7 @@ void RTC_TMRFunCfg(RTC_TMRCycTypeDef t)
/*********************************************************************
* @fn RTC_TRIGFunCfg
*
* @brief RTC时间触发模式配置
* @brief RTC Timer Trigger configuration
*
* @param cyc - LSE/LSI时钟周期数
*
@ -580,11 +583,11 @@ void RTC_ModeFunDisable(RTC_MODETypeDef m)
/*********************************************************************
* @fn RTC_GetITFlag
*
* @brief RTC中断标志
* @brief Get RTC interrupt flags
*
* @param f - refer to RTC_EVENTTypeDef
*
* @return
* @return Interrupt flag status
*/
uint8_t RTC_GetITFlag(RTC_EVENTTypeDef f)
{
@ -601,7 +604,7 @@ uint8_t RTC_GetITFlag(RTC_EVENTTypeDef f)
/*********************************************************************
* @fn RTC_ClearITFlag
*
* @brief RTC中断标志
* @brief Clear RTC interrupt flags
*
* @param f - refer to RTC_EVENTTypeDef
*

View File

@ -10,6 +10,16 @@
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*
* true's notes:
*
* flash is practically undocumented.
*
* R8_FLASH_CTRL - 0x51 flash on, no waitstates?
* 0x51 flash on, unknown waitstates?
* 0x04 flash off
*/
#include "CH59x_common.h"
/* RESET_EN */
@ -56,7 +66,7 @@ void FLASH_ROM_READ(uint32_t StartAddr, void *Buffer, uint32_t len)
* @brief Configure User Option Byte.,
* (使使.S文件线)
*
* @param RESET_EN - 使
* @param RESET_EN - External reset pin enable
* @param BOOT_PIN - ENABLE-使boot脚-PB22,DISABLE-使boot脚-PB11
* @param UART_NO_KEY_EN - 使
* @param FLASHProt_Size - (4K)
@ -173,7 +183,7 @@ void UserOptionByte_Active(void)
*
* @param Buffer - Pointer to the buffer where data should be stored, Must be aligned to 4 bytes.
*
* @return 0-SUCCESS (!0)-FAILURE
* @return none
*/
void GET_UNIQUE_ID(uint8_t *Buffer)
{

View File

@ -15,10 +15,10 @@
/*********************************************************************
* @fn GPIOA_ModeCfg
*
* @brief GPIOA端口引脚模式配置
* @brief GPIOA Port Pin Mode configuration
*
* @param pin - PA0-PA15
* @param mode -
* @param mode - GPIO input / output mode
*
* @return none
*/
@ -62,10 +62,10 @@ void GPIOA_ModeCfg(uint32_t pin, GPIOModeTypeDef mode)
/*********************************************************************
* @fn GPIOB_ModeCfg
*
* @brief GPIOB端口引脚模式配置
* @brief GPIOB Port Pin Mode configuration
*
* @param pin - PB0-PB23
* @param mode -
* @param mode - GPIO input / output mode
*
* @return none
*/
@ -109,10 +109,10 @@ void GPIOB_ModeCfg(uint32_t pin, GPIOModeTypeDef mode)
/*********************************************************************
* @fn GPIOA_ITModeCfg
*
* @brief GPIOA引脚中断模式配置
* @brief GPIOA interrupt configuration
*
* @param pin - PA0-PA15
* @param mode -
* @param mode - Interrupt Trigger type (edge, level)
*
* @return none
*/
@ -120,22 +120,22 @@ void GPIOA_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode)
{
switch(mode)
{
case GPIO_ITMode_LowLevel: // 低电平触发
case GPIO_ITMode_LowLevel:
R16_PA_INT_MODE &= ~pin;
R32_PA_CLR |= pin;
break;
case GPIO_ITMode_HighLevel: // 高电平触发
case GPIO_ITMode_HighLevel:
R16_PA_INT_MODE &= ~pin;
R32_PA_OUT |= pin;
break;
case GPIO_ITMode_FallEdge: // 下降沿触发
case GPIO_ITMode_FallEdge:
R16_PA_INT_MODE |= pin;
R32_PA_CLR |= pin;
break;
case GPIO_ITMode_RiseEdge: // 上升沿触发
case GPIO_ITMode_RiseEdge:
R16_PA_INT_MODE |= pin;
R32_PA_OUT |= pin;
break;
@ -150,10 +150,10 @@ void GPIOA_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode)
/*********************************************************************
* @fn GPIOB_ITModeCfg
*
* @brief GPIOB引脚中断模式配置
* @brief GPIOB interrupt configuration
*
* @param pin - PB0-PB23
* @param mode -
* @param mode - Interrupt Trigger type (edge, level)
*
* @return none
*/
@ -162,22 +162,22 @@ void GPIOB_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode)
uint32_t Pin = pin | ((pin & (GPIO_Pin_22 | GPIO_Pin_23)) >> 14);
switch(mode)
{
case GPIO_ITMode_LowLevel: // 低电平触发
case GPIO_ITMode_LowLevel:
R16_PB_INT_MODE &= ~Pin;
R32_PB_CLR |= pin;
break;
case GPIO_ITMode_HighLevel: // 高电平触发
case GPIO_ITMode_HighLevel:
R16_PB_INT_MODE &= ~Pin;
R32_PB_OUT |= pin;
break;
case GPIO_ITMode_FallEdge: // 下降沿触发
case GPIO_ITMode_FallEdge:
R16_PB_INT_MODE |= Pin;
R32_PB_CLR |= pin;
break;
case GPIO_ITMode_RiseEdge: // 上升沿触发
case GPIO_ITMode_RiseEdge:
R16_PB_INT_MODE |= Pin;
R32_PB_OUT |= pin;
break;
@ -192,17 +192,17 @@ void GPIOB_ITModeCfg(uint32_t pin, GPIOITModeTpDef mode)
/*********************************************************************
* @fn GPIOPinRemap
*
* @brief
* @brief GPIO Pin Peripheral Function Remapping
*
* @param s - 使
* @param s - Enable / Disable remap
* @param perph - RB_RF_ANT_SW_EN - RF antenna switch control output on PA4/PA5/PA12/PA13/PA14/PA15
* RB_PIN_U0_INV - RXD0/RXD0_/TXD0/TXD0_ invert input/output
* RB_PIN_INTX - INTX: INT24/INT25 PB8/PB9 -> INT24_/INT25_ PB22/PB23
* RB_PIN_MODEM - MODEM: PA6/PA7 -> PB12/PB13
* RB_PIN_I2C - I2C: PB14/PB15 -> PB14/PB15
* RB_PIN_I2C - I2C: PB14/PB15 -> PB14/PB15 (no remap possible)
* RB_PIN_PWMX - PWMX: PA12/PA13 -> PA6/PA7
* RB_PIN_SPI0 - SPI0: PA12/PA13/PA14/PA15 -> PB12/PB13/PB14/PB15
* RB_PIN_UART3 - UART3: PA4/PA5 -> PA4/PA5
* RB_PIN_UART3 - UART3: PA4/PA5 -> PA4/PA5 (no remap possible)
* RB_PIN_UART2 - UART2: PB22/PB23 -> PA6/PA7
* RB_PIN_UART1 - UART1: PA8/PA9 -> PB12/PB13
* RB_PIN_UART0 - UART0: PB4/PB7 -> PA15/PA14
@ -228,25 +228,25 @@ void GPIOPinRemap(FunctionalState s, uint16_t perph)
/*********************************************************************
* @fn GPIOAGPPCfg
*
* @brief GPIO引脚功能控制
* @brief GPIO Analog Pin Peripheral Function configuration
*
* @param s - ENABLE -
* DISABLE -
* @param perph - RB_PIN_ADC8_9_IE - ADC/TKEY 9/8
* RB_PIN_ADC6_7_IE - ADC/TKEY 7/6
* RB_PIN_ADC10_IE - ADC/TKEY 10
* RB_PIN_ADC11_IE - ADC/TKEY 11
* RB_PIN_USB2_DP_PU - USB2 U2D+
* @param s - ENABLE - Turn on analog, disable digital functions
* DISABLE - Turn off analog, enable digital functions
* @param perph - RB_PIN_ADC8_9_IE - ADC/TKEY 9/8
* RB_PIN_ADC6_7_IE - ADC/TKEY 7/6
* RB_PIN_ADC10_IE - ADC/TKEY 10
* RB_PIN_ADC11_IE - ADC/TKEY 11
* RB_PIN_USB2_DP_PU - USB2 U2D+ internal pullup
* RB_PIN_USB2_IE - USB2Òý½Å
* RB_PIN_USB_DP_PU - USB UD+
* RB_PIN_USB_DP_PU - USB UD+ internal pullup
* RB_PIN_USB_IE - USB Òý½Å
* RB_PIN_ADC0_IE - ADC/TKEY 0
* RB_PIN_ADC1_IE - ADC/TKEY 1
* RB_PIN_ADC12_IE - ADC/TKEY 12
* RB_PIN_ADC13_IE - ADC/TKEY 13
* RB_PIN_XT32K_IE - 32KHz晶振LSE引脚
* RB_PIN_ADC2_3_IE - ADC/TKEY 2/3
* RB_PIN_ADC4_5_IE - ADC/TKEY 4/5
* RB_PIN_ADC0_IE - ADC/TKEY 0
* RB_PIN_ADC1_IE - ADC/TKEY 1
* RB_PIN_ADC12_IE - ADC/TKEY 12
* RB_PIN_ADC13_IE - ADC/TKEY 13
* RB_PIN_XT32K_IE - 32KHz LSE
* RB_PIN_ADC2_3_IE - ADC/TKEY 2/3
* RB_PIN_ADC4_5_IE - ADC/TKEY 4/5
*
* @return none
*/

View File

@ -15,9 +15,9 @@
/*********************************************************************
* @fn PWR_DCDCCfg
*
* @brief DC/DC电源
* @brief Enable DC/DC converter, to reduce power consumption
*
* @param s - DCDC电源
* @param s - ENABLE or DISABLE the DC/DC converter
*
* @return none
*/
@ -30,7 +30,7 @@ void PWR_DCDCCfg(FunctionalState s)
{
adj &= ~RB_DCDC_CHARGE;
plan &= ~(RB_PWR_DCDC_EN | RB_PWR_DCDC_PRE); // 旁路 DC/DC
plan &= ~(RB_PWR_DCDC_EN | RB_PWR_DCDC_PRE); // bypass DC/DC
sys_safe_access_enable();
R16_AUX_POWER_ADJ = adj;
R16_POWER_PLAN = plan;
@ -88,10 +88,10 @@ void PWR_UnitModCfg(FunctionalState s, uint8_t unit)
/*********************************************************************
* @fn PWR_PeriphClkCfg
*
* @brief
* @brief Peripheral clock control
*
* @param s -
* @param perph - please refer to Peripher CLK control bit define
* @param s - ENABLE or DISABLE peripheral clock
* @param perph - please refer to Peripheral CLK control bit define
*
* @return none
*/
@ -182,9 +182,9 @@ void PWR_PeriphWakeUpCfg(FunctionalState s, uint8_t perph, WakeUP_ModeypeDef mod
/*********************************************************************
* @fn PowerMonitor
*
* @brief
* @brief Battery Monitoring
*
* @param s -
* @param s - ENABLE or DISABLE
* @param vl - refer to VolM_LevelypeDef
*
* @return none
@ -238,7 +238,7 @@ __HIGH_CODE
void LowPower_Idle(void)
{
FLASH_ROM_SW_RESET();
R8_FLASH_CTRL = 0x04; //flash关闭
R8_FLASH_CTRL = 0x04; // disable flash
PFIC->SCTLR &= ~(1 << 2); // sleep
__WFI();
@ -261,14 +261,14 @@ void LowPower_Halt(void)
uint8_t x32Kpw, x32Mpw;
FLASH_ROM_SW_RESET();
R8_FLASH_CTRL = 0x04; //flash关闭
R8_FLASH_CTRL = 0x04; // disable flash
x32Kpw = R8_XT32K_TUNE;
x32Mpw = R8_XT32M_TUNE;
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150%额定电流
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE驱动电流降低到额定电流
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% current
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE drive current reduced to normal
sys_safe_access_enable();
R8_BAT_DET_CTRL = 0; // 关闭电压监控
R8_BAT_DET_CTRL = 0; // disable battery voltage monitoring
sys_safe_access_disable();
sys_safe_access_enable();
R8_XT32K_TUNE = x32Kpw;
@ -292,11 +292,11 @@ void LowPower_Halt(void)
* Description : -Sleep模式
80M时flash内代码退30us延迟
* Input : rm:
RB_PWR_RAM2K - 2K retention SRAM
RB_PWR_RAM24K - 24K main SRAM
RB_PWR_EXTEND - USB BLE
RB_PWR_XROM - FlashROM
NULL -
RB_PWR_RAM2K - 2K retention SRAM is powered
RB_PWR_RAM24K - 24K main SRAM is powered
RB_PWR_EXTEND - USB & BLE µ¥Ôª±£ÁôÇøÓò¹©µç
RB_PWR_XROM - FlashROM is powered
NULL - All above peripherals are powered off
* Return : None
*******************************************************************************/
__HIGH_CODE
@ -310,11 +310,11 @@ void LowPower_Sleep(uint16_t rm)
x32Kpw = R8_XT32K_TUNE;
x32Mpw = R8_XT32M_TUNE;
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150%额定电流
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE驱动电流降低到额定电流
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% current
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE drive current reduced to normal
sys_safe_access_enable();
R8_BAT_DET_CTRL = 0; // 关闭电压监控
R8_BAT_DET_CTRL = 0; // disable battery voltage monitoring
sys_safe_access_disable();
sys_safe_access_enable();
R8_XT32K_TUNE = x32Kpw;
@ -379,11 +379,11 @@ void LowPower_Shutdown(uint16_t rm)
FLASH_ROM_SW_RESET();
x32Kpw = R8_XT32K_TUNE;
x32Mpw = R8_XT32M_TUNE;
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150%额定电流
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE驱动电流降低到额定电流
x32Mpw = (x32Mpw & 0xfc) | 0x03; // 150% current
x32Kpw = (x32Kpw & 0xfc) | 0x01; // LSE drive current reduced to normal
sys_safe_access_enable();
R8_BAT_DET_CTRL = 0; // 关闭电压监控
R8_BAT_DET_CTRL = 0; // disable battery voltage monitoring
sys_safe_access_disable();
sys_safe_access_enable();
R8_XT32K_TUNE = x32Kpw;

View File

@ -15,9 +15,9 @@
/*********************************************************************
* @fn SetSysClock
*
* @brief
* @brief Configure main system clock
*
* @param sc - refer to SYS_CLKTypeDef
* @param sc - System clock source selection (refer to SYS_CLKTypeDef)
*
* @return none
*/
@ -70,7 +70,7 @@ void SetSysClock(SYS_CLKTypeDef sc)
/*********************************************************************
* @fn GetSysClock
*
* @brief
* @brief Get main system clock configuration
*
* @param none
*
@ -82,15 +82,15 @@ uint32_t GetSysClock(void)
rev = R32_CLK_SYS_CFG & 0xff;
if((rev & 0x40) == (0 << 6))
{ // 32M进行分频
{ // 32M HSE division
return (32000000 / (rev & 0x1f));
}
else if((rev & RB_CLK_SYS_MOD) == (1 << 6))
{ // PLL进行分频
{ // 480M PLL division
return (480000000 / (rev & 0x1f));
}
else
{ // 32K做主频
{ // 32KHz
return (32000);
}
}
@ -119,7 +119,7 @@ uint8_t SYS_GetInfoSta(SYS_InfoStaTypeDef i)
/*********************************************************************
* @fn SYS_ResetExecute
*
* @brief
* @brief Perform a software reset
*
* @param none
*
@ -137,9 +137,9 @@ void SYS_ResetExecute(void)
/*********************************************************************
* @fn SYS_DisableAllIrq
*
* @brief
* @brief Disable all interrupts, get current interrupt values
*
* @param pirqv -
* @param pirqv - Save old interrupt values
*
* @return none
*/
@ -153,9 +153,9 @@ void SYS_DisableAllIrq(uint32_t *pirqv)
/*********************************************************************
* @fn SYS_RecoverIrq
*
* @brief
* @brief Restore all interrupts from previously stored values
*
* @param irq_status -
* @param irq_status - Old interrupt values to restore
*
* @return none
*/
@ -168,11 +168,11 @@ void SYS_RecoverIrq(uint32_t irq_status)
/*********************************************************************
* @fn SYS_GetSysTickCnt
*
* @brief (SYSTICK)
* @brief Get the current SysTick count
*
* @param none
*
* @return
* @return Current SysTick count
*/
uint32_t SYS_GetSysTickCnt(void)
{
@ -255,7 +255,7 @@ void WWDG_ClearFlag(void)
/*********************************************************************
* @fn HardFault_Handler
*
* @brief
* @brief Resets with a power-on reset on a hardware error
*
* @param none
*

View File

@ -0,0 +1,41 @@
/*
* i2c.h
*
* Created on: Oct 13, 2024
* Author: true
*/
#ifndef USER_COMM_I2C_H_
#define USER_COMM_I2C_H_
#ifdef SOFT_I2C_MASTER
#include "soft_i2c_master.h"
#define i2c_init() i2cm_init()
#define i2c_start() i2cm_start()
#define i2c_restart() i2cm_restart()
#define i2c_stop() i2cm_stop()
#define i2c_rd(ack) i2cm_rd(ack)
#define i2c_wr(dat) i2cm_wr(dat)
#define i2c_addr(a, w) i2c_start(); i2cm_addr(a, w)
#define i2c_rdbuf(d, x) i2cm_rdbuf(d, x)
#define i2c_wrbuf(d, x) i2cm_wrbuf(d, x)
#else
#error HW I2C NOT YET IMPLEMENTED!
#endif
#endif /* USER_COMM_I2C_H_ */

View File

@ -1,128 +1,166 @@
/*
* soft_i2c.c
*
* Created on: Oct 11, 2024
* Author: true
* copied some random code and fixed it up for intricacies of CH59x.
*
* i2c hardware peripheral is shared on CH59x with debug pins and is
* not attached to DMA. while having the peripheral would result in
* higher speeds and less power usage, without debug it makes soft I2C
* master a helpful tool.
*
* quirks and features:
* - this implementation is blocking.
* - this implementation uses a hardware timer.
* - clock stretching is not supported.
* - timings likely haven't been tested so long as this message exists.
*/
// ===================================================================================
// Software I2C Master Functions for CH32X035/X034/X033 * v1.1 *
// modified for CH592 by true
// ===================================================================================
//
// Simple I2C bitbanging. ACK bit of the slave is ignored. Clock stretching by the
// slave is not allowed. External pull-up resistors (4k7 - 10k) are mandatory!
//
// Further information: https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// 2023 by Stefan Wagner: https://github.com/wagiminator
//
// This implementation requires 48MHz clock for 400KHz communication.
#include "soft_i2c_master.h"
// ===================================================================================
// I2C Delay
// ===================================================================================
#define I2C_DLY_TICKS_H (((F_CPU * 9) / (I2C_CLKRATE * 25)) - 41)
#define I2C_DLY_TICKS_L (((F_CPU * 16) / (I2C_CLKRATE * 25)) - 76)
#if I2C_DLY_TICKS_H >= 1
#define I2C_DELAY_H() DLY_ticks(I2C_DLY_TICKS_H)
#else
#define I2C_DELAY_H()
#endif
#if I2C_DLY_TICKS_L >= 1
#define I2C_DELAY_L() DLY_ticks(I2C_DLY_TICKS_L)
#else
#define I2C_DELAY_L()
#endif
#define CYCLES_TO_HI 16
#define CYCLES_TO_LO 16
// ===================================================================================
// I2C Pin Macros
// ===================================================================================
#define I2C_SDA_HIGH() PIN_input(PIN_SDA) // release SDA -> pulled HIGH by resistor
#define I2C_SDA_LOW() PIN_output(PIN_SDA) // SDA LOW -> pulled LOW by MCU
#define I2C_SCL_HIGH() PIN_input(PIN_SCL) // release SCL -> pulled HIGH by resistor
#define I2C_SCL_LOW() PIN_output(PIN_SCL) // SCL LOW -> pulled LOW by MCU
#define I2C_SDA_READ() PIN_read(PIN_SDA) // read SDA pin
#define I2C_CLOCKOUT() I2C_DELAY_L();I2C_SCL_HIGH();I2C_DELAY_H();I2C_SCL_LOW()
#define CYCLES_RD 2 // cycles spent in read routine
#define CYCLES_EXTRA_WR_HI 2 // extra cycles spent in write routine
#define CYCLES_EXTRA_WR_LO 4
// ===================================================================================
// I2C Functions
// ===================================================================================
// I2C init function
void I2C_init(void) {
PIN_input(PIN_SCL); // release SCL
PIN_input(PIN_SDA); // release SDA
PIN_low(PIN_SCL); // preset for SCL low
PIN_low(PIN_SDA); // preset for SDA low
static uint16_t delay_hi, delay_lo;
static volatile uint16_t spin;
#define bit_delay_hi() spin = delay_hi; while(spin--)
#define bit_delay_lo() spin = delay_lo; while(spin--)
#define rd_delay() spin = delay_hi - CYCLES_RD; while(spin--)
#define wr_delay_hi() spin = delay_hi - CYCLES_EXTRA_WR_HI; while(spin--)
#define wr_delay_lo() spin = delay_lo - CYCLES_EXTRA_WR_LO; while(spin--)
// re-run init any time the clock speed changes to recalculate delays.
void i2cm_init()
{
uint32_t sysclk;
uint32_t cycles;
// configure GPIO
SCL_IN_HI();
SCL_SET_LO();
SDA_IN_HI();
SDA_SET_LO();
// configure timer
sysclk = GetSysClock();
cycles = sysclk / 500000;
delay_hi = (cycles - CYCLES_TO_HI) / 4;
delay_lo = (cycles - CYCLES_TO_LO) / 4;
}
// I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed
void I2C_write(uint8_t data) {
uint8_t i;
for(i=8; i; i--, data<<=1) { // transmit 8 bits, MSB first
(data & 0x80) ? (I2C_SDA_HIGH()) : (I2C_SDA_LOW()); // SDA HIGH if bit is 1
I2C_CLOCKOUT(); // clock out -> slave reads the bit
}
I2C_SDA_HIGH(); // release SDA for ACK bit of slave
I2C_CLOCKOUT(); // 9th clock pulse is for the ignored ACK bit
void i2cm_start()
{
SDA_IN_HI(); bit_delay_hi();
SCL_IN_HI(); bit_delay_hi();
SDA_OUTLO(); bit_delay_lo();
SCL_OUTLO(); bit_delay_lo();
}
// I2C start transmission
void I2C_start(uint8_t addr) {
I2C_SDA_LOW(); // start condition: SDA goes LOW first
I2C_DELAY_H(); // delay
I2C_SCL_LOW(); // start condition: SCL goes LOW second
I2C_write(addr); // send slave address
void i2cm_restart()
{
SDA_IN_HI(); bit_delay_hi();
SCL_IN_HI();
i2cm_start();
}
// I2C restart transmission
void I2C_restart(uint8_t addr) {
I2C_SDA_HIGH(); // prepare SDA for HIGH to LOW transition
I2C_DELAY_H(); // delay
I2C_SCL_HIGH(); // restart condition: clock HIGH
I2C_start(addr); // start again
void i2cm_stop()
{
SDA_OUTLO(); bit_delay_lo();
SCL_IN_HI(); bit_delay_hi();
SDA_IN_HI(); bit_delay_hi();
}
// I2C stop transmission
void I2C_stop(void) {
I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition
I2C_DELAY_H(); // delay
I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first
I2C_DELAY_H(); // delay
I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second
// returns: data byte
uint8_t i2cm_rd(uint8_t ack)
{
uint8_t x, in = 0;
SDA_IN_HI();
for (x = 8; x; x--) {
in <<= 1; // clock next bit
SCL_IN_HI();
while (!SCL_GET()); // clock stretch
rd_delay();
if (SDA_GET()) in |= 1;
SCL_OUTLO();
}
if (ack) { SDA_OUTLO(); } // ack
else { SDA_IN_HI(); } // nack
SCL_IN_HI(); bit_delay_hi();
SDA_IN_HI();
SCL_OUTLO();
return in;
}
// I2C receive one data byte from the slave (ack=0 for last byte, ack>0 if more bytes to follow)
uint8_t I2C_read(uint8_t ack) {
uint8_t i;
uint8_t data = 0; // variable for the received byte
I2C_SDA_HIGH(); // release SDA -> will be toggled by slave
for(i=8; i; i--) { // receive 8 bits
data <<= 1; // bits shifted in right (MSB first)
I2C_DELAY_L(); // delay
I2C_SCL_HIGH(); // clock HIGH
I2C_DELAY_H(); // delay
if(I2C_SDA_READ()) data |= 1; // read bit
I2C_SCL_LOW(); // clock LOW -> slave prepares next bit
}
if(ack) I2C_SDA_LOW(); // pull SDA LOW to acknowledge (ACK)
I2C_CLOCKOUT(); // clock out -> slave reads ACK bit
return data; // return the received byte
// returns: possible ack from target
uint8_t i2cm_wr(uint8_t dat)
{
uint8_t x;
uint8_t ack;
for (x = 8; x; x--) {
if (dat & 0x80) { SDA_IN_HI(); SCL_IN_HI(); wr_delay_hi(); }
else { SDA_OUTLO(); SCL_OUTLO(); wr_delay_lo(); }
dat <<= 1;
SCL_OUTLO();
}
SDA_IN_HI();
SCL_IN_HI(); bit_delay_hi();
ack = SDA_GET();
SCL_OUTLO();
return ack;
}
// Send data buffer via I2C bus and stop
void I2C_writeBuffer(uint8_t* buf, uint16_t len) {
while(len--) I2C_write(*buf++); // write buffer
I2C_stop(); // stop transmission
// use a left-aligned address with this implementation.
uint8_t i2cm_addr(uint8_t addr, uint8_t write)
{
addr &= ~0x1;
addr |= write ? 0 : 1;
i2cm_wr(addr);
}
// Read data via I2C bus to buffer and stop
void I2C_readBuffer(uint8_t* buf, uint16_t len) {
while(len--) *buf++ = I2C_read(len > 0);
I2C_stop();
void i2cm_rdbuf(uint8_t *dat, uint8_t len)
{
while(len--) *dat++ = i2cm_rd(len > 0);
i2cm_stop();
}
void i2cm_wrbuf(uint8_t *dat, uint8_t len)
{
uint8_t nack;
while(len--) {
nack = i2cm_wr(*dat++);
if (nack) break;
}
i2cm_stop();
}

View File

@ -1,13 +1,55 @@
/*
* soft_i2c.h
*
* Created on: Oct 11, 2024
* Author: true
*/
#ifndef USER_COMM_SOFT_I2C_H_
#define USER_COMM_SOFT_I2C_H_
#ifndef USER_COMM_SOFT_I2C_MASTER_H_
#define USER_COMM_SOFT_I2C_MASTER_H_
#include <CH59x_common.h>
#include <stdint.h>
#endif /* USER_COMM_SOFT_I2C_H_ */
#define SDA_PIN 4
#define SDA_BIT (1 << (SDA_PIN % 8))
#define SDA_DIR_REG R8_PA_DIR_0
#define SDA_DRV_REG R8_PA_PD_DRV_0
#define SDA_CLR_REG R8_PA_CLR_0
#define SDA_GET_REG R8_PA_PIN_0
#define SCL_PIN 12
#define SCL_BIT (1 << (SCL_PIN % 8))
#define SCL_DRV_REG R8_PB_PD_DRV_1
#define SCL_DIR_REG R8_PB_DIR_1
#define SCL_CLR_REG R8_PB_CLR_1
#define SCL_GET_REG R8_PB_PIN_1
#define SDA_IN_HI() { SDA_DRV_REG &= ~SDA_BIT; SDA_DIR_REG &= ~SDA_BIT; }
#define SDA_OUTLO() { SDA_DIR_REG |= SDA_BIT; SDA_DRV_REG |= SDA_BIT; }
#define SDA_SET_LO() SDA_CLR_REG = SDA_BIT
#define SDA_GET() ( SDA_GET_REG & SDA_BIT )
#define SCL_IN_HI() { SCL_DRV_REG &= ~SCL_BIT; SCL_DIR_REG &= ~SCL_BIT; }
#define SCL_OUTLO() { SCL_DIR_REG |= SCL_BIT; SCL_DRV_REG |= SCL_BIT; }
#define SCL_SET_LO() SCL_CLR_REG = SCL_BIT
#define SCL_GET() ( SCL_GET_REG & SCL_BIT )
void i2cm_init();
void i2cm_start();
void i2cm_restart();
void i2cm_stop();
uint8_t i2cm_rd(uint8_t ack);
uint8_t i2cm_wr(uint8_t dat);
uint8_t i2cm_addr(uint8_t addr, uint8_t write);
void i2cm_rdbuf(uint8_t *dat, uint8_t len);
void i2cm_wrbuf(uint8_t *dat, uint8_t len);
#endif /* USER_COMM_SOFT_I2C_MASTER_H_ */

View File

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

View File

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

View File

@ -14,10 +14,14 @@
#include "ch32sub.h"
#include "port_intr.h"
#include "../comm/i2c.h"
volatile uint8_t intr_flag = 0;
void ch32sub_isr()
{
// we'll check what the MCU has to say when we're done processing
@ -45,3 +49,9 @@ void ch32sub_init()
// configure interrupt to be rising edge
GPIOB_ITModeCfg(SUB_INTR_PIN, GPIO_ITMode_RiseEdge);
}
// things to do
void ch32sub_rgb_hwen(uint8_t en)
{
i2c_addr(SUB_I2C_ADDR, 1);
}

View File

@ -14,6 +14,8 @@
#define SUB_I2C_ADDR 0x5e
#define SUB_INTR_PORT GPIOB
#define SUB_INTR_PIN GPIO_Pin_13
#define SUB_INTR_PIN_NR 13
@ -28,6 +30,8 @@
void ch32sub_init();
void ch32sub_process();
void ch32sub_rgb_hwen(uint8_t en);
#endif /* USER_DEVICE_CH32SUB_H_ */

View File

@ -1,8 +1,13 @@
/*
* gat_gpio.c
*
* Created on: Oct 11, 2024
* Author: true
* just gets the GAT ID and does nothing else.
* but if you wanted to do things with the GPIO, this is where you could do it.
*
* hardware peripherals:
* - GP1 has UART0 TX and PWM9.
* - GP2 has UART0 RX and PWM7.
*
*/

View File

@ -0,0 +1,62 @@
/*
* rgbled.c
*
* Created on: Oct 13, 2024
* Author: true
*/
#include <CH59x_common.h>
#include "rgbled.h"
#include "../device/aw20xxx.h"
#define AW20X_DIM 31 // initial global current setting
#define AW20X_COLS 4
#define AW20X_ROWS 9
#define AW20X_FADE_COUNT (AW20X_ROWS * AW20X_COLS)
static const uint16_t pwm_cie_256in_1024out[] = {
0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15,
15, 16, 17, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44,
45, 47, 48, 50, 51, 52, 54, 55, 57, 58, 60, 61, 63, 65, 66, 68,
70, 71, 73, 75, 77, 79, 81, 83, 84, 86, 88, 90, 93, 95, 97, 99,
101, 103, 106, 108, 110, 113, 115, 118, 120, 123, 125, 128, 130, 133, 136, 138,
141, 144, 147, 149, 152, 155, 158, 161, 164, 167, 171, 174, 177, 180, 183, 187,
190, 194, 197, 200, 204, 208, 211, 215, 218, 222, 226, 230, 234, 237, 241, 245,
249, 254, 258, 262, 266, 270, 275, 279, 283, 288, 292, 297, 301, 306, 311, 315,
320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 376, 381, 386, 392, 397,
403, 408, 414, 420, 425, 431, 437, 443, 449, 455, 461, 467, 473, 480, 486, 492,
499, 505, 512, 518, 525, 532, 538, 545, 552, 559, 566, 573, 580, 587, 594, 601,
609, 616, 624, 631, 639, 646, 654, 662, 669, 677, 685, 693, 701, 709, 717, 726,
734, 742, 751, 759, 768, 776, 785, 794, 802, 811, 820, 829, 838, 847, 857, 866,
875, 885, 894, 903, 913, 923, 932, 942, 952, 962, 972, 982, 992, 1002, 1013, 1023,
};
AW20x awled;
static uint8_t awled_fade[AW20X_FADE_COUNT];
static uint8_t led_matrix_updated = 0;
void rgbled_init()
{
volatile uint32_t x;
ch32sub_rgb_hwen(1);
// wait a little while to ensure controller is awake
x = GetSysClock() / 16384;
while (x--);
aw20x_init(&awled, AW20X_ADDR_GND << 1, AW20X_COLS, AW20X_ROWS, AW20X_IMAX_13_3MA);
}

View File

@ -0,0 +1,25 @@
/*
* rgbled.h
*
* Created on: Oct 13, 2024
* Author: true
*/
#ifndef USER_LED_RGBLED_H_
#define USER_LED_RGBLED_H_
#include <stdint.h>
#include "../comm/i2c.h"
#include "../device/ch32sub.h"
void rgbled_init();
#endif /* USER_LED_RGBLED_H_ */

View File

@ -1,19 +1,31 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : Main.c
* Author : WCH
* Version : V1.0
* Date : 2020/08/06
* Description : 1
*********************************************************************************
* 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.
*******************************************************************************/
/*
* true's GAT Nametag
* Rushed SUPERCON 8 EDITION
* main MCU firmware
* 2024 true
*
* the main MCU is responsible for the following:
* - rendering OLED UI
* - rendering LED programs
* - storing fonts
* - communicating with I2C devices, including:
* - oled display, accelerometer
* - eeprom, sub MCU, RGBLED controller
* - implementing USB interface
* - implementing BLE (maybe)
*
*/
#include "CH59x_common.h"
#include "port_intr.h"
#include "comm/i2c.h"
#include "gat/gat_gpio.h"
#include "led/rgbled.h"
#include "device/ch32sub.h"
@ -24,120 +36,34 @@ void ch59x_xtal_conf()
HSECFG_Capacitance(HSECap_14p);
}
uint8_t TxBuff[] = "This is a tx exam\r\n";
uint8_t RxBuff[100];
uint8_t trigB;
/*********************************************************************
* @fn main
*
* @brief
*
* @return none
*/
int main()
{
//uint8_t len;
// configure clock
ch59x_xtal_conf();
SetSysClock(CLK_SOURCE_PLL_32MHz);
SetSysClock(CLK_SOURCE_HSE_16MHz);
// enable DC-DC converter
// enable DC-DC converter; brings significant power saving
PWR_DCDCCfg(ENABLE);
// configure port-based interrupts
// get i2c up and running
i2c_init();
// configure port-based interrupts (used for chsub interrupt, mainly)
port_intr_init();
// configure aux MCU initial settings, attention interrupt
ch32sub_init();
// and enable RGBLED controller hardware pin so the controller can wake up
// configure RGBLED controller
rgbled_init();
// configure GAT aux GPIOs, get gat ID
gat_gpio_init();
// configure GAT i2c slave
/*
// 配置串口1先配置IO口模式再配置串口
GPIOA_SetBits(GPIO_Pin_9);
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出注意先让IO口输出高电平
UART1_DefInit();
#if 1 // 测试串口发送字符串
UART1_SendString(TxBuff, sizeof(TxBuff));
#endif
#if 1 // 查询方式:接收数据后发送出去
while(1)
{
len = UART1_RecvString(RxBuff);
if(len)
{
UART1_SendString(RxBuff, len);
}
}
#endif
#if 0 // 中断方式:接收数据后发送出去
UART1_ByteTrigCfg(UART_7BYTE_TRIG);
trigB = 7;
UART1_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);
PFIC_EnableIRQ(UART1_IRQn);
#endif
*/
while(1) {
// only care about aux MCU when all other processing is done
ch32sub_process();
}
}
/*********************************************************************
* @fn UART1_IRQHandler
*
* @brief UART1中断函数
*
* @return none
*/
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler(void)
{
/*
volatile uint8_t i;
switch(UART1_GetITFlag())
{
case UART_II_LINE_STAT: // 线路状态错误
{
UART1_GetLinSTA();
break;
}
case UART_II_RECV_RDY: // 数据达到设置触发点
for(i = 0; i != trigB; i++)
{
RxBuff[i] = UART1_RecvByte();
UART1_SendByte(RxBuff[i]);
}
break;
case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成
i = UART1_RecvString(RxBuff);
UART1_SendString(RxBuff, i);
break;
case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
break;
case UART_II_MODEM_CHG: // 只支持串口0
break;
default:
break;
}
*/
}

View File

@ -1,8 +1,14 @@
/*
* port_intr.c
*
* Created on: Oct 11, 2024
* Author: true
* notes:
*
* - pins 22 and 23 are shifted into pins 9 and 10,
* so configure these pins if you want to use them.
*
* - maximum real possible then is 22 entries.
* I have no need above 15 so that's the maximum I set.
*
*/
#include "CH59x_common.h"
@ -12,16 +18,25 @@
static void (*cb[2][24])(void) = {0};
#define MAX_PIN 15+1
static void (*cb[2][MAX_PIN-4])(void) = {0};
void port_intr_cb_register(uint8_t port, uint8_t idx, void (*fn)(void))
{
if (idx >= 24) return;
if (idx == 22) idx = 9;
if (idx == 23) idx = 10;
if (idx < 4) return;
if (idx > MAX_PIN) return;
if (port > 2) return;
cb[port][idx] = fn;
cb[port][idx - 4] = fn;
}
void port_intr_init()
@ -46,6 +61,9 @@ void GPIOB_IRQHandler(void)
uint8_t i;
uint16_t flag = R16_PB_INT_IF;
// clear flags
R16_PB_INT_IF = flag;
// high priority actions
// none.
@ -58,6 +76,5 @@ void GPIOB_IRQHandler(void)
}
}
// clear flags
R16_PB_INT_IF = flag;
}