Refactored the included files from the ch32fun project.

+ updated as of 2025-12-23
This commit is contained in:
Jake Goodwin 2025-12-23 14:33:54 -08:00
parent 56e38aeeae
commit 7e0b1e3244
58 changed files with 90369 additions and 56430 deletions

File diff suppressed because it is too large Load diff

18083
inc/ch32h41xhw.h Normal file

File diff suppressed because it is too large Load diff

9094
inc/ch32l103hw.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

6237
inc/ch32v10xhw.h Normal file

File diff suppressed because it is too large Load diff

8450
inc/ch32v20xhw.h Normal file

File diff suppressed because it is too large Load diff

10625
inc/ch32v30xhw.h Normal file

File diff suppressed because it is too large Load diff

5002
inc/ch32x00xhw.h Normal file

File diff suppressed because it is too large Load diff

6245
inc/ch32x03xhw.h Normal file

File diff suppressed because it is too large Load diff

2645
inc/ch5xxhw.h Normal file

File diff suppressed because it is too large Load diff

5187
inc/ch641hw.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,26 @@
// 2023-06-26 recallmenot
// ######## necessities
//######## necessities
// include guards
#ifndef CH32V003_GPIO_BR_H
#define CH32V003_GPIO_BR_H
// includes
#include <stdint.h> //uintN_t support
#include "../ch32fun/ch32fun.h"
#include <stdint.h> //uintN_t support
/*######## library description
This is a speedy and light GPIO library due to
static inlining of most functions
compile-time abstraction
branchless where it counts
static inlining of most functions
compile-time abstraction
branchless where it counts
*/
/*######## library usage and configuration
first, enable the desired port.
@ -94,81 +98,78 @@ Writing `TIMx->SWEVGR |= TIM_UG` will immediately update the shadow register and
*/
// ######## ports, pins and states: use these for the functions below!
#define GPIOv_from_PORT_PIN(GPIO_port_n, pin)
enum GPIO_port_n
{
GPIO_port_A = 0b00,
GPIO_port_C = 0b10,
GPIO_port_D = 0b11,
//######## ports, pins and states: use these for the functions below!
#define GPIOv_from_PORT_PIN( GPIO_port_n, pin )
enum GPIO_port_n {
GPIO_port_A = 0b00,
GPIO_port_C = 0b10,
GPIO_port_D = 0b11,
};
enum GPIO_pinModes
{
GPIO_pinMode_I_floating,
GPIO_pinMode_I_pullUp,
GPIO_pinMode_I_pullDown,
GPIO_pinMode_I_analog,
GPIO_pinMode_O_pushPull,
GPIO_pinMode_O_openDrain,
GPIO_pinMode_O_pushPullMux,
GPIO_pinMode_O_openDrainMux,
enum GPIO_pinModes {
GPIO_pinMode_I_floating,
GPIO_pinMode_I_pullUp,
GPIO_pinMode_I_pullDown,
GPIO_pinMode_I_analog,
GPIO_pinMode_O_pushPull,
GPIO_pinMode_O_openDrain,
GPIO_pinMode_O_pushPullMux,
GPIO_pinMode_O_openDrainMux,
};
enum lowhigh
{
low,
high,
enum lowhigh {
low,
high,
};
// analog inputs
enum GPIO_analog_inputs
{
GPIO_Ain0_A2,
GPIO_Ain1_A1,
GPIO_Ain2_C4,
GPIO_Ain3_D2,
GPIO_Ain4_D3,
GPIO_Ain5_D5,
GPIO_Ain6_D6,
GPIO_Ain7_D4,
GPIO_AinVref,
GPIO_AinVcal,
enum GPIO_analog_inputs {
GPIO_Ain0_A2,
GPIO_Ain1_A1,
GPIO_Ain2_C4,
GPIO_Ain3_D2,
GPIO_Ain4_D3,
GPIO_Ain5_D5,
GPIO_Ain6_D6,
GPIO_Ain7_D4,
GPIO_AinVref,
GPIO_AinVcal,
};
// how many cycles the ADC shall sample the input for (speed vs precision)
enum GPIO_ADC_sampletimes
{
GPIO_ADC_sampletime_3cy,
GPIO_ADC_sampletime_9cy,
GPIO_ADC_sampletime_15cy,
GPIO_ADC_sampletime_30cy,
GPIO_ADC_sampletime_43cy,
GPIO_ADC_sampletime_57cy,
GPIO_ADC_sampletime_73cy,
GPIO_ADC_sampletime_241cy_default,
enum GPIO_ADC_sampletimes {
GPIO_ADC_sampletime_3cy,
GPIO_ADC_sampletime_9cy,
GPIO_ADC_sampletime_15cy,
GPIO_ADC_sampletime_30cy,
GPIO_ADC_sampletime_43cy,
GPIO_ADC_sampletime_57cy,
GPIO_ADC_sampletime_73cy,
GPIO_ADC_sampletime_241cy_default,
};
enum GPIO_tim1_output_sets
{
GPIO_tim1_output_set_0__D2_A1_C3_C4__D0_A2_D1,
GPIO_tim1_output_set_1__C6_C7_C0_D3__C3_C4_D1,
GPIO_tim1_output_set_2__D2_A1_C3_C4__D0_A2_D1,
GPIO_tim1_output_set_3__C4_C7_C5_D4__C3_D2_C6,
enum GPIO_tim1_output_sets {
GPIO_tim1_output_set_0__D2_A1_C3_C4__D0_A2_D1,
GPIO_tim1_output_set_1__C6_C7_C0_D3__C3_C4_D1,
GPIO_tim1_output_set_2__D2_A1_C3_C4__D0_A2_D1,
GPIO_tim1_output_set_3__C4_C7_C5_D4__C3_D2_C6,
};
enum GPIO_tim2_output_sets
{
GPIO_tim2_output_set_0__D4_D3_C0_D7,
GPIO_tim2_output_set_1__C5_C2_D2_C1,
GPIO_tim2_output_set_2__C1_D3_C0_D7,
GPIO_tim2_output_set_3__C1_C7_D6_D5,
enum GPIO_tim2_output_sets {
GPIO_tim2_output_set_0__D4_D3_C0_D7,
GPIO_tim2_output_set_1__C5_C2_D2_C1,
GPIO_tim2_output_set_2__C1_D3_C0_D7,
GPIO_tim2_output_set_3__C1_C7_D6_D5,
};
// ######## interface function overview: use these!
// most functions have been reduced to function-like macros, actual definitions downstairs
//######## interface function overview: use these!
// most functions have been reduced to function-like macros, actual definitions downstairs
// setup
#define GPIO_port_enable(GPIO_port_n)
@ -201,66 +202,72 @@ static inline void GPIO_tim2_init();
#define GPIO_tim1_analogWrite(channel, value)
#define GPIO_tim2_analogWrite(channel, value)
// ######## internal function declarations
// ######## internal variables
// ######## preprocessor macros
//######## internal function declarations
#define CONCAT(a, b) a##b
//######## internal variables
//######## preprocessor macros
#define CONCAT(a, b) a ## b
#define CONCAT_INDIRECT(a, b) CONCAT(a, b)
#undef GPIOv_from_PORT_PIN
#define GPIOv_from_PORT_PIN(GPIO_port_n, pin) ((GPIO_port_n << 4) | (pin))
#define GPIOv_to_PORT(GPIOv) (GPIOv >> 4)
#define GPIOv_to_PIN(GPIOv) (GPIOv & 0b1111)
#define GPIOv_to_GPIObase(GPIOv) ((GPIO_TypeDef *)(uintptr_t)((GPIOA_BASE + (0x400 * (GPIOv >> 4)))))
#define GPIOv_from_PORT_PIN( GPIO_port_n, pin ) ((GPIO_port_n << 4 ) | (pin))
#define GPIOv_to_PORT( GPIOv ) (GPIOv >> 4 )
#define GPIOv_to_PIN( GPIOv ) (GPIOv & 0b1111)
#define GPIOv_to_GPIObase( GPIOv ) ((GPIO_TypeDef*)(uintptr_t)((GPIOA_BASE + (0x400 * (GPIOv >> 4)))))
#define GPIOx_to_port_n2(GPIOx) GPIOx_to_port_n_##GPIOx
#define GPIOx_to_port_n(GPIOx) GPIOx_to_port_n2(GPIOx)
#define GPIOx_to_port_n_GPIO_port_A 0b00
#define GPIOx_to_port_n_GPIO_port_C 0b10
#define GPIOx_to_port_n_GPIO_port_D 0b11
#define GPIOx_to_port_n2(GPIOx) GPIOx_to_port_n_##GPIOx
#define GPIOx_to_port_n(GPIOx) GPIOx_to_port_n2(GPIOx)
#define GPIOx_to_port_n_GPIO_port_A 0b00
#define GPIOx_to_port_n_GPIO_port_C 0b10
#define GPIOx_to_port_n_GPIO_port_D 0b11
#define GPIO_port_n_to_GPIOx2(GPIO_port_n) GPIO_port_n_to_GPIOx_##GPIO_port_n
#define GPIO_port_n_to_GPIOx(GPIO_port_n) GPIO_port_n_to_GPIOx2(GPIO_port_n)
#define GPIO_port_n_to_GPIOx_GPIO_port_A GPIOA
#define GPIO_port_n_to_GPIOx_GPIO_port_C GPIOC
#define GPIO_port_n_to_GPIOx_GPIO_port_D GPIOD
#define GPIO_port_n_to_GPIOx2(GPIO_port_n) GPIO_port_n_to_GPIOx_##GPIO_port_n
#define GPIO_port_n_to_GPIOx(GPIO_port_n) GPIO_port_n_to_GPIOx2(GPIO_port_n)
#define GPIO_port_n_to_GPIOx_GPIO_port_A GPIOA
#define GPIO_port_n_to_GPIOx_GPIO_port_C GPIOC
#define GPIO_port_n_to_GPIOx_GPIO_port_D GPIOD
#define GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph_##GPIO_port_n
#define GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n)
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_A RCC_APB2Periph_GPIOA
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_C RCC_APB2Periph_GPIOC
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_D RCC_APB2Periph_GPIOD
#define GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph_##GPIO_port_n
#define GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n)
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_A RCC_APB2Periph_GPIOA
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_C RCC_APB2Periph_GPIOC
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_D RCC_APB2Periph_GPIOD
#define GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG_##GPIO_pinMode(GPIO_Speed)
#define GPIO_pinMode_to_CFG(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_floating(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_FLOATING)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullUp(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullDown(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_analog(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_ANALOG)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPull(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrain(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_OD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPullMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP_AF)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrainMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_IN_ANALOG)
#define GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG_##GPIO_pinMode(GPIO_Speed)
#define GPIO_pinMode_to_CFG(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_floating(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_FLOATING)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullUp(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullDown(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_analog(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_ANALOG)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPull(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrain(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_OD)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPullMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP_AF)
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrainMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_IN_ANALOG)
#define GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD_##GPIO_pinMode(GPIOv)
#define GPIO_pinMode_set_PUPD(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv)
#define GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD_##GPIO_pinMode(GPIOv)
#define GPIO_pinMode_set_PUPD(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_floating(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (GPIOv_to_PIN(GPIOv) + 16))
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (GPIOv_to_PIN(GPIOv) + 16))
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_analog(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrainMux(GPIOv)
#define GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD_##GPIO_pinMode(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD_##GPIO_pinMode(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_floating(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b11111111
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b00000000
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b11111111
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b00000000
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_analog(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(GPIO_port_n)
@ -280,74 +287,91 @@ static inline void GPIO_tim2_init();
#endif
#if !defined(GPIO_timer_prescaler)
#define GPIO_timer_prescaler TIM_CKD_DIV2 // APB_CLOCK / 1024 / 2 = 23.4kHz
#define GPIO_timer_prescaler TIM_CKD_DIV2 // APB_CLOCK / 1024 / 2 = 23.4kHz
#endif
// ######## define requirements / maintenance defines
//######## define requirements / maintenance defines
//######## small function definitions, static inline
// ######## small function definitions, static inline
#undef GPIO_port_enable
#define GPIO_port_enable(GPIO_port_n) RCC->APB2PCENR |= GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n);
#define GPIO_port_pinMode(GPIO_port_n, pinMode, GPIO_Speed) ({ \
GPIO_port_n_to_GPIOx(GPIO_port_n)->CFGLR = (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 0)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 1)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 2)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 3)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 4)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 5)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 6)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 7)); \
GPIO_port_pinMode_set_PUPD(pinMode, GPIO_port_n); \
#define GPIO_port_pinMode(GPIO_port_n, pinMode, GPIO_Speed) ({ \
GPIO_port_n_to_GPIOx(GPIO_port_n)->CFGLR = (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 0)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 1)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 2)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 3)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 4)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 5)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 6)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 7)); \
GPIO_port_pinMode_set_PUPD(pinMode, GPIO_port_n); \
})
#undef GPIO_port_digitalWrite
#define GPIO_port_digitalWrite(GPIO_port_n, byte) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = byte
#define GPIO_port_digitalWrite(GPIO_port_n, byte) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = byte
#undef GPIO_port_digitalRead
#define GPIO_port_digitalRead(GPIO_port_n) (GPIO_port_n_to_GPIOx(GPIO_port_n)->INDR & 0b11111111)
#define GPIO_port_digitalRead(GPIO_port_n) (GPIO_port_n_to_GPIOx(GPIO_port_n)->INDR & 0b11111111)
#undef GPIO_pinMode
#define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed) ({ \
GPIOv_to_GPIObase(GPIOv)->CFGLR &= ~(0b1111 << (4 * GPIOv_to_PIN(GPIOv))); \
GPIOv_to_GPIObase(GPIOv)->CFGLR |= (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * GPIOv_to_PIN(GPIOv))); \
GPIO_pinMode_set_PUPD(pinMode, GPIOv); \
#define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed) ({ \
GPIOv_to_GPIObase(GPIOv)->CFGLR &= ~(0b1111 << (4 * GPIOv_to_PIN(GPIOv))); \
GPIOv_to_GPIObase(GPIOv)->CFGLR |= (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * GPIOv_to_PIN(GPIOv))); \
GPIO_pinMode_set_PUPD(pinMode, GPIOv); \
})
#undef GPIO_digitalWrite_hi
#define GPIO_digitalWrite_hi(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
#define GPIO_digitalWrite_hi(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
#undef GPIO_digitalWrite_lo
#define GPIO_digitalWrite_lo(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (16 + GPIOv_to_PIN(GPIOv)))
#define GPIO_digitalWrite_lo(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (16 + GPIOv_to_PIN(GPIOv)))
#undef GPIO_digitalWrite
#define GPIO_digitalWrite(GPIOv, lowhigh) GPIO_digitalWrite_##lowhigh(GPIOv)
#define GPIO_digitalWrite_low(GPIOv) GPIO_digitalWrite_lo(GPIOv)
#define GPIO_digitalWrite_0(GPIOv) GPIO_digitalWrite_lo(GPIOv)
#define GPIO_digitalWrite_high(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#define GPIO_digitalWrite_1(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#define GPIO_digitalWrite(GPIOv, lowhigh) GPIO_digitalWrite_##lowhigh(GPIOv)
#define GPIO_digitalWrite_low(GPIOv) GPIO_digitalWrite_lo(GPIOv)
#define GPIO_digitalWrite_0(GPIOv) GPIO_digitalWrite_lo(GPIOv)
#define GPIO_digitalWrite_high(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#define GPIO_digitalWrite_1(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#undef GPIO_digitalWrite_branching
#define GPIO_digitalWrite_branching(GPIOv, lowhigh) (lowhigh ? GPIO_digitalWrite_hi(GPIOv) : GPIO_digitalWrite_lo(GPIOv))
#define GPIO_digitalWrite_branching(GPIOv, lowhigh) (lowhigh ? (GPIO_digitalWrite_hi(GPIOv)) : (GPIO_digitalWrite_lo(GPIOv)))
#undef GPIO_digitalRead
#define GPIO_digitalRead(GPIOv) ((GPIOv_to_GPIObase(GPIOv)->INDR >> GPIOv_to_PIN(GPIOv)) & 0b1)
#define GPIO_digitalRead(GPIOv) ((GPIOv_to_GPIObase(GPIOv)->INDR >> GPIOv_to_PIN(GPIOv)) & 0b1)
#undef GPIO_ADC_set_sampletime
// 0:7 => 3/9/15/30/43/57/73/241 cycles
#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \
ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \
#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \
ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \
})
#undef GPIO_ADC_set_sampletimes_all
#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= 0; \
ADC1->SAMPTR2 |= \
GPIO_ADC_sampletime << (0 * 3) | GPIO_ADC_sampletime << (1 * 3) | GPIO_ADC_sampletime << (2 * 3) | GPIO_ADC_sampletime << (3 * 3) | GPIO_ADC_sampletime << (4 * 3) | GPIO_ADC_sampletime << (5 * 3) | GPIO_ADC_sampletime << (6 * 3) | GPIO_ADC_sampletime << (7 * 3) | GPIO_ADC_sampletime << (8 * 3) | GPIO_ADC_sampletime << (9 * 3); \
ADC1->SAMPTR1 &= 0; \
ADC1->SAMPTR1 |= \
GPIO_ADC_sampletime << (0 * 3) | GPIO_ADC_sampletime << (1 * 3) | GPIO_ADC_sampletime << (2 * 3) | GPIO_ADC_sampletime << (3 * 3) | GPIO_ADC_sampletime << (4 * 3) | GPIO_ADC_sampletime << (5 * 3); \
#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= 0; \
ADC1->SAMPTR2 |= \
GPIO_ADC_sampletime << (0 * 3) \
| GPIO_ADC_sampletime << (1 * 3) \
| GPIO_ADC_sampletime << (2 * 3) \
| GPIO_ADC_sampletime << (3 * 3) \
| GPIO_ADC_sampletime << (4 * 3) \
| GPIO_ADC_sampletime << (5 * 3) \
| GPIO_ADC_sampletime << (6 * 3) \
| GPIO_ADC_sampletime << (7 * 3) \
| GPIO_ADC_sampletime << (8 * 3) \
| GPIO_ADC_sampletime << (9 * 3); \
ADC1->SAMPTR1 &= 0; \
ADC1->SAMPTR1 |= \
GPIO_ADC_sampletime << (0 * 3) \
| GPIO_ADC_sampletime << (1 * 3) \
| GPIO_ADC_sampletime << (2 * 3) \
| GPIO_ADC_sampletime << (3 * 3) \
| GPIO_ADC_sampletime << (4 * 3) \
| GPIO_ADC_sampletime << (5 * 3); \
})
#undef GPIO_ADC_set_power
@ -357,137 +381,133 @@ static inline void GPIO_tim2_init();
#define GPIO_ADC_set_power_0 ADC1->CTLR2 &= ~(ADC_ADON)
#undef GPIO_ADC_calibrate
#define GPIO_ADC_calibrate() ({ \
ADC1->CTLR2 |= ADC_RSTCAL; \
while (ADC1->CTLR2 & ADC_RSTCAL) \
; \
ADC1->CTLR2 |= ADC_CAL; \
while (ADC1->CTLR2 & ADC_CAL) \
; \
#define GPIO_ADC_calibrate() ({ \
ADC1->CTLR2 |= ADC_RSTCAL; \
while(ADC1->CTLR2 & ADC_RSTCAL); \
ADC1->CTLR2 |= ADC_CAL; \
while(ADC1->CTLR2 & ADC_CAL); \
})
// large but will likely only ever be called once
static inline void GPIO_ADCinit()
{
// select ADC clock source
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2
RCC->CFGR0 &= ~(0x1F << 11);
static inline void GPIO_ADCinit() {
// select ADC clock source
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2
RCC->CFGR0 &= ~(0x1F<<11);
// enable clock to the ADC
RCC->APB2PCENR |= RCC_APB2Periph_ADC1;
// enable clock to the ADC
RCC->APB2PCENR |= RCC_APB2Periph_ADC1;
// Reset the ADC to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
// Reset the ADC to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
// set sampling time for all inputs to 241 cycles
GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime);
// set sampling time for all inputs to 241 cycles
GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime);
// set trigger to software
ADC1->CTLR2 |= ADC_EXTSEL;
// set trigger to software
ADC1->CTLR2 |= ADC_EXTSEL;
// pre-clear conversion queue
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
ADC1->RSQR3 = 0;
// pre-clear conversion queue
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
ADC1->RSQR3 = 0;
// power the ADC
GPIO_ADC_set_power(1);
GPIO_ADC_calibrate();
// power the ADC
GPIO_ADC_set_power(1);
GPIO_ADC_calibrate();
}
static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input)
{
// set mux to selected input
ADC1->RSQR3 = input;
// allow everything to precharge
Delay_Us(GPIO_ADC_MUX_DELAY);
// start sw conversion (auto clears)
ADC1->CTLR2 |= ADC_SWSTART;
// wait for conversion complete
while (!(ADC1->STATR & ADC_EOC)) {}
// get result
return ADC1->RDATAR;
static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input) {
// set mux to selected input
ADC1->RSQR3 = input;
// allow everything to precharge
Delay_Us(GPIO_ADC_MUX_DELAY);
// start sw conversion (auto clears)
ADC1->CTLR2 |= ADC_SWSTART;
// wait for conversion complete
while(!(ADC1->STATR & ADC_EOC)) {}
// get result
return ADC1->RDATAR;
}
#undef GPIO_tim1_map
#define GPIO_tim1_map(GPIO_tim1_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \
#define GPIO_tim1_map(GPIO_tim1_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \
})
#undef GPIO_tim2_map
#define GPIO_tim2_map(GPIO_tim2_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \
#define GPIO_tim2_map(GPIO_tim2_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \
})
static inline void GPIO_tim1_init()
{
// enable TIM1
RCC->APB2PCENR |= RCC_APB2Periph_TIM1;
// reset TIM1 to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
// SMCFGR: default clk input is CK_INT
// set clock prescaler divider
TIM1->PSC = GPIO_timer_prescaler;
// set PWM total cycle width
TIM1->ATRLR = GPIO_timer_resolution;
// CTLR1: default is up, events generated, edge align
// enable auto-reload of preload
TIM1->CTLR1 |= TIM_ARPE;
// initialize counter
TIM1->SWEVGR |= TIM_UG;
// disengage brake
TIM1->BDTR |= TIM_MOE;
// Enable TIM1
TIM1->CTLR1 |= TIM_CEN;
static inline void GPIO_tim1_init() {
// enable TIM1
RCC->APB2PCENR |= RCC_APB2Periph_TIM1;
// reset TIM1 to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
// SMCFGR: default clk input is CK_INT
// set clock prescaler divider
TIM1->PSC = GPIO_timer_prescaler;
// set PWM total cycle width
TIM1->ATRLR = GPIO_timer_resolution;
// CTLR1: default is up, events generated, edge align
// enable auto-reload of preload
TIM1->CTLR1 |= TIM_ARPE;
// initialize counter
TIM1->SWEVGR |= TIM_UG;
// disengage brake
TIM1->BDTR |= TIM_MOE;
// Enable TIM1
TIM1->CTLR1 |= TIM_CEN;
}
static inline void GPIO_tim2_init()
{
// enable TIM2
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
// reset TIM2 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
// SMCFGR: default clk input is CK_INT
// set clock prescaler divider
TIM2->PSC = GPIO_timer_prescaler;
// set PWM total cycle width
TIM2->ATRLR = GPIO_timer_resolution;
// CTLR1: default is up, events generated, edge align
// enable auto-reload of preload
TIM2->CTLR1 |= TIM_ARPE;
// initialize counter
TIM2->SWEVGR |= TIM_UG;
// Enable TIM2
TIM2->CTLR1 |= TIM_CEN;
static inline void GPIO_tim2_init() {
// enable TIM2
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
// reset TIM2 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
// SMCFGR: default clk input is CK_INT
// set clock prescaler divider
TIM2->PSC = GPIO_timer_prescaler;
// set PWM total cycle width
TIM2->ATRLR = GPIO_timer_resolution;
// CTLR1: default is up, events generated, edge align
// enable auto-reload of preload
TIM2->CTLR1 |= TIM_ARPE;
// initialize counter
TIM2->SWEVGR |= TIM_UG;
// Enable TIM2
TIM2->CTLR1 |= TIM_CEN;
}
#define GPIO_timer_channel_set2(timer, channel) GPIO_timer_channel_set_##channel(timer)
#define GPIO_timer_channel_set(timer, channel) GPIO_timer_channel_set2(timer, channel)
#define GPIO_timer_channel_set_1(timer) timer->CHCTLR1 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
#define GPIO_timer_channel_set_2(timer) timer->CHCTLR1 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
#define GPIO_timer_channel_set_3(timer) timer->CHCTLR2 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
#define GPIO_timer_channel_set_4(timer) timer->CHCTLR2 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
#define GPIO_timer_channel_set2(timer, channel) GPIO_timer_channel_set_##channel(timer)
#define GPIO_timer_channel_set(timer, channel) GPIO_timer_channel_set2(timer, channel)
#define GPIO_timer_channel_set_1(timer) timer->CHCTLR1 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
#define GPIO_timer_channel_set_2(timer) timer->CHCTLR1 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
#define GPIO_timer_channel_set_3(timer) timer->CHCTLR2 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
#define GPIO_timer_channel_set_4(timer) timer->CHCTLR2 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
#undef GPIO_tim1_enableCH
#define GPIO_tim1_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM1, channel); \
TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
#define GPIO_tim1_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM1, channel); \
TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
})
#undef GPIO_tim2_enableCH
#define GPIO_tim2_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM2, channel); \
TIM2->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
#define GPIO_tim2_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM2, channel); \
TIM2->CCER |= (TIM_OutputState_Enable ) << (4 * (channel - 1)); \
})
#define GPIO_timer_CVR(channel) CONCAT_INDIRECT(CH, CONCAT_INDIRECT(channel, CVR))
#define GPIO_timer_CVR(channel) CONCAT_INDIRECT(CH, CONCAT_INDIRECT(channel, CVR))
#undef GPIO_tim1_analogWrite
#define GPIO_tim1_analogWrite(channel, value) TIM1->GPIO_timer_CVR(channel) = value;
#define GPIO_tim1_analogWrite(channel, value) TIM1->GPIO_timer_CVR(channel) = value;
#undef GPIO_tim2_analogWrite
#define GPIO_tim2_analogWrite(channel, value) TIM2->GPIO_timer_CVR(channel) = value;
#define GPIO_tim2_analogWrite(channel, value) TIM2->GPIO_timer_CVR(channel) = value;
#endif // CH32V003_GPIO_BR_H

View file

@ -1,15 +1,15 @@
// ######## necessities
//######## necessities
// include guards
#ifndef CH32V003_SPI_H
#define CH32V003_SPI_H
// includes
#include<stdint.h> //uintN_t support
#include "ch32fun.h"
#include <stdint.h> //uintN_t support
#ifndef APB_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#endif
/*######## library usage and configuration
@ -20,7 +20,7 @@ SYSTEM_CORE_CLOCK and APB_CLOCK should be defined already as APB_CLOCK is used b
#ifndef APB_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#endif
to enable using the functions of this library:
@ -47,8 +47,10 @@ then pick the desired setting of each group:
#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually!
*/
// ######## function overview (declarations): use these!
// initialize and configure the SPI peripheral
//######## function overview (declarations): use these!
// initialize and configure the SPI peripheral
static inline void SPI_init();
// establish / end a connection to the SPI device
@ -65,14 +67,14 @@ static inline void SPI_NSS_software_high();
// read / write the SPI device
// these commands are raw, you'll have to consider all other steps in SPI_transfer!
static inline uint8_t SPI_read_8();
static inline uint8_t SPI_read_8();
static inline uint16_t SPI_read_16();
static inline void SPI_write_8(uint8_t data);
static inline void SPI_write_16(uint16_t data);
static inline void SPI_write_8(uint8_t data);
static inline void SPI_write_16(uint16_t data);
// send a command and get a response from the SPI device
// you'll use this for most devices
static inline uint8_t SPI_transfer_8(uint8_t data);
static inline uint8_t SPI_transfer_8(uint8_t data);
static inline uint16_t SPI_transfer_16(uint16_t data);
// SPI peripheral power enable / disable (default off, init() automatically enables)
@ -85,25 +87,31 @@ static inline void SPI_poweron();
static inline void kill_interrrupts();
static inline void restore_interrupts();
// ######## internal function declarations
static inline void SPI_wait_TX_complete();
static inline uint8_t SPI_is_RX_empty();
static inline void SPI_wait_RX_available();
// ######## internal variables
//######## internal function declarations
static inline void SPI_wait_TX_complete();
static inline uint8_t SPI_is_RX_empty();
static inline void SPI_wait_RX_available();
//######## internal variables
static uint16_t EXT1_INTENR_backup;
// ######## preprocessor macros
// min and max helper macros
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
//######## preprocessor macros
// min and max helper macros
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
// stringify for displaying what #defines evaluated to at preprocessor stage
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "=" VALUE(var)
#define VAR_NAME_VALUE(var) #var "=" VALUE(var)
// compile-time log2
//compile-time log2
#define LOG2(x) ((x) == 0 ? -1 : __builtin_ctz(x))
// compile-time clock prescaler calculation: log2(APB_CLOCK/SPEED_BUS)
@ -112,249 +120,233 @@ static uint16_t EXT1_INTENR_backup;
// ensure that CLOCK_PRESCALER_VALUE is within the range of 0..7
_Static_assert(SPI_CLK_PRESCALER >= 0 && SPI_CLK_PRESCALER <= 7, "SPI_CLK_PRESCALER is out of range (0..7). Please set a different SPI bus speed. prescaler = log2(f_CPU/f_SPI)");
// #pragma message(VAR_NAME_VALUE(SPI_CLK_PRESCALER))
#pragma message(VAR_NAME_VALUE(SPI_CLK_PRESCALER))
// ######## preprocessor #define requirements
//######## preprocessor #define requirements
#if !defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && !defined(CH32V003_SPI_DIRECTION_1LINE_TX)
#warning "none of the CH32V003_SPI_DIRECTION_ options were defined!"
#warning "none of the CH32V003_SPI_DIRECTION_ options were defined!"
#endif
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && defined(CH32V003_SPI_DIRECTION_1LINE_TX)
#warning "both CH32V003_SPI_DIRECTION_ options were defined!"
#warning "both CH32V003_SPI_DIRECTION_ options were defined!"
#endif
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!"
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!"
#endif
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!"
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!"
#endif
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_NSS_ options were defined!"
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_NSS_ options were defined!"
#endif
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_NSS_ options were defined!"
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_NSS_ options were defined!"
#endif
// ######## small function definitions, static inline
static inline void SPI_init()
{
SPI_poweron();
// reset control register
SPI1->CTLR1 = 0;
// set prescaler
SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER << 3);
//######## small function definitions, static inline
static inline void SPI_init() {
SPI_poweron();
// reset control register
SPI1->CTLR1 = 0;
// set clock polarity and phase
#if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL0_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge);
#endif
// set prescaler
SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER<<3);
// configure NSS pin, master mode
#if defined(CH32V003_SPI_NSS_HARDWARE_PC0)
// _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf << (4 * 0));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 0);
AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0)
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_HARDWARE_PC1)
// NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf << (4 * 1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 1);
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf << (4 * 3));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 3);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf << (4 * 4));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
#endif
// set clock polarity and phase
#if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge);
#elif defined (CH32V003_SPI_CLK_MODE_POL0_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge);
#elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge);
#elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge);
#endif
// configure NSS pin, master mode
#if defined(CH32V003_SPI_NSS_HARDWARE_PC0)
// _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf<<(4*0));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*0);
AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0)
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_HARDWARE_PC1)
// NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf<<(4*1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*1);
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf<<(4*3));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*3);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf<<(4*4));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
#endif
// SCK on PC5, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf << (4 * 5));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5);
// SCK on PC5, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf<<(4*5));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
// CH32V003 is master
SPI1->CTLR1 |= SPI_Mode_Master;
// CH32V003 is master
SPI1->CTLR1 |= SPI_Mode_Master;
// set data direction and configure data pins
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX)
SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex;
// set data direction and configure data pins
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX)
SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex;
// MOSI on PC6, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf<<(4*6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6);
// MISO on PC7, 10MHz input, floating
GPIOC->CFGLR &= ~(0xf<<(4*7));
GPIOC->CFGLR |= GPIO_CNF_IN_FLOATING<<(4*7);
#elif defined(CH32V003_SPI_DIRECTION_1LINE_TX)
SPI1->CTLR1 |= SPI_Direction_1Line_Tx;
// MOSI on PC6, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
// MISO on PC7, 10MHz input, floating
GPIOC->CFGLR &= ~(0xf << (4 * 7));
GPIOC->CFGLR |= GPIO_CNF_IN_FLOATING << (4 * 7);
#elif defined(CH32V003_SPI_DIRECTION_1LINE_TX)
SPI1->CTLR1 |= SPI_Direction_1Line_Tx;
// MOSI on PC6, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
#endif
// MOSI on PC6, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf<<(4*6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6);
#endif
}
static inline void SPI_begin_8()
{
SPI1->CTLR1 &= ~(SPI_CTLR1_DFF); // DFF 16bit data-length enable, writable only when SPE is 0
SPI1->CTLR1 |= SPI_CTLR1_SPE;
static inline void SPI_begin_8() {
SPI1->CTLR1 &= ~(SPI_CTLR1_DFF); // DFF 16bit data-length enable, writable only when SPE is 0
SPI1->CTLR1 |= SPI_CTLR1_SPE;
}
static inline void SPI_begin_16()
{
SPI1->CTLR1 |= SPI_CTLR1_DFF; // DFF 16bit data-length enable, writable only when SPE is 0
SPI1->CTLR1 |= SPI_CTLR1_SPE;
static inline void SPI_begin_16() {
SPI1->CTLR1 |= SPI_CTLR1_DFF; // DFF 16bit data-length enable, writable only when SPE is 0
SPI1->CTLR1 |= SPI_CTLR1_SPE;
}
static inline void SPI_end()
{
SPI1->CTLR1 &= ~(SPI_CTLR1_SPE);
static inline void SPI_end() {
SPI1->CTLR1 &= ~(SPI_CTLR1_SPE);
}
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
static inline void SPI_NSS_software_high()
{
GPIOC->BSHR = (1 << 3);
static inline void SPI_NSS_software_high() {
GPIOC->BSHR = (1<<3);
}
static inline void SPI_NSS_software_low()
{
GPIOC->BSHR = (1 << (16 + 3));
static inline void SPI_NSS_software_low() {
GPIOC->BSHR = (1<<(16+3));
}
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
static inline void SPI_NSS_software_high()
{
GPIOC->BSHR = (1 << 4);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
static inline void SPI_NSS_software_high() {
GPIOC->BSHR = (1<<4);
}
static inline void SPI_NSS_software_low()
{
GPIOC->BSHR = (1 << (16 + 4));
static inline void SPI_NSS_software_low() {
GPIOC->BSHR = (1<<(16+4));
}
#endif
static inline uint8_t SPI_read_8()
{
return SPI1->DATAR;
static inline uint8_t SPI_read_8() {
return SPI1->DATAR;
}
static inline uint16_t SPI_read_16()
{
return SPI1->DATAR;
static inline uint16_t SPI_read_16() {
return SPI1->DATAR;
}
static inline void SPI_write_8(uint8_t data)
{
SPI1->DATAR = data;
static inline void SPI_write_8(uint8_t data) {
SPI1->DATAR = data;
}
static inline void SPI_write_16(uint16_t data)
{
SPI1->DATAR = data;
static inline void SPI_write_16(uint16_t data) {
SPI1->DATAR = data;
}
static inline uint8_t SPI_transfer_8(uint8_t data)
{
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_high();
#endif
SPI_write_8(data);
SPI_wait_TX_complete();
asm volatile("nop");
SPI_wait_RX_available();
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_low();
#endif
return SPI_read_8();
static inline uint8_t SPI_transfer_8(uint8_t data) {
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_high();
#endif
SPI_write_8(data);
SPI_wait_TX_complete();
asm volatile("nop");
SPI_wait_RX_available();
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_low();
#endif
return SPI_read_8();
}
static inline uint16_t SPI_transfer_16(uint16_t data)
{
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_high();
#endif
SPI_write_16(data);
SPI_wait_TX_complete();
asm volatile("nop");
SPI_wait_RX_available();
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_low();
#endif
return SPI_read_16();
static inline uint16_t SPI_transfer_16(uint16_t data) {
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_high();
#endif
SPI_write_16(data);
SPI_wait_TX_complete();
asm volatile("nop");
SPI_wait_RX_available();
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI_NSS_software_low();
#endif
return SPI_read_16();
}
static inline void SPI_poweroff()
{
SPI_end();
RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1;
static inline void SPI_poweroff() {
SPI_end();
RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1;
}
static inline void SPI_poweron()
{
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
static inline void SPI_poweron() {
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
}
static inline void kill_interrrupts()
{
EXT1_INTENR_backup = EXTI->INTENR;
// zero the interrupt enable register to disable all interrupts
EXTI->INTENR = 0;
static inline void kill_interrrupts() {
EXT1_INTENR_backup = EXTI->INTENR;
// zero the interrupt enable register to disable all interrupts
EXTI->INTENR = 0;
}
static inline void restore_interrupts()
{
EXTI->INTENR = EXT1_INTENR_backup;
static inline void restore_interrupts() {
EXTI->INTENR = EXT1_INTENR_backup;
}
// ######## small internal function definitions, static inline
static inline void SPI_wait_TX_complete()
{
while (!(SPI1->STATR & SPI_STATR_TXE)) {}
//######## small internal function definitions, static inline
static inline void SPI_wait_TX_complete() {
while(!(SPI1->STATR & SPI_STATR_TXE)) {}
}
static inline uint8_t SPI_is_RX_empty()
{
return SPI1->STATR & SPI_STATR_RXNE;
static inline uint8_t SPI_is_RX_empty() {
return SPI1->STATR & SPI_STATR_RXNE;
}
static inline void SPI_wait_RX_available()
{
while (!(SPI1->STATR & SPI_STATR_RXNE)) {}
static inline void SPI_wait_RX_available() {
while(!(SPI1->STATR & SPI_STATR_RXNE)) {}
}
static inline void SPI_wait_not_busy()
{
while ((SPI1->STATR & SPI_STATR_BSY) != 0) {}
static inline void SPI_wait_not_busy() {
while((SPI1->STATR & SPI_STATR_BSY) != 0) {}
}
static inline void SPI_wait_transmit_finished()
{
SPI_wait_TX_complete();
SPI_wait_not_busy();
static inline void SPI_wait_transmit_finished() {
SPI_wait_TX_complete();
SPI_wait_not_busy();
}
// ######## implementation block
// #define CH32V003_SPI_IMPLEMENTATION //enable so LSP can give you text colors while working on the implementation block, disable for normal use of the library
//######## implementation block
//#define CH32V003_SPI_IMPLEMENTATION //enable so LSP can give you text colors while working on the implementation block, disable for normal use of the library
#if defined(CH32V003_SPI_IMPLEMENTATION)
// no functions here because I think all of the functions are small enough to static inline
//no functions here because I think all of the functions are small enough to static inline
#endif // CH32V003_SPI_IMPLEMENTATION
#endif // CH32V003_SPI_H

View file

@ -3,42 +3,44 @@
/** ADC-based Capactive Touch Control.
see cap_touch_adc.c for an example.
see cap_touch_adc.c for an example.
// Enable GPIOD, C and ADC
RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1;
InitTouchADC();
// Enable GPIOD, C and ADC
RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1;
InitTouchADC();
// Then do this any time you want to read some touches.
sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations );
sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations );
sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations );
sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations );
sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations );
sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations );
sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations );
sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations );
// Then do this any time you want to read some touches.
sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations );
sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations );
sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations );
sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations );
sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations );
sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations );
sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations );
sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations );
*/
#define TOUCH_ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation.
#define TOUCH_ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation.
// Can either be 0 or 1.
// If 0: Measurement low and rises high. So more pressed is smaller number.
// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT.
// If you are doing more prox, use mode 0, otherwise, use mode 1.
#define TOUCH_SLOPE 1
#define TOUCH_SLOPE 1
// If you set this to 1, it will glitch the line, so it will only read
// anything reasonable if the capacitance can overcome that initial spike.
// Typically, it seems if you use this you probbly don't need to do
// any pre-use calibration.
#define TOUCH_FLAT 0
#define TOUCH_FLAT 0
// Macro used for force-alingining ADC timing
#define FORCEALIGNADC \
asm volatile( \
"\n\
asm volatile( \
"\n\
.balign 4\n\
andi a2, %[cyccnt], 3\n\
c.slli a2, 1\n\
@ -48,172 +50,169 @@
jalr a2, 1\n\
.long 0x00010001\n\
.long 0x00010001\n\
" ::[cyccnt] "r"(SysTick->CNT) : "a1", "a2");
"\
:: [cyccnt]"r"(SysTick->CNT) : "a1", "a2"\
);
static void InitTouchADC();
void InitTouchADC()
static void InitTouchADC( );
void InitTouchADC( )
{
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
RCC->CFGR0 &= ~(0x1F << 11);
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
RCC->CFGR0 &= ~(0x1F<<11);
// Set up single conversion on chl 2
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
// Set up single conversion on chl 2
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
// turn on ADC and set rule group to sw trig
ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
// Reset calibration
ADC1->CTLR2 |= ADC_RSTCAL;
while (ADC1->CTLR2 & ADC_RSTCAL)
;
// Calibrate
ADC1->CTLR2 |= ADC_CAL;
while (ADC1->CTLR2 & ADC_CAL)
;
// turn on ADC and set rule group to sw trig
ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
// Reset calibration
ADC1->CTLR2 |= ADC_RSTCAL;
while(ADC1->CTLR2 & ADC_RSTCAL);
// Calibrate
ADC1->CTLR2 |= ADC_CAL;
while(ADC1->CTLR2 & ADC_CAL);
}
// Run from RAM to get even more stable timing.
// This function call takes about 8.1uS to execute.
static uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
static uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations )
{
uint32_t ret = 0;
uint32_t ret = 0;
__disable_irq();
FORCEALIGNADC
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
__enable_irq();
__disable_irq();
FORCEALIGNADC
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno);
__enable_irq();
uint32_t CFGBASE = io->CFGLR & (~(0xf << (4 * portpin)));
uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | CFGBASE;
uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | CFGBASE;
uint32_t CFGBASE = io->CFGLR & (~(0xf<<(4*portpin)));
uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | CFGBASE;
uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | CFGBASE;
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
#if TOUCH_FLAT == 1
#define RELEASEIO \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
io->CFGLR = CFGFLOAT;
#define RELEASEIO io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT;
#else
#define RELEASEIO \
io->CFGLR = CFGFLOAT; \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE);
#define RELEASEIO io->CFGLR = CFGFLOAT; io->BSHR = 1<<(portpin+16*TOUCH_SLOPE);
#endif
#define INNER_LOOP(n) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS(n) \
\
RELEASEIO \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while (!(ADC1->STATR & ADC_EOC)) \
; \
io->CFGLR = CFGDRIVE; \
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
#define INNER_LOOP( n ) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS( n ) \
\
RELEASEIO \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while(!(ADC1->STATR & ADC_EOC)); \
io->CFGLR = CFGDRIVE; \
io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
int i;
for (i = 0; i < iterations; i++)
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
int i;
for( i = 0; i < iterations; i++ )
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
INNER_LOOP(0);
INNER_LOOP(2);
INNER_LOOP(4);
}
INNER_LOOP( 0 );
INNER_LOOP( 2 );
INNER_LOOP( 4 );
}
return ret;
return ret;
}
// Run from RAM to get even more stable timing.
// This function call takes about 8.1uS to execute.
static uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
static uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata")));
uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations )
{
uint32_t ret = 0;
uint32_t ret = 0;
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
ADC1->RSQR3 = adcno;
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno);
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
#define INNER_LOOP_SAFE(n) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
\
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS(n) \
\
io->CFGLR = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while (!(ADC1->STATR & ADC_EOC)) \
; \
__disable_irq(); \
io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
__enable_irq(); \
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
#define INNER_LOOP_SAFE( n ) \
{ \
/* Only lock IRQ for a very narrow window. */ \
__disable_irq(); \
\
FORCEALIGNADC \
\
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
this We are catching it onthe slope much more effectively. */ \
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
\
ADD_N_NOPS( n ) \
\
io->CFGLR = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | (io->CFGLR & (~(0xf<<(4*portpin)))); \
io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); \
\
/* Sampling actually starts here, somewhere, so we can let other \
interrupts run */ \
__enable_irq(); \
while(!(ADC1->STATR & ADC_EOC)); \
__disable_irq(); \
io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | (io->CFGLR & (~(0xf<<(4*portpin)))); \
__enable_irq(); \
io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE))); \
ret += ADC1->RDATAR; \
}
int i;
for (i = 0; i < iterations; i++)
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
int i;
for( i = 0; i < iterations; i++ )
{
// Wait a variable amount of time based on loop iteration, in order
// to get a variety of RC points and minimize DNL.
INNER_LOOP_SAFE(0);
INNER_LOOP_SAFE(2);
INNER_LOOP_SAFE(4);
}
INNER_LOOP_SAFE( 0 );
INNER_LOOP_SAFE( 2 );
INNER_LOOP_SAFE( 4 );
}
return ret;
return ret;
}
#endif
/*
* MIT License
*
*
* Copyright (c) 2023 Valve Corporation
*
*
* 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
@ -222,3 +221,4 @@ uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int i
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

View file

@ -0,0 +1,944 @@
/*
* Single-File-Header for the CH32V208 (and CH579?) built-in 10BASE-T MAC/PHY.
*
* This peripheral uses POINTER-BASED DMA, not descriptor-based DMA:
*
* Hardware:
* - ERXST register: Points to buffer for next RX packet
* - ETXST register: Points to buffer for current TX packet
* - Hardware DMAs directly to/from these addresses
* - Software must manually update pointers after each packet
*
* Software (this driver):
* - Creates descriptor ring for bookkeeping
* - Descriptors are NEVER accessed by hardware
* - OWN bit is pure software: 1=empty, 0=filled
* - Provides ring-buffer API on top of simple pointers
*
* USAGE
*
* Include once with implementation:
*
* #define CH32V208_ETH_IMPLEMENTATION
* #include "ch32v208_eth.h"
*
* Init:
*
* eth_config_t config = {
* .mac_addr = my_mac, // NULL = use chip default
* .rx_callback = eth_rx_cb, // NULL if using manual polling
* .link_callback = link_cb, // optional
* .activity_callback = led_cb, // optional
* .broadcast_filter = true,
* };
* eth_init(&config);
*
* Main loop:
*
* while (1) {
* eth_poll_link(); // call every 50-100ms
* eth_process_rx(); // IF using callback mode
* }
*
* RECEIVING PACKETS
*
* Callback mode:
*
* void eth_rx_cb(const uint8_t *packet, uint16_t length) {
* // process packet (called from eth_process_rx)
* }
*
* Manual polling (zero-copy, for sfhip, lwIP etc):
*
* uint16_t length;
* const uint8_t *packet;
* while ((packet = eth_get_rx_packet(&length)) != NULL) {
* // process packet directly from DMA buffer
* eth_release_rx_packet(); // must call when done
* }
*
* SENDING PACKETS
*
* Simple (with memcpy):
*
* uint8_t packet[64];
* // ... fill packet ...
* int ret = eth_send_packet(packet, 64);
* // ret: 0=success, -1=queue full
*
* Zero-copy mode (build directly in DMA buffer):
*
* uint16_t max_len;
* uint8_t *buf = eth_get_tx_buffer(&max_len);
* if (buf) {
* // build packet directly in DMA buffer
* eth_send_packet_zerocopy(actual_length);
* }
*
* CONFIGURATION
*
* Define before including header to customize:
*
* ETH_RX_BUF_COUNT RX descriptor ring size (default: 4)
* ETH_TX_BUF_COUNT TX queue depth (default: 2)
* ETH_MAX_PACKET_SIZE MAC MTU (default: 1536)
* ETH_RX_BUF_SIZE RX buffer size (default: ETH_MAX_PACKET_SIZE)
* ETH_TX_BUF_SIZE TX buffer size (default: ETH_MAX_PACKET_SIZE)
* ETH_ENABLE_STATS Enable eth_get_stats() and eth_reset_stats()
*/
#ifndef _CH32V208_ETH_H
#define _CH32V208_ETH_H
#include <stdbool.h>
#include <stdint.h>
#ifndef ETH_RX_BUF_COUNT
#define ETH_RX_BUF_COUNT 4
#endif
#ifndef ETH_TX_BUF_COUNT
#define ETH_TX_BUF_COUNT 2
#endif
#ifndef ETH_MAX_PACKET_SIZE
#define ETH_MAX_PACKET_SIZE 1536
#endif
#ifndef ETH_RX_BUF_SIZE
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE
#endif
#ifndef ETH_TX_BUF_SIZE
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE
#endif
#define ETH_MAC_ADDR_LEN 6
// Define ETH_ENABLE_STATS before including this header to enable stats collection
// #define ETH_ENABLE_STATS
// Callback types
typedef void ( *eth_rx_callback_t )( const uint8_t *packet, uint16_t length );
typedef void ( *eth_link_callback_t )( bool link_up );
typedef void ( *eth_activity_callback_t )( void );
// Ethernet configuration
typedef struct
{
uint8_t *mac_addr; // MAC address (can be NULL to use chip default)
eth_rx_callback_t rx_callback; // Called when packet received
eth_link_callback_t link_callback; // Called when link status changes
eth_activity_callback_t activity_callback; // Called on TX/RX activity, can be used for LED
bool promiscuous_mode; // Enable promiscuous mode
bool broadcast_filter; // Accept broadcast packets
bool multicast_filter; // Accept multicast packets
} eth_config_t;
#ifdef ETH_ENABLE_STATS
// Ethernet statistics
typedef struct
{
uint32_t rx_packets;
uint32_t tx_packets;
uint32_t rx_errors;
uint32_t tx_errors;
uint32_t rx_dropped;
uint32_t tx_dropped;
} eth_stats_t;
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/**
* Init the Ethernet peripheral
* @param config Ptr to config struct
* @return 0 on success, negative on error
*/
int eth_init( const eth_config_t *config );
/**
* Send an Ethernet packet
* @param packet Ptr to packet data (incl. Ethernet header)
* @param length Length of pkt in bytes
* @return 0: success, -1: queue full
*/
int eth_send_packet( const uint8_t *packet, uint16_t length );
/**
* Get pointer to next available TX buffer for zero-copy transmission
* @param length max length available in buffer
* @return pointer to TX buffer, or NULL if queue full
* @note After writing packet data, call eth_send_packet_zerocopy() with actual length
*/
uint8_t *eth_get_tx_buffer( uint16_t *max_length );
/**
* Commit a zero-copy TX buffer for transmission
* @param length packet length written to buffer from eth_get_tx_buffer()
* @return 0 on success
*/
int eth_send_packet_zerocopy( uint16_t length );
/**
* Process received packets (call from main loop)
* This will invoke the rx_callback for each received pkt
*/
void eth_process_rx( void );
/**
* Get pointer to next received packet (alternative to eth_process_rx with callback)
* @param length Pointer to store packet length
* @return Pointer to packet data, or NULL if no packet ready
* @note Caller MUST call eth_release_rx_packet() when done with the packet
* @note Packet data is valid only until eth_release_rx_packet() is called
*/
const uint8_t *eth_get_rx_packet( uint16_t *length );
/**
* Release currently held RX packet back to DMA
* @note Must be called after eth_get_rx_packet() to free the descriptor
*/
void eth_release_rx_packet( void );
/**
* Poll link status and handle auto-negotiation
* this is based on WCHNET_LinkProcess
* https://github.com/openwch/ch32v20x/blob/main/EVT/EXAM/ETH/NetLib/eth_driver.c#L131
* TODO: cable polarity reversal is untested
*
* call this periodically from main loop (i.e. every 50-100ms)
*/
void eth_poll_link( void );
/**
* Get current MAC
* @param mac_addr Buffer to store MAC address (6 bytes)
*/
void eth_get_mac_address( uint8_t *mac_addr );
/**
* Check if link is up
* @return true if link is up
*/
bool eth_is_link_up( void );
#ifdef ETH_ENABLE_STATS
/**
* Get stats
* @param stats Pointer to statistics structure to fill
*/
void eth_get_stats( eth_stats_t *stats );
/**
* Reset stat counters
*/
void eth_reset_stats( void );
#endif
#ifdef __cplusplus
}
#endif
#endif // _CH32V208_ETH_H
#ifdef CH32V208_ETH_IMPLEMENTATION
#include "ch32fun.h"
#include <string.h>
// DMA descriptor
typedef struct
{
volatile uint32_t Status;
volatile uint32_t Buffer1Addr;
} ETH_DMADESCTypeDef;
// TX queue management
typedef struct
{
volatile uint32_t head;
volatile uint32_t tail;
volatile bool is_full; // this is only really needed if ETH_TX_BUF_COUNT == 1
} tx_queue_t;
// driver state
typedef struct
{
uint32_t rx_head_idx;
uint32_t rx_tail_idx;
tx_queue_t tx_q;
uint8_t mac_addr[ETH_MAC_ADDR_LEN];
eth_rx_callback_t rx_callback;
eth_link_callback_t link_callback;
eth_activity_callback_t activity_callback;
volatile bool link_irq_flag;
#ifdef ETH_ENABLE_STATS
eth_stats_t stats;
#endif
// autoneg & polarity state
uint8_t phy_mdix_mode; // current MDI/MDIX setting
uint8_t crc_error_count; // CRC errors since link up
bool polarity_detect_active;
uint8_t negotiation_poll_count;
enum
{
LINK_STATE_DOWN,
LINK_STATE_NEGOTIATING,
LINK_STATE_UP
} link_state;
} eth_driver_state_t;
__attribute__( ( aligned( 4 ) ) ) static ETH_DMADESCTypeDef g_dma_rx_descs[ETH_RX_BUF_COUNT];
__attribute__( ( aligned( 4 ) ) ) static ETH_DMADESCTypeDef g_dma_tx_descs[ETH_TX_BUF_COUNT];
__attribute__( ( aligned( 4 ) ) ) static uint8_t g_mac_rx_bufs[ETH_RX_BUF_COUNT * ETH_RX_BUF_SIZE];
__attribute__( ( aligned( 4 ) ) ) static uint8_t g_mac_tx_bufs[ETH_TX_BUF_COUNT * ETH_TX_BUF_SIZE];
static eth_driver_state_t g_eth_state = { 0 };
static void phy_write_reg( uint8_t reg_add, uint16_t reg_val )
{
R32_ETH_MIWR = ( reg_add & RB_ETH_MIREGADR_MASK ) | RB_ETH_MIWR_MIIWR | ( reg_val << RB_ETH_MIWR_DATA_SHIFT );
}
static uint16_t phy_read_reg( uint8_t reg_add )
{
ETH10M->MIERGADR = reg_add;
return ETH10M->MIRD;
}
static inline void tx_queue_init( tx_queue_t *q )
{
q->head = 0;
q->tail = 0;
q->is_full = false;
}
static inline bool tx_queue_is_empty( const tx_queue_t *q )
{
return !q->is_full && ( q->head == q->tail );
}
static inline bool tx_queue_is_full( const tx_queue_t *q )
{
return q->is_full;
}
static inline void tx_queue_produce( tx_queue_t *q )
{
q->head = ( q->head + 1 ) % ETH_TX_BUF_COUNT;
if ( q->head == q->tail )
{
q->is_full = true; // head caught up to tail = full
}
}
static inline void tx_queue_consume( tx_queue_t *q )
{
q->tail = ( q->tail + 1 ) % ETH_TX_BUF_COUNT;
q->is_full = false; // after consuming, definitely not full
}
static void eth_get_chip_mac_addr( uint8_t *mac )
{
const uint8_t *macaddr_src = (const uint8_t *)( ROM_CFG_USERADR_ID + 5 );
for ( int i = 0; i < 6; i++ )
{
mac[i] = *( macaddr_src-- );
}
}
static void tx_start_if_possible( void )
{
if ( ETH10M->ECON1 & RB_ETH_ECON1_TXRTS )
{
return;
}
if ( tx_queue_is_empty( &g_eth_state.tx_q ) )
{
return;
}
uint32_t idx = g_eth_state.tx_q.tail;
ETH_DMADESCTypeDef *desc = &g_dma_tx_descs[idx];
uint16_t len = desc->Status;
ETH10M->ETXLN = len; // set tx packet len
ETH10M->ETXST = desc->Buffer1Addr; // set tx buf start address (DMA source)
ETH10M->ECON1 |= RB_ETH_ECON1_TXRTS; // set tx req flag to start DMA transmission
}
/**
* Start PHY auto-negotiation with specific MDI/MDIX mode
*/
static void phy_start_autoneg( uint8_t mdix_mode )
{
// reset and restart auto-negotiation
phy_write_reg( PHY_BMCR, PHY_BMCR_RESET );
Delay_Us( 100 );
// configure MDI/MDIX mode
phy_write_reg( PHY_MDIX, ( mdix_mode & MDIX_MODE_MASK ) | MDIX_PN_POLARITY_NORMAL );
// enable auto-negotiation
phy_write_reg( PHY_BMCR, PHY_BMCR_AN_ENABLE | PHY_BMCR_AN_RESTART );
g_eth_state.phy_mdix_mode = mdix_mode;
}
/**
* Try next MDI/MDIX mode in sequence: AUTO -> MDIX -> MDI -> AUTO
*/
static void phy_try_next_mdix_mode( void )
{
uint8_t next_mode;
switch ( g_eth_state.phy_mdix_mode & MDIX_MODE_MASK )
{
case MDIX_MODE_AUTO:
next_mode = MDIX_MODE_MDIX; // try forced MDIX
break;
case MDIX_MODE_MDIX:
next_mode = MDIX_MODE_MDI; // try forced MDI
break;
default:
next_mode = MDIX_MODE_AUTO; // back to auto
break;
}
phy_start_autoneg( next_mode );
}
/**
* Handle cable polarity issue (P/N reversal)
*/
static void phy_fix_polarity( void )
{
uint16_t mdix_reg = phy_read_reg( PHY_MDIX );
// toggle P/N polarity between NORMAL and REVERSED
mdix_reg ^= MDIX_PN_POLARITY_REVERSED;
phy_write_reg( PHY_MDIX, mdix_reg );
g_eth_state.crc_error_count = 0;
}
static void eth_link_up_handler( void )
{
// read auto-negotiation registers
uint16_t anar = phy_read_reg( PHY_ANAR ); // what we advertised
uint16_t anlpar = phy_read_reg( PHY_ANLPAR ); // what partner advertised
uint16_t common = anar & anlpar;
// check if both sides support full-duplex
bool is_full_duplex = ( common & PHY_ANLPAR_10BASE_TFD ) != 0;
// configure MAC to match negotiated duplex mode
if ( is_full_duplex )
{
ETH10M->MACON2 |= RB_ETH_MACON2_FULDPX;
}
else
{
ETH10M->MACON2 &= ~RB_ETH_MACON2_FULDPX;
}
// enable CRC error packet reception for polarity detection
ETH10M->ERXFCON |= RB_ETH_ERXFCON_CRCEN;
g_eth_state.link_state = LINK_STATE_UP;
g_eth_state.crc_error_count = 0;
g_eth_state.polarity_detect_active = true;
g_eth_state.negotiation_poll_count = 0;
if ( g_eth_state.link_callback )
{
g_eth_state.link_callback( true );
}
}
/**
* Configure MAC for link down
*/
static void eth_link_down_handler( void )
{
// Disable polarity detection
g_eth_state.polarity_detect_active = false;
ETH10M->ERXFCON &= ~RB_ETH_ERXFCON_CRCEN;
g_eth_state.link_state = LINK_STATE_DOWN;
if ( g_eth_state.link_callback )
{
g_eth_state.link_callback( false );
}
}
int eth_init( const eth_config_t *config )
{
if ( !config )
{
return -1;
}
memset( &g_eth_state, 0, sizeof( g_eth_state ) );
g_eth_state.rx_callback = config->rx_callback;
g_eth_state.link_callback = config->link_callback;
g_eth_state.activity_callback = config->activity_callback;
if ( config->mac_addr )
{
memcpy( g_eth_state.mac_addr, config->mac_addr, ETH_MAC_ADDR_LEN );
}
else
{
eth_get_chip_mac_addr( g_eth_state.mac_addr );
}
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
RCC->CFGR0 |= RCC_ETHPRE; // Ethernet clock prescaler
EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; // Extended Control Register, 10M Ethernet enable and clock enable
// Transmit/Receive module reset
ETH10M->ECON1 = RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST;
ETH10M->ECON1 = 0;
uint32_t rx_filter;
if ( config->promiscuous_mode )
{
// Promiscuous: disable filtering (ERXFCON=0 *probably* receives everything)
rx_filter = 0;
}
else
{
// Normal mode: enable filtering and ALWAYS receive unicast to our MAC
rx_filter = RB_ETH_ERXFCON_EN | RB_ETH_ERXFCON_UCEN;
// broadcast packet reception
if ( config->broadcast_filter )
{
rx_filter |= RB_ETH_ERXFCON_BCEN;
}
// multicast packet reception
if ( config->multicast_filter )
{
rx_filter |= RB_ETH_ERXFCON_MCEN;
}
}
ETH10M->ERXFCON = rx_filter;
// MAC layer receive enable
ETH10M->MACON1 = RB_ETH_MACON1_MARXEN;
// Pad short packets with 0x00 to 60 bytes, then append 4-byte CRC
// Hardware pads CRC
ETH10M->MACON2 = PADCFG_AUTO_3 | RB_ETH_MACON2_TXCRCEN;
// Set maximum frame length (MTU)
ETH10M->MAMXFL = ETH_MAX_PACKET_SIZE;
// MAC is reversed
R8_ETH_MAADRL1 = g_eth_state.mac_addr[5];
R8_ETH_MAADRL2 = g_eth_state.mac_addr[4];
R8_ETH_MAADRL3 = g_eth_state.mac_addr[3];
R8_ETH_MAADRL4 = g_eth_state.mac_addr[2];
R8_ETH_MAADRL5 = g_eth_state.mac_addr[1];
R8_ETH_MAADRL6 = g_eth_state.mac_addr[0];
// PHY Analog Parameter Setting, default value and "Rated driver"
ETH10M->ECON2 = RB_ETH_ECON2_DEFAULT;
tx_queue_init( &g_eth_state.tx_q );
for ( int i = 0; i < ETH_TX_BUF_COUNT; i++ )
{
g_dma_tx_descs[i].Status = 0;
g_dma_tx_descs[i].Buffer1Addr = (uint32_t)&g_mac_tx_bufs[i * ETH_TX_BUF_SIZE];
}
// init RX descriptor ring (DMA reads from these)
g_eth_state.rx_head_idx = 0;
g_eth_state.rx_tail_idx = 0;
for ( int i = 0; i < ETH_RX_BUF_COUNT; i++ )
{
g_dma_rx_descs[i].Status = ETH_DMARxDesc_OWN; // DMA owns all initially
g_dma_rx_descs[i].Buffer1Addr = (uint32_t)&g_mac_rx_bufs[i * ETH_RX_BUF_SIZE];
}
// start RX
ETH10M->ERXST = g_dma_rx_descs[0].Buffer1Addr;
ETH10M->ECON1 = RB_ETH_ECON1_RXEN;
g_eth_state.link_state = LINK_STATE_DOWN;
g_eth_state.negotiation_poll_count = 0;
g_eth_state.polarity_detect_active = false;
g_eth_state.crc_error_count = 0;
// start auto-negotiation with AUTO MDI/MDIX
phy_start_autoneg( MDIX_MODE_AUTO );
Delay_Ms( 100 ); // Give PHY time to initialize
// clear all pending interrupt flags
ETH10M->EIR = 0xFF;
ETH10M->ESTAT |= RB_ETH_ESTAT_INT | RB_ETH_ESTAT_BUFER;
ETH10M->EIE = RB_ETH_EIE_INTIE | // Ethernet interrupt enable (master enable)
RB_ETH_EIE_RXIE | // RX complete interrupt
RB_ETH_EIE_TXIE | // TX complete interrupt
RB_ETH_EIE_LINKIE | // Link status change interrupt
RB_ETH_EIE_TXERIE | // TX error interrupt (collision, underrun, etc.)
RB_ETH_EIE_RXERIE | // RX error interrupt (CRC error, overrun, etc.)
RB_ETH_EIE_R_EN50; // Built-in 50ohm impedance matching resistor enable
NVIC_EnableIRQ( ETH_IRQn );
return 0;
}
int eth_send_packet( const uint8_t *packet, uint16_t length )
{
if ( tx_queue_is_full( &g_eth_state.tx_q ) )
{
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.tx_dropped++;
#endif
return -1;
}
// reserve our slot in the queue
uint32_t idx = g_eth_state.tx_q.head;
tx_queue_produce( &g_eth_state.tx_q );
uint8_t *tx_buf = (uint8_t *)g_dma_tx_descs[idx].Buffer1Addr;
memcpy( tx_buf, packet, length );
g_dma_tx_descs[idx].Status = length;
tx_start_if_possible();
return 0;
}
uint8_t *eth_get_tx_buffer( uint16_t *max_length )
{
if ( tx_queue_is_full( &g_eth_state.tx_q ) )
{
return NULL;
}
if ( max_length )
{
*max_length = ETH_TX_BUF_SIZE;
}
uint32_t idx = g_eth_state.tx_q.head;
return (uint8_t *)g_dma_tx_descs[idx].Buffer1Addr;
}
int eth_send_packet_zerocopy( uint16_t length )
{
uint32_t idx = g_eth_state.tx_q.head;
g_dma_tx_descs[idx].Status = length;
tx_queue_produce( &g_eth_state.tx_q );
tx_start_if_possible();
return 0;
}
const uint8_t *eth_get_rx_packet( uint16_t *length )
{
if ( !length )
{
return NULL;
}
uint32_t tail_idx = g_eth_state.rx_tail_idx;
if ( g_dma_rx_descs[tail_idx].Status & ETH_DMARxDesc_OWN )
{
return NULL; // no packet ready
}
// extract packet length from descriptor status field
*length = ( g_dma_rx_descs[tail_idx].Status & ETH_DMARxDesc_FL ) >> ETH_DMARxDesc_FrameLengthShift;
// return pointer to packet buffer
return (const uint8_t *)g_dma_rx_descs[tail_idx].Buffer1Addr;
}
void eth_release_rx_packet( void )
{
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.rx_packets++;
#endif
uint32_t tail_idx = g_eth_state.rx_tail_idx;
// give descriptor back to DMA
g_dma_rx_descs[tail_idx].Status = ETH_DMARxDesc_OWN;
// advance to next descriptor in ring
g_eth_state.rx_tail_idx = ( tail_idx + 1 ) % ETH_RX_BUF_COUNT;
}
void eth_process_rx( void )
{
uint16_t length;
const uint8_t *packet;
// process all packets that DMA has released
while ( ( packet = eth_get_rx_packet( &length ) ) != NULL )
{
// deliver to user callback if registered
if ( g_eth_state.rx_callback )
{
g_eth_state.rx_callback( packet, length );
}
eth_release_rx_packet();
}
}
void eth_poll_link( void )
{
if ( g_eth_state.link_irq_flag )
{
g_eth_state.link_irq_flag = false;
g_eth_state.negotiation_poll_count = 0;
}
uint16_t bmsr = phy_read_reg( PHY_BMSR );
uint16_t anlpar = phy_read_reg( PHY_ANLPAR );
bool phy_link = ( bmsr & PHY_BMSR_LINK_STATUS ) != 0;
bool an_complete = ( bmsr & PHY_BMSR_AN_COMPLETE ) != 0;
switch ( g_eth_state.link_state )
{
case LINK_STATE_DOWN:
if ( phy_link && an_complete && ( anlpar != 0 ) )
{
// valid link with successful negotiation
eth_link_up_handler();
}
else if ( phy_link && an_complete && ( anlpar == 0 ) )
{
// false auto-negotiation completion (ANLPAR=0)
// reset PHY and try different mode
g_eth_state.link_state = LINK_STATE_NEGOTIATING;
g_eth_state.negotiation_poll_count = 0;
phy_write_reg( PHY_BMCR, PHY_BMCR_RESET );
Delay_Us( 100 );
phy_try_next_mdix_mode();
}
break;
case LINK_STATE_NEGOTIATING:
if ( phy_link && an_complete && ( anlpar != 0 ) )
{
// negotiation succeeded
eth_link_up_handler();
}
else if ( phy_link && an_complete && ( anlpar == 0 ) )
{
// still no valid partner response after negotiation
g_eth_state.negotiation_poll_count++;
if ( g_eth_state.negotiation_poll_count >= 10 )
{
// try next MDI/MDIX mode after 10 polls
g_eth_state.negotiation_poll_count = 0;
phy_write_reg( PHY_BMCR, PHY_BMCR_RESET );
Delay_Us( 100 );
phy_try_next_mdix_mode();
}
}
else if ( !phy_link )
{
// link went down during negotiation
g_eth_state.link_state = LINK_STATE_DOWN;
g_eth_state.negotiation_poll_count = 0;
}
break;
case LINK_STATE_UP:
if ( !phy_link )
{
// link went down
eth_link_down_handler();
phy_start_autoneg( MDIX_MODE_AUTO );
}
else if ( g_eth_state.polarity_detect_active )
{
// monitor for polarity issues
if ( g_eth_state.crc_error_count >= 3 )
{
phy_fix_polarity();
g_eth_state.polarity_detect_active = false;
ETH10M->ERXFCON &= ~RB_ETH_ERXFCON_CRCEN;
}
}
break;
}
}
void eth_get_mac_address( uint8_t *mac_addr )
{
if ( mac_addr )
{
memcpy( mac_addr, g_eth_state.mac_addr, ETH_MAC_ADDR_LEN );
}
}
bool eth_is_link_up( void )
{
return g_eth_state.link_state == LINK_STATE_UP;
}
#ifdef ETH_ENABLE_STATS
void eth_get_stats( eth_stats_t *stats )
{
if ( stats )
{
*stats = g_eth_state.stats;
}
}
void eth_reset_stats( void )
{
memset( &g_eth_state.stats, 0, sizeof( eth_stats_t ) );
}
#endif
void ETH_IRQHandler( void ) __attribute__( ( interrupt ) ) __attribute__( ( used ) );
void ETH_IRQHandler( void )
{
uint32_t flags = ETH10M->EIR;
uint32_t head_idx = g_eth_state.rx_head_idx;
if ( flags & RB_ETH_EIR_RXIF )
{
ETH10M->EIR = RB_ETH_EIR_RXIF; // clear interrupt flag
// check if DMA still owns the current head descriptor
if ( g_dma_rx_descs[head_idx].Status & ETH_DMARxDesc_OWN )
{
uint16_t rx_len = ETH10M->ERXLN;
if ( rx_len == 0 || rx_len > ETH_RX_BUF_SIZE )
{
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.rx_errors++;
#endif
return;
}
// check for RX errors
uint8_t estat = ETH10M->ESTAT;
const uint8_t error_mask =
RB_ETH_ESTAT_BUFER | RB_ETH_ESTAT_RXCRCER | RB_ETH_ESTAT_RXNIBBLE | RB_ETH_ESTAT_RXMORE;
if ( estat & error_mask )
{
// track CRC errors specifically for polarity detection
if ( ( estat & RB_ETH_ESTAT_RXCRCER ) && g_eth_state.polarity_detect_active )
{
g_eth_state.crc_error_count++;
}
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.rx_errors++;
#endif
return; // discard
}
// check if next descriptor is available
uint32_t next_idx = ( head_idx + 1 ) % ETH_RX_BUF_COUNT;
if ( !( g_dma_rx_descs[next_idx].Status & ETH_DMARxDesc_OWN ) )
{
// ring full
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.rx_dropped++;
#endif
}
else
{
// packet is ready and we have space
// mark current descriptor as ready for CPU processing
g_dma_rx_descs[head_idx].Status &= ~ETH_DMARxDesc_OWN;
// add frame metadata
g_dma_rx_descs[head_idx].Status |= ( ETH_DMARxDesc_FS | ETH_DMARxDesc_LS | // Single segment frame
( ETH10M->ERXLN << ETH_DMARxDesc_FrameLengthShift ) );
// advance head to next descriptor for DMA
g_eth_state.rx_head_idx = next_idx;
// tell MAC where to write next packet
ETH10M->ERXST = g_dma_rx_descs[next_idx].Buffer1Addr;
// signal activity
if ( g_eth_state.activity_callback )
{
g_eth_state.activity_callback();
}
}
}
}
if ( flags & RB_ETH_EIR_TXIF )
{
ETH10M->EIR = RB_ETH_EIR_TXIF;
if ( !tx_queue_is_empty( &g_eth_state.tx_q ) )
{
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.tx_packets++;
#endif
tx_queue_consume( &g_eth_state.tx_q );
}
tx_start_if_possible();
// signal activity
if ( g_eth_state.activity_callback )
{
g_eth_state.activity_callback();
}
}
if ( flags & RB_ETH_EIR_TXERIF )
{
ETH10M->EIR = RB_ETH_EIR_TXERIF;
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.tx_errors++;
#endif
if ( !tx_queue_is_empty( &g_eth_state.tx_q ) )
{
tx_queue_consume( &g_eth_state.tx_q );
}
tx_start_if_possible();
}
if ( flags & RB_ETH_EIR_RXERIF )
{
ETH10M->EIR = RB_ETH_EIR_RXERIF;
ETH10M->ECON1 |= RB_ETH_ECON1_RXEN;
#ifdef ETH_ENABLE_STATS
g_eth_state.stats.rx_errors++;
#endif
}
if ( flags & RB_ETH_EIR_LINKIF )
{
g_eth_state.link_irq_flag = true;
ETH10M->EIR = RB_ETH_EIR_LINKIF;
}
}
#endif // CH32V208_ETH_IMPLEMENTATION

View file

@ -2,7 +2,7 @@
#define _CH32V307GIGABIT_H
/* This file is written against the RTL8211E
*/
*/
// #define CH32V307GIGABIT_MCO25 1
// #define CH32V307GIGABIT_PHYADDRESS 0
@ -34,515 +34,515 @@
// ETH DMA structure definition (From ch32v30x_eth.c
typedef struct
{
uint32_t volatile Status; /* Status */
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
uint32_t Buffer1Addr; /* Buffer1 address pointer */
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
uint32_t volatile Status; /* Status */
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
uint32_t Buffer1Addr; /* Buffer1 address pointer */
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
} ETH_DMADESCTypeDef;
// You must provide:
void ch32v307ethHandleReconfig(int link, int speed, int duplex);
void ch32v307ethHandleReconfig( int link, int speed, int duplex );
// Return non-zero to suppress OWN return (for if you are still holding onto the buffer)
int ch32v307ethInitHandlePacket(uint8_t *data, int frame_length, ETH_DMADESCTypeDef *dmadesc);
int ch32v307ethInitHandlePacket( uint8_t * data, int frame_length, ETH_DMADESCTypeDef * dmadesc );
void ch32v307ethInitHandleTXC(void);
void ch32v307ethInitHandleTXC( void );
// This library provides:
static void ch32v307ethGetMacInUC(uint8_t *mac);
static int ch32v307ethInit(void);
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc); // Does not copy.
static int ch32v307ethTickPhy(void);
static void ch32v307ethGetMacInUC( uint8_t * mac );
static int ch32v307ethInit( void );
static int ch32v307ethTransmitStatic(uint8_t * buffer, uint32_t length, int enable_txc); // Does not copy.
static int ch32v307ethTickPhy( void );
// Data pursuent to ethernet.
uint8_t ch32v307eth_mac[6] = {0};
uint16_t ch32v307eth_phyid = 0; // 0xc916 = RTL8211FS / 0xc915 = RTL8211E-VB
ETH_DMADESCTypeDef ch32v307eth_DMARxDscrTab[CH32V307GIGABIT_RXBUFNB] __attribute__((aligned(4))); // MAC receive descriptor, 4-byte aligned
ETH_DMADESCTypeDef ch32v307eth_DMATxDscrTab[CH32V307GIGABIT_TXBUFNB] __attribute__((aligned(4))); // MAC send descriptor, 4-byte aligned
uint8_t ch32v307eth_MACRxBuf[CH32V307GIGABIT_RXBUFNB * CH32V307GIGABIT_BUFFSIZE] __attribute__((aligned(4))); // MAC receive buffer, 4-byte aligned
ETH_DMADESCTypeDef *pDMARxGet;
ETH_DMADESCTypeDef *pDMATxSet;
uint8_t ch32v307eth_mac[6] = { 0 };
uint16_t ch32v307eth_phyid = 0; // 0xc916 = RTL8211FS / 0xc915 = RTL8211E-VB
ETH_DMADESCTypeDef ch32v307eth_DMARxDscrTab[CH32V307GIGABIT_RXBUFNB] __attribute__((aligned(4))); // MAC receive descriptor, 4-byte aligned
ETH_DMADESCTypeDef ch32v307eth_DMATxDscrTab[CH32V307GIGABIT_TXBUFNB] __attribute__((aligned(4))); // MAC send descriptor, 4-byte aligned
uint8_t ch32v307eth_MACRxBuf[CH32V307GIGABIT_RXBUFNB*CH32V307GIGABIT_BUFFSIZE] __attribute__((aligned(4))); // MAC receive buffer, 4-byte aligned
ETH_DMADESCTypeDef * pDMARxGet;
ETH_DMADESCTypeDef * pDMATxSet;
// Internal functions
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val);
static int ch32v307ethPHYRegAsyncRead(int reg, int *value);
static int ch32v307ethPHYRegRead(uint32_t reg);
static int ch32v307ethPHYRegWrite( uint32_t reg, uint32_t val );
static int ch32v307ethPHYRegAsyncRead( int reg, int * value );
static int ch32v307ethPHYRegRead( uint32_t reg );
static int ch32v307ethPHYRegAsyncRead(int reg, int *value)
static int ch32v307ethPHYRegAsyncRead( int reg, int * value )
{
static uint8_t reg_request_count = 0;
static uint8_t reg_request_count = 0;
uint32_t miiar = ETH->MACMIIAR;
if (miiar & ETH_MACMIIAR_MB)
{
return -1;
}
if (((miiar & ETH_MACMIIAR_MR) >> 6) != reg || reg_request_count < 2)
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
reg_request_count++;
return -1;
}
reg_request_count = 0;
*value = ETH->MACMIIDR;
ETH->MACMIIAR |= ETH_MACMIIAR_MR; // Poison register.
return 0;
uint32_t miiar = ETH->MACMIIAR;
if( miiar & ETH_MACMIIAR_MB )
{
return -1;
}
if( ( ( miiar & ETH_MACMIIAR_MR ) >> 6 ) != reg || reg_request_count < 2 )
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
reg_request_count++;
return -1;
}
reg_request_count = 0;
*value = ETH->MACMIIDR;
ETH->MACMIIAR |= ETH_MACMIIAR_MR; // Poison register.
return 0;
}
static int ch32v307ethTickPhy(void)
{
int speed, linked, duplex;
const int reg = (ch32v307eth_phyid == 0xc916) ? 0x1a : 0x11; // PHYSR (different on each part)
int miidr;
if (ch32v307ethPHYRegAsyncRead(reg, &miidr)) return -1;
int speed, linked, duplex;
const int reg = (ch32v307eth_phyid == 0xc916) ? 0x1a : 0x11; // PHYSR (different on each part)
int miidr;
if( ch32v307ethPHYRegAsyncRead( reg, &miidr ) ) return -1;
printf("REG: %02x / %04x / %04x\n", reg, miidr, ch32v307eth_phyid);
printf( "REG: %02x / %04x / %04x\n", reg, miidr, ch32v307eth_phyid );
if (reg == 0x1a)
{
speed = ((miidr >> 4) & 3);
linked = ((miidr >> 2) & 1);
duplex = ((miidr >> 3) & 1);
}
else
{
speed = ((miidr >> 14) & 3);
linked = ((miidr >> 10) & 1);
duplex = ((miidr >> 13) & 1);
}
if( reg == 0x1a )
{
speed = ((miidr>>4)&3);
linked = ((miidr>>2)&1);
duplex = ((miidr>>3)&1);
}
else
{
speed = ((miidr>>14)&3);
linked = ((miidr>>10)&1);
duplex = ((miidr>>13)&1);
}
printf("LINK INFO: %d %d %d\n", speed, linked, duplex);
if (linked)
{
uint32_t oldmaccr = ETH->MACCR;
uint32_t newmaccr = (oldmaccr & ~((1 << 11) | (3 << 14))) | (speed << 14) | (duplex << 11);
if (newmaccr != oldmaccr)
{
ETH->MACCR = newmaccr;
ch32v307ethHandleReconfig(linked, speed, duplex);
return 1;
}
}
return 0;
printf( "LINK INFO: %d %d %d\n", speed, linked, duplex );
if( linked )
{
uint32_t oldmaccr = ETH->MACCR;
uint32_t newmaccr = (oldmaccr & ~( ( 1<<11 ) | (3<<14) ) ) | (speed<<14) | ( duplex<<11);
if( newmaccr != oldmaccr )
{
ETH->MACCR = newmaccr;
ch32v307ethHandleReconfig( linked, speed, duplex );
return 1;
}
}
return 0;
}
// Based on ETH_WritePHYRegister
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val)
static int ch32v307ethPHYRegWrite( uint32_t reg, uint32_t val )
{
ETH->MACMIIDR = val;
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
(((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11)) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
ETH->MACMIIDR = val;
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
(((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11)) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
uint32_t timeout = 0x100000;
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
;
uint32_t timeout = 0x100000;
while( ( ETH->MACMIIAR & ETH_MACMIIAR_MB ) && --timeout );
// If timeout = 0, is an error.
return timeout ? 0 : -1;
// If timeout = 0, is an error.
return timeout ? 0 : -1;
}
static int ch32v307ethPHYRegRead(uint32_t reg)
static int ch32v307ethPHYRegRead( uint32_t reg )
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
uint32_t timeout = 0x100000;
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
;
uint32_t timeout = 0x100000;
while( ( ETH->MACMIIAR & ETH_MACMIIAR_MB ) && --timeout );
// If timeout = 0, is an error.
return timeout ? ETH->MACMIIDR : -1;
// If timeout = 0, is an error.
return timeout ? ETH->MACMIIDR : -1;
}
static void ch32v307ethGetMacInUC(uint8_t *mac)
static void ch32v307ethGetMacInUC( uint8_t * mac )
{
// Mac is backwards.
const uint8_t *macaddr = (const uint8_t *)(ROM_CFG_USERADR_ID + 5);
for (int i = 0; i < 6; i++)
{
mac[i] = *(macaddr--);
}
// Mac is backwards.
const uint8_t *macaddr = (const uint8_t *)(ROM_CFG_USERADR_ID+5);
for( int i = 0; i < 6; i++ )
{
mac[i] = *(macaddr--);
}
}
static int ch32v307ethInit(void)
static int ch32v307ethInit( void )
{
int i;
int i;
#ifdef CH32V307GIGABIT_PHY_RSTB
funPinMode(CH32V307GIGABIT_PHY_RSTB, GPIO_CFGLR_OUT_50Mhz_PP); // PHY_RSTB (For reset)
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_LOW);
funPinMode( CH32V307GIGABIT_PHY_RSTB, GPIO_CFGLR_OUT_50Mhz_PP ); //PHY_RSTB (For reset)
funDigitalWrite( CH32V307GIGABIT_PHY_RSTB, FUN_LOW );
#endif
// Configure strapping.
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
funDigitalWrite(PA1, FUN_HIGH);
funDigitalWrite(PA0, FUN_HIGH);
funDigitalWrite(PC3, FUN_HIGH); // No TX Delay
funDigitalWrite(PC2, FUN_HIGH);
// Configure strapping.
funPinMode( PA1, GPIO_CFGLR_IN_PUPD ); // GMII_RXD3
funPinMode( PA0, GPIO_CFGLR_IN_PUPD ); // GMII_RXD2
funPinMode( PC3, GPIO_CFGLR_IN_PUPD ); // GMII_RXD1
funPinMode( PC2, GPIO_CFGLR_IN_PUPD ); // GMII_RXD0
funDigitalWrite( PA1, FUN_HIGH );
funDigitalWrite( PA0, FUN_HIGH );
funDigitalWrite( PC3, FUN_HIGH ); // No TX Delay
funDigitalWrite( PC2, FUN_HIGH );
// Pull-up MDIO
funPinMode(PD9, GPIO_CFGLR_OUT_50Mhz_PP); // Pull-up control (DO NOT CHECK IN, ADD RESISTOR)
funDigitalWrite(PD9, FUN_HIGH);
// Pull-up MDIO
funPinMode( PD9, GPIO_CFGLR_OUT_50Mhz_PP ); //Pull-up control (DO NOT CHECK IN, ADD RESISTOR)
funDigitalWrite( PD9, FUN_HIGH );
// Will be required later.
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
// Will be required later.
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
// https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8c&HSI=1,8&HSE=0,8&PREDIV2=1,1&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=0,4&PLLVCO=1,144&SYSCLK=1,2&
// Clock Tree:
// 8MHz Input
// PREDIV2 = 2 (1 in register) = 4MHz
// PLL2 = 9 (7 in register) = 36MHz / PLL2VCO = 72MHz
// PLL3CLK = 12.5 (1 in register) = 50MHz = 100MHz VCO
// PREDIV1SRC = HSE (1 in register) = 8MHz
// PREDIV1 = 2 (1 in register).
// PLLSRC = PREDIV1 (0 in register) = 4MHz
// PLL = 18 (0 in register) = 72MHz
// PLLVCO = 144MHz
// SYSCLK = PLLVCO = 144MHz
// Use EXT_125M (ETH1G_SRC)
// https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8c&HSI=1,8&HSE=0,8&PREDIV2=1,1&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=0,4&PLLVCO=1,144&SYSCLK=1,2&
// Clock Tree:
// 8MHz Input
// PREDIV2 = 2 (1 in register) = 4MHz
// PLL2 = 9 (7 in register) = 36MHz / PLL2VCO = 72MHz
// PLL3CLK = 12.5 (1 in register) = 50MHz = 100MHz VCO
// PREDIV1SRC = HSE (1 in register) = 8MHz
// PREDIV1 = 2 (1 in register).
// PLLSRC = PREDIV1 (0 in register) = 4MHz
// PLL = 18 (0 in register) = 72MHz
// PLLVCO = 144MHz
// SYSCLK = PLLVCO = 144MHz
// Use EXT_125M (ETH1G_SRC)
// Switch processor back to HSI so we don't eat dirt.
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
// Switch processor back to HSI so we don't eat dirt.
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
// Setup clock tree.
RCC->CFGR2 |=
(1 << RCC_PREDIV2_OFFSET) | // PREDIV = /2; Prediv Freq = 4MHz
(1 << RCC_PLL3MUL_OFFSET) | // PLL3 = x12.5 (PLL3 = 50MHz)
(2 << RCC_ETH1GSRC_OFFSET) | // External source for RGMII
(7 << RCC_PLL2MUL_OFFSET) | // PLL2 = x9 (PLL2 = 36MHz)
(1 << RCC_PREDIV1_OFFSET) | // PREDIV1 = /2; Prediv freq = 50MHz
0;
// Setup clock tree.
RCC->CFGR2 |=
(1<<RCC_PREDIV2_OFFSET) | // PREDIV = /2; Prediv Freq = 4MHz
(1<<RCC_PLL3MUL_OFFSET) | // PLL3 = x12.5 (PLL3 = 50MHz)
(2<<RCC_ETH1GSRC_OFFSET)| // External source for RGMII
(7<<RCC_PLL2MUL_OFFSET) | // PLL2 = x9 (PLL2 = 36MHz)
(1<<RCC_PREDIV1_OFFSET) | // PREDIV1 = /2; Prediv freq = 50MHz
0;
// Power on PLLs
RCC->CTLR |= RCC_PLL3ON | RCC_PLL2ON;
int timeout;
// Power on PLLs
RCC->CTLR |= RCC_PLL3ON | RCC_PLL2ON;
int timeout;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLL3RDY) break;
if (timeout == 0) return -5;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLL2RDY) break;
if (timeout == 0) return -6;
for( timeout = 10000; timeout > 0; timeout--) if (RCC->CTLR & RCC_PLL3RDY) break;
if( timeout == 0 ) return -5;
for( timeout = 10000; timeout > 0; timeout--) if (RCC->CTLR & RCC_PLL2RDY) break;
if( timeout == 0 ) return -6;
// PLL = x18 (0 in register)
RCC->CFGR0 = (RCC->CFGR0 & ~(0xf << 18)) | 0;
RCC->CTLR |= RCC_PLLON;
// PLL = x18 (0 in register)
RCC->CFGR0 = ( RCC->CFGR0 & ~(0xf<<18)) | 0;
RCC->CTLR |= RCC_PLLON;
for (timeout = 10000; timeout > 0; timeout--)
if (RCC->CTLR & RCC_PLLRDY) break;
if (timeout == 0) return -7;
for( timeout = 10000; timeout > 0; timeout--) if (RCC->CTLR & RCC_PLLRDY) break;
if( timeout == 0 ) return -7;
// Switch to PLL.
// Switch to PLL.
#ifdef CH32V307GIGABIT_MCO25
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL | (9 << 24); // And output clock on PA8
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL | (9<<24); // And output clock on PA8
#else
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
#endif
// For clock in.
funPinMode(PB1, GPIO_CFGLR_IN_FLOAT); // GMII_CLK125
// For clock in.
funPinMode( PB1, GPIO_CFGLR_IN_FLOAT ); //GMII_CLK125
RCC->CFGR2 |= RCC_ETH1G_125M_EN; // Enable 125MHz clock.
RCC->CFGR2 |= RCC_ETH1G_125M_EN; // Enable 125MHz clock.
// Power on and reset.
RCC->AHBPCENR |= RCC_ETHMACEN | RCC_ETHMACTXEN | RCC_ETHMACRXEN;
RCC->AHBRSTR |= RCC_ETHMACRST;
RCC->AHBRSTR &= ~RCC_ETHMACRST;
// Power on and reset.
RCC->AHBPCENR |= RCC_ETHMACEN | RCC_ETHMACTXEN | RCC_ETHMACRXEN;
RCC->AHBRSTR |= RCC_ETHMACRST;
RCC->AHBRSTR &=~RCC_ETHMACRST;
ETH->DMABMR |= ETH_DMABMR_SR;
ETH->DMABMR |= ETH_DMABMR_SR;
// Wait for reset to complete.
for (timeout = 10000; timeout > 0 && (ETH->DMABMR & ETH_DMABMR_SR); timeout--)
{
Delay_Us(10);
}
// Wait for reset to complete.
for( timeout = 10000; timeout > 0 && (ETH->DMABMR & ETH_DMABMR_SR); timeout-- )
{
Delay_Us(10);
}
// Use RGMII
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL; // EXTEN_ETH_RGMII_SEL;
// Use RGMII
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL; //EXTEN_ETH_RGMII_SEL;
funPinMode(PB13, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDIO
funPinMode(PB12, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDC
funPinMode( PB13, GPIO_CFGLR_OUT_50Mhz_AF_PP ); //GMII_MDIO
funPinMode( PB12, GPIO_CFGLR_OUT_50Mhz_AF_PP ); //GMII_MDC
// For clock output to Ethernet module.
funPinMode(PA8, GPIO_CFGLR_OUT_50Mhz_AF_PP); // PHY_CKTAL
// For clock output to Ethernet module.
funPinMode( PA8, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // PHY_CKTAL
// Release PHY from reset.
// Release PHY from reset.
#ifdef CH32V307GIGABIT_PHY_RSTB
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_HIGH);
funDigitalWrite( CH32V307GIGABIT_PHY_RSTB, FUN_HIGH );
#endif
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 23ms (But only on the RTL8211FS) None is needed on the RTL8211E
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 23ms (But only on the RTL8211FS) None is needed on the RTL8211E
funPinMode(PB0, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD3
funPinMode(PC5, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD2
funPinMode(PC4, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD1
funPinMode(PA7, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD0
funPinMode(PA3, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXCTL
funPinMode(PA2, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXC
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
funPinMode(PC1, GPIO_CFGLR_IN_PUPD); // GMII_RXCTL
funPinMode(PC0, GPIO_CFGLR_IN_FLOAT); // GMII_RXC
funPinMode( PB0, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXD3
funPinMode( PC5, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXD2
funPinMode( PC4, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXD1
funPinMode( PA7, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXD0
funPinMode( PA3, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXCTL
funPinMode( PA2, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // GMII_TXC
funPinMode( PA1, GPIO_CFGLR_IN_PUPD ); // GMII_RXD3
funPinMode( PA0, GPIO_CFGLR_IN_PUPD ); // GMII_RXD2
funPinMode( PC3, GPIO_CFGLR_IN_PUPD ); // GMII_RXD1
funPinMode( PC2, GPIO_CFGLR_IN_PUPD ); // GMII_RXD0
funPinMode( PC1, GPIO_CFGLR_IN_PUPD ); // GMII_RXCTL
funPinMode( PC0, GPIO_CFGLR_IN_FLOAT ); // GMII_RXC
funDigitalWrite(PA1, FUN_HIGH); // SELGRV = 3.3V
funDigitalWrite(PA0, FUN_HIGH); // TXDelay = 1
funDigitalWrite(PC3, FUN_HIGH); // AN[0] = 1
funDigitalWrite(PC2, FUN_HIGH); // AN[1] = 1
funDigitalWrite(PC1, FUN_LOW); // PHYAD[0]
funDigitalWrite( PA1, FUN_HIGH ); // SELGRV = 3.3V
funDigitalWrite( PA0, FUN_HIGH ); // TXDelay = 1
funDigitalWrite( PC3, FUN_HIGH ); // AN[0] = 1
funDigitalWrite( PC2, FUN_HIGH ); // AN[1] = 1
funDigitalWrite( PC1, FUN_LOW ); // PHYAD[0]
// Configure MDC/MDIO
// Conflicting notes - some say /42, others don't.
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42;
// Configure MDC/MDIO
// Conflicting notes - some say /42, others don't.
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42;
// Update MACCR
ETH->MACCR =
(CH32V307GIGABIT_CFG_CLOCK_DELAY << 29) | // No clock delay
(0 << 23) | // Max RX = 2kB (Revisit if looking into jumbo frames)
(0 << 22) | // Max TX = 2kB (Revisit if looking into jumbo frames)
(0 << 21) | // Rated Drive (instead of energy savings mode) (10M PHY only)
(1 << 20) | // Bizarre re-use of termination resistor terminology? (10M PHY Only)
(0 << 17) | // IFG = 0, 96-bit guard time.
(0 << 14) | // FES = 2 = GBE, 1=100MBit/s (UNSET TO START)
(0 << 12) | // Self Loop = 0
(0 << 11) | // Full-Duplex Mode (UNSET TO START)
(1 << 10) | // IPCO = 1, Check TCP, UDP, ICMP header checksums.
(1 << 7) | // APCS (automatically strip frames)
(1 << 3) | // TE (Transmit enable!)
(1 << 2) | // RE (Receive Enable)
(CH32V307GIGABIT_CFG_CLOCK_PHASE << 1) | // TCF = 0 (Potentailly change if clocking is wrong)
0;
// Update MACCR
ETH->MACCR =
( CH32V307GIGABIT_CFG_CLOCK_DELAY << 29 ) | // No clock delay
( 0 << 23 ) | // Max RX = 2kB (Revisit if looking into jumbo frames)
( 0 << 22 ) | // Max TX = 2kB (Revisit if looking into jumbo frames)
( 0 << 21 ) | // Rated Drive (instead of energy savings mode) (10M PHY only)
( 1 << 20 ) | // Bizarre re-use of termination resistor terminology? (10M PHY Only)
( 0 << 17 ) | // IFG = 0, 96-bit guard time.
( 0 << 14 ) | // FES = 2 = GBE, 1=100MBit/s (UNSET TO START)
( 0 << 12 ) | // Self Loop = 0
( 0 << 11 ) | // Full-Duplex Mode (UNSET TO START)
( 1 << 10 ) | // IPCO = 1, Check TCP, UDP, ICMP header checksums.
( 1 << 7 ) | // APCS (automatically strip frames)
( 1 << 3 ) | // TE (Transmit enable!)
( 1 << 2 ) | // RE (Receive Enable)
( CH32V307GIGABIT_CFG_CLOCK_PHASE << 1 ) | // TCF = 0 (Potentailly change if clocking is wrong)
0;
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 19ms.
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 19ms.
// Reset the physical layer
ch32v307ethPHYRegWrite(PHY_BCR,
PHY_Reset |
1 << 12 | // Auto negotiate
1 << 8 | // Duplex
1 << 6 | // Speed Bit.
0);
// Reset the physical layer
ch32v307ethPHYRegWrite( PHY_BCR,
PHY_Reset |
1<<12 | // Auto negotiate
1<<8 | // Duplex
1<<6 | // Speed Bit.
0 );
// De-assert reset.
ch32v307ethPHYRegWrite(PHY_BCR,
1 << 12 | // Auto negotiate
1 << 8 | // Duplex
1 << 6 | // Speed Bit.
0);
// De-assert reset.
ch32v307ethPHYRegWrite( PHY_BCR,
1<<12 | // Auto negotiate
1<<8 | // Duplex
1<<6 | // Speed Bit.
0 );
ch32v307ethPHYRegRead(0x03);
ch32v307eth_phyid = ch32v307ethPHYRegRead(0x03); // Read twice to be safe.
if (ch32v307eth_phyid == 0xc916)
ch32v307ethPHYRegWrite(0x1F, 0x0a43); // RTL8211FS needs page select.
ch32v307ethPHYRegRead( 0x03 );
ch32v307eth_phyid = ch32v307ethPHYRegRead( 0x03 ); // Read twice to be safe.
if( ch32v307eth_phyid == 0xc916 )
ch32v307ethPHYRegWrite( 0x1F, 0x0a43 ); // RTL8211FS needs page select.
ch32v307ethGetMacInUC(ch32v307eth_mac);
ch32v307ethGetMacInUC( ch32v307eth_mac );
ETH->MACA0HR = (uint32_t)((ch32v307eth_mac[5] << 8) | ch32v307eth_mac[4]);
ETH->MACA0LR = (uint32_t)(ch32v307eth_mac[0] | (ch32v307eth_mac[1] << 8) | (ch32v307eth_mac[2] << 16) | (ch32v307eth_mac[3] << 24));
ETH->MACA0HR = (uint32_t)((ch32v307eth_mac[5]<<8) | ch32v307eth_mac[4]);
ETH->MACA0LR = (uint32_t)(ch32v307eth_mac[0] | (ch32v307eth_mac[1]<<8) | (ch32v307eth_mac[2]<<16) | (ch32v307eth_mac[3]<<24));
ETH->MACFFR = (uint32_t)(ETH_ReceiveAll_Disable |
ETH_SourceAddrFilter_Disable |
ETH_PassControlFrames_BlockAll |
ETH_BroadcastFramesReception_Enable |
ETH_DestinationAddrFilter_Normal |
ETH_PromiscuousMode_Disable |
ETH_MulticastFramesFilter_Perfect |
ETH_UnicastFramesFilter_Perfect);
ETH->MACFFR = (uint32_t)(ETH_ReceiveAll_Disable |
ETH_SourceAddrFilter_Disable |
ETH_PassControlFrames_BlockAll |
ETH_BroadcastFramesReception_Enable |
ETH_DestinationAddrFilter_Normal |
ETH_PromiscuousMode_Disable |
ETH_MulticastFramesFilter_Perfect |
ETH_UnicastFramesFilter_Perfect);
ETH->MACHTHR = (uint32_t)0;
ETH->MACHTLR = (uint32_t)0;
ETH->MACVLANTR = (uint32_t)(ETH_VLANTagComparison_16Bit);
ETH->MACHTHR = (uint32_t)0;
ETH->MACHTLR = (uint32_t)0;
ETH->MACVLANTR = (uint32_t)(ETH_VLANTagComparison_16Bit);
ETH->MACFCR = 0; // No pause frames.
ETH->MACFCR = 0; // No pause frames.
// Configure RX/TX chains.
ETH_DMADESCTypeDef *tdesc;
for (i = 0; i < CH32V307GIGABIT_TXBUFNB; i++)
{
tdesc = ch32v307eth_DMATxDscrTab + i;
tdesc->ControlBufferSize = 0;
tdesc->Status = ETH_DMATxDesc_TCH | ETH_DMATxDesc_IC | ETH_DMATxDesc_FS;
tdesc->Buffer1Addr = (uint32_t)0; // Populate with data.
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_TXBUFNB - 1) ? ((uint32_t)(ch32v307eth_DMATxDscrTab + i + 1)) : (uint32_t)ch32v307eth_DMATxDscrTab;
}
ETH->DMATDLAR = (uint32_t)ch32v307eth_DMATxDscrTab;
for (i = 0; i < CH32V307GIGABIT_RXBUFNB; i++)
{
tdesc = ch32v307eth_DMARxDscrTab + i;
tdesc->Status = ETH_DMARxDesc_OWN;
tdesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)CH32V307GIGABIT_BUFFSIZE;
tdesc->Buffer1Addr = (uint32_t)(&ch32v307eth_MACRxBuf[i * CH32V307GIGABIT_BUFFSIZE]);
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_RXBUFNB - 1) ? (uint32_t)(ch32v307eth_DMARxDscrTab + i + 1) : (uint32_t)(ch32v307eth_DMARxDscrTab);
}
ETH->DMARDLAR = (uint32_t)ch32v307eth_DMARxDscrTab;
// Configure RX/TX chains.
ETH_DMADESCTypeDef *tdesc;
for(i = 0; i < CH32V307GIGABIT_TXBUFNB; i++)
{
tdesc = ch32v307eth_DMATxDscrTab + i;
tdesc->ControlBufferSize = 0;
tdesc->Status = ETH_DMATxDesc_TCH | ETH_DMATxDesc_IC | ETH_DMATxDesc_FS;
tdesc->Buffer1Addr = (uint32_t)0; // Populate with data.
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_TXBUFNB - 1) ? ((uint32_t)(ch32v307eth_DMATxDscrTab + i + 1)) : (uint32_t)ch32v307eth_DMATxDscrTab;
}
ETH->DMATDLAR = (uint32_t)ch32v307eth_DMATxDscrTab;
for(i = 0; i < CH32V307GIGABIT_RXBUFNB; i++)
{
tdesc = ch32v307eth_DMARxDscrTab + i;
tdesc->Status = ETH_DMARxDesc_OWN;
tdesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)CH32V307GIGABIT_BUFFSIZE;
tdesc->Buffer1Addr = (uint32_t)(&ch32v307eth_MACRxBuf[i * CH32V307GIGABIT_BUFFSIZE]);
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_RXBUFNB - 1) ? (uint32_t)(ch32v307eth_DMARxDscrTab + i + 1) : (uint32_t)(ch32v307eth_DMARxDscrTab);
}
ETH->DMARDLAR = (uint32_t)ch32v307eth_DMARxDscrTab;
pDMARxGet = ch32v307eth_DMARxDscrTab;
pDMATxSet = ch32v307eth_DMATxDscrTab;
pDMARxGet = ch32v307eth_DMARxDscrTab;
pDMATxSet = ch32v307eth_DMATxDscrTab;
// Receive a good frame half interrupt mask.
// Receive CRC error frame half interrupt mask.
// For the future: Why do we want this?
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;
// Receive a good frame half interrupt mask.
// Receive CRC error frame half interrupt mask.
// For the future: Why do we want this?
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;
ETH->DMAIER = ETH_DMA_IT_NIS | // Normal interrupt enable.
ETH_DMA_IT_R | // Receive
ETH_DMA_IT_T | // Transmit
ETH_DMA_IT_AIS | // Abnormal interrupt
ETH_DMA_IT_RBU; // Receive buffer unavailable interrupt enable
ETH->DMAIER = ETH_DMA_IT_NIS | // Normal interrupt enable.
ETH_DMA_IT_R | // Receive
ETH_DMA_IT_T | // Transmit
ETH_DMA_IT_AIS | // Abnormal interrupt
ETH_DMA_IT_RBU; // Receive buffer unavailable interrupt enable
NVIC_EnableIRQ(ETH_IRQn);
NVIC_EnableIRQ( ETH_IRQn );
// Actually enable receiving process.
ETH->DMAOMR = ETH_DMAOMR_SR | ETH_DMAOMR_ST | ETH_DMAOMR_TSF | ETH_DMAOMR_FEF;
// Actually enable receiving process.
ETH->DMAOMR = ETH_DMAOMR_SR | ETH_DMAOMR_ST | ETH_DMAOMR_TSF | ETH_DMAOMR_FEF;
return 0;
return 0;
}
void ETH_IRQHandler(void) __attribute__((interrupt));
void ETH_IRQHandler(void)
void ETH_IRQHandler( void ) __attribute__((interrupt));
void ETH_IRQHandler( void )
{
uint32_t int_sta;
do
{
int_sta = ETH->DMASR;
if ((int_sta & (ETH_DMA_IT_AIS | ETH_DMA_IT_NIS)) == 0)
{
break;
}
do
{
int_sta = ETH->DMASR;
if ( ( int_sta & ( ETH_DMA_IT_AIS | ETH_DMA_IT_NIS ) ) == 0 )
{
break;
}
// Off nominal situations.
if (int_sta & ETH_DMA_IT_AIS)
{
// Receive buffer unavailable interrupt enable.
if (int_sta & ETH_DMA_IT_RBU)
{
ETH->DMASR = ETH_DMA_IT_RBU;
if ((INFO->CHIPID & 0xf0) == 0x10)
{
((ETH_DMADESCTypeDef *)(((ETH_DMADESCTypeDef *)(ETH->DMACHRDR))->Buffer2NextDescAddr))->Status = ETH_DMARxDesc_OWN;
ETH->DMARPDR = 0;
}
}
ETH->DMASR = ETH_DMA_IT_AIS;
}
// Off nominal situations.
if (int_sta & ETH_DMA_IT_AIS)
{
// Receive buffer unavailable interrupt enable.
if (int_sta & ETH_DMA_IT_RBU)
{
ETH->DMASR = ETH_DMA_IT_RBU;
if((INFO->CHIPID & 0xf0) == 0x10)
{
((ETH_DMADESCTypeDef *)(((ETH_DMADESCTypeDef *)(ETH->DMACHRDR))->Buffer2NextDescAddr))->Status = ETH_DMARxDesc_OWN;
ETH->DMARPDR = 0;
}
}
ETH->DMASR = ETH_DMA_IT_AIS;
}
// Nominal interrupts.
if (int_sta & ETH_DMA_IT_NIS)
{
if (int_sta & ETH_DMA_IT_R)
{
// Received a packet, normally.
// Status is in Table 27-17 Definitions of RDes0
do
{
// XXX TODO: Is this a good place to acknowledge? REVISIT: Should this go lower?
// XXX TODO: Restructure this to allow for
ETH->DMASR = ETH_DMA_IT_R;
// Nominal interrupts.
if( int_sta & ETH_DMA_IT_NIS )
{
if( int_sta & ETH_DMA_IT_R )
{
// Received a packet, normally.
// Status is in Table 27-17 Definitions of RDes0
do
{
// XXX TODO: Is this a good place to acknowledge? REVISIT: Should this go lower?
// XXX TODO: Restructure this to allow for
ETH->DMASR = ETH_DMA_IT_R;
uint32_t status = pDMARxGet->Status;
if (status & ETH_DMARxDesc_OWN) break;
uint32_t status = pDMARxGet->Status;
if( status & ETH_DMARxDesc_OWN ) break;
// We only have a valid packet in a specific situation.
// So, we take the status, then mask off the bits we care about
// And see if they're equal to the ones that need to be set/unset.
const uint32_t mask =
ETH_DMARxDesc_OWN |
ETH_DMARxDesc_LS |
ETH_DMARxDesc_ES |
ETH_DMARxDesc_FS;
const uint32_t eq =
0 |
ETH_DMARxDesc_LS |
0 |
ETH_DMARxDesc_FS;
// We only have a valid packet in a specific situation.
// So, we take the status, then mask off the bits we care about
// And see if they're equal to the ones that need to be set/unset.
const uint32_t mask =
ETH_DMARxDesc_OWN |
ETH_DMARxDesc_LS |
ETH_DMARxDesc_ES |
ETH_DMARxDesc_FS;
const uint32_t eq =
0 |
ETH_DMARxDesc_LS |
0 |
ETH_DMARxDesc_FS;
int suppress_own = 0;
int suppress_own = 0;
if ((status & mask) == eq)
{
int32_t frame_length = ((status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT) - 4;
if (frame_length > 0)
{
uint8_t *data = (uint8_t *)pDMARxGet->Buffer1Addr;
suppress_own = ch32v307ethInitHandlePacket(data, frame_length, pDMARxGet);
}
}
// Otherwise, Invalid Packet
if( ( status & mask ) == eq )
{
int32_t frame_length = ((status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT) - 4;
if( frame_length > 0 )
{
uint8_t * data = (uint8_t*)pDMARxGet->Buffer1Addr;
suppress_own = ch32v307ethInitHandlePacket( data, frame_length, pDMARxGet );
}
}
// Otherwise, Invalid Packet
// Relinquish control back to underlying hardware.
if (!suppress_own)
pDMARxGet->Status = ETH_DMARxDesc_OWN;
// Relinquish control back to underlying hardware.
if( !suppress_own )
pDMARxGet->Status = ETH_DMARxDesc_OWN;
// Tricky logic for figuring out the next packet. Originally
// discussed in ch32v30x_eth.c in ETH_DropRxPkt
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(pDMARxGet->Buffer2NextDescAddr);
else
{
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(ETH->DMARDLAR);
else
pDMARxGet = (ETH_DMADESCTypeDef *)((uint32_t)pDMARxGet + 0x10 + ((ETH->DMABMR & ETH_DMABMR_DSL) >> 2));
}
} while (1);
}
if (int_sta & ETH_DMA_IT_T)
{
ch32v307ethInitHandleTXC();
ETH->DMASR = ETH_DMA_IT_T;
}
ETH->DMASR = ETH_DMA_IT_NIS;
}
} while (1);
// Tricky logic for figuring out the next packet. Originally
// discussed in ch32v30x_eth.c in ETH_DropRxPkt
if((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(pDMARxGet->Buffer2NextDescAddr);
else
{
if((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET)
pDMARxGet = (ETH_DMADESCTypeDef *)(ETH->DMARDLAR);
else
pDMARxGet = (ETH_DMADESCTypeDef *)((uint32_t)pDMARxGet + 0x10 + ((ETH->DMABMR & ETH_DMABMR_DSL) >> 2));
}
} while( 1 );
}
if( int_sta & ETH_DMA_IT_T )
{
ch32v307ethInitHandleTXC();
ETH->DMASR = ETH_DMA_IT_T;
}
ETH->DMASR = ETH_DMA_IT_NIS;
}
} while( 1 );
}
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc)
static int ch32v307ethTransmitStatic(uint8_t * buffer, uint32_t length, int enable_txc)
{
// The official SDK waits until ETH_DMATxDesc_TTSS is set.
// This also provides a transmit timestamp, which could be
// used for PTP.
// But we don't want to do that.
// We just want to go. If anyone cares, they can check later.
// The official SDK waits until ETH_DMATxDesc_TTSS is set.
// This also provides a transmit timestamp, which could be
// used for PTP.
// But we don't want to do that.
// We just want to go. If anyone cares, they can check later.
if (pDMATxSet->Status & ETH_DMATxDesc_OWN)
{
ETH->DMATPDR = 0;
return -1;
}
if( pDMATxSet->Status & ETH_DMATxDesc_OWN )
{
ETH->DMATPDR = 0;
return -1;
}
pDMATxSet->ControlBufferSize = (length & ETH_DMATxDesc_TBS1);
pDMATxSet->Buffer1Addr = (uint32_t)buffer;
pDMATxSet->Buffer1Addr = (uint32_t)buffer;
// Status is in Table 27-12 "Definitions of TDes0 bits"
enable_txc = enable_txc ? ETH_DMATxDesc_IC : 0;
pDMATxSet->Status =
ETH_DMATxDesc_LS | // Last Segment (This is all you need to have to transmit)
ETH_DMATxDesc_FS | // First Segment (Beginning of transmission)
enable_txc | // Interrupt when complete
ETH_DMATxDesc_TCH | // Next Descriptor Address Valid
ETH_DMATxDesc_CIC_TCPUDPICMP_Full | // Do all header checksums.
ETH_DMATxDesc_OWN; // Own back to hardware
// Status is in Table 27-12 "Definitions of TDes0 bits"
enable_txc = enable_txc ? ETH_DMATxDesc_IC : 0;
pDMATxSet->Status =
ETH_DMATxDesc_LS | // Last Segment (This is all you need to have to transmit)
ETH_DMATxDesc_FS | // First Segment (Beginning of transmission)
enable_txc | // Interrupt when complete
ETH_DMATxDesc_TCH | // Next Descriptor Address Valid
ETH_DMATxDesc_CIC_TCPUDPICMP_Full | // Do all header checksums.
ETH_DMATxDesc_OWN; // Own back to hardware
pDMATxSet = (ETH_DMADESCTypeDef *)pDMATxSet->Buffer2NextDescAddr;
pDMATxSet = (ETH_DMADESCTypeDef*)pDMATxSet->Buffer2NextDescAddr;
ETH->DMASR = ETH_DMASR_TBUS; // This resets the transmit process (or "starts" it)
ETH->DMATPDR = 0;
ETH->DMASR = ETH_DMASR_TBUS; // This resets the transmit process (or "starts" it)
ETH->DMATPDR = 0;
return 0;
return 0;
}
#endif

File diff suppressed because it is too large Load diff

1036
inc/extralibs/fsusb.c Normal file

File diff suppressed because it is too large Load diff

419
inc/extralibs/fsusb.h Normal file
View file

@ -0,0 +1,419 @@
#ifndef _FSUSB_H
#define _FSUSB_H
#include <stdint.h>
#include "ch32fun.h"
#include "usb_defines.h"
#include "usb_config.h"
#if defined(FUSB_FROM_RAM) && (FUSB_FROM_RAM)
#if defined(CH5xx)
#define __USBFS_FUN_ATTRIBUTE __attribute__((section(".srodata"), used))
#else
#define __USBFS_FUN_ATTRIBUTE
#endif
#else
#define __USBFS_FUN_ATTRIBUTE
#endif
#if defined(CH5xx) || defined(CH32X03x) || defined(CH32V10x)
#if !defined (CH32X03x) && !defined(CH32V10x)
#define USBFS_BASE USB_BASE_ADDR
typedef struct
{
__IO uint8_t BASE_CTRL;
__IO uint8_t UDEV_CTRL;
__IO uint8_t INT_EN;
__IO uint8_t DEV_ADDR;
__IO uint8_t USB_STATUS;
__IO uint8_t MIS_ST;
__IO uint8_t INT_FG; // "Combined" register in some situations. (ST_FG)
__IO uint8_t INT_ST;
__IO uint8_t RX_LEN;
__IO uint8_t Reserved0;
__IO uint16_t Reserved1;
__IO uint8_t UEP4_1_MOD;
__IO uint8_t UEP2_3_MOD;
__IO uint8_t UEP567_MOD;
__IO uint8_t Reserved2;
__IO uint16_t UEP0_DMA;
__IO uint16_t Reserved3;
__IO uint16_t UEP1_DMA;
__IO uint16_t Reserved4;
__IO uint16_t UEP2_DMA;
__IO uint16_t Reserved5;
__IO uint16_t UEP3_DMA;
__IO uint16_t Reserved6;
__IO uint8_t UEP0_TX_LEN;
__IO uint8_t Reserved7;
__IO uint8_t UEP0_TX_CTRL;
__IO uint8_t Reserved8;
__IO uint8_t UEP1_TX_LEN;
__IO uint8_t Reserved9;
__IO uint8_t UEP1_TX_CTRL;
__IO uint8_t Reserved10;
__IO uint8_t UEP2_TX_LEN;
__IO uint8_t Reserved11;
__IO uint8_t UEP2_TX_CTRL;
__IO uint8_t Reserved12;
__IO uint8_t UEP3_TX_LEN;
__IO uint8_t Reserved13;
__IO uint8_t UEP3_TX_CTRL;
__IO uint8_t Reserved14;
__IO uint8_t UEP4_TX_LEN;
__IO uint8_t Reserved15;
__IO uint8_t UEP4_TX_CTRL;
__IO uint8_t Reserved16;
__IO uint32_t Reserved17;
__IO uint32_t Reserved18;
__IO uint32_t Reserved19;
__IO uint32_t Reserved20;
__IO uint32_t Reserved21;
__IO uint32_t Reserved22;
__IO uint32_t Reserved23;
__IO uint32_t Reserved24;
__IO uint16_t UEP5_DMA;
__IO uint16_t UEP6_DMA;
__IO uint16_t UEP7_DMA;
__IO uint32_t Reserved25;
__IO uint8_t UEP5_TX_LEN;
__IO uint8_t Reserved26;
__IO uint8_t UEP5_TX_CTRL;
__IO uint8_t Reserved27;
__IO uint8_t UEP6_TX_LEN;
__IO uint8_t Reserved28;
__IO uint8_t UEP6_TX_CTRL;
__IO uint8_t Reserved29;
__IO uint8_t UEP7_TX_LEN;
__IO uint8_t UEP7_TX_CTRL;
__IO uint8_t Reserved30;
__IO uint32_t EPX_MODE;
} USBFS_TypeDef;
#define UEP_CTRL_LEN(n) (((uint8_t*)&USBFS->UEP0_TX_LEN)[n*4])
#define UEP_CTRL_TX(n) (((uint8_t*)&USBFS->UEP0_TX_CTRL)[n*4])
#define UEP_CTRL_RX(n) (((uint8_t*)&USBFS->UEP0_TX_CTRL)[n*4])
#define UEP_DMA(n) (((uint16_t*)&USBFS->UEP0_DMA)[n*2])
#endif
#if defined(CH32V10x)
#define USBFS_BASE 0x40023400
#define USB_IRQn USBFS_IRQn
typedef struct
{
__IO uint8_t BASE_CTRL;
__IO uint8_t UDEV_CTRL;
__IO uint8_t INT_EN;
__IO uint8_t DEV_ADDR;
__IO uint8_t USB_STATUS;
__IO uint8_t MIS_ST;
__IO uint8_t INT_FG; // "Combined" register in some situations. (ST_FG)
__IO uint8_t INT_ST;
__IO uint8_t RX_LEN;
__IO uint8_t Reserved0;
__IO uint16_t Reserved1;
__IO uint8_t UEP4_1_MOD;
__IO uint8_t UEP2_3_MOD;
__IO uint8_t UEP5_6_MOD;
__IO uint8_t UEP7_MOD;
__IO uint16_t UEP0_DMA;
__IO uint16_t Reserved3;
__IO uint16_t UEP1_DMA;
__IO uint16_t Reserved4;
__IO uint16_t UEP2_DMA;
__IO uint16_t Reserved5;
__IO uint16_t UEP3_DMA;
__IO uint16_t Reserved6;
__IO uint16_t UEP4_DMA;
__IO uint16_t Reserved7;
__IO uint16_t UEP5_DMA;
__IO uint16_t Reserved8;
__IO uint16_t UEP6_DMA;
__IO uint16_t Reserved9;
__IO uint16_t UEP7_DMA;
__IO uint16_t Reserved10;
__IO uint16_t UEP0_TX_LEN;
__IO uint8_t UEP0_TX_CTRL;
__IO uint8_t Reserved11;
__IO uint16_t UEP1_TX_LEN;
__IO uint8_t UEP1_TX_CTRL;
__IO uint8_t Reserved12;
__IO uint16_t UEP2_TX_LEN;
__IO uint8_t UEP2_TX_CTRL;
__IO uint8_t Reserved13;
__IO uint16_t UEP3_TX_LEN;
__IO uint8_t UEP3_TX_CTRL;
__IO uint8_t Reserved14;
__IO uint16_t UEP4_TX_LEN;
__IO uint8_t UEP4_TX_CTRL;
__IO uint8_t Reserved15;
__IO uint16_t UEP5_TX_LEN;
__IO uint8_t UEP5_TX_CTRL;
__IO uint8_t Reserved16;
__IO uint16_t UEP6_TX_LEN;
__IO uint8_t UEP6_TX_CTRL;
__IO uint8_t Reserved17;
__IO uint16_t UEP7_TX_LEN;
__IO uint8_t UEP7_TX_CTRL;
__IO uint8_t Reserved18;
} USBFS_TypeDef;
#define UEP_CTRL_LEN(n) (((volatile uint16_t*)&USBFS->UEP0_TX_LEN)[n*2])
#define UEP_CTRL_TX(n) (((volatile uint8_t*)&USBFS->UEP0_TX_CTRL)[n*4])
#define UEP_CTRL_RX(n) (((volatile uint8_t*)&USBFS->UEP0_TX_CTRL)[n*4])
#define UEP_DMA(n) (((volatile uint16_t*)&USBFS->UEP0_DMA)[n*2])
#endif
#if !defined (CH32X03x)
#define DEBUG_PIN PA8
#define DEF_USBD_UEP0_SIZE 64 /* usb hs/fs device end-point 0 size */
#define UEP_SIZE 64
#define DEF_UEP_IN 0x80
#define DEF_UEP_OUT 0x00
#define DEF_UEP_BUSY 0x01
#define DEF_UEP_FREE 0x00
#define DEF_UEP0 0
#define DEF_UEP1 1
#define DEF_UEP2 2
#define DEF_UEP3 3
#define DEF_UEP4 4
#define DEF_UEP5 5
#define DEF_UEP6 6
#define DEF_UEP7 7
#define UNUM_EP 8
#define USBFS_UEP_T_RES_MASK MASK_UEP_T_RES
#define USBFS_UEP_T_RES_ACK UEP_T_RES_ACK
#define USBFS_UEP_T_RES_NAK UEP_T_RES_NAK
#define USBFS_UEP_T_TOG RB_UEP_T_TOG
#define USBFS_UEP_T_RES_STALL UEP_T_RES_STALL
#define USBFS_UEP_R_RES_MASK MASK_UEP_R_RES
#define USBFS_UEP_R_RES_ACK UEP_R_RES_ACK
#define USBFS_UEP_R_RES_NAK UEP_R_RES_NAK
#define USBFS_UEP_R_TOG RB_UEP_R_TOG
#define USBFS_UEP_R_RES_STALL UEP_R_RES_STALL
#define USBFS_UDA_GP_BIT RB_UDA_GP_BIT
#define USBFS_UMS_SUSPEND RB_UMS_SUSPEND
#define USBFS_UC_RESET_SIE RB_UC_RESET_SIE
#define USBFS_UC_CLR_ALL RB_UC_CLR_ALL
#define USBFS_UIE_SUSPEND RB_UIE_SUSPEND
#define USBFS_UIE_TRANSFER RB_UIE_TRANSFER
#define USBFS_UIE_BUS_RST RB_UIE_BUS_RST
#define USBFS_UC_INT_BUSY RB_UC_INT_BUSY
#define USBFS_UC_DMA_EN RB_UC_DMA_EN
#define USBFS_UC_DEV_PU_EN RB_UC_DEV_PU_EN
#endif
#define USBFS_UD_PORT_EN RB_UD_PORT_EN
#define USBFS_UD_PD_DIS RB_UD_PD_DIS
#define USBFS_UEP1_RX_EN RB_UEP1_RX_EN
#define USBFS_UEP2_RX_EN RB_UEP2_RX_EN
#define USBFS_UEP3_RX_EN RB_UEP3_RX_EN
#define USBFS_UEP4_RX_EN RB_UEP4_RX_EN
#define USBFS_UEP1_TX_EN RB_UEP1_TX_EN
#define USBFS_UEP2_TX_EN RB_UEP2_TX_EN
#define USBFS_UEP3_TX_EN RB_UEP3_TX_EN
#define USBFS_UEP4_TX_EN RB_UEP4_TX_EN
#define CHECK_USBFS_UEP_AUTO_TOG RB_UEP_AUTO_TOG
#define CHECK_USBFS_UEP_T_AUTO_TOG RB_UEP_AUTO_TOG
// #define CHECK_USBFS_UEP_R_AUTO_TOG USBOTG_UEP_R_AUTO_TOG
#define USBFS ((USBFS_TypeDef *)USBFS_BASE)
#ifdef CH32X03x
#define UEP_CTRL_LEN(n) (((volatile uint16_t*)&USBFS->UEP0_TX_LEN)[n*2])
#define UEP_CTRL_TX(n) (((volatile uint16_t*)&USBFS->UEP0_CTRL_H)[n*2])
#define UEP_CTRL_RX(n) (((volatile uint16_t*)&USBFS->UEP0_CTRL_H)[n*2])
#define UEP_DMA(n) (((volatile uint32_t*)&USBFS->UEP0_DMA)[n])
#define DEBUG_PIN PB12
#define USB_IRQn USBFS_IRQn
#endif
#else
#define DEBUG_PIN PB0
#define USBFS ((USBOTG_FS_TypeDef *)USBFS_BASE)
#define UEP_CTRL_LEN(n) (((volatile uint16_t*)&USBFS->UEP0_TX_LEN)[n*2])
#define UEP_CTRL_TX(n) (((volatile uint8_t*)&USBFS->UEP0_TX_CTRL)[n*4])
#define UEP_CTRL_RX(n) (((volatile uint8_t*)&USBFS->UEP0_RX_CTRL)[n*4])
#define UEP_DMA(n) (((volatile uint32_t*)&USBFS->UEP0_DMA)[n])
#define CHECK_USBFS_UEP_T_AUTO_TOG USBOTG_UEP_T_AUTO_TOG
#define CHECK_USBFS_UEP_R_AUTO_TOG USBOTG_UEP_R_AUTO_TOG
#define USBFS_UEP_T_RES_MASK USBOTG_UEP_T_RES_MASK
#define USBFS_UEP_T_RES_ACK USBOTG_UEP_T_RES_ACK
#define USBFS_UEP_T_RES_NAK USBOTG_UEP_T_RES_NAK
#define USBFS_UEP_T_TOG USBOTG_UEP_T_TOG
#define USBFS_UEP_T_RES_STALL USBOTG_UEP_T_RES_STALL
#define USBFS_UEP_R_RES_MASK USBOTG_UEP_R_RES_MASK
#define USBFS_UEP_R_RES_ACK USBOTG_UEP_R_RES_ACK
#define USBFS_UEP_R_RES_NAK USBOTG_UEP_R_RES_NAK
#define USBFS_UEP_R_TOG USBOTG_UEP_R_TOG
#define USBFS_UEP_R_RES_STALL USBOTG_UEP_R_RES_STALL
// #define USBFS_UDA_GP_BIT USBOTG_UDA_GP_BIT
#define USBFS_UMS_SUSPEND USBOTG_UMS_SUSPEND
#define USBFS_UEP1_RX_EN USBOTG_UEP1_RX_EN
#define USBFS_UEP2_RX_EN USBOTG_UEP2_RX_EN
#define USBFS_UEP3_RX_EN USBOTG_UEP3_RX_EN
#define USBFS_UEP4_RX_EN USBOTG_UEP4_RX_EN
#define USBFS_UEP1_TX_EN USBOTG_UEP1_TX_EN
#define USBFS_UEP2_TX_EN USBOTG_UEP2_TX_EN
#define USBFS_UEP3_TX_EN USBOTG_UEP3_TX_EN
#define USBFS_UEP4_TX_EN USBOTG_UEP4_TX_EN
#define USBFS_UC_RESET_SIE USBOTG_UC_RESET_SIE
#define USBFS_UC_CLR_ALL USBOTG_UC_CLR_ALL
#define USBFS_UIE_SUSPEND USBOTG_UIE_SUSPEND
#define USBFS_UIE_TRANSFER USBOTG_UIE_TRANSFER
#define USBFS_UIE_BUS_RST USBOTG_UIE_BUS_RST
#define USBFS_UC_INT_BUSY USBOTG_UC_INT_BUSY
#define USBFS_UC_DMA_EN USBOTG_UC_DMA_EN
#define USBFS_UC_DEV_PU_EN USBOTG_UC_DEV_PU_EN
#define USBFS_UD_PD_DIS USBOTG_UD_PD_DIS
#define USBFS_UD_PORT_EN USBOTG_UD_PORT_EN
#ifdef CH32V30x_D8C
#define USB_IRQn OTG_FS_IRQn
#else
#define USB_IRQn USBHD_IRQn
#endif
#endif
// Mask for the combined USBFS->INT_FG + USBFS->INT_ST
#define CRB_UIS_IS_NAK (1<<7)
#define CRB_U_TOG_OK (1<<6)
#define CRB_U_SIE_FREE (1<<5)
#define CRB_UIF_FIFO_OV (1<<4)
#define CRB_UIF_HST_SOF (1<<3)
#define CRB_UIF_SUSPEND (1<<2)
#define CRB_UIF_TRANSFER (1<<1)
#define CRB_UIF_BUS_RST (1<<0)
#if defined(CH5xx) || defined(CH32X03x) || defined(CH32V10x)
#define CRB_UIF_SETUP_ACT (1<<15)
#endif
#define CRB_UIS_TOG_OK (1<<14)
#define CMASK_UIS_TOKEN (3<<12)
#define CMASK_UIS_ENDP (0xf<<8)
#define CUIS_TOKEN_OUT 0x0
#define CUIS_TOKEN_SOF 0x1
#define CUIS_TOKEN_IN 0x2
#define CUIS_TOKEN_SETUP 0x3
#define USBFS_PACKET_SIZE 64
extern uint32_t USBDEBUG0, USBDEBUG1, USBDEBUG2;
struct _USBState;
// Provided functions:
int USBFSSetup();
uint8_t USBFS_Endp_DataUp(uint8_t endp, const uint8_t *pbuf, uint16_t len, uint8_t mod);
static inline uint8_t * USBFS_GetEPBufferIfAvailable( int endp );
static inline int USBFS_SendEndpoint( int endp, int len );
static inline int USBFS_SendACK( int endp, int tx );
static inline int USBFS_SendNAK( int endp, int tx );
#if FUSB_USE_DMA7_COPY
static inline void copyBuffer( uint8_t * dest, const uint8_t * src, int len );
static inline void copyBufferComplete();
#endif
// Implement the following:
#if FUSB_HID_USER_REPORTS
__USBFS_FUN_ATTRIBUTE int HandleHidUserGetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
__USBFS_FUN_ATTRIBUTE int HandleHidUserSetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
void HandleHidUserReportDataOut( struct _USBState * ctx, uint8_t * data, int len );
int HandleHidUserReportDataIn( struct _USBState * ctx, uint8_t * data, int len );
void HandleHidUserReportOutComplete( struct _USBState * ctx );
#endif
#if FUSB_USER_HANDLERS
__USBFS_FUN_ATTRIBUTE int HandleInRequest( struct _USBState * ctx, int endp, uint8_t * data, int len );
__USBFS_FUN_ATTRIBUTE void HandleDataOut( struct _USBState * ctx, int endp, uint8_t * data, int len );
__USBFS_FUN_ATTRIBUTE int HandleSetupCustom( struct _USBState * ctx, int setup_code);
#endif
typedef enum
{
USBFS_EP_OFF = 0,
USBFS_EP_RX = -1,
USBFS_EP_TX = 1,
} USBFS_EP_mode;
#ifndef FUSB_EP1_MODE
#define FUSB_EP1_MODE 0
#endif
#ifndef FUSB_EP2_MODE
#define FUSB_EP2_MODE 0
#endif
#ifndef FUSB_EP3_MODE
#define FUSB_EP3_MODE 0
#endif
#ifndef FUSB_EP4_MODE
#define FUSB_EP4_MODE 0
#endif
#ifndef FUSB_EP5_MODE
#define FUSB_EP5_MODE 0
#endif
#ifndef FUSB_EP6_MODE
#define FUSB_EP6_MODE 0
#endif
#ifndef FUSB_EP7_MODE
#define FUSB_EP7_MODE 0
#endif
struct _USBState
{
// Setup Request
uint8_t USBFS_SetupReqCode;
uint8_t USBFS_SetupReqType;
uint16_t USBFS_SetupReqLen; // Used for tracking place along send.
uint32_t USBFS_IndexValue;
// USB Device Status
uint8_t USBFS_DevConfig;
uint8_t USBFS_DevAddr;
uint8_t USBFS_DevSleepStatus;
uint8_t USBFS_DevEnumStatus;
uint8_t* pCtrlPayloadPtr;
uint8_t ENDPOINTS[FUSB_CONFIG_EPS][64];
USBFS_EP_mode endpoint_mode[FUSB_CONFIG_EPS+1]; // IN -1, OUT 1, OFF 0
#define CTRL0BUFF (USBFSCTX.ENDPOINTS[0])
#define pUSBFS_SetupReqPak ((tusb_control_request_t*)CTRL0BUFF)
#if FUSB_HID_INTERFACES > 0
uint8_t USBFS_HidIdle[FUSB_HID_INTERFACES];
uint8_t USBFS_HidProtocol[FUSB_HID_INTERFACES];
#endif
volatile uint8_t USBFS_Endp_Busy[FUSB_CONFIG_EPS];
volatile uint8_t USBFS_errata_dont_send_endpoint_in_window;
};
extern struct _USBState USBFSCTX;
#include "fsusb.c"
#endif

1022
inc/extralibs/hsusb.c Normal file

File diff suppressed because it is too large Load diff

475
inc/extralibs/hsusb.h Normal file
View file

@ -0,0 +1,475 @@
#ifndef _HSUSB_H
#define _HSUSB_H
#include <stdint.h>
#include "ch32fun.h"
#include "usb_defines.h"
#include "usb_config.h"
#if !defined(CH584_CH585) && !defined(CH32V205) && !defined(CH32H4xx) && !defined(CH32V30x) && !defined(CH565_CH569)
#error "This lib doesn't work on this MCU"
#endif
#if defined(CH32V30x) || defined(CH565_CH569)
#define USBHS_IMPL 1
// #elif defined(CH584_CH585) || defined(CH32V205) || defined(CH32H4xx)
#else
#define USBHS_IMPL 2
#endif
#ifndef __HIGH_CODE
#define __HIGH_CODE
#endif
#ifndef USBHS_BASE
#if defined(CH32V30x) || defined(CH32V205)
#define USBHS_BASE (0x40023400)
#elif defined(CH32H4xx)
#define USBHS_BASE (0x40030000)
#else
#define USBHS_BASE (0x40009000)
#endif
#endif
#if (USBHS_IMPL==1)
#if (FUNCONF_USE_HSI) && !defined(FUSB_SOF_HSITRIM)
#define FUSB_SOF_HSITRIM 1
#endif
#define TICKS_PER_HSITRIM (FUNCONF_PLL_MULTIPLIER * 20000 * 125) / 1000000 // SOF is sent every 125uS, each HSITRIM changes HSI by 20kHz
typedef struct
{
__IO uint8_t BASE_CTRL;
__IO uint8_t HOST_CTRL;
__IO uint8_t INT_EN;
__IO uint8_t DEV_AD;
__IO uint16_t FRAME_NO;
__IO uint8_t SUSPEND;
__IO uint8_t RESERVED0;
__IO uint8_t SPEED_TYPE;
__IO uint8_t MIS_ST;
__IO uint8_t INT_FG;
__IO uint8_t INT_ST;
__IO uint16_t RX_LEN;
__IO uint16_t RESERVED1;
__IO uint32_t UEP_CONFIG;
__IO uint32_t UEP_TYPE;
__IO uint32_t UEP_BUF_MODE;
__IO uint32_t UEP0_DMA;
__IO uint32_t UEP1_RX_DMA;
__IO uint32_t UEP2_RX_DMA;
__IO uint32_t UEP3_RX_DMA;
__IO uint32_t UEP4_RX_DMA;
__IO uint32_t UEP5_RX_DMA;
__IO uint32_t UEP6_RX_DMA;
__IO uint32_t UEP7_RX_DMA;
__IO uint32_t UEP8_RX_DMA;
__IO uint32_t UEP9_RX_DMA;
__IO uint32_t UEP10_RX_DMA;
__IO uint32_t UEP11_RX_DMA;
__IO uint32_t UEP12_RX_DMA;
__IO uint32_t UEP13_RX_DMA;
__IO uint32_t UEP14_RX_DMA;
__IO uint32_t UEP15_RX_DMA;
__IO uint32_t UEP1_TX_DMA;
__IO uint32_t UEP2_TX_DMA;
__IO uint32_t UEP3_TX_DMA;
__IO uint32_t UEP4_TX_DMA;
__IO uint32_t UEP5_TX_DMA;
__IO uint32_t UEP6_TX_DMA;
__IO uint32_t UEP7_TX_DMA;
__IO uint32_t UEP8_TX_DMA;
__IO uint32_t UEP9_TX_DMA;
__IO uint32_t UEP10_TX_DMA;
__IO uint32_t UEP11_TX_DMA;
__IO uint32_t UEP12_TX_DMA;
__IO uint32_t UEP13_TX_DMA;
__IO uint32_t UEP14_TX_DMA;
__IO uint32_t UEP15_TX_DMA;
__IO uint16_t UEP0_MAX_LEN;
__IO uint16_t RESERVED2;
__IO uint16_t UEP1_MAX_LEN;
__IO uint16_t RESERVED3;
__IO uint16_t UEP2_MAX_LEN;
__IO uint16_t RESERVED4;
__IO uint16_t UEP3_MAX_LEN;
__IO uint16_t RESERVED5;
__IO uint16_t UEP4_MAX_LEN;
__IO uint16_t RESERVED6;
__IO uint16_t UEP5_MAX_LEN;
__IO uint16_t RESERVED7;
__IO uint16_t UEP6_MAX_LEN;
__IO uint16_t RESERVED8;
__IO uint16_t UEP7_MAX_LEN;
__IO uint16_t RESERVED9;
__IO uint16_t UEP8_MAX_LEN;
__IO uint16_t RESERVED10;
__IO uint16_t UEP9_MAX_LEN;
__IO uint16_t RESERVED11;
__IO uint16_t UEP10_MAX_LEN;
__IO uint16_t RESERVED12;
__IO uint16_t UEP11_MAX_LEN;
__IO uint16_t RESERVED13;
__IO uint16_t UEP12_MAX_LEN;
__IO uint16_t RESERVED14;
__IO uint16_t UEP13_MAX_LEN;
__IO uint16_t RESERVED15;
__IO uint16_t UEP14_MAX_LEN;
__IO uint16_t RESERVED16;
__IO uint16_t UEP15_MAX_LEN;
__IO uint16_t RESERVED17;
__IO uint16_t UEP0_T_LEN;
__IO uint8_t UEP0_TX_CTRL;
__IO uint8_t UEP0_RX_CTRL;
__IO uint16_t UEP1_T_LEN;
__IO uint8_t UEP1_TX_CTRL;
__IO uint8_t UEP1_RX_CTRL;
__IO uint16_t UEP2_T_LEN;
__IO uint8_t UEP2_TX_CTRL;
__IO uint8_t UEP2_RX_CTRL;
__IO uint16_t UEP3_T_LEN;
__IO uint8_t UEP3_TX_CTRL;
__IO uint8_t UEP3_RX_CTRL;
__IO uint16_t UEP4_T_LEN;
__IO uint8_t UEP4_TX_CTRL;
__IO uint8_t UEP4_RX_CTRL;
__IO uint16_t UEP5_T_LEN;
__IO uint8_t UEP5_TX_CTRL;
__IO uint8_t UEP5_RX_CTRL;
__IO uint16_t UEP6_T_LEN;
__IO uint8_t UEP6_TX_CTRL;
__IO uint8_t UEP6_RX_CTRL;
__IO uint16_t UEP7_T_LEN;
__IO uint8_t UEP7_TX_CTRL;
__IO uint8_t UEP7_RX_CTRL;
__IO uint16_t UEP8_T_LEN;
__IO uint8_t UEP8_TX_CTRL;
__IO uint8_t UEP8_RX_CTRL;
__IO uint16_t UEP9_T_LEN;
__IO uint8_t UEP9_TX_CTRL;
__IO uint8_t UEP9_RX_CTRL;
__IO uint16_t UEP10_T_LEN;
__IO uint8_t UEP10_TX_CTRL;
__IO uint8_t UEP10_RX_CTRL;
__IO uint16_t UEP11_T_LEN;
__IO uint8_t UEP11_TX_CTRL;
__IO uint8_t UEP11_RX_CTRL;
__IO uint16_t UEP12_T_LEN;
__IO uint8_t UEP12_TX_CTRL;
__IO uint8_t UEP12_RX_CTRL;
__IO uint16_t UEP13_T_LEN;
__IO uint8_t UEP13_TX_CTRL;
__IO uint8_t UEP13_RX_CTRL;
__IO uint16_t UEP14_T_LEN;
__IO uint8_t UEP14_TX_CTRL;
__IO uint8_t UEP14_RX_CTRL;
__IO uint16_t UEP15_T_LEN;
__IO uint8_t UEP15_TX_CTRL;
__IO uint8_t UEP15_RX_CTRL;
} USBHS_TypeDef;
#define USBHS_CHECK_NAK_RX (USBHS->INT_ST & 0x80)
#define CRB_U_IS_NAK (1<<7)
#define CTOG_MATCH_SYNC (1<<6)
#define CRB_UIF_SETUP_ACT (1<<5) // CRB_U_SIE_FREE on USBFS
#define CRB_UIF_FIFO_OV (1<<4)
#define CRB_UIF_HST_SOF (1<<3)
#define CRB_UIF_SUSPEND (1<<2)
#define CRB_UIF_TRANSFER (1<<1)
#define CRB_UIF_BUS_RST (1<<0)
#define CSETUP_ACT (1<<15)
#define CRB_UIS_TOG_OK (1<<14)
#define CMASK_UIS_TOKEN (3<<12)
#define CMASK_UIS_ENDP (0xf<<8)
#define CUIS_TOKEN_OUT 0x0
#define CUIS_TOKEN_SOF 0x1
#define CUIS_TOKEN_IN 0x2
#define CUIS_TOKEN_SETUP 0x3
#define UEP_TX_EN(n) USBHS->UEP_CONFIG |= ((uint32_t)(1<<n))
#define UEP_RX_EN(n) USBHS->UEP_CONFIG |= ((uint32_t)(1<<(n+16)))
#define USBHS_DONE_TX(n)
#define USBHS_DONE_RX(n)
#elif (USBHS_IMPL==2)
typedef struct
{
__IO uint8_t BASE_CTRL;
__IO uint8_t BASE_MODE;
__IO uint8_t INT_EN;
__IO uint8_t DEV_AD;
__IO uint8_t WAKE_CTRL;
__IO uint8_t TEST_MODE;
__IO uint16_t LPM_DATA;
__IO uint8_t INT_FG; // "Combined" register in some situations. (ST_FG)
__IO uint8_t INT_ST;
__IO uint8_t MIS_ST;
__IO uint16_t FRAME_NO;
__IO uint16_t BUS;
__IO uint16_t UEP_TX_EN;
__IO uint16_t UEP_RX_EN;
__IO uint16_t UEP_T_TOG_AUTO;
__IO uint16_t UEP_R_TOG_AUTO;
__IO uint8_t UEP_T_BURST;
__IO uint8_t UEP_T_BURST_MODE;
__IO uint8_t UEP_R_BURST;
__IO uint8_t UEP_R_RES_MODE;
__IO uint32_t UEP_AF_MODE;
__IO uint32_t UEP0_DMA;
__IO uint32_t UEP1_RX_DMA;
__IO uint32_t UEP2_RX_DMA;
__IO uint32_t UEP3_RX_DMA;
__IO uint32_t UEP4_RX_DMA;
__IO uint32_t UEP5_RX_DMA;
__IO uint32_t UEP6_RX_DMA;
__IO uint32_t UEP7_RX_DMA;
__IO uint32_t UEP1_TX_DMA;
__IO uint32_t UEP2_TX_DMA;
__IO uint32_t UEP3_TX_DMA;
__IO uint32_t UEP4_TX_DMA;
__IO uint32_t UEP5_TX_DMA;
__IO uint32_t UEP6_TX_DMA;
__IO uint32_t UEP7_TX_DMA;
__IO uint32_t UEP0_MAX_LEN;
__IO uint32_t UEP1_MAX_LEN;
__IO uint32_t UEP2_MAX_LEN;
__IO uint32_t UEP3_MAX_LEN;
__IO uint32_t UEP4_MAX_LEN;
__IO uint32_t UEP5_MAX_LEN;
__IO uint32_t UEP6_MAX_LEN;
__IO uint32_t UEP7_MAX_LEN;
__IO uint16_t UEP0_RX_LEN;
__IO uint16_t reserved;
__IO uint16_t UEP1_RX_LEN;
__IO uint16_t UEP1_R_SIZE;
__IO uint16_t UEP2_RX_LEN;
__IO uint16_t UEP2_R_SIZE;
__IO uint16_t UEP3_RX_LEN;
__IO uint16_t UEP3_R_SIZE;
__IO uint16_t UEP4_RX_LEN;
__IO uint16_t UEP4_R_SIZE;
__IO uint16_t UEP5_RX_LEN;
__IO uint16_t UEP5_R_SIZE;
__IO uint16_t UEP6_RX_LEN;
__IO uint16_t UEP6_R_SIZE;
__IO uint16_t UEP7_RX_LEN;
__IO uint16_t UEP7_R_SIZE;
__IO uint16_t UEP0_T_LEN;
__IO uint8_t UEP0_TX_CTRL;
__IO uint8_t UEP0_RX_CTRL;
__IO uint16_t UEP1_T_LEN;
__IO uint8_t UEP1_TX_CTRL;
__IO uint8_t UEP1_RX_CTRL;
__IO uint16_t UEP2_T_LEN;
__IO uint8_t UEP2_TX_CTRL;
__IO uint8_t UEP2_RX_CTRL;
__IO uint16_t UEP3_T_LEN;
__IO uint8_t UEP3_TX_CTRL;
__IO uint8_t UEP3_RX_CTRL;
__IO uint16_t UEP4_T_LEN;
__IO uint8_t UEP4_TX_CTRL;
__IO uint8_t UEP4_RX_CTRL;
__IO uint16_t UEP5_T_LEN;
__IO uint8_t UEP5_TX_CTRL;
__IO uint8_t UEP5_RX_CTRL;
__IO uint16_t UEP6_T_LEN;
__IO uint8_t UEP6_TX_CTRL;
__IO uint8_t UEP6_RX_CTRL;
__IO uint16_t UEP7_T_LEN;
__IO uint8_t UEP7_TX_CTRL;
__IO uint8_t UEP7_RX_CTRL;
__IO uint16_t UEP_T_ISO;
__IO uint16_t UEP_R_ISO;
} USBHS_TypeDef;
#define USBHS_CHECK_NAK_RX (USBHS->UEP0_RX_CTRL & (1<<6))
#define UEP_RX_LEN(n) (((volatile uint16_t*)&USBHS->UEP1_RX_LEN)[(n-1)*2])
#define UEP_TX_EN(n) USBHS->UEP_TX_EN |= ((uint16_t)(1<<n))
#define UEP_RX_EN(n) USBHS->UEP_RX_EN |= ((uint16_t)(1<<n))
#define DEBUG_PIN PB17
#define TEST_ENABLE 0x01
#define TEST_MASK 0x0F
#define USBHS_UMS_SUSPEND (1<<1)
#define USBHS_UC_RESET_SIE (1<<1)
#define USBHS_UC_CLR_ALL (1<<2)
#define USBHS_UC_DMA_EN (1<<4)
#define USBHS_UC_PORT_EN (1<<5)
#define USBHS_UIE_SUSPEND (1<<1)
#define USBHS_UIE_TRANSFER (1<<4)
#define USBHS_UIE_BUS_RST (1<<0)
// Mask for the combined USBHS->INT_FG + USBHS->INT_ST
#define CRB_UIF_FIFO_OV (1<<7)
#define CRB_UIF_LINK_RDY (1<<6)
#define CRB_UIF_RX_SOF (1<<5)
#define CRB_UIF_TRANSFER (1<<4)
#define CRB_UIF_LPM_ACT (1<<3)
#define CRB_UIF_BUS_SLEEP (1<<2)
#define CRB_UIF_SUSPEND (1<<1)
#define CRB_UIF_BUS_RST (1<<0)
#define CMASK_UIS_TOKEN (1<<12)
#define CMASK_UIS_ENDP (7<<8)
#define CUIS_TOKEN_OUT 0x0
#define CUIS_TOKEN_IN 0x1
#define USBHS_DONE_TX(n) UEP_CTRL_TX(n) &= ~(USBHS_UEP_T_DONE)
#define USBHS_DONE_RX(n) UEP_CTRL_RX(n) &= ~(USBHS_UEP_R_DONE)
#endif
#define USBHS_SPEED_TYPE_MASK ((uint8_t)(0x03))
#define USBHS_DEF_UEP_IN 0x80
#define USBHS_DEF_UEP_OUT 0x00
#define USBHS_DEF_UEP_BUSY 0x01
#define USBHS_DEF_UEP_FREE 0x00
#define USBHS_NUM_EP 8
#define USBHS_DEF_UEP0 0
#define USBHS_DEF_UEP1 1
#define USBHS_DEF_UEP2 2
#define USBHS_DEF_UEP3 3
#define USBHS_DEF_UEP4 4
#define USBHS_DEF_UEP5 5
#define USBHS_DEF_UEP6 6
#define USBHS_DEF_UEP7 7
#define USBHS_DEF_UEP8 8
#define USBHS_DEF_UEP9 9
#define USBHS_DEF_UEP10 10
#define USBHS_DEF_UEP11 11
#define USBHS_DEF_UEP12 12
#define USBHS_DEF_UEP13 13
#define USBHS_DEF_UEP14 14
#define USBHS_DEF_UEP15 15
#define USBHS_DEF_UEP0_SIZE 64
#ifndef FUSB_EP_SIZE
#define USBHS_UEP_SIZE 64
#else
#define USBHS_UEP_SIZE FUSB_EP_SIZE
#endif
#define UEP_CTRL_LEN(n) (((volatile uint16_t*)&USBHS->UEP0_T_LEN)[n*2])
#define UEP_CTRL_TX(n) (((volatile uint8_t*)&USBHS->UEP0_TX_CTRL)[n*4])
#define UEP_CTRL_RX(n) (((volatile uint8_t*)&USBHS->UEP0_RX_CTRL)[n*4])
#define UEP_DMA_RX(n) (((volatile uint32_t*)&USBHS->UEP0_DMA)[n])
#define UEP_DMA_TX(n) (((volatile uint32_t*)&USBHS->UEP1_TX_DMA)[n-1])
#define USBHS ((USBHS_TypeDef *)USBHS_BASE)
extern uint32_t USBDEBUG0, USBDEBUG1, USBDEBUG2;
struct _USBState;
// Provided functions:
int USBHSSetup();
static inline int USBHS_SendEndpointNEW( int endp, const uint8_t* data, int len, int copy);
static inline uint8_t * USBHS_GetEPBufferIfAvailable( int endp );
static inline int USBHS_SendEndpoint( int endp, int len );
static inline int USBHS_SendACK( int endp, int tx );
static inline int USBHS_SendNAK( int endp, int tx );
// Implement the following:
#if FUSB_HID_USER_REPORTS
__HIGH_CODE int HandleHidUserGetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
__HIGH_CODE int HandleHidUserSetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
void HandleHidUserReportDataOut( struct _USBState * ctx, uint8_t * data, int len );
int HandleHidUserReportDataIn( struct _USBState * ctx, uint8_t * data, int len );
void HandleHidUserReportOutComplete( struct _USBState * ctx );
#endif
#if FUSB_USER_HANDLERS
__HIGH_CODE int HandleInRequest( struct _USBState * ctx, int endp, uint8_t * data, int len );
__HIGH_CODE void HandleDataOut( struct _USBState * ctx, int endp, uint8_t * data, int len );
__HIGH_CODE int HandleSetupCustom( struct _USBState * ctx, int setup_code);
#endif
#if FUSB_OUT_FLOW_CONTROL > 0
void USBHS_RxReady(int endp);
#endif
typedef enum
{
USB_SPEED_FULL = 0,
USB_SPEED_HIGH = 1,
USB_SPEED_LOW = 2,
} USB_SPEED_t;
typedef enum
{
USBHS_EP_OFF = 0,
USBHS_EP_RX = -1,
USBHS_EP_TX = 1,
} USBHS_EP_mode;
#ifndef FUSB_EP1_MODE
#define FUSB_EP1_MODE 0
#endif
#ifndef FUSB_EP2_MODE
#define FUSB_EP2_MODE 0
#endif
#ifndef FUSB_EP3_MODE
#define FUSB_EP3_MODE 0
#endif
#ifndef FUSB_EP4_MODE
#define FUSB_EP4_MODE 0
#endif
#ifndef FUSB_EP5_MODE
#define FUSB_EP5_MODE 0
#endif
#ifndef FUSB_EP6_MODE
#define FUSB_EP6_MODE 0
#endif
#ifndef FUSB_EP7_MODE
#define FUSB_EP7_MODE 0
#endif
struct _USBState
{
__attribute__ ((aligned(4))) uint8_t CTRL0BUFF[64];
__attribute__ ((aligned(4))) uint8_t ENDPOINTS[FUSB_CONFIG_EPS-1][FUSB_EP_SIZE];
// Setup Request
uint8_t USBHS_SetupReqCode;
uint8_t USBHS_SetupReqType;
uint16_t USBHS_SetupReqLen; // Used for tracking place along send.
uint32_t USBHS_IndexValue;
// USB Device Status
uint8_t USBHS_DevConfig;
uint8_t USBHS_DevAddr;
uint8_t USBHS_DevSleepStatus;
uint8_t USBHS_DevEnumStatus;
uint8_t* pCtrlPayloadPtr;
USBHS_EP_mode endpoint_mode[FUSB_CONFIG_EPS+1]; // IN -1, OUT 1, OFF 0
#define pUSBHS_SetupReqPak ((tusb_control_request_t*)USBHSCTX.CTRL0BUFF)
#if FUSB_HID_INTERFACES > 0
uint8_t USBHS_HidIdle[FUSB_HID_INTERFACES];
uint8_t USBHS_HidProtocol[FUSB_HID_INTERFACES];
#endif
volatile uint8_t USBHS_Endp_Busy[FUSB_CONFIG_EPS];
volatile uint8_t USBHS_errata_dont_send_endpoint_in_window;
volatile uint64_t USBHS_sof_timestamp;
};
extern struct _USBState USBHSCTX;
#include "hsusb.c"
#endif

File diff suppressed because it is too large Load diff

View file

@ -7,74 +7,75 @@
This is referenced in Chapter 22 USB Host/Device Controller (USBHD) of CH32FV2x_V3xRM.pdf
*/
#include "ch32fun.h"
#include "usb_config.h"
#include "usb_defines.h"
#include <stdint.h>
#include "ch32fun.h"
#include "usb_defines.h"
#include "usb_config.h"
struct _USBState
{
// Setup Request
uint8_t USBHS_SetupReqCode;
uint8_t USBHS_SetupReqType;
uint16_t USBHS_SetupReqLen; // Used for tracking place along send.
uint32_t USBHS_IndexValue;
// Setup Request
uint8_t USBHS_SetupReqCode;
uint8_t USBHS_SetupReqType;
uint16_t USBHS_SetupReqLen; // Used for tracking place along send.
uint32_t USBHS_IndexValue;
// USB Device Status
uint16_t USBHS_DevConfig;
uint16_t USBHS_DevAddr;
uint8_t USBHS_DevSleepStatus;
uint8_t USBHS_DevEnumStatus;
// USB Device Status
uint16_t USBHS_DevConfig;
uint16_t USBHS_DevAddr;
uint8_t USBHS_DevSleepStatus;
uint8_t USBHS_DevEnumStatus;
uint8_t *pCtrlPayloadPtr;
uint8_t * pCtrlPayloadPtr;
uint8_t ENDPOINTS[HUSB_CONFIG_EPS][64];
uint8_t ENDPOINTS[HUSB_CONFIG_EPS][64];
#define CTRL0BUFF (HSUSBCTX.ENDPOINTS[0])
#define pUSBHS_SetupReqPak ((tusb_control_request_t *)CTRL0BUFF)
#define CTRL0BUFF (HSUSBCTX.ENDPOINTS[0])
#define pUSBHS_SetupReqPak ((tusb_control_request_t*)CTRL0BUFF)
#if HUSB_HID_INTERFACES > 0
uint8_t USBHS_HidIdle[HUSB_HID_INTERFACES];
uint8_t USBHS_HidProtocol[HUSB_HID_INTERFACES];
uint8_t USBHS_HidIdle[HUSB_HID_INTERFACES];
uint8_t USBHS_HidProtocol[HUSB_HID_INTERFACES];
#endif
volatile uint8_t USBHS_Endp_Busy[HUSB_CONFIG_EPS];
volatile uint8_t USBHS_Endp_Busy[HUSB_CONFIG_EPS];
};
// Provided functions:
int HSUSBSetup();
int HSUSBSetup();
uint8_t USBHS_Endp_DataUp(uint8_t endp, const uint8_t *pbuf, uint16_t len, uint8_t mod);
// Implement the following:
#if HUSB_HID_USER_REPORTS
int HandleHidUserGetReportSetup(struct _USBState *ctx, tusb_control_request_t *req);
int HandleHidUserSetReportSetup(struct _USBState *ctx, tusb_control_request_t *req);
void HandleHidUserReportDataOut(struct _USBState *ctx, uint8_t *data, int len);
int HandleHidUserReportDataIn(struct _USBState *ctx, uint8_t *data, int len);
void HandleHidUserReportOutComplete(struct _USBState *ctx);
int HandleHidUserGetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
int HandleHidUserSetReportSetup( struct _USBState * ctx, tusb_control_request_t * req );
void HandleHidUserReportDataOut( struct _USBState * ctx, uint8_t * data, int len );
int HandleHidUserReportDataIn( struct _USBState * ctx, uint8_t * data, int len );
void HandleHidUserReportOutComplete( struct _USBState * ctx );
#endif
#if HUSB_BULK_USER_REPORTS
void HandleGotEPComplete(struct _USBState *ctx, int ep);
void HandleGotEPComplete( struct _USBState * ctx, int ep );
#endif
extern struct _USBState HSUSBCTX;
// To TX, you can use USBFS_GetEPBufferIfAvailable or USBHSD_UEP_TXBUF( endp )
static inline uint8_t *USBHS_GetEPBufferIfAvailable(int endp)
static inline uint8_t * USBHS_GetEPBufferIfAvailable( int endp )
{
if (HSUSBCTX.USBHS_Endp_Busy[endp]) return 0;
return USBHSD_UEP_TXBUF(endp);
if( HSUSBCTX.USBHS_Endp_Busy[ endp ] ) return 0;
return USBHSD_UEP_TXBUF( endp );
}
static inline void USBHS_SendEndpoint(int endp, int len, const uint8_t *data)
static inline void USBHS_SendEndpoint( int endp, int len, const uint8_t * data )
{
if (endp)
{
(((uint32_t *)(&USBHSD->UEP1_TX_DMA))[2 - 1]) = (uintptr_t)data;
}
USBHSD_UEP_TLEN(endp) = len;
USBHSD_UEP_TXCTRL(endp) = (USBHSD_UEP_TXCTRL(endp) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_ACK;
HSUSBCTX.USBHS_Endp_Busy[endp] = 0x01;
if( endp )
{
(((uint32_t*)(&USBHSD->UEP1_TX_DMA))[2-1]) = (uintptr_t)data;
}
USBHSD_UEP_TLEN( endp ) = len;
USBHSD_UEP_TXCTRL( endp ) = ( USBHSD_UEP_TXCTRL( endp ) & ~USBHS_UEP_T_RES_MASK ) | USBHS_UEP_T_RES_ACK;
HSUSBCTX.USBHS_Endp_Busy[ endp ] = 0x01;
}
#endif

852
inc/extralibs/iSLER.h Normal file
View file

@ -0,0 +1,852 @@
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#ifdef CH570_CH572
#define CRCPOLY1 BB2
#define ACCESSADDRESS1 BB3
#define RSSI BB12 // ? couldn't find it, not sure
#define CTRL_TX BB13
#define CRCINIT2 BB22
#define CRCPOLY2 BB23
#define ACCESSADDRESS2 BB24
#define TMR LL25
#define TXBUF LL30
#define RXBUF LL31
#define CTRL_MOD_RFSTOP 0xfffff8ff
#define DEVSETMODE_ON ((BB->CTRL_CFG & 0xfffcffff) | 0x20000)
#define DEVSETMODE_OFF ((BB->CTRL_CFG & 0xfffcffff) | 0x10000)
#define DEVSETMODE_TUNE 0x0558
#define DEVSETMODE_TX 0x0258
#define DEVSETMODE_RX 0x0158
#define CTRL_CFG_PHY_1M ((BB->CTRL_CFG & 0xfffffcff) | 0x100)
#define CTRL_CFG_PHY_2M (BB->CTRL_CFG & 0xfffffcff)
#define LL_STATUS_TX 0x20000
#define CTRL_CFG_START_TX 0x1000000
#elif defined(CH571_CH573)
#define TXBUF DMA4
#define ACCESSADDRESS1 BB2
#define CTRL_TX BB11
#define RSSI BB12 // ? couldn't find it, not sure
#define TMR LL24
#define TXBUF LL28
#define RXBUF LL29
#define RFEND_TXCTUNE_INIT 0x180000
#define CTRL_MOD_RFSTOP 0xfffffff8
#define DEVSETMODE_TUNE 0x5d
#define DEVSETMODE_TX 0x5a
#define DEVSETMODE_RX 0x59
#define CTRL_CFG_PHY_1M (BB->CTRL_CFG | 0x10000000)
#define LL_STATUS_TX 0x20000
#define CTRL_CFG_START_TX (BB->CTRL_CFG & 0xefffffff)
#elif defined(CH582_CH583)
#define ACCESSADDRESS1 BB2
#define CTRL_TX BB11
#define RSSI BB12
#define TMR LL25
#define TXBUF LL28
#define RXBUF LL29
#define RFEND_TXCTUNE_INIT 0x880000
#define CTRL_TX_TXPOWER 0x80010e78
#define CTRL_MOD_RFSTOP 0xfffffff8
#define DEVSETMODE_ON ((BB->CTRL_CFG & 0xfffffe7f) | 0x100)
#define DEVSETMODE_OFF ((BB->CTRL_CFG & 0xfffffe7f) | 0x80)
#define DEVSETMODE_TUNE 0x00dd
#define DEVSETMODE_TX 0x00da
#define DEVSETMODE_RX 0x00d9
#define CTRL_CFG_PHY_1M ((BB->CTRL_CFG & 0xffff0fff) | 0x1000)
#define CTRL_CFG_PHY_2M (BB->CTRL_CFG & 0xffff0fff)
#define CTRL_CFG_PHY_CODED ((BB->CTRL_CFG & 0xffff0fff) | 0x2000)
#define LL_STATUS_TX 0x2000
#define CTRL_CFG_START_TX 0x800000
#elif (defined(CH584_CH585) || defined(CH591_CH592))
#define ACCESSADDRESS1 BB2
#define CTRL_TX BB11
#define RSSI BB12
#define TMR LL25
#define TXBUF LL30
#define RXBUF LL31
#define CTRL_MOD_RFSTOP 0xfffff8ff
#define DEVSETMODE_ON ((BB->CTRL_CFG & 0xfffffcff) | 0x280)
#define DEVSETMODE_OFF ((BB->CTRL_CFG & 0xfffffcff) | 0x100)
#define DEVSETMODE_TUNE 0x0558
#define DEVSETMODE_TX 0x0258
#define DEVSETMODE_RX 0x0158
#define CTRL_CFG_PHY_1M (BB->CTRL_CFG & 0xffffff7f)
#define CTRL_CFG_PHY_2M (BB->CTRL_CFG | 0x80)
#define LL_STATUS_TX 0x20000
#define CTRL_CFG_START_TX 0x800000
#elif defined(CH32V20x)
#define CH32V208
#define ACCESSADDRESS1 BB2
#define CTRL_TX BB11
#define RSSI BB12
#define TMR LL25
#define TXBUF LL28
#define RXBUF LL29
#define RFEND_TXCTUNE_INIT 0x100000
#define CTRL_TX_TXPOWER 0x80010ec8
#define CTRL_MOD_RFSTOP 0xfffffff8
#define DEVSETMODE_ON ((BB->CTRL_CFG & 0xfffffe7f) | 0x100)
#define DEVSETMODE_OFF ((BB->CTRL_CFG & 0xfffffe7f) | 0x80)
#define DEVSETMODE_TUNE 0x5d
#define DEVSETMODE_TX 0x5a
#define DEVSETMODE_RX 0x59
#define CTRL_CFG_PHY_1M ((BB->CTRL_CFG & 0xffff0fff) | 0x1000)
#define CTRL_CFG_PHY_2M (BB->CTRL_CFG & 0xffff0fff)
#define CTRL_CFG_PHY_CODED ((BB->CTRL_CFG & 0xffff0fff) | 0x2000)
#define LL_STATUS_TX 0x2000
#define CTRL_CFG_START_TX 0x800000
#else
#error "MCU_TARGET selected in Makefile is not supported"
#endif
#ifdef CH32V208
#define BB_BASE (0x40024100) // Baseband, digital part of the PHY
#define LL_BASE (0x40024200) // Link Layer, MAC
#define RF_BASE (0x40025000) // Radio frontend, analog part of the PHY
#else
#define DMA_BASE (0x4000c000)
#define BB_BASE (0x4000c100)
#define LL_BASE (0x4000c200)
#define RF_BASE (0x4000d000)
#endif
#define DMA ((DMA_Type *) DMA_BASE)
#define BB ((BB_Type *) BB_BASE)
#define LL ((LL_Type *) LL_BASE)
#define RF ((RF_Type *) RF_BASE)
#ifdef CH571_CH573
typedef struct {
volatile uint32_t DMA0;
volatile uint32_t DMA1;
volatile uint32_t DMA2;
volatile uint32_t DMA3;
volatile uint32_t DMA4;
volatile uint32_t DMA5;
volatile uint32_t DMA6;
volatile uint32_t DMA7;
} DMA_Type;
#endif
typedef struct {
// bits 0..5 = Channel
// bit 6 = disable whitening.
// bit 8 = 1 during normal TX/operation, but clearing does not affect TX. Note: 0 at reset, set in software.
// bit 9 = settable, but unknown effect.
// bit 10 = 1 during normal TX/operation, but clearing does not affect TX. Note: 1 at reset, not touched in software.
// bit 16 = cleared by firmware upon TX, but does not seem to have an effect on the TX.
// bit 17 = settable, but unknown effect
// bit 20 = settable, but unknown effect.
// bit 24 = set at end of tx routine
// bit 29-31 = settable, but unknown effect.
volatile uint32_t CTRL_CFG;
volatile uint32_t CRCINIT1;
volatile uint32_t BB2; // ch570/2: CRCPOLY1, [ch582/3 ch591/2]: ACCESSADDRESS1
volatile uint32_t BB3; // ch570/2 ACCESSADDRESS1
volatile uint32_t BB4;
volatile uint32_t BB5;
volatile uint32_t BB6;
volatile uint32_t BB7;
volatile uint32_t BB8;
volatile uint32_t BB9;
volatile uint32_t BB10;
volatile uint32_t BB11; // ch582/3, ch584/5, ch591/2: CTRL_TX
volatile uint32_t BB12;
// default, pre TX is a4000009
// bit 0: Set normally, but cleared in software when TXing (maybe a ready bit?)
// bit 1: Unset normally, but cleared anyway by software when TXing (maybe a fault bit?)
// bit 2: Disables TX.
// bit 4: Normally 0, but, if set to 1, seems to increase preamble length.
// bit 8: Normally 0, but, if set, no clear effect.
// bit 9: Normally 0, but, if set, no clear effect.
// bits 24-30: TX Power. Normally 0xA4
// Oddly, bit 31 seems to maybe be always set.
volatile uint32_t BB13; // ch570/2: CTRL_TX
volatile uint32_t BB14;
volatile uint32_t BB15;
volatile uint32_t BB16;
volatile uint32_t BB17;
volatile uint32_t BB18;
volatile uint32_t BB19;
volatile uint32_t BB20;
volatile uint32_t BB21;
volatile uint32_t BB22; // ch570/2: CRCINIT2
volatile uint32_t BB23; // ch570/2: CRCPOLY2
volatile uint32_t BB24; // ch570/2: ACCESSADDRESS2
} BB_Type;
typedef struct {
volatile uint32_t LL0;
volatile uint32_t LL1;
volatile uint32_t STATUS;
volatile uint32_t INT_EN;
volatile uint32_t LL4;
volatile uint32_t LL5;
volatile uint32_t LL6;
volatile uint32_t LL7;
volatile uint32_t LL8;
volatile uint32_t LL9;
volatile uint32_t LL10;
volatile uint32_t LL11;
volatile uint32_t LL12;
volatile uint32_t LL13;
volatile uint32_t LL14;
volatile uint32_t LL15;
volatile uint32_t LL16;
volatile uint32_t LL17;
volatile uint32_t LL18;
volatile uint32_t LL19;
// Controls a lot of higher-level functions.
// For Tuning: 0x30558
// For Idle: 0x30000
// For Sending:0x30258
// Bit 3: Somehow, enables BB
// Bit 4: Normally 1, controls length/send times of BB, if unset, BB will double-send part of signals.
// Bit 6: Normally 1, Unknown effect.
// Bit 9: If 0, no output.
// Bit 10: Somehow required for TX?
// Bit 16-17: Normally 1, unknown effect. Seems to suppress odd carrier burst after message.
volatile uint32_t CTRL_MOD;
volatile uint32_t LL21;
volatile uint32_t LL22;
volatile uint32_t LL23;
volatile uint32_t LL24; // ch571/3: TMR
volatile uint32_t LL25; // ch570/2, ch582/3, ch591/2: TMR
volatile uint32_t LL26;
volatile uint32_t LL27;
volatile uint32_t LL28; // ch582/3: TXBUF
volatile uint32_t LL29; // ch582/3: RXBUF
volatile uint32_t LL30; // ch570/2, ch591/2: TXBUF
volatile uint32_t LL31; // ch570/2, ch591/2: RXBUF
} LL_Type;
typedef struct {
volatile uint32_t RF0;
volatile uint32_t RF1;
volatile uint32_t RF2;
volatile uint32_t RF3;
volatile uint32_t RF4;
volatile uint32_t RF5;
volatile uint32_t RF6;
volatile uint32_t RF7;
volatile uint32_t RF8;
volatile uint32_t RF9;
volatile uint32_t RF10;
volatile uint32_t RF11;
volatile uint32_t RF12;
volatile uint32_t RF13;
volatile uint32_t TXTUNE_CTRL;
volatile uint32_t RF15;
volatile uint32_t RF16;
volatile uint32_t RF17;
volatile uint32_t RF18;
volatile uint32_t RF19;
volatile uint32_t RF20;
volatile uint32_t RF21;
volatile uint32_t RF22;
volatile uint32_t RF23;
volatile uint32_t RF24;
volatile uint32_t RF25;
volatile uint32_t RF26;
volatile uint32_t RF27;
volatile uint32_t RF28;
volatile uint32_t RF29;
volatile uint32_t RF30;
volatile uint32_t RF31;
volatile uint32_t RF32;
volatile uint32_t RF33;
volatile uint32_t RF34;
volatile uint32_t RF35;
volatile uint32_t TXCTUNE_CO_CTRL;
volatile uint32_t TXCTUNE_GA_CTRL;
volatile uint32_t RF38;
volatile uint32_t RXTUNE;
volatile uint32_t TXCTUNE_CO[10];
volatile uint32_t TXCTUNE_GA[3];
} RF_Type;
uint8_t channel_map[] = {1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,0,11,39};
#define CO_MID (uint8_t)(RF->TXTUNE_CTRL & ~0xffffffc0)
#define GA_MID (uint8_t)((RF->TXTUNE_CTRL & ~0x80ffffff) >> 24)
#define PHY_1M 1
#define PHY_2M 2
#define PHY_S2 4
#define PHY_S8 8
void DevSetMode(uint16_t mode);
__attribute__((aligned(4))) uint32_t LLE_BUF[0x110];
#ifdef CH571_CH573
__attribute__((aligned(4))) uint32_t LLE_BUF2[0x110];
#endif
volatile uint32_t tuneFilter;
volatile uint32_t tuneFilter2M;
volatile uint32_t rx_ready;
#ifdef CH571_CH573
__attribute__((interrupt))
void BB_IRQHandler() {
// printf("BB\n");
if(BB->BB14 & (1<<6)) {
BB->BB14 &= 0xffffff9f;
}
if(BB->BB14 & (1<<1)) {
BB->BB14 = 0xfffffffd;
BB->BB20 = 0x45;
}
if(BB->BB14 & (1<<4)) {
BB->BB14 = 0xffffffef;
BB->BB20 = 0;
}
}
#endif
__attribute__((interrupt))
void LLE_IRQHandler() {
// printf("LL\n");
#ifdef CH571_CH573
if(LL->STATUS & (1<<9)) {
LL->TMR = 400;
BB->CTRL_TX = (BB->CTRL_TX & 0xfffffffc) | 2;
BB->CTRL_CFG |= 0x10000000;
}
LL->STATUS = 0;
#elif defined(CH582_CH583)
if((LL->STATUS & (1<<14)) && (LL->INT_EN & (1<<14))) {
LL->LL26 = 0xffffffff;
LL->STATUS = 0x4000;
}
else
#endif
{
LL->STATUS &= LL->INT_EN;
BB->CTRL_TX = (BB->CTRL_TX & 0xfffffffc) | 1;
}
DevSetMode(0);
LL->CTRL_MOD &= CTRL_MOD_RFSTOP;
LL->LL0 |= 0x08;
#ifdef ISLER_CALLBACK
ISLER_CALLBACK();
#else
rx_ready = 1;
#endif
}
void RFEND_Reset() {
#ifdef CH571_CH573
RF->RF3 |= 0x1000;
ADD_N_NOPS(20);
RF->RF3 &= 0xffffefff;
ADD_N_NOPS(20);
RF->RF3 |= 0x1000;
ADD_N_NOPS(20);
RF->RF3 |= 1;
ADD_N_NOPS(20);
RF->RF3 &= 0xfffffffe;
ADD_N_NOPS(20);
RF->RF3 |= 1;
ADD_N_NOPS(20);
RF->RF3 |= 0x100;
ADD_N_NOPS(20);
RF->RF3 &= 0xfffffeff;
ADD_N_NOPS(20);
RF->RF3 |= 0x100;
ADD_N_NOPS(20);
#elif defined(CH32V208)
RF->RF3 = 0x1101;
ADD_N_NOPS(20);
RF->RF3 = 0;
ADD_N_NOPS(20);
RF->RF3 = 0x1101;
#endif
}
void DevInit(uint8_t TxPower) {
#ifdef CH571_CH573
DMA->DMA4 = (uint32_t)LLE_BUF;
DMA->DMA5 = (uint32_t)LLE_BUF;
DMA->DMA6 = (uint32_t)LLE_BUF2;
DMA->DMA7 = (uint32_t)LLE_BUF2;
DMA->DMA2 |= 0x2000;
DMA->DMA3 |= 0x2000;
DMA->DMA2 |= 0x1000;
DMA->DMA3 |= 0x1000;
DMA->DMA0 |= 2;
DMA->DMA0 |= 0x20;
LL->LL5 = 0x50;
LL->LL7 = 10;
LL->LL9 = 0x8c;
LL->LL13 = 0x8c;
LL->LL17 = 0x50;
LL->LL19 = 10;
#elif defined(CH570_CH572) || defined(CH582_CH583) || defined(CH584_CH585) || defined(CH591_CH592)
LL->LL5 = 0x8c;
LL->LL7 = 0x76;
LL->LL9 = 0x8c;
LL->LL13 = 0x8c;
LL->LL17 = 0x8c;
LL->LL19 = 0x76;
#elif defined(CH32V208)
LL->LL5 = 0x8c;
LL->LL7 = 0x6c;
LL->LL9 = 0x8c;
LL->LL13 = 0x8c;
LL->LL17 = 0x8c;
LL->LL19 = 0x6c;
#endif
#ifdef CH570_CH572
LL->LL11 = 0x6c;
LL->LL15 = 0x6c;
LL->LL1 = 0x78;
LL->LL21 = 0;
LL->INT_EN = 0x16000f;
#elif defined(CH571_CH573)
LL->LL11 = 0x3c;
LL->LL15 = 0x3c;
LL->LL22 = 0xf6;
LL->INT_EN = 0xc303;
NVIC->FIBADDRR = 0x20000000;
NVIC->VTFADDR[2] = (uint32_t)LLE_IRQHandler -NVIC->FIBADDRR;
#elif defined(CH582_CH583) || defined(CH32V208)
LL->LL11 = 0x3c;
LL->LL15 = 0x3c;
LL->INT_EN = 0xf00f;
#elif defined(CH584_CH585)
LL->LL11 = 0x6e;
LL->LL15 = 0x6e;
LL->LL1 &= 0xffffffe1;
LL->LL21 = 0;
LL->INT_EN = 0x1f000f;
#elif defined(CH591_CH592)
LL->LL6 = 0x78;
LL->LL8 = 0xffffffff;
LL->LL11 = 0x6e;
LL->LL21 = 0x14;
LL->INT_EN = 0x1f000f;
#endif
LL->RXBUF = (uint32_t)LLE_BUF;
LL->STATUS = 0xffffffff;
RF->RF10 = 0x480;
#ifdef CH570_CH572
RF->RF12 &= 0xfff9ffff;
RF->RF12 |= 0x70000000;
RF->RF15 = (RF->RF15 & 0xf8ffffff) | 0x2000000;
RF->RF15 = (RF->RF15 & 0x1fffffff) | 0x40000000;
RF->RF18 &= 0xfff8ffff;
RF->RF20 = (RF->RF20 & 0xfffff8ff) | 0x300;
RF->RF23 |= 0x70000;
RF->RF23 |= 0x700000;
BB->BB14 = 0x2020c;
BB->BB15 = 0x50;
BB->CTRL_TX = (BB->CTRL_TX & 0x1ffffff) | (TxPower | 0x40) << 0x19;
BB->CTRL_CFG &= 0xfffffcff;
#elif defined(CH571_CH573) || defined(CH582_CH583) || defined(CH32V208)
RFEND_Reset();
RF->RF18 = (RF->RF18 & 0x8fffffff) | 0x20000000;
RF->RF18 = (RF->RF18 & 0xf8ffffff) | 0x4000000;
RF->RF18 = (RF->RF18 & 0xfffffff0) | 9;
RF->RF18 &= 0xfff8ffff;
RF->RF18 |= 0x80000000;
RF->RF19 = (RF->RF19 & 0xfffffff8) | 3;
RF->RF19 = (RF->RF19 & 0xffffff8f) | 0x30;
RF->RF19 = (RF->RF19 & 0xfffff8ff) | 0x300;
RF->RF19 &= 0xfeffffff;
RF->RF19 |= 0x2000000;
RF->RF20 = (RF->RF20 & 0xffff0fff) | 0x4000;
RF->RF21 = (RF->RF21 & 0xfffffff0) | 0xc;
RF->RF21 |= 0x80;
RF->RF21 &= 0xffffefff;
RF->RF15 = (RF->RF15 & 0xffff0fff) | 0x8000;
RF->RF15 = (RF->RF15 & 0xf8ffffff) | 0x2000000;
RF->RF15 = (RF->RF15 & 0x1fffffff) | 0x40000000;
RF->RF11 |= 0x700000;
RF->RF11 &= 0xf8ffffff;
RF->RF11 = (RF->RF11 & 0xffffcfff) | 0x2000;
RF->RF11 = (RF->RF11 & 0xfffcffff) | 0x20000;
RF->RF12 &= 0xfffffff0;
RF->RF12 &= 0xffffff0f;
RF->RF12 &= 0xfffff8ff;
RF->RF12 |= 0x700000;
RF->RF12 = (RF->RF12 & 0x8fffffff) | 0x50000000;
RF->TXTUNE_CTRL = (RF->TXTUNE_CTRL & 0xff07ffff) | RFEND_TXCTUNE_INIT;
RF->TXTUNE_CTRL |= 0x80000000;
#ifdef CH571_CH573
BB->CTRL_CFG = (TxPower << 8) | BB->CTRL_CFG | 0x1008000;
BB->CTRL_CFG = (BB->CTRL_CFG & 0xffffc0ff) | (TxPower & 0x3f) << 8;
SYS_SAFE_ACCESS(
R16_AUX_POWER_ADJ = (TxPower < 0x15) ? (R16_AUX_POWER_ADJ & 0xffef):
(R16_AUX_POWER_ADJ | 0x10);
);
BB->CTRL_TX = 0x10e78;
BB->BB6 |= 0x8000;
BB->BB6 = (BB->BB6 & 0xffff807f) | 0x3500;
BB->BB13 = 0x152;
// NVIC->VTFADDR[3] = (uint32_t)BB_IRQHandler +0x14000000; // why 14000000?
#elif defined(CH582_CH583) || defined(CH32V208)
BB->CTRL_CFG |= 0x800000;
BB->CTRL_CFG |= 0x10000000;
BB->BB13 = 0x1d0;
BB->CTRL_TX = TxPower << 0x19 | CTRL_TX_TXPOWER;
BB->CTRL_TX = (BB->CTRL_TX & 0x81ffffff) | (TxPower & 0x3f) << 0x19;
BB->BB8 = 0x90083;
// NVIC->VTFADDR[3] = (uint32_t)BB_IRQHandler +0x20000000; // why 20000000?
#endif
#elif defined(CH584_CH585) || defined(CH591_CH592)
RF->RF12 = (RF->RF12 & 0x8fffffff) | 0x10077700;
RF->RF15 = (RF->RF15 & 0x18ff0fff) | 0x42005000;
RF->RF19 &= 0xfffcff88;
RF->RF21 = (RF->RF21 & 0xfffffff0) | 9;
RF->RF23 &= 0xff88ffff;
BB->CTRL_CFG |= 0x800000;
BB->BB14 = 0x3ff; // ch584/5
BB->BB13 = 0x50;
BB->CTRL_TX = (BB->CTRL_TX & 0x81ffffff) | (TxPower & 0x3f) << 0x19;
uint32_t uVar3 = 0x1000000;
uint32_t uVar4 = RF->RF23 & 0xf8ffffff;
if(TxPower < 29) { // ch585: 27
/* uVar3 and uVar4 are initialized properly already */
}
else if(TxPower < 35) {
uVar3 = 0x3000000;
}
else if(TxPower < 59) {
uVar3 = 0x5000000;
}
else {
uVar4 = RF->RF23;
uVar3 = 0x7000000;
}
RF->RF23 = uVar4 | uVar3;
BB->BB15 = 0x2020c; // ch584/5
BB->BB4 = (BB->BB4 & 0xffffffc0) | 0xe;
#endif
NVIC->VTFIDR[3] = 0x14;
}
void DevSetMode(uint16_t mode) {
#if !defined(CH571_CH573)
if(mode) {
BB->CTRL_CFG = DEVSETMODE_ON;
RF->RF2 |= 0x330000;
}
else {
BB->CTRL_CFG = DEVSETMODE_OFF;
RF->RF2 &= 0xffcdffff;
}
#ifdef CH582_CH583
mode = (mode == 0) ? 0x80 : mode;
#elif !defined(CH32V208)
mode |= 0x30000;
#endif
#endif // ! CH571_CH573
LL->CTRL_MOD = mode;
}
uint32_t RFEND_TXCTune(uint8_t channel) {
// 0xbf = 2401 MHz
RF->RF1 &= 0xfffffffe;
RF->TXTUNE_CTRL = (RF->TXTUNE_CTRL & 0xfffe00ff) | (0xbf00 + (channel_map[channel] << 8));
RF->RF1 |= 1;
LL->TMR = 8000;
while(!(RF->TXCTUNE_CO_CTRL & (1 << 25)) || !(RF->TXCTUNE_CO_CTRL & (1 << 26))) {
if(LL->TMR == 0) {
break;
}
}
uint8_t nCO = (uint8_t)RF->TXCTUNE_CO_CTRL & 0x3f;
uint8_t nGA = (uint8_t)(RF->TXCTUNE_GA_CTRL >> 10) & 0x7f;
// printf("nCO,nGA ch:%u idx:%u %u,%u\n", channel, channel_map[channel], nCO,nGA);
return (nGA << 24) | nCO;
}
void RFEND_TXTune() {
RF->RF1 &= 0xfffffeff;
RF->RF10 &= 0xffffefff;
RF->RF11 &= 0xffffffef;
RF->RF2 |= 0x20000;
RF->RF1 |= 0x10;
// 2401 MHz
uint32_t tune2401 = RFEND_TXCTune(37);
uint8_t nCO2401 = (uint8_t)(tune2401 & 0x3f);
uint8_t nGA2401 = (uint8_t)(tune2401 >> 24) & 0x7f;
// 2480 MHz
uint32_t tune2480 = RFEND_TXCTune(39);
uint8_t nCO2480 = (uint8_t)(tune2480 & 0x3f);
uint8_t nGA2480 = (uint8_t)(tune2480 >> 24) & 0x7f;
// 2440 MHz
uint32_t tune2440 = RFEND_TXCTune(18);
uint8_t nCO2440 = (uint8_t)(tune2440 & 0x3f);
uint8_t nGA2440 = (uint8_t)(tune2440 >> 24) & 0x7f;
uint32_t dCO0140 = nCO2401 - nCO2440;
uint32_t dCO4080 = nCO2440 - nCO2480;
uint8_t tune = 0;
uint8_t int_points = sizeof(RF->TXCTUNE_CO) /2;
uint8_t txctune_co[sizeof(RF->TXCTUNE_CO)] = {0};
for(int f = 0; f < int_points; f++) {
tune = (dCO0140 * (int_points -f)) / int_points;
txctune_co[f] = tune | (tune << 4);
}
for(int f = int_points; f < sizeof(RF->TXCTUNE_CO); f++) {
tune = (dCO4080 * (f -int_points)) / int_points;
txctune_co[f] = tune | (tune << 4);
}
for(int i = 0; i < sizeof(txctune_co) /4; i++) {
RF->TXCTUNE_CO[i] = ((uint32_t*)txctune_co)[i];
}
// This GA interpolating is not exactly what is done in EVT
// Actually the reception on a BLE monitor is better when this is left out completely
// This will need some proper experimentation by people with 2.4GHz SDRs
uint32_t dGA0140 = nGA2401 - nGA2440;
uint32_t dGA4080 = nGA2440 - nGA2480;
int_points = sizeof(RF->TXCTUNE_GA) /2;
uint8_t txctune_ga[sizeof(RF->TXCTUNE_GA)] = {0};
for(int f = 1; f < int_points; f++) {
tune = (dGA0140 * (int_points -f)) / int_points;
txctune_ga[f] = tune | (tune << 4);
}
for(int f = int_points; f < sizeof(RF->TXCTUNE_GA) -1; f++) {
tune = (dGA4080 * (f -int_points)) / int_points;
txctune_ga[f] = tune | (tune << 4);
}
for(int i = 0; i < (sizeof(txctune_ga) /4); i++) {
RF->TXCTUNE_GA[i] = ((uint32_t*)txctune_ga)[i];
}
#if 0
printf("2401 2440 2480 CO: %u %u %u, GA: %u %u %u\n", nCO2401, nCO2440, nCO2480, nGA2401, nGA2440, nGA2480);
for(int i = 0; i < 10; i++ ) {
printf( "%d: %08lx\n", i, RF->TXCTUNE_CO[i] );
}
for(int i = 0; i < 3; i++ ) {
printf( "%d: %08lx\n", i, RF->TXCTUNE_GA[i] );
}
#endif
RF->RF1 &= 0xffffffef;
RF->RF1 &= 0xfffffffe;
RF->RF10 |= 0x1000;
RF->RF11 |= 0x10;
RF->TXTUNE_CTRL = (RF->TXTUNE_CTRL & 0xffffffc0) | (tune2440 & 0x3f);
RF->TXTUNE_CTRL = (RF->TXTUNE_CTRL & 0x80ffffff) | (tune2440 & 0x7f000000);
// FTune
RF->RF1 |= 0x100;
}
void RFEND_RXTune() {
RF->RF20 &= 0xfffeffff;
RF->RF2 |= 0x200000;
RF->RF3 = (RF->RF3 & 0xffffffef) | 0x10;
RF->RF1 |= 0x1000;
LL->TMR = 100;
while(LL->TMR && ((RF->RXTUNE >> 8) & 1));
tuneFilter = RF->RXTUNE & 0x1f;
RF->RF20 |= 0x10000;
RF->RF20 = (RF->RF20 & 0xffffffe0) | tuneFilter;
RF->RF2 &= 0xffdfffff;
tuneFilter2M = (tuneFilter +2 < 0x1f) ? (tuneFilter +2) : 0x1f;
// RXADC
RF->RF22 &= 0xfffeffff;
RF->RF2 |= 0x10000;
RF->RF3 = (RF->RF3 & 0xfffffeff) | 0x100;
RF->RF1 = (RF->RF1 & 0xfffeffff) | 0x100000;
}
void RegInit() {
DevSetMode(DEVSETMODE_TUNE);
RFEND_TXTune();
RFEND_RXTune();
DevSetMode(0);
}
void RFCoreInit(uint8_t TxPower) {
#if defined(CH571_CH573) || defined(CH584_CH585) // maybe all?
NVIC->IENR[0] = 0x1000;
NVIC->IRER[0] = 0x1000;
#endif
DevInit(TxPower);
RegInit();
NVIC->IPRIOR[0x15] |= 0x80;
NVIC_EnableIRQ(LLE_IRQn);
}
void DevSetChannel(uint8_t channel) {
#ifdef CH571_CH573
BB->BB6 = (BB->BB6 & 0xf8ffffff) | 0x4000000;
BB->BB6 = (BB->BB6 & 0xffffff83) | 0x1c;
#endif
RF->RF11 &= 0xfffffffd;
BB->CTRL_CFG = (BB->CTRL_CFG & 0xffffff80) | (channel & 0x7f);
}
__HIGH_CODE
int8_t ReadRSSI() {
return (int8_t)(BB->RSSI >> 0xf);
}
__HIGH_CODE
void Frame_TX(uint32_t access_address, uint8_t adv[], size_t len, uint8_t channel, uint8_t phy_mode) {
BB->CTRL_TX = (BB->CTRL_TX & 0xfffffffc) | 1;
DevSetChannel(channel);
// Uncomment to disable whitening to debug RF.
//BB->CTRL_CFG |= (1<<6);
DevSetMode(DEVSETMODE_TX);
BB->ACCESSADDRESS1 = access_address; // access address
BB->CRCINIT1 = 0x555555; // crc init
#ifdef CH570_CH572
BB->ACCESSADDRESS2 = access_address;
BB->CRCINIT2 = 0x555555;
BB->CRCPOLY1 = (BB->CRCPOLY1 & 0xff000000) | 0x80032d; // crc poly
BB->CRCPOLY2 = (BB->CRCPOLY2 & 0xff000000) | 0x80032d;
#endif
#if defined(CH571_CH573)
DMA->TXBUF = (uint32_t)adv;
#else
LL->TXBUF = (uint32_t)adv;
#endif
// Wait for tuning bit to clear.
for( int timeout = 3000; !(RF->RF26 & 0x1000000) && timeout >= 0; timeout-- );
#if defined(CH582_CH583) || defined(CH32V208)
BB->CTRL_CFG = (phy_mode == PHY_2M) ? CTRL_CFG_PHY_2M:
(phy_mode == PHY_S2) ? CTRL_CFG_PHY_CODED:
(phy_mode == PHY_S8) ? CTRL_CFG_PHY_CODED:
CTRL_CFG_PHY_1M; // default 1M for now
if(phy_mode > PHY_2M) { // coded phy
BB->CTRL_CFG = (BB->CTRL_CFG & 0xffff3fff) | ((phy_mode == PHY_S2) ? 0x4000 : 0);
}
#elif defined(CH571_CH573)
BB->CTRL_CFG = CTRL_CFG_PHY_1M; // no 2M PHY on ch571/3
#else
BB->CTRL_CFG = (phy_mode == PHY_2M) ? CTRL_CFG_PHY_2M:
CTRL_CFG_PHY_1M; // default 1M for now
#endif
#if defined(CH570_CH572)
BB->BB9 = (BB->BB9 & 0xf9ffffff) | ((phy_mode == PHY_2M) ? 0 : 0x2000000);
#endif
#if defined(CH571_CH573)
BB->BB11 = (BB->BB11 & 0xfffffffc); // |2 for RX
#endif
// This clears bit 17 (If set, seems to have no impact.)
LL->LL4 &= 0xfffdffff;
#if !defined(CH571_CH573)
LL->STATUS = LL_STATUS_TX;
#endif
LL->TMR = (uint32_t)(len *512); // needs optimisation, per phy mode
BB->CTRL_CFG |= CTRL_CFG_START_TX;
BB->CTRL_TX &= 0xfffffffc;
LL->LL0 = 2; // Not sure what this does, but on RX it's 1
while(LL->TMR); // wait for tx buffer to empty
DevSetMode(0);
if(LL->LL0 & 3) {
LL->CTRL_MOD &= CTRL_MOD_RFSTOP;
LL->LL0 |= 0x08;
}
}
__HIGH_CODE
void Frame_RX(uint32_t access_address, uint8_t channel, uint8_t phy_mode) {
DevSetMode(0);
if(LL->LL0 & 3) {
LL->CTRL_MOD &= CTRL_MOD_RFSTOP;
LL->LL0 |= 0x08;
}
LL->TMR = 0;
DevSetChannel(channel);
DevSetMode(DEVSETMODE_RX);
#if defined(CH582_CH583) || defined(CH32V208)
BB->CTRL_CFG = (phy_mode == PHY_2M) ? CTRL_CFG_PHY_2M:
(phy_mode == PHY_S2) ? CTRL_CFG_PHY_CODED:
(phy_mode == PHY_S8) ? CTRL_CFG_PHY_CODED:
CTRL_CFG_PHY_1M; // default 1M for now
if(phy_mode > PHY_2M) { // coded phy
BB->CTRL_CFG = (BB->CTRL_CFG & 0xffff3fff) | ((phy_mode == PHY_S2) ? 0x4000 : 0);
}
#elif defined(CH571_CH573)
BB->CTRL_CFG = CTRL_CFG_PHY_1M; // no 2M PHY on ch571/3
#else
BB->CTRL_CFG = (phy_mode == PHY_2M) ? CTRL_CFG_PHY_2M:
CTRL_CFG_PHY_1M; // default 1M for now
#endif
#ifdef CH570_CH572
BB->BB9 = (BB->BB9 & 0xf9ffffff) | ((phy_mode == PHY_2M) ? 0 : 0x2000000);
RF->RF20 = (RF->RF20 & 0xffffffe0) | ((phy_mode == PHY_2M) ? (tuneFilter2M & 0x1f) : (tuneFilter & 0x1f));
BB->BB5 = (BB->BB5 & 0xffffffc0) | ((phy_mode == PHY_2M) ? 0xd : 0xb);
BB->BB7 = (BB->BB7 & 0xff00fc00) | ((phy_mode == PHY_2M) ? 0x7f00a0 : 0x79009c);
#elif defined(CH571_CH573)
BB->BB11 = (BB->BB11 & 0xfffffffc) | 2; // no |2 for TX
#elif defined(CH582_CH583) || defined(CH32V208)
#if defined(CH582_CH583)
BB->BB4 = (phy_mode < PHY_S2) ? 0x3722d0 : 0x3722df;
#elif defined(CH32V208)
BB->BB4 = (phy_mode < PHY_S2) ? 0x3222d0 : 0x34a4df;
#endif
BB->BB5 = (phy_mode < PHY_S2) ? 0x8101901 : 0x8301ff1;
BB->BB6 = (phy_mode < PHY_S2) ? 0x31624 : 0x31619;
BB->BB8 = (phy_mode < PHY_S2) ? 0x90083 : 0x90086;
BB->BB9 = 0x1006310;
BB->BB10 = (phy_mode < PHY_S2) ? 0x28be : 0x28de;
#elif defined(CH584_CH585) || defined(CH591_CH592)
BB->BB6 = (BB->BB6 & 0xfffffc00) | ((phy_mode == PHY_2M) ? 0x13a : 0x132);
BB->BB4 = (BB->BB4 & 0x00ffffff) | ((phy_mode == PHY_2M) ? 0x78000000 : 0x7f000000);
#endif
BB->ACCESSADDRESS1 = access_address; // access address
BB->CRCINIT1 = 0x555555; // crc init
#ifdef CH570_CH572
BB->ACCESSADDRESS2 = access_address;
BB->CRCINIT2 = 0x555555;
BB->CRCPOLY1 = (BB->CRCPOLY1 & 0xff000000) | 0x80032d; // crc poly
BB->CRCPOLY2 = (BB->CRCPOLY2 & 0xff000000) | 0x80032d;
#endif
LL->LL0 = 1; // Not sure what this does, but on TX it's 2
rx_ready = 0;
}

36
inc/extralibs/lib_crc.h Normal file
View file

@ -0,0 +1,36 @@
// should work for CH32v10x, CH32v20x, and CH32v30x
// Although has only been tested on CH32v303
static void CRC_init(void) {
// Enable CRC clock
RCC->AHBPCENR |= RCC_AHBPeriph_CRC;
}
// Calculate CRC for a single 32-bit value
static u32 CRC_calculate32(u32 data) {
// Reset CRC unit
CRC->CTLR = CRC_CTLR_RESET;
// Write data to trigger CRC calculation
CRC->DATAR = data;
// Wait for CRC calculation
u32 timeout = 10000;
while (CRC->DATAR == 0 && --timeout);
// read the CRC result
return CRC->DATAR;
}
// Calculate CRC for an array of 32-bit values
static u32 CRC_calculateArray32(u32 *data, u32 length) {
CRC->CTLR = CRC_CTLR_RESET;
// Process each 32-bit word
for(u32 i = 0; i < length; i++) {
CRC->DATAR = data[i];
}
// Return final CRC
return CRC->DATAR;
}

26
inc/extralibs/lib_pvd.h Normal file
View file

@ -0,0 +1,26 @@
// Tested with CH32V002, CH32V006, and CH32V303
#include "ch32fun.h"
static void PVD_init(u8 threshold) {
if (threshold > PVD_MAX_THRESHOLD_LVL) threshold = PVD_MAX_THRESHOLD_LVL;
// Enable PWR clock
RCC->APB1PCENR |= RCC_APB1Periph_PWR;
// Enable PVD
PWR->CTLR |= PWR_CTLR_PVDE;
// Clear the existing PLS bits and set new threshold
PWR->CTLR = (PWR->CTLR & ~PWR_CTLR_PLS) | (threshold << 5);
}
// Get threshold setting: the PLS[1:0] bits
static int PVD_getThreshold() {
return (PWR->CTLR & PWR_CTLR_PLS) >> 5;
}
// return PVD flag: 1 if VDD below threshold
static int PVD_getAlert() {
return PWR->CSR & PWR_CSR_PVDO;
}

View file

@ -1,30 +1,30 @@
/******************************************************************************
* Psuedo Random Number Generator using a Linear Feedback Shift Register
* See the GitHub for more information:
* https://github.com/ADBeta/CH32V003_lib_rand
*
* Ver 1.1 09 Sep 2024
*
* Released under the MIT Licence
* Copyright ADBeta (c) 2024
*
* 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.
******************************************************************************/
* Psuedo Random Number Generator using a Linear Feedback Shift Register
* See the GitHub for more information:
* https://github.com/ADBeta/CH32V003_lib_rand
*
* Ver 1.1 09 Sep 2024
*
* Released under the MIT Licence
* Copyright ADBeta (c) 2024
*
* 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.
******************************************************************************/
#ifndef CH32V003_LIB_RAND
#define CH32V003_LIB_RAND
@ -34,13 +34,14 @@
// Strength 3: Genetate two 32bit values using the LFSR, then XOR them together
// Example: #define RANDOM_STRENGTH 2
#ifndef RANDOM_STRENGTH
#error "Error in lib_rand. Must define RANDOM_STRENGTH"
#ifndef RANDOM_STRENGTH
#error "Error in lib_rand. Must define RANDOM_STRENGTH"
#endif
// @brief set the random LFSR values seed by default to a known-good value
static uint32_t _rand_lfsr = 0x747AA32F;
/*** Library specific Functions - Do Not Use *********************************/
/****************************************************************************/
/// @brief Updates the LFSR by getting a new tap bit, for MSB, then shifting
@ -50,59 +51,63 @@ static uint32_t _rand_lfsr = 0x747AA32F;
/// @return 0x01 or 0x00, as a LSB translation of the tapped MSB for the LFSR
uint8_t _rand_lfsr_update(void)
{
// Shifting to MSB to make calculations more efficient later
uint32_t bit_31 = _rand_lfsr & 0x80000000;
uint32_t bit_21 = (_rand_lfsr << 10) & 0x80000000;
uint32_t bit_01 = (_rand_lfsr << 30) & 0x80000000;
uint32_t bit_00 = (_rand_lfsr << 31) & 0x80000000;
// Shifting to MSB to make calculations more efficient later
uint32_t bit_31 = _rand_lfsr & 0x80000000;
uint32_t bit_21 = (_rand_lfsr << 10) & 0x80000000;
uint32_t bit_01 = (_rand_lfsr << 30) & 0x80000000;
uint32_t bit_00 = (_rand_lfsr << 31) & 0x80000000;
// Calculate the MSB to be put into the LFSR
uint32_t msb = bit_31 ^ bit_21 ^ bit_01 ^ bit_00;
// Shift the lfsr and append the MSB to it
_rand_lfsr = (_rand_lfsr >> 1) | msb;
// Return the LSB instead of MSB
return msb >> 31;
// Calculate the MSB to be put into the LFSR
uint32_t msb = bit_31 ^ bit_21 ^ bit_01 ^ bit_00;
// Shift the lfsr and append the MSB to it
_rand_lfsr = (_rand_lfsr >> 1) | msb;
// Return the LSB instead of MSB
return msb >> 31;
}
/// @brief Generates a Random 32-bit number, using the LFSR - by generating
/// a random bit from LFSR taps, 32 times.
/// @param None
/// @return a (psuedo)random 32-bit value
uint32_t _rand_gen_32b(void)
{
uint32_t rand_out = 0;
uint8_t bits = 32;
while (bits--)
{
// Shift the current rand value for the new LSB
rand_out = rand_out << 1;
// Append the LSB
rand_out |= _rand_lfsr_update();
}
return rand_out;
uint32_t rand_out = 0;
uint8_t bits = 32;
while(bits--)
{
// Shift the current rand value for the new LSB
rand_out = rand_out << 1;
// Append the LSB
rand_out |= _rand_lfsr_update();
}
return rand_out;
}
/// @brief Generates a Random n-bit number, using the LFSR - by generating
/// a random bit from LFSR taps, n times.
/// @param None
/// @return a (psuedo)random n-bit value
uint32_t _rand_gen_nb(int bits)
uint32_t _rand_gen_nb( int bits)
{
uint32_t rand_out = 0;
uint32_t rand_out = 0;
while (bits--)
{
// Shift the current rand value for the new LSB
rand_out = rand_out << 1;
// Append the LSB
rand_out |= _rand_lfsr_update();
}
while(bits--)
{
// Shift the current rand value for the new LSB
rand_out = rand_out << 1;
// Append the LSB
rand_out |= _rand_lfsr_update();
}
return rand_out;
return rand_out;
}
/*** API Functions ***********************************************************/
/*****************************************************************************/
/// @brief seeds the Random LFSR to the value passed
@ -110,39 +115,40 @@ uint32_t _rand_gen_nb(int bits)
/// @return None
void seed(const uint32_t seed_val)
{
_rand_lfsr = seed_val;
_rand_lfsr = seed_val;
}
/// @brief Generates a Random (32-bit) Number, based on the RANDOM_STRENGTH
/// you have selected
/// you have selected
/// @param None
/// @return 32bit Random value
uint32_t rand(void)
{
uint32_t rand_out = 0;
uint32_t rand_out = 0;
// If RANDOM_STRENGTH is level 1, Update LFSR Once, then return it
#if RANDOM_STRENGTH == 1
// Update the LFSR, discard result, and return _lsfr raw
(void)_rand_lfsr_update();
rand_out = _rand_lfsr;
#endif
// If RANDOM_STRENGTH is level 1, Update LFSR Once, then return it
#if RANDOM_STRENGTH == 1
// Update the LFSR, discard result, and return _lsfr raw
(void)_rand_lfsr_update();
rand_out = _rand_lfsr;
#endif
// If RANDOM_STRENGTH is level 2, generate a 32-bit output, using 32 random
// bits from the LFSR
#if RANDOM_STRENGTH == 2
rand_out = _rand_gen_32b();
#endif
// If RANDOM_STRENGTH is level 2, generate a 32-bit output, using 32 random
// bits from the LFSR
#if RANDOM_STRENGTH == 2
rand_out = _rand_gen_32b();
#endif
// If RANDOM_STRENGTH is level 3, generate 2 32-bit outputs, then XOR them
// together
#if RANDOM_STRENGTH == 3
uint32_t rand_a = _rand_gen_32b();
uint32_t rand_b = _rand_gen_32b();
rand_out = rand_a ^ rand_b;
#endif
// If RANDOM_STRENGTH is level 3, generate 2 32-bit outputs, then XOR them
// together
#if RANDOM_STRENGTH == 3
uint32_t rand_a = _rand_gen_32b();
uint32_t rand_b = _rand_gen_32b();
rand_out = rand_a ^ rand_b;
#endif
return rand_out;
return rand_out;
}
#endif

View file

@ -0,0 +1,142 @@
#include <stdio.h>
//! ####################################
//! PRINT BITS
//! ####################################
// eg: UTIL_PRINT_BITS(reg, 32, 16);
void UTIL_PRINT_BITS(u32 val, u8 len, u8 divider_len) {
const char* separator = "\n";
for (int i = (len)-1; i >= 0; i--) {
printf("%d| ", i);
if (i > 0) printf(i % divider_len ? "" : "%s", separator);
}
printf("\n");
for (int i = (len)-1; i >= 0; i--) {
printf(i < 10 ? "%2d" : "%3d", ((val) >> i) & 1);
if (i > 0) printf(i % divider_len ? " " : " %s", separator);
}
}
//! ####################################
//! PRINT BITS VALUES
//! ####################################
// eg: UTIL_PRINT_BITS_VALUES(
// reg,
// "a", 0,
// "b", 1,
// "c", 2,
// );
void UTIL_PRINT_BITS_VALUES(u32 reg, ...) {
struct BitPair { const char* name; int pos; };
va_list args;
va_start(args, reg);
// Count how many pairs were passed (until NULL name)
int pair_count = 0;
va_list count_args;
va_copy(count_args, args);
while (1) {
const char* name = va_arg(count_args, const char*);
if (name == NULL) break;
int pos = va_arg(count_args, int);
(void)pos;
pair_count++;
}
va_end(count_args);
// Read all the pairs
struct BitPair pairs[pair_count];
for (int i = 0; i < pair_count; i++) {
pairs[i].name = va_arg(args, const char*);
pairs[i].pos = va_arg(args, int);
}
va_end(args);
// Print the pairs
for (int i = 0; i < pair_count; i++) {
if (i > 0) printf(", ");
printf("%s=%d", pairs[i].name, (unsigned int)((reg >> pairs[i].pos) & 1));
}
}
//! ####################################
//! PRINT BITS RANGE
//! ####################################
// eg: UTIL_PRINT_BIT_RANGE(
// reg,
// "FIELD3", 5, 3,
// "FIELD2", 2, 1,
// "FIELD1", 0, 0,
// NULL
// );
// NOTE: NULL terminated REQUIRED
void UTIL_PRINT_BIT_RANGE(u32 reg, ...) {
// Define the struct
typedef struct { const char* name; int end; int start; } BitField;
va_list args;
va_start(args, reg);
// Count how many fields were passed (until NULL name)
int field_count = 0;
va_list count_args;
va_copy(count_args, args);
while (1) {
const char* name = va_arg(count_args, const char*);
if (name == NULL) break;
int start = va_arg(count_args, int);
(void)start;
int end = va_arg(count_args, int);
(void)end;
field_count++;
}
va_end(count_args);
// Read all the fields
BitField fields[field_count];
for (int i = 0; i < field_count; i++) {
fields[i].name = va_arg(args, const char*);
fields[i].end = va_arg(args, int); // get end first
fields[i].start = va_arg(args, int); // then get start
}
va_end(args);
// Print the fields
for (int i = 0; i < field_count; i++) {
if (i > 0) printf(", ");
int start = fields[i].start;
int end = fields[i].end;
if (end == start) {
// Single bit
printf("%s = %d [%d]", fields[i].name, (reg >> start) & 1, start);
} else {
// Multiple bits
int mask = ((1 << (end - start + 1)) - 1);
printf("%s = 0x%02X [%d:%d]", fields[i].name, (reg >> start) & mask, end, start);
}
}
printf("\n");
}
//! ####################################
//! PRINT REGS
//! ####################################
#define UTIL_PRINT_REG8(reg, label) printf("%s: 0x%02X\n", label, (unsigned int)(reg));
#define UTIL_PRINT_REG16(reg, label) printf("%s: 0x%04X\n", label, (unsigned int)(reg));
#define UTIL_PRINT_REG32(reg, label) printf("%s: 0x%08X\n", label, (unsigned int)(reg));

127
inc/extralibs/rtc_helper.h Normal file
View file

@ -0,0 +1,127 @@
// MIT License
// Copyright (c) 2025 UniTheCat
#define RTC_TICKS_PER_SECOND 32768
#define SECONDS_PER_MINUTE 60
#define SECONDS_PER_HOUR 3600
#define SECONDS_PER_DAY 86400
#define IS_LEAP_YEAR(year) ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0))
// Array of days in each month (non-leap year)
const u8 DAYS_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
typedef struct {
u16 year;
u8 month;
u8 day;
} rtc_date_t;
typedef struct {
u8 hr;
u8 min;
u8 sec;
u16 ms;
} rtc_time_t;
typedef struct {
rtc_date_t date;
rtc_time_t time;
} rtc_datetime_t;
// calculate days of the current year. eg. 2020-02-05 is 36 day
u32 RTC_days_of_year(u16 year, u8 month, u8 day) {
u32 day_of_year = 0;
// Add days from January to month-1
for (u8 m = 0; m < month - 1; m++) {
day_of_year += DAYS_IN_MONTH[m];
}
// Add extra day for February (full month) if it's a leap year
if (month > 2 && IS_LEAP_YEAR(year)) {
day_of_year += 1;
}
// Add days in the current month
day_of_year += day;
return day_of_year;
}
u32 RTC_get_seconds(u16 year, u8 month, u8 day, u8 hr, u8 min, u8 sec) {
//# Validate input
if (month < 1 || month > 12 || day < 1 || day > 31 ||
hr > 23 || min > 59 || sec > 59 || year < 1970) { return 0; }
// calculate days of the current year, -1 for 0-based days
u32 days = RTC_days_of_year(year, month, day) - 1;
// add the days count excluding the current year
// (start from epoch time 1970-01-01 00:00:00)
for (int y=1970; y < year; y++) {
days += IS_LEAP_YEAR(y) ? 366 : 365;
}
// calculate total seconds
return days * SECONDS_PER_DAY +
hr * SECONDS_PER_HOUR +
min * SECONDS_PER_MINUTE + sec;
}
rtc_time_t RTC_get_time(u32 total_seconds, u32 ms) {
u32 minutes = total_seconds / 60;
return (rtc_time_t) {
.sec = total_seconds % 60,
.min = minutes % 60,
.hr = (minutes / 60) % 24,
.ms = ms
};
}
rtc_date_t RTC_get_date(u32 total_seconds, u16 year_base) {
rtc_date_t output = {
.year = year_base,
.month = 1,
.day = 1
};
// Days since epoch
u32 days_remaining = total_seconds / SECONDS_PER_DAY;
// Find the year
while(1) {
u32 days_in_year = IS_LEAP_YEAR(output.year) ? 366 : 365;
if (days_remaining < days_in_year) break;
days_remaining -= days_in_year;
output.year++;
}
// find the month
for (u8 m = 0; m < 12; m++) {
u8 days_in_month = DAYS_IN_MONTH[m];
if (m == 1 && IS_LEAP_YEAR(output.year)) days_in_month = 29;
if (days_remaining < days_in_month) break;
days_remaining -= days_in_month;
output.month++;
}
// add 1 because days_remaining is 0-based
output.day = days_remaining + 1;
return output;
}
void RTC_print_date(rtc_date_t date, char *delimiter) {
printf("%04d", date.year);
printf("%s%02d", delimiter, date.month);
printf("%s%02d", delimiter, date.day);
}
void RTC_print_time(rtc_time_t time) {
printf("%02d:%02d:%02d.%03d",
time.hr, time.min, time.sec, time.ms);
}

File diff suppressed because it is too large Load diff

View file

@ -27,14 +27,14 @@
#define TIMEOUT_MAX 100000
// uncomment this to enable IRQ-driven operation
// #define SSD1306_I2C_IRQ
//#define SSD1306_I2C_IRQ
#ifdef SSD1306_I2C_IRQ
// some stuff that IRQ mode needs
volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state;
// uncomment this to enable time diags in IRQ
// #define IRQ_DIAG
//#define IRQ_DIAG
#endif
/*
@ -42,62 +42,62 @@ volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c
*/
void ssd1306_i2c_setup(void)
{
uint16_t tempreg;
// Reset I2C1 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
// set freq
tempreg = I2C1->CTLR2;
tempreg &= ~I2C_CTLR2_FREQ;
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / SSD1306_I2C_PRERATE) & I2C_CTLR2_FREQ;
I2C1->CTLR2 = tempreg;
// Set clock config
tempreg = 0;
uint16_t tempreg;
// Reset I2C1 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
// set freq
tempreg = I2C1->CTLR2;
tempreg &= ~I2C_CTLR2_FREQ;
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ;
I2C1->CTLR2 = tempreg;
// Set clock config
tempreg = 0;
#if (SSD1306_I2C_CLKRATE <= 100000)
// standard mode good to 100kHz
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (2 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
// standard mode good to 100kHz
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(2*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
#else
// fast mode over 100kHz
// fast mode over 100kHz
#ifndef SSD1306_I2C_DUTY
// 33% duty cycle
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (3 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
// 33% duty cycle
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(3*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
#else
// 36% duty cycle
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (25 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
tempreg |= I2C_CKCFGR_DUTY;
// 36% duty cycle
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
tempreg |= I2C_CKCFGR_DUTY;
#endif
tempreg |= I2C_CKCFGR_FS;
tempreg |= I2C_CKCFGR_FS;
#endif
I2C1->CKCFGR = tempreg;
I2C1->CKCFGR = tempreg;
#ifdef SSD1306_I2C_IRQ
// enable IRQ driven operation
NVIC_EnableIRQ(I2C1_EV_IRQn);
// initialize the state
ssd1306_i2c_irq_state = 0;
// enable IRQ driven operation
NVIC_EnableIRQ(I2C1_EV_IRQn);
// initialize the state
ssd1306_i2c_irq_state = 0;
#endif
// Enable I2C
I2C1->CTLR1 |= I2C_CTLR1_PE;
// Enable I2C
I2C1->CTLR1 |= I2C_CTLR1_PE;
// set ACK mode
I2C1->CTLR1 |= I2C_CTLR1_ACK;
// set ACK mode
I2C1->CTLR1 |= I2C_CTLR1_ACK;
}
/*
* error descriptions
*/
char *errstr[] =
{
"not busy",
"master mode",
"transmit mode",
"tx empty",
"transmit complete",
{
"not busy",
"master mode",
"transmit mode",
"tx empty",
"transmit complete",
};
/*
@ -105,28 +105,28 @@ char *errstr[] =
*/
uint8_t ssd1306_i2c_error(uint8_t err)
{
// report error
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
// report error
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
// reset & initialize I2C
ssd1306_i2c_setup();
// reset & initialize I2C
ssd1306_i2c_setup();
return 1;
return 1;
}
// event codes we use
#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
/*
* check for 32-bit event codes
*/
uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask)
{
/* read order matters here! STAR1 before STAR2!! */
uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16);
return (status & event_mask) == event_mask;
/* read order matters here! STAR1 before STAR2!! */
uint32_t status = I2C1->STAR1 | (I2C1->STAR2<<16);
return (status & event_mask) == event_mask;
}
#ifdef SSD1306_I2C_IRQ
@ -135,67 +135,63 @@ uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask)
*/
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
{
int32_t timeout;
int32_t timeout;
#ifdef IRQ_DIAG
GPIOC->BSHR = (1<<(3));
#endif
// error out if buffer under/overflow
if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
return 2;
// wait for previous packet to finish
while(ssd1306_i2c_irq_state);
#ifdef IRQ_DIAG
GPIOC->BSHR = (1<<(16+3));
GPIOC->BSHR = (1<<(4));
#endif
// init buffer for sending
ssd1306_i2c_send_sz = sz;
ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer;
memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz);
// wait for not busy
timeout = TIMEOUT_MAX;
while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(0);
// Set START condition
I2C1->CTLR1 |= I2C_CTLR1_START;
// wait for master mode select
timeout = TIMEOUT_MAX;
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(1);
// send 7-bit address + write flag
I2C1->DATAR = addr<<1;
// wait for transmit condition
timeout = TIMEOUT_MAX;
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(2);
// Enable TXE interrupt
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
ssd1306_i2c_irq_state = 1;
#ifdef IRQ_DIAG
GPIOC->BSHR = (1 << (3));
GPIOC->BSHR = (1<<(16+4));
#endif
// error out if buffer under/overflow
if ((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
return 2;
// wait for previous packet to finish
while (ssd1306_i2c_irq_state)
;
#ifdef IRQ_DIAG
GPIOC->BSHR = (1 << (16 + 3));
GPIOC->BSHR = (1 << (4));
#endif
// init buffer for sending
ssd1306_i2c_send_sz = sz;
ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer;
memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz);
// wait for not busy
timeout = TIMEOUT_MAX;
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(0);
// Set START condition
I2C1->CTLR1 |= I2C_CTLR1_START;
// wait for master mode select
timeout = TIMEOUT_MAX;
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(1);
// send 7-bit address + write flag
I2C1->DATAR = addr << 1;
// wait for transmit condition
timeout = TIMEOUT_MAX;
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(2);
// Enable TXE interrupt
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
ssd1306_i2c_irq_state = 1;
#ifdef IRQ_DIAG
GPIOC->BSHR = (1 << (16 + 4));
#endif
// exit
return 0;
// exit
return 0;
}
/*
@ -204,43 +200,42 @@ uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
void I2C1_EV_IRQHandler(void)
{
uint16_t STAR1, STAR2 __attribute__((unused));
uint16_t STAR1, STAR2 __attribute__((unused));
#ifdef IRQ_DIAG
GPIOC->BSHR = (1 << (4));
GPIOC->BSHR = (1<<(4));
#endif
// read status, clear any events
STAR1 = I2C1->STAR1;
STAR2 = I2C1->STAR2;
// read status, clear any events
STAR1 = I2C1->STAR1;
STAR2 = I2C1->STAR2;
/* check for TXE */
if(STAR1 & I2C_STAR1_TXE)
{
/* check for remaining data */
if(ssd1306_i2c_send_sz--)
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
/* check for TXE */
if (STAR1 & I2C_STAR1_TXE)
{
/* check for remaining data */
if (ssd1306_i2c_send_sz--)
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
/* was that the last byte? */
if(!ssd1306_i2c_send_sz)
{
// disable TXE interrupt
I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
// reset IRQ state
ssd1306_i2c_irq_state = 0;
// wait for tx complete
while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* was that the last byte? */
if (!ssd1306_i2c_send_sz)
{
// disable TXE interrupt
I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
// reset IRQ state
ssd1306_i2c_irq_state = 0;
// wait for tx complete
while (!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED))
;
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
}
}
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
}
}
#ifdef IRQ_DIAG
GPIOC->BSHR = (1 << (16 + 4));
GPIOC->BSHR = (1<<(16+4));
#endif
}
#else
@ -249,61 +244,56 @@ void I2C1_EV_IRQHandler(void)
*/
uint8_t ssd1306_i2c_send(uint8_t addr, const uint8_t *data, int sz)
{
int32_t timeout;
int32_t timeout;
// wait for not busy
timeout = TIMEOUT_MAX;
while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(0);
// wait for not busy
timeout = TIMEOUT_MAX;
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(0);
// Set START condition
I2C1->CTLR1 |= I2C_CTLR1_START;
// wait for master mode select
timeout = TIMEOUT_MAX;
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(1);
// send 7-bit address + write flag
I2C1->DATAR = addr<<1;
// Set START condition
I2C1->CTLR1 |= I2C_CTLR1_START;
// wait for transmit condition
timeout = TIMEOUT_MAX;
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(2);
// wait for master mode select
timeout = TIMEOUT_MAX;
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(1);
// send data one byte at a time
while(sz--)
{
// wait for TX Empty
timeout = TIMEOUT_MAX;
while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(3);
// send command
I2C1->DATAR = *data++;
}
// send 7-bit address + write flag
I2C1->DATAR = addr << 1;
// wait for tx complete
timeout = TIMEOUT_MAX;
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--));
if(timeout==-1)
return ssd1306_i2c_error(4);
// wait for transmit condition
timeout = TIMEOUT_MAX;
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(2);
// send data one byte at a time
while (sz--)
{
// wait for TX Empty
timeout = TIMEOUT_MAX;
while (!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(3);
// send command
I2C1->DATAR = *data++;
}
// wait for tx complete
timeout = TIMEOUT_MAX;
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(4);
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
// we're happy
return 0;
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
// we're happy
return 0;
}
#endif
@ -312,20 +302,20 @@ uint8_t ssd1306_i2c_send(uint8_t addr, const uint8_t *data, int sz)
*/
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
{
uint8_t pkt[33];
/* build command or data packets */
if (cmd)
{
pkt[0] = 0;
pkt[1] = *data;
}
else
{
pkt[0] = 0x40;
memcpy(&pkt[1], data, sz);
}
return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz + 1);
uint8_t pkt[33];
/* build command or data packets */
if(cmd)
{
pkt[0] = 0;
pkt[1] = *data;
}
else
{
pkt[0] = 0x40;
memcpy(&pkt[1], data, sz);
}
return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz+1);
}
/*
@ -333,51 +323,51 @@ uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
*/
uint8_t ssd1306_i2c_init(void)
{
// Enable GPIOC and I2C
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
// Enable GPIOC and I2C
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
#ifdef CH32V20x
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
#ifdef SSD1306_REMAP_I2C
AFIO->PCFR1 |= AFIO_PCFR1_I2C1_REMAP;
funPinMode(PB8, GPIO_CFGLR_OUT_10Mhz_AF_OD);
funPinMode(PB9, GPIO_CFGLR_OUT_10Mhz_AF_OD);
AFIO->PCFR1 |= AFIO_PCFR1_I2C1_REMAP;
funPinMode( PB8, GPIO_CFGLR_OUT_10Mhz_AF_OD );
funPinMode( PB9, GPIO_CFGLR_OUT_10Mhz_AF_OD );
#else
funPinMode(PB6, GPIO_CFGLR_OUT_10Mhz_AF_OD);
funPinMode(PB7, GPIO_CFGLR_OUT_10Mhz_AF_OD);
funPinMode( PB6, GPIO_CFGLR_OUT_10Mhz_AF_OD );
funPinMode( PB7, GPIO_CFGLR_OUT_10Mhz_AF_OD );
#endif
#else
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO;
// PC1 is SDA, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf << (4 * 1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 1);
// PC2 is SCL, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf << (4 * 2));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 2);
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO;
// PC1 is SDA, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf<<(4*1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1);
// PC2 is SCL, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf<<(4*2));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2);
#endif
#ifdef IRQ_DIAG
// GPIO diags on PC3/PC4
GPIOC->CFGLR &= ~(0xf << (4 * 3));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 3);
GPIOC->BSHR = (1 << (16 + 3));
GPIOC->CFGLR &= ~(0xf << (4 * 4));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 4);
GPIOC->BSHR = (1 << (16 + 4));
// GPIO diags on PC3/PC4
GPIOC->CFGLR &= ~(0xf<<(4*3));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3);
GPIOC->BSHR = (1<<(16+3));
GPIOC->CFGLR &= ~(0xf<<(4*4));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
GPIOC->BSHR = (1<<(16+4));
#endif
// load I2C regs
ssd1306_i2c_setup();
// load I2C regs
ssd1306_i2c_setup();
#if 0
// test if SSD1306 is on the bus by sending display off command
uint8_t command = 0xAF;
return ssd1306_pkt_send(&command, 1, 1);
#else
return 0;
return 0;
#endif
}

View file

@ -26,115 +26,120 @@
*/
void ssd1306_i2c_setup(void)
{
funGpioInitAll();
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP);
funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1);
funPinMode(SSD1306_I2C_BITBANG_SCL, GPIO_CFGLR_OUT_10Mhz_PP);
funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1);
funGpioInitAll();
funPinMode( SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP );
funDigitalWrite( SSD1306_I2C_BITBANG_SDA, 1 );
funPinMode( SSD1306_I2C_BITBANG_SCL, GPIO_CFGLR_OUT_10Mhz_PP );
funDigitalWrite( SSD1306_I2C_BITBANG_SCL, 1 );
}
#define SDA_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1);
#define SCL_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1);
#define SDA_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 0);
#define SCL_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 0);
#define SDA_IN funDigitalRead(SSD1306_I2C_BITBANG_SDA);
#define SDA_HIGH funDigitalWrite( SSD1306_I2C_BITBANG_SDA, 1 );
#define SCL_HIGH funDigitalWrite( SSD1306_I2C_BITBANG_SCL, 1 );
#define SDA_LOW funDigitalWrite( SSD1306_I2C_BITBANG_SDA, 0 );
#define SCL_LOW funDigitalWrite( SSD1306_I2C_BITBANG_SCL, 0 );
#define SDA_RELEASE { funPinMode( SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_IN_PUPD ); funDigitalWrite( SSD1306_I2C_BITBANG_SDA, 1 ); }
#define SDA_IN funDigitalRead( SSD1306_I2C_BITBANG_SDA );
#define SDA_DRIVE { funDigitalWrite( SSD1306_I2C_BITBANG_SDA, 1 ); funPinMode( SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP ); }
#ifndef I2CSPEEDBASE
#define I2CSPEEDBASE 1
#define I2CDELAY_FUNC(x) ADD_N_NOPS(x * 1)
// Delay_Us(x*1);
#endif
#ifndef I2CDELAY_FUNC
#define I2CDELAY_FUNC(x) ADD_N_NOPS(x*1)
#endif
static void ssd1306_i2c_sendstart()
{
SCL_HIGH
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SDA_LOW
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_LOW
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_HIGH
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
}
void ssd1306_i2c_sendstop()
{
SDA_LOW
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_LOW
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_HIGH
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SDA_HIGH
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_HIGH
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_HIGH
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
}
// Return nonzero on failure.
unsigned char ssd1306_i2c_sendbyte(unsigned char data)
//Return nonzero on failure.
unsigned char ssd1306_i2c_sendbyte( unsigned char data )
{
unsigned int i;
for (i = 0; i < 8; i++)
{
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
if (data & 0x80)
{
SDA_HIGH;
}
else
{
SDA_LOW;
}
data <<= 1;
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_HIGH
I2CDELAY_FUNC(2 * I2CSPEEDBASE);
SCL_LOW
}
unsigned int i;
for( i = 0; i < 8; i++ )
{
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
if( data & 0x80 )
{ SDA_HIGH; }
else
{ SDA_LOW; }
data<<=1;
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_HIGH
I2CDELAY_FUNC( 2 * I2CSPEEDBASE );
SCL_LOW
}
// Immediately after sending last bit, open up DDDR for control.
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_IN_PUPD);
SDA_HIGH
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_HIGH
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
i = SDA_IN;
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SCL_LOW
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
SDA_HIGH // Maybe?
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP);
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
return !!i;
//Immediately after sending last bit, open up DDDR for control.
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_HIGH
SDA_RELEASE
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_HIGH
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
i = SDA_IN;
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_DRIVE
SDA_HIGH
funPinMode( SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP );
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
return !!i;
}
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
{
ssd1306_i2c_sendstart();
int r = ssd1306_i2c_sendbyte(SSD1306_I2C_ADDR << 1);
if (r) return r;
// ssd1306_i2c_sendstart(); For some reason displays don't want repeated start
if (cmd)
{
if (ssd1306_i2c_sendbyte(0x00))
return 1; // Control
}
else
{
if (ssd1306_i2c_sendbyte(0x40))
return 1; // Data
}
for (int i = 0; i < sz; i++)
{
if (ssd1306_i2c_sendbyte(data[i]))
return 1;
}
ssd1306_i2c_sendstop();
return 0;
ssd1306_i2c_sendstart();
int r = ssd1306_i2c_sendbyte( SSD1306_I2C_ADDR<<1 );
if( r ) return r;
//ssd1306_i2c_sendstart(); For some reason displays don't want repeated start
if(cmd)
{
if( ssd1306_i2c_sendbyte( 0x00 ) )
return 1; // Control
}
else
{
if( ssd1306_i2c_sendbyte( 0x40 ) )
return 1; // Data
}
for( int i = 0; i < sz; i++ )
{
if( ssd1306_i2c_sendbyte( data[i] ) )
return 1;
}
ssd1306_i2c_sendstop();
return 0;
}
void ssd1306_rst(void)
{
funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_10Mhz_PP);
funDigitalWrite(SSD1306_RST_PIN, 0);
Delay_Ms(10);
funDigitalWrite(SSD1306_RST_PIN, 1);
Delay_Us(10);
funPinMode( SSD1306_RST_PIN, GPIO_CFGLR_OUT_10Mhz_PP );
funDigitalWrite( SSD1306_RST_PIN, 0 );
Delay_Ms(10);
funDigitalWrite( SSD1306_RST_PIN, 1 );
Delay_Us(10);
}
#endif

View file

@ -24,43 +24,66 @@
#endif
#ifndef SSD1306_SCK_PIN
#define SSD1306_SCK_PIN PC5
#define SSD1306_SCK_PIN PC5
#endif
#ifndef SSD1306_BAUD_RATE_PRESCALER
#define SSD1306_BAUD_RATE_PRESCALER SPI_BaudRatePrescaler_2
#endif
#ifndef SSD1306_SOFT_SPI
#define SSD1306_SOFT_SPI 0
#endif
/*
* init SPI and GPIO for SSD1306 OLED
*/
uint8_t ssd1306_spi_init(void)
{
// Enable GPIOC and SPI
RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
// Enable GPIOC and SPI
#ifdef CH5xx
#else
RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
#endif
funGpioInitAll();
funPinMode( SSD1306_RST_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
funPinMode( SSD1306_CS_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
funPinMode( SSD1306_DC_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
#if defined( CH5xx ) || SSD1306_SOFT_SPI
funPinMode( SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
funPinMode( SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
#else
funPinMode( SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP );
funPinMode( SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP );
#endif
funGpioInitAll();
funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
funPinMode(SSD1306_CS_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
funPinMode(SSD1306_DC_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
funPinMode(SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
funPinMode(SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
funDigitalWrite( SSD1306_RST_PIN, FUN_HIGH );
funDigitalWrite( SSD1306_CS_PIN, FUN_HIGH );
funDigitalWrite( SSD1306_DC_PIN, FUN_LOW );
funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
// Configure SPI
#if SSD1306_SOFT_SPI
funDigitalWrite( SSD1306_SCK_PIN, FUN_HIGH );
#elif defined( CH5xx )
R8_SPI0_CLOCK_DIV = FUNCONF_SYSTEM_CORE_CLOCK / 12000000; // 16MHz is the fastest I want to go - though it does seem to work up to ~60MHz.
R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR;
R8_SPI0_CTRL_MOD = RB_SPI_MOSI_OE | RB_SPI_2WIRE_MOD | RB_SPI_SCK_OE;
R8_SPI0_CTRL_MOD |= RB_SPI_MST_SCK_MOD;
// | RB_SPI_MST_SCK_MOD; // Mode 3 / mode 0
R8_SPI0_CTRL_CFG = RB_MST_CLK_SEL;
#else
SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b |
SPI_Mode_Master | SPI_Direction_1Line_Tx |
SSD1306_BAUD_RATE_PRESCALER;
// Configure SPI
SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b |
SPI_Mode_Master | SPI_Direction_1Line_Tx |
SSD1306_BAUD_RATE_PRESCALER;
// enable SPI port
SPI1->CTLR1 |= CTLR1_SPE_Set;
// always succeed
return 0;
// enable SPI port
SPI1->CTLR1 |= CTLR1_SPE_Set;
#endif
// always succeed
return 0;
}
/*
@ -68,9 +91,9 @@ uint8_t ssd1306_spi_init(void)
*/
void ssd1306_rst(void)
{
funDigitalWrite(SSD1306_RST_PIN, FUN_LOW);
Delay_Ms(10);
funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
funDigitalWrite( SSD1306_RST_PIN, FUN_LOW );
Delay_Ms(10);
funDigitalWrite( SSD1306_RST_PIN, FUN_HIGH );
}
/*
@ -78,35 +101,56 @@ void ssd1306_rst(void)
*/
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
{
if (cmd)
{
funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
}
else
{
funDigitalWrite(SSD1306_DC_PIN, FUN_HIGH);
}
if(cmd)
{
funDigitalWrite( SSD1306_DC_PIN, FUN_LOW );
}
else
{
funDigitalWrite( SSD1306_DC_PIN, FUN_HIGH );
}
funDigitalWrite(SSD1306_CS_PIN, FUN_LOW);
funDigitalWrite( SSD1306_CS_PIN, FUN_LOW );
// send data
while(sz--)
{
#if SSD1306_SOFT_SPI
uint8_t c = *data++;
int i = 8;
do
{
funDigitalWrite( SSD1306_SCK_PIN, FUN_LOW ); ADD_N_NOPS(1)
funDigitalWrite( SSD1306_MOSI_PIN, !!(c & 0x80) ); ADD_N_NOPS(1)
funDigitalWrite( SSD1306_SCK_PIN, FUN_HIGH ); ADD_N_NOPS(1)
c<<=1;
} while( --i );
// send data
while (sz--)
{
// wait for TXE
while (!(SPI1->STATR & SPI_STATR_TXE))
;
#elif defined( CH5xx )
while(! (R8_SPI0_INT_FLAG & RB_SPI_FREE) );
R8_SPI0_BUFFER = *data++;
#else
// wait for TXE
while(!(SPI1->STATR & SPI_STATR_TXE));
// Send byte
SPI1->DATAR = *data++;
#endif
}
// wait for not busy before exiting
#if SSD1306_SOFT_SPI
// Nothing needed here.
#elif defined( CH5xx )
while( !(R8_SPI0_INT_FLAG & RB_SPI_FREE)) { }
#else
while(SPI1->STATR & SPI_STATR_BSY) { }
#endif
// Send byte
SPI1->DATAR = *data++;
}
// wait for not busy before exiting
while (SPI1->STATR & SPI_STATR_BSY) {}
funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
// we're happy
return 0;
funDigitalWrite( SSD1306_CS_PIN, FUN_HIGH );
// we're happy
return 0;
}
#endif

203
inc/extralibs/static_i2c.h Normal file
View file

@ -0,0 +1,203 @@
/*
Copyright 2012-2025 <>< Charles Lohr
This file may be used for any purposes (commercial or private) just please
leave this copyright notice in there somewhere. You may cross license
this as MIT or NewBSD.
Now, generic I2C library for any GPIO-based system.
Include this in your .c file only!!!
Include it after the following are defined:
I2CDELAY_FUNC -> If you wish to override the internal delay functions.
I2CSPEEDBASE -> define speed base multiplier 1 = normal, 10 = slow, .1 =
fast; failure to define this will result in the clock being as fast as
possible. I2CNEEDGETBYTE -> Do we need to be able to read data?
I2CPREFIX -> #define to be the prefix, i.e. BOB will cause BOBConfigI2C
to be generated.
I2CNOSTATIC -> #define if you want the functions to be generated as
not-static code.
NOTE: You must initially configure the port to be outputs on both DSDA
and DSCL and set them both to be driven high.
*/
/* Example:
#define DELAY1 Delay_Us(1);
#define DELAY2 Delay_Us(2);
#define DSCL_IHIGH { funPinMode( PIN_SCL, GPIO_CFGLR_IN_PUPD ); funDigitalWrite( PIN_SCL, 1 ); }
#define DSDA_IHIGH { funPinMode( PIN_SDA, GPIO_CFGLR_IN_PUPD ); funDigitalWrite( PIN_SDA, 1 ); }
#define DSDA_INPUT { funPinMode( PIN_SDA, GPIO_CFGLR_IN_PUPD ); funDigitalWrite( PIN_SDA, 1 ); }
#define DSCL_OUTPUT { funDigitalWrite( PIN_SCL, 0 ); funPinMode( PIN_SCL, GPIO_CFGLR_OUT_2Mhz_PP ); }
#define DSDA_OUTPUT { funDigitalWrite( PIN_SDA, 0 ); funPinMode( PIN_SDA, GPIO_CFGLR_OUT_2Mhz_PP ); }
#define READ_DSDA funDigitalRead( PIN_SDA )
#define I2CNEEDGETBYTE 1
#define I2CNEEDSCAN 1
#include "static_i2c.h"
...
*/
#ifndef I2CPREFIX
#define I2CPREFIX
#endif
#ifndef I2CNOSTATIC
#define I2CSTATICODE
#else
#define I2CSTATICODE static
#endif
#ifndef I2CFNCOLLAPSE
#define INTI2CFNCOLLAPSE(PFX, name) PFX##name
#define I2CFNCOLLAPSE(PFX, name) INTI2CFNCOLLAPSE(PFX, name)
#endif
#ifndef I2CNEEDGETBYTE
#define I2CNEEDGETBYTE 1
#endif
#ifndef DSCL_IHIGH
#define DSCL_IHIGH DSCL_INPUT
#endif
#ifndef DSDA_IHIGH
#define DSDA_IHIGH DSDA_INPUT
#endif
I2CSTATICODE void I2CFNCOLLAPSE(I2CPREFIX, ConfigI2C)()
{
DSDA_IHIGH
DSCL_IHIGH
}
I2CSTATICODE void I2CFNCOLLAPSE(I2CPREFIX, SendStart)()
{
DELAY1
DSCL_IHIGH
DELAY1
DSDA_OUTPUT
DELAY1
DSCL_OUTPUT
DELAY1
}
I2CSTATICODE void I2CFNCOLLAPSE(I2CPREFIX, SendStop)()
{
DELAY1
DSDA_OUTPUT
DELAY1
DSCL_IHIGH
DELAY1
DSDA_IHIGH
DELAY1
}
// Return nonzero on failure.
I2CSTATICODE unsigned char I2CFNCOLLAPSE(I2CPREFIX, SendByte)(unsigned char data)
{
unsigned int i;
// Assume we are in a started state (DSCL = 0 & DSDA = 0)
for (i = 0; i < 8; i++)
{
DELAY1
if (data & 0x80)
{
DSDA_IHIGH
}
else
{
DSDA_OUTPUT
}
data <<= 1;
DELAY2
DSCL_IHIGH
DELAY2
DSCL_OUTPUT
}
// Immediately after sending last bit, open up DDDR for control.
DELAY1
DSDA_INPUT
DELAY1
DSCL_IHIGH
DELAY1
i = READ_DSDA;
DELAY1
DSCL_OUTPUT
DELAY1
return !!i;
}
#if I2CNEEDGETBYTE
I2CSTATICODE unsigned char I2CFNCOLLAPSE(I2CPREFIX, GetByte)(uint8_t send_nak)
{
unsigned char i;
unsigned char ret = 0;
DSDA_INPUT
for (i = 0; i < 8; i++)
{
DELAY1
DSCL_IHIGH
DELAY2
ret <<= 1;
if (READ_DSDA)
ret |= 1;
DSCL_OUTPUT
}
// Send ack.
if (send_nak)
{
}
else
{
DSDA_OUTPUT
}
DELAY1
DSCL_IHIGH
DELAY2
DSCL_OUTPUT
DELAY1
DSDA_IHIGH
return ret;
}
#endif
// In case you want SCAN code
#if I2CNEEDSCAN
I2CSTATICODE void I2CFNCOLLAPSE(I2CPREFIX, Scan)()
{
int i;
printf( " " );
for( i = 0; i < 16; i++ )
{
printf( " %x", i );
}
for( i = 0; i < 128; i++ )
{
if( ( i & 0xf ) == 0 )
{
printf( "\n%02x ", i );
}
SendStart();
int b = SendByte( i<<1 );
SendStop();
printf( "%c ", b?'.':'#' );
}
printf( "\n" );
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -2,28 +2,30 @@
I may write another version of this to use DMA to timer ports, but, the SPI port can be used
to generate outputs very efficiently. So, for now, SPI Port. Additionally, it uses FAR less
internal bus resources than to do the same thing with timers.
**For the CH32V003 this means output will be on PORTC Pin 6**
**For the CH570/2 - PORTA Pin 7**
**For the CH582/3 or CH591/2 PORTA Pin 14**
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
If you are including this in main, simply
#define WS2812DMA_IMPLEMENTATION
If you are including this in main, simply
#define WS2812DMA_IMPLEMENTATION
Other defines inclue:
#define WSRAW
#define WSRBG
#define WSGRB
#define WS2812B_ALLOW_INTERRUPT_NESTING
#define WSRAW
#define WSRBG
#define WSGRB
#define WS2812B_ALLOW_INTERRUPT_NESTING
You will need to implement the following two functions, as callbacks from the ISR.
uint32_t WS2812BLEDCallback( int ledno );
uint32_t WS2812BLEDCallback( int ledno );
You willalso need to call
WS2812BDMAInit();
WS2812BDMAInit();
Then, whenyou want to update the LEDs, call:
WS2812BDMAStart( int num_leds );
WS2812BDMAStart( int num_leds );
*/
#ifndef _WS2812_LED_DRIVER_H
@ -32,11 +34,11 @@
#include <stdint.h>
// Use DMA and SPI to stream out WS2812B LED Data via the MOSI pin.
void WS2812BDMAInit();
void WS2812BDMAStart(int leds);
void WS2812BDMAInit( );
void WS2812BDMAStart( int leds );
// Callbacks that you must implement.
uint32_t WS2812BLEDCallback(int ledno);
uint32_t WS2812BLEDCallback( int ledno );
#ifdef WS2812DMA_IMPLEMENTATION
@ -46,241 +48,293 @@ uint32_t WS2812BLEDCallback(int ledno);
#endif
// Note first n LEDs of DMA Buffer are 0's as a "break"
// Need one extra LED at end to leave line high.
// Need one extra LED at end to leave line high.
// This must be greater than WS2812B_RESET_PERIOD.
#define WS2812B_RESET_PERIOD 2
#ifdef WSRAW
#define DMA_BUFFER_LEN (((DMALEDS) / 2) * 8)
#define DMA_BUFFER_LEN (((DMALEDS)/2)*8)
#else
#define DMA_BUFFER_LEN (((DMALEDS) / 2) * 6)
#define DMA_BUFFER_LEN (((DMALEDS)/2)*6)
#endif
static uint16_t WS2812dmabuff[DMA_BUFFER_LEN];
static uint16_t WS2812dmabuff[DMA_BUFFER_LEN];
static volatile int WS2812LEDs;
static volatile int WS2812LEDPlace;
static volatile int WS2812BLEDInUse;
#ifdef CH5xx
#ifdef CH570_CH572
#define bMOSI PA7
#else
#define bMOSI PA14
#endif
#endif
// This is the code that updates a portion of the WS2812dmabuff with new data.
// This effectively creates the bitstream that outputs to the LEDs.
static void WS2812FillBuffSec(uint16_t *ptr, int numhalfwords, int tce)
static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int tce )
{
const static uint16_t bitquartets[16] = {
0b1000100010001000,
0b1000100010001110,
0b1000100011101000,
0b1000100011101110,
0b1000111010001000,
0b1000111010001110,
0b1000111011101000,
0b1000111011101110,
0b1110100010001000,
0b1110100010001110,
0b1110100011101000,
0b1110100011101110,
0b1110111010001000,
0b1110111010001110,
0b1110111011101000,
0b1110111011101110,
};
int i;
uint16_t *end = ptr + numhalfwords;
int ledcount = WS2812LEDs;
int place = WS2812LEDPlace;
#ifdef WSRAW
while (place < 0 && ptr != end)
{
uint32_t *lptr = (uint32_t *)ptr;
lptr[0] = 0;
lptr[1] = 0;
lptr[2] = 0;
lptr[3] = 0;
ptr += 8;
place++;
}
#ifdef CH5xx
// Reversing bit order because CH5xx SPI FIFO is only half of what CH32 have
const static uint16_t bitquartets[16] = {
0b0001000100010001, 0b0111000100010001, 0b0001011100010001, 0b0111011100010001,
0b0001000101110001, 0b0111000101110001, 0b0001011101110001, 0b0111011101110001,
0b0001000100010111, 0b0111000100010111, 0b0001011100010111, 0b0111011100010111,
0b0001000101110111, 0b0111000101110111, 0b0001011101110111, 0b0111011101110111, };
#else
while (place < 0 && ptr != end)
{
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
place++;
}
const static uint16_t bitquartets[16] = {
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, };
#endif
while (ptr != end)
{
if (place >= ledcount)
{
// Optionally, leave line high.
while (ptr != end)
(*ptr++) = 0; // 0xffff;
// Only safe to do this when we're on the second leg.
if (tce)
{
if (place == ledcount)
{
// Take the DMA out of circular mode and let it expire.
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
WS2812BLEDInUse = 0;
}
place++;
}
break;
}
int i;
uint16_t * end = ptr + numhalfwords;
int ledcount = WS2812LEDs;
int place = WS2812LEDPlace;
#ifdef WSRAW
uint32_t ledval32bit = WS2812BLEDCallback(place++);
ptr[6] = bitquartets[(ledval32bit >> 28) & 0xf];
ptr[7] = bitquartets[(ledval32bit >> 24) & 0xf];
ptr[4] = bitquartets[(ledval32bit >> 20) & 0xf];
ptr[5] = bitquartets[(ledval32bit >> 16) & 0xf];
ptr[2] = bitquartets[(ledval32bit >> 12) & 0xf];
ptr[3] = bitquartets[(ledval32bit >> 8) & 0xf];
ptr[0] = bitquartets[(ledval32bit >> 4) & 0xf];
ptr[1] = bitquartets[(ledval32bit >> 0) & 0xf];
ptr += 8;
i += 8;
while( place < 0 && ptr != end )
{
uint32_t * lptr = (uint32_t *)ptr;
lptr[0] = 0;
lptr[1] = 0;
lptr[2] = 0;
lptr[3] = 0;
ptr += 8;
place++;
}
#else
// Use a LUT to figure out how we should set the SPI line.
uint32_t ledval24bit = WS2812BLEDCallback(place++);
while( place < 0 && ptr != end )
{
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
place++;
}
#endif
while( ptr != end )
{
if( place >= ledcount )
{
// Optionally, leave line high.
while( ptr != end )
(*ptr++) = 0;//0xffff;
// Only safe to do this when we're on the second leg.
if( tce )
{
if( place == ledcount )
{
// Take the DMA out of circular mode and let it expire.
#ifdef CH5xx
R8_SPI0_INTER_EN &= ~RB_SPI_IE_DMA_END; // Disable DMA end interrupt
#else
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
#endif
WS2812BLEDInUse = 0;
}
place++;
}
break;
}
#ifdef WSRAW
uint32_t ledval32bit = WS2812BLEDCallback( place++ );
ptr[6] = bitquartets[(ledval32bit>>28)&0xf];
ptr[7] = bitquartets[(ledval32bit>>24)&0xf];
ptr[4] = bitquartets[(ledval32bit>>20)&0xf];
ptr[5] = bitquartets[(ledval32bit>>16)&0xf];
ptr[2] = bitquartets[(ledval32bit>>12)&0xf];
ptr[3] = bitquartets[(ledval32bit>>8)&0xf];
ptr[0] = bitquartets[(ledval32bit>>4)&0xf];
ptr[1] = bitquartets[(ledval32bit>>0)&0xf];
ptr += 8;
i += 8;
#else
// Use a LUT to figure out how we should set the SPI line.
uint32_t ledval24bit = WS2812BLEDCallback( place++ );
#ifdef WSRBG
ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[2] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[3] = bitquartets[(ledval24bit >> 16) & 0xf];
ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
#elif defined(WSGRB)
ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[2] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[3] = bitquartets[(ledval24bit >> 0) & 0xf];
ptr[4] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[5] = bitquartets[(ledval24bit >> 16) & 0xf];
ptr[0] = bitquartets[(ledval24bit>>12)&0xf];
ptr[1] = bitquartets[(ledval24bit>>8)&0xf];
ptr[2] = bitquartets[(ledval24bit>>20)&0xf];
ptr[3] = bitquartets[(ledval24bit>>16)&0xf];
ptr[4] = bitquartets[(ledval24bit>>4)&0xf];
ptr[5] = bitquartets[(ledval24bit>>0)&0xf];
#elif defined( WSGRB )
ptr[0] = bitquartets[(ledval24bit>>12)&0xf];
ptr[1] = bitquartets[(ledval24bit>>8)&0xf];
ptr[2] = bitquartets[(ledval24bit>>4)&0xf];
ptr[3] = bitquartets[(ledval24bit>>0)&0xf];
ptr[4] = bitquartets[(ledval24bit>>20)&0xf];
ptr[5] = bitquartets[(ledval24bit>>16)&0xf];
#else
ptr[0] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[1] = bitquartets[(ledval24bit >> 16) & 0xf];
ptr[2] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[3] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
ptr[0] = bitquartets[(ledval24bit>>20)&0xf];
ptr[1] = bitquartets[(ledval24bit>>16)&0xf];
ptr[2] = bitquartets[(ledval24bit>>12)&0xf];
ptr[3] = bitquartets[(ledval24bit>>8)&0xf];
ptr[4] = bitquartets[(ledval24bit>>4)&0xf];
ptr[5] = bitquartets[(ledval24bit>>0)&0xf];
#endif
ptr += 6;
i += 6;
ptr += 6;
i += 6;
#endif
}
WS2812LEDPlace = place;
}
WS2812LEDPlace = place;
}
void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt));
void DMA1_Channel3_IRQHandler(void)
#ifdef CH5xx
void SPI0_IRQHandler( void ) __attribute__((interrupt));
void SPI0_IRQHandler( void )
{
// GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
uint8_t intf = R8_SPI0_INT_FLAG;
if( (intf & RB_SPI_IF_DMA_END) )
{
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 1 );
R16_SPI0_TOTAL_CNT = DMA_BUFFER_LEN * 2;
}
}
#else
void DMA1_Channel3_IRQHandler( void ) __attribute__((interrupt));
void DMA1_Channel3_IRQHandler( void )
{
//GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
// Backup flags.
volatile int intfr = DMA1->INTFR;
do
{
// Clear all possible flags.
DMA1->INTFCR = DMA1_IT_GL3;
// Backup flags.
volatile int intfr = DMA1->INTFR;
do
{
// Clear all possible flags.
DMA1->INTFCR = DMA1_IT_GL3;
// Strange note: These are backwards. DMA1_IT_HT3 should be HALF and
// DMA1_IT_TC3 should be COMPLETE. But for some reason, doing this causes
// LED jitter. I am henseforth flipping the order.
// Strange note: These are backwards. DMA1_IT_HT3 should be HALF and
// DMA1_IT_TC3 should be COMPLETE. But for some reason, doing this causes
// LED jitter. I am henseforth flipping the order.
if (intfr & DMA1_IT_HT3)
{
// Halfwaay (Fill in first part)
WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN / 2, 1);
}
if (intfr & DMA1_IT_TC3)
{
// Complete (Fill in second part)
WS2812FillBuffSec(WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0);
}
intfr = DMA1->INTFR;
} while (intfr & DMA1_IT_GL3);
if( intfr & DMA1_IT_HT3 )
{
// Halfwaay (Fill in first part)
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN / 2, 1 );
}
if( intfr & DMA1_IT_TC3 )
{
// Complete (Fill in second part)
WS2812FillBuffSec( WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0 );
}
intfr = DMA1->INTFR;
} while( intfr & DMA1_IT_GL3 );
// GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling
//GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling
}
#endif
void WS2812BDMAStart( int leds )
{
// Enter critical section.
__disable_irq();
WS2812BLEDInUse = 1;
#ifdef CH5xx
R8_SPI0_INTER_EN &= ~RB_SPI_IE_DMA_END;
R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE;
R16_SPI0_TOTAL_CNT = 0;
#else
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
DMA1_Channel3->CNTR = 0;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
#endif
__enable_irq();
WS2812LEDs = leds;
WS2812LEDPlace = -WS2812B_RESET_PERIOD;
#ifdef CH5xx
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 0 );
R16_SPI0_TOTAL_CNT = DMA_BUFFER_LEN * 2;
R16_SPI0_DMA_BEG = (uint32_t)WS2812dmabuff;
R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END;
R8_SPI0_INTER_EN = RB_SPI_IE_DMA_END;
R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE;
#else
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 0 );
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
#endif
}
void WS2812BDMAStart(int leds)
void WS2812BDMAInit( )
{
// Enter critical section.
__disable_irq();
WS2812BLEDInUse = 1;
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
DMA1_Channel3->CNTR = 0;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
WS2812LEDs = leds;
WS2812LEDPlace = -WS2812B_RESET_PERIOD;
__enable_irq();
// Enable DMA + Peripherals
#ifdef CH5xx
funPinMode( bMOSI, GPIO_CFGLR_OUT_2Mhz_PP );
R8_SPI0_CLOCK_DIV = FUNCONF_SYSTEM_CORE_CLOCK / 3000000; // div = Fsys/3MHz
R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR;
R8_SPI0_CTRL_MOD = RB_SPI_MOSI_OE | RB_SPI_2WIRE_MOD;
R16_SPI0_DMA_END = ( (uint32_t)WS2812dmabuff + (DMA_BUFFER_LEN * 2) );
R8_SPI0_CTRL_CFG |= RB_SPI_BIT_ORDER;
WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN, 0);
NVIC_EnableIRQ( SPI0_IRQn );
#else
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
}
// MOSI, Configure GPIO Pin
GPIOC->CFGLR &= ~(0xf<<(4*6));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6);
void WS2812BDMAInit()
{
// Enable DMA + Peripherals
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
// Configure SPI
SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_16b |
SPI_Mode_Master | SPI_Direction_1Line_Tx |
3<<3; // Divisior = 16 (48/16 = 3MHz)
// MOSI, Configure GPIO Pin
GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN; // Enable Tx buffer DMA
// Configure SPI
SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_16b |
SPI_Mode_Master | SPI_Direction_1Line_Tx |
3 << 3; // Divisior = 16 (48/16 = 3MHz)
#if defined(CH32V003)
SPI1->HSCR = 1; // Enable high-speed read mode
#endif
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
SPI1->HSCR = 1;
SPI1->CTLR1 |= CTLR1_SPE_Set; // Enable SPI
SPI1->CTLR1 |= CTLR1_SPE_Set;
SPI1->DATAR = 0; // Set SPI line Low.
SPI1->DATAR = 0; // Set SPI line Low.
//DMA1_Channel3 is for SPI1TX
DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
DMA1_Channel3->CNTR = 0;// sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!)
DMA1_Channel3->CFGR =
DMA_M2M_Disable |
DMA_Priority_VeryHigh |
DMA_MemoryDataSize_HalfWord |
DMA_PeripheralDataSize_HalfWord |
DMA_MemoryInc_Enable |
DMA_Mode_Normal | // OR DMA_Mode_Circular or DMA_Mode_Normal
DMA_DIR_PeripheralDST |
DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts.
// DMA1_Channel3 is for SPI1TX
DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
DMA1_Channel3->CNTR = 0; // sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!)
DMA1_Channel3->CFGR =
DMA_M2M_Disable |
DMA_Priority_VeryHigh |
DMA_MemoryDataSize_HalfWord |
DMA_PeripheralDataSize_HalfWord |
DMA_MemoryInc_Enable |
DMA_Mode_Normal | // OR DMA_Mode_Circular or DMA_Mode_Normal
DMA_DIR_PeripheralDST |
DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts.
// NVIC_SetPriority( DMA1_Channel3_IRQn, 0<<4 ); //We don't need to tweak priority.
NVIC_EnableIRQ(DMA1_Channel3_IRQn);
DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
// NVIC_SetPriority( DMA1_Channel3_IRQn, 0<<4 ); //We don't need to tweak priority.
NVIC_EnableIRQ( DMA1_Channel3_IRQn );
DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
#ifdef WS2812B_ALLOW_INTERRUPT_NESTING
__set_INTSYSCR(__get_INTSYSCR() | 2); // Enable interrupt nesting.
PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3
__set_INTSYSCR( __get_INTSYSCR() | 2 ); // Enable interrupt nesting.
PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3
#endif
#endif
}
#endif
#endif

View file

@ -3,10 +3,10 @@
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
If you are including this in main, simply
#define WS2812BSIMPLE_IMPLEMENTATION
#define WS2812BSIMPLE_IMPLEMENTATION
You may also want to define
#define WS2812BSIMPLE_NO_IRQ_TWEAKING
#define WS2812BSIMPLE_NO_IRQ_TWEAKING
*/
@ -15,7 +15,7 @@
#include <stdint.h>
void WS2812BSimpleSend(GPIO_TypeDef *port, int pin, uint8_t *data, int len_in_bytes);
void WS2812BSimpleSend( GPIO_TypeDef * port, int pin, uint8_t * data, int len_in_bytes );
#ifdef WS2812BSIMPLE_IMPLEMENTATION
@ -25,58 +25,59 @@ void WS2812BSimpleSend(GPIO_TypeDef *port, int pin, uint8_t *data, int len_in_by
#error WS2812B Driver Requires FUNCONF_SYSTICK_USE_HCLK
#endif
void WS2812BSimpleSend(GPIO_TypeDef *port, int pin, uint8_t *data, int len_in_bytes)
void WS2812BSimpleSend( GPIO_TypeDef * port, int pin, uint8_t * data, int len_in_bytes )
{
int port_id = (((intptr_t)port - (intptr_t)GPIOA) >> 10);
RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA << port_id); // Make sure port is enabled.
int port_id = (((intptr_t)port-(intptr_t)GPIOA)>>10);
RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA<<port_id); // Make sure port is enabled.
int poffset = (pin * 4);
port->CFGLR = (port->CFGLR & (~(0xf << poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP) << (poffset));
int poffset = (pin*4);
port->CFGLR = ( port->CFGLR & (~(0xf<<poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP)<<(poffset));
int maskon = 1 << pin;
int maskoff = 1 << (16 + pin);
int maskon = 1<<pin;
int maskoff = 1<<(16+pin);
port->BSHR = maskoff;
port->BSHR = maskoff;
uint8_t *end = data + len_in_bytes;
while (data != end)
{
uint8_t byte = *data;
uint8_t * end = data + len_in_bytes;
while( data != end )
{
uint8_t byte = *data;
int i;
for (i = 0; i < 8; i++)
{
if (byte & 0x80)
{
// WS2812B's need AT LEAST 625ns for a logical "1"
port->BSHR = maskon;
DelaySysTick(25);
port->BSHR = maskoff;
DelaySysTick(1);
}
else
{
// WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0"
int i;
for( i = 0; i < 8; i++ )
{
if( byte & 0x80 )
{
// WS2812B's need AT LEAST 625ns for a logical "1"
port->BSHR = maskon;
DelaySysTick(25);
port->BSHR = maskoff;
DelaySysTick(1);
}
else
{
// WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0"
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
__disable_irq();
__disable_irq();
#endif
port->BSHR = maskon;
asm volatile("nop\nnop\nnop\nnop");
port->BSHR = maskoff;
port->BSHR = maskon;
asm volatile( "nop\nnop\nnop\nnop" );
port->BSHR = maskoff;
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
__enable_irq();
__enable_irq();
#endif
DelaySysTick(15);
}
byte <<= 1;
}
DelaySysTick(15);
}
byte <<= 1;
}
data++;
}
data++;
}
port->BSHR = maskoff;
port->BSHR = maskoff;
}
#endif
#endif

View file

@ -1,5 +0,0 @@
{
"DisableFormat": true,
"SortIncludes": "Never"
}

View file

@ -0,0 +1,5 @@
#!/bin/bash
rm -f ch5xx_blink.o ch5xx_blink.bin
riscv64-unknown-elf-as ch5xx_blink.asm -march=rv32imac -mabi=ilp32 -o ch5xx_blink.o
riscv64-unknown-elf-objcopy -O binary ch5xx_blink.o ch5xx_blink.bin
xxd -i ch5xx_blink.bin > ch5xx_blink.h

View file

@ -0,0 +1,5 @@
#!/bin/bash
rm -f ch5xx_verify.o ch5xx_verify.bin
riscv64-unknown-elf-as ch5xx_verify.asm -march=rv32imac_zicsr -o ch5xx_verify.o
riscv64-unknown-elf-objcopy -O binary ch5xx_verify.o ch5xx_verify.bin
xxd -i ch5xx_verify.bin > ch5xx_verify.h

View file

@ -0,0 +1,5 @@
#!/bin/bash
rm -f ch5xx_write_block.o ch5xx_write_block.bin
riscv64-unknown-elf-as ch5xx_write_block.asm -march=rv32imac_zicsr -o ch5xx_write_block.o
riscv64-unknown-elf-objcopy -O binary ch5xx_write_block.o ch5xx_write_block.bin
xxd -i ch5xx_write_block.bin > ch5xx_write_block.h

View file

@ -0,0 +1,23 @@
#
# The simplest blink fuction that can fit into PROGBUF.
# This is the version suitable for CH5xx series of chips by WCH.
# You need to preload respective registers with values for port, pin and delay.
# Copyright 2025 monte-monte
#
#a2 = GPIO port (0 - A, 1 - B...)
#a3 = value to count down from for a delay between blinks
#a4 = pin mask
BEGIN:
c.sw a4, 0(a2);
c.mv a5, a3;
sw zero, 8(a2);
LOOP1:
c.addi a5, -1;
c.bnez a5, LOOP1;
c.mv a5, a3;
c.sw a4, 8(a2);
LOOP2:
c.addi a5, -1;
c.bnez a5, LOOP2;
c.j BEGIN;
c.ebreak

View file

@ -0,0 +1,10 @@
c.nop;
li a5,4;
LOOP:
lb a4,6(a3);
blt a4,zero,LOOP;
lb a4,4(a3);
c.addi a5,-1;
c.bnez a5,LOOP;
c.lw a1,0(a3);
c.sw a1,0(a0);

View file

@ -0,0 +1,116 @@
#
# A procedure to write data to flash on CH5xx series of chips by WCH.
# This should be compiled into a binary blob and placed into RAM.
# Then you will be able to run it and send data via a programmer by writing it to DMDATA0/1
# Copyright 2025 monte-monte
#
#s0 = incomming data from dmdata0
#s1 = incomming data from dmdata1
#t0 = general reg
#t1 = address global
#a0 = address in loop
#a1 = dmdata0 offset
#a2 = general reg
#a3 = R32_FLASH_DATA
#a4 = general operational
#a5 = len counter
PREAMBLE:
#c.li t0,0;
#c.li a2,0;
#c.li a4,0;
#c.li a5,0;
lw s0,0(a1);
lw s1,4(a1);
bne s1,s0,PREAMBLE;#Wait for DMDATA0/1 to be cleared;
c.bnez s0,PREAMBLE;
START:
#BEGIN
addi a0,t1,0;
sb zero,6(a3);
c.li a4,5;
sb a4,6(a3);
c.li a4,6;
sb a4,4(a3);
END:
lb a4,6(a3);
blt a4,zero,END;
sb zero,6(a3);
#BEGIN with CMD = 2
sb zero,6(a3);
c.li a4,5;
sb a4,6(a3);
c.li a4,2;
sb a4,4(a3);
c.li a2,3;
OUT:
srli t0,a0,16;
andi t0,t0,0xff;
OUT_LOOP:
lb a4,6(a3);
blt a4,zero,OUT_LOOP;
sb t0,4(a3);
c.slli a0,8;
c.addi a2,-1;
c.bnez a2,OUT;
ADDRES_IS_SET:
sw zero,0(a1);#Clear DMDATA0 to indicate we are ready to write again
addi a5,zero,64;#Load up the byte counter
DATA_RETRIEVER:
c.lw s0,0(a1);
c.lw s1,4(a1);
c.bnez s1, WRITE_ZERO
c.beqz s0, DATA_RETRIEVER;
WRITE:
c.sw s0,0(a3);
c.li a4,0x4;
WRITE_LOOP1:
lb a2,6(a3);
blt a2,zero,WRITE_LOOP1;
c.li a2,21;
sb a2,6(a3);
c.addi a4,-1;
c.bnez a4,WRITE_LOOP1;
c.addi a5,-1;
sw zero,0(a1);
c.bnez a5,DATA_RETRIEVER;
addi t1,t1,256;
sw t1,0(a1);#Write current address to DMDATA0 to indicate that we are in wait loop
lui t0,0x80;
WAIT:
#END
lb a4,6(a3);
blt a4,zero,WAIT;
sb zero,6(a3)
#BEGIN with CMD = 5
WAIT_LOOP:
sb zero,6(a3);
c.li a4,5;
sb a4,6(a3);
sb a4,4(a3)
IN_LOOP1:
lb a4,6(a3);
blt a4,zero,IN_LOOP1;
lbu a2,4(a3);
IN_LOOP2:
lb a4,6(a3);
blt a4,zero,IN_LOOP2;
lb a2,4(a3);
WAIT_END:
lb a4,6(a3);
blt a4,zero,WAIT_END;
sb zero,6(a3)
#IF
andi a4,a2,1;
c.bnez a4,WAIT_LOOP_BACK;
j START;
WAIT_LOOP_BACK:
c.addi t0,-1;
bne t0,zero,WAIT_LOOP;
EXIT:
c.ebreak;
WRITE_ZERO:
sw zero,4(a1);
beq s1,a1,WAIT;
sw zero,0(a3);
c.li a4,0x4;
c.j WRITE_LOOP1;

View file

@ -20,7 +20,7 @@ typedef enum IRQn
Ecall_U_Mode_IRQn = 8, /* 8 Ecall U Mode Interrupt */
Break_Point_IRQn = 9, /* 9 Break Point Interrupt */
#endif
SysTicK_IRQn = 12, /* 12 System timer Interrupt */
SysTick_IRQn = 12, /* 12 System timer Interrupt */
Software_IRQn = 14, /* 14 software Interrupt */
#if defined(CH32V003) || defined(CH32X03x)
@ -1864,8 +1864,6 @@ typedef struct
#define USB_PHY_V33 (1<<6)
#define USB_IOEN (1<<7)
#define USBFSD_UEP_MOD_BASE 0x4002340C
@ -2266,11 +2264,7 @@ typedef struct
#define ESIG_BASE ((uint32_t)0x1FFFF7E0)
#define INFO_BASE ((uint32_t)0x1FFFF704)
#if defined(CH32V003) || defined(CH32V10x)
#define EXTEN_BASE ((uint32_t)0x40023800)
#elif defined(CH32V20x) || defined(CH32V30x)
#define EXTEN_BASE (AHBPERIPH_BASE + 0x3800)
#endif
#define PFIC_BASE (CORE_PERIPH_BASE + 0xE000)
#define SysTick_BASE (CORE_PERIPH_BASE + 0xF000)
@ -2283,118 +2277,6 @@ typedef struct
// AFIO CTLR Bits
#define PB6_FILT_EN (1<<27)
#define PB5_FILT_EN (1<<26)
#define PA4_FILT_EN (1<<25)
#define PA3_FILT_EN (1<<24)
#define UDM_BC_CMPO (1<<19)
#define UDP_BC_CMPO (1<<17)
#define UDM_BC_VSRC (1<<17)
#define UDP_BC_VSRC (1<<16)
#define USBPD_IN_HVT (1<<9)
#define USBPD_PHY_V33 (1<<8)
#define USB_IOEN (1<<7)
#define USB_PHY_V33 (1<<6)
#define UDP_PUE_00 (0b00<<2)
#define UDP_PUE_01 (0b01<<2)
#define UDP_PUE_10 (0b10<<2)
#define UDP_PUE_11 (0b11<<2)
#define UDM_PUE_00 (0b00<<0)
#define UDM_PUE_01 (0b01<<0)
#define UDM_PUE_10 (0b10<<0)
#define UDM_PUE_11 (0b11<<0)
#define UDP_PUE_MASK 0x0000000C
#define UDP_PUE_DISABLE 0x00000000
#define UDP_PUE_35UA 0x00000004
#define UDP_PUE_10K 0x00000008
#define UDP_PUE_1K5 0x0000000C
#define UDM_PUE_MASK 0x00000003
#define UDM_PUE_DISABLE 0x00000000
#define UDM_PUE_35UA 0x00000001
#define UDM_PUE_10K 0x00000002
#define UDM_PUE_1K5 0x00000003
// USB PD Bits
#define IE_TX_END (1<<15)
#define IE_RX_RESET (1<<14)
#define IE_RX_ACT (1<<13)
#define IE_RX_BYTE (1<<12)
#define IE_RX_BIT (1<<11)
#define IE_PD_IO (1<<10)
#define WAKE_POLAR (1<<5)
#define PD_RST_EN (1<<4)
#define PD_DMA_EN (1<<3)
#define CC_SEL (1<<2)
#define PD_ALL_CLR (1<<1)
#define PD_FILT_EN (1<<0)
#define BMC_CLK_CNT_MASK (0xff)
//R8_CONTROL
#define BMC_BYTE_HI (1<<7)
#define TX_BIT_BACK (1<<6)
#define DATA_FLAG (1<<5)
#define RX_STATE_MASK (0x7<<2)
#define RX_STATE_0 (1<<2)
#define RX_STATE_1 (1<<3)
#define RX_STATE_2 (1<<4)
#define BMC_START (1<<1)
#define PD_TX_EN (1<<0)
#define TX_SEL4_MASK (3<<6)
#define TX_SEL4_0 (1<<6)
#define TX_SEL4_1 (1<<7)
#define TX_SEL3_MASK (3<<4)
#define TX_SEL3_0 (1<<4)
#define TX_SEL3_1 (1<<5)
#define TX_SEL2_MASK (3<<2)
#define TX_SEL2_0 (1<<2)
#define TX_SEL2_1 (1<<3)
#define TX_SEL1 (1<<0)
#define BMC_TX_SZ_MASK (0x1ff)
//R8_STATUS
#define IF_TX_END (1<<7)
#define IF_RX_RESET (1<<6)
#define IF_RX_ACT (1<<5)
#define IF_RX_BYTE (1<<4)
#define IF_RX_BIT (1<<3)
#define IFBUF_ERR (1<<2)
#define BMC_AUX_MASK (3<<0)
#define BMC_AUX_1 (1<<1)
#define BMC_AUX_0 (1<<0)
// PORT CC1
#define CC1_CE_MASK (7<<5)
#define CC1_CE_0 (1<<5)
#define CC1_CE_1 (2<<5)
#define CC1_CE_2 (4<<5)
#define CC1_LVE (1<<4)
#define CC1_PU_MASK (3<<2)
#define CC1_PU_DISABLE (0<<2)
#define CC1_PU_330uA (1<<2)
#define CC1_PU_180uA (2<<2)
#define CC1_PU_80uA (3<<2)
#define PA_CC1_AI (1<<0)
#define CC2_CE_MASK (7<<5)
#define CC2_CE_0 (1<<5)
#define CC2_CE_1 (2<<5)
#define CC2_CE_2 (4<<5)
#define CC2_LVE (1<<4)
#define CC2_PU_MASK (3<<2)
#define CC2_PU_DISABLE (0<<2)
#define CC2_PU_330uA (1<<2)
#define CC2_PU_180uA (2<<2)
#define CC2_PU_80uA (3<<2)
#define PA_CC2_AI (1<<0)
@ -5356,33 +5238,33 @@ typedef struct
#if defined(CH32V003)
/***************** Bit definition for AFIO_EXTICR register *****************/
#define AFIO_EXTICR_EXTI0 ((uint16_t)0x0003) /* EXTI 0 configuration */
#define AFIO_EXTICR_EXTI1 ((uint16_t)0x000C) /* EXTI 1 configuration */
#define AFIO_EXTICR_EXTI2 ((uint16_t)0x0030) /* EXTI 2 configuration */
#define AFIO_EXTICR_EXTI3 ((uint16_t)0x00C0) /* EXTI 3 configuration */
#define AFIO_EXTICR_EXTI4 ((uint16_t)0x0300) /* EXTI 4 configuration */
#define AFIO_EXTICR_EXTI5 ((uint16_t)0x0C00) /* EXTI 5 configuration */
#define AFIO_EXTICR_EXTI6 ((uint16_t)0x3000) /* EXTI 6 configuration */
#define AFIO_EXTICR_EXTI7 ((uint16_t)0xC000) /* EXTI 7 configuration */
#define AFIO_EXTICR1_EXTI0 ((uint16_t)0x0003) /* EXTI 0 configuration */
#define AFIO_EXTICR1_EXTI1 ((uint16_t)0x000C) /* EXTI 1 configuration */
#define AFIO_EXTICR1_EXTI2 ((uint16_t)0x0030) /* EXTI 2 configuration */
#define AFIO_EXTICR1_EXTI3 ((uint16_t)0x00C0) /* EXTI 3 configuration */
#define AFIO_EXTICR1_EXTI4 ((uint16_t)0x0300) /* EXTI 4 configuration */
#define AFIO_EXTICR1_EXTI5 ((uint16_t)0x0C00) /* EXTI 5 configuration */
#define AFIO_EXTICR1_EXTI6 ((uint16_t)0x3000) /* EXTI 6 configuration */
#define AFIO_EXTICR1_EXTI7 ((uint16_t)0xC000) /* EXTI 7 configuration */
#define AFIO_EXTICR_EXTI0_PC ((uint16_t)0x0002) /* PC[0] pin */
#define AFIO_EXTICR_EXTI0_PD ((uint16_t)0x0003) /* PD[0] pin */
#define AFIO_EXTICR_EXTI1_PA ((uint16_t)0x0000) /* PA[1] pin */
#define AFIO_EXTICR_EXTI1_PC ((uint16_t)0x0008) /* PC[1] pin */
#define AFIO_EXTICR_EXTI1_PD ((uint16_t)0x000C) /* PD[1] pin */
#define AFIO_EXTICR_EXTI2_PA ((uint16_t)0x0000) /* PA[2] pin */
#define AFIO_EXTICR_EXTI2_PC ((uint16_t)0x0020) /* PC[2] pin */
#define AFIO_EXTICR_EXTI2_PD ((uint16_t)0x0030) /* PD[2] pin */
#define AFIO_EXTICR_EXTI3_PC ((uint16_t)0x0080) /* PC[3] pin */
#define AFIO_EXTICR_EXTI3_PD ((uint16_t)0x00C0) /* PD[3] pin */
#define AFIO_EXTICR_EXTI4_PC ((uint16_t)0x0200) /* PC[4] pin */
#define AFIO_EXTICR_EXTI4_PD ((uint16_t)0x0300) /* PD[4] pin */
#define AFIO_EXTICR_EXTI5_PC ((uint16_t)0x0800) /* PC[5] pin */
#define AFIO_EXTICR_EXTI5_PD ((uint16_t)0x0C00) /* PD[5] pin */
#define AFIO_EXTICR_EXTI6_PC ((uint16_t)0x2000) /* PC[6] pin */
#define AFIO_EXTICR_EXTI6_PD ((uint16_t)0x3000) /* PD[6] pin */
#define AFIO_EXTICR_EXTI7_PC ((uint16_t)0x8000) /* PC[7] pin */
#define AFIO_EXTICR_EXTI7_PD ((uint16_t)0xC000) /* PD[7] pin */
#define AFIO_EXTICR1_EXTI0_PC ((uint16_t)0x0002) /* PC[0] pin */
#define AFIO_EXTICR1_EXTI0_PD ((uint16_t)0x0003) /* PD[0] pin */
#define AFIO_EXTICR1_EXTI1_PA ((uint16_t)0x0000) /* PA[1] pin */
#define AFIO_EXTICR1_EXTI1_PC ((uint16_t)0x0008) /* PC[1] pin */
#define AFIO_EXTICR1_EXTI1_PD ((uint16_t)0x000C) /* PD[1] pin */
#define AFIO_EXTICR1_EXTI2_PA ((uint16_t)0x0000) /* PA[2] pin */
#define AFIO_EXTICR1_EXTI2_PC ((uint16_t)0x0020) /* PC[2] pin */
#define AFIO_EXTICR1_EXTI2_PD ((uint16_t)0x0030) /* PD[2] pin */
#define AFIO_EXTICR1_EXTI3_PC ((uint16_t)0x0080) /* PC[3] pin */
#define AFIO_EXTICR1_EXTI3_PD ((uint16_t)0x00C0) /* PD[3] pin */
#define AFIO_EXTICR1_EXTI4_PC ((uint16_t)0x0200) /* PC[4] pin */
#define AFIO_EXTICR1_EXTI4_PD ((uint16_t)0x0300) /* PD[4] pin */
#define AFIO_EXTICR1_EXTI5_PC ((uint16_t)0x0800) /* PC[5] pin */
#define AFIO_EXTICR1_EXTI5_PD ((uint16_t)0x0C00) /* PD[5] pin */
#define AFIO_EXTICR1_EXTI6_PC ((uint16_t)0x2000) /* PC[6] pin */
#define AFIO_EXTICR1_EXTI6_PD ((uint16_t)0x3000) /* PD[6] pin */
#define AFIO_EXTICR1_EXTI7_PC ((uint16_t)0x8000) /* PC[7] pin */
#define AFIO_EXTICR1_EXTI7_PD ((uint16_t)0xC000) /* PD[7] pin */
#endif
#if defined(CH32V10x) || defined(CH32V20x) || defined(CH32V30x)
@ -9507,7 +9389,7 @@ typedef enum
#define OB_RST_NoEN ((uint16_t)0x0018) /* Reset IO disable (PD7)*/
#define OB_RST_EN_DT12ms ((uint16_t)0x0010) /* Reset IO enable (PD7) and Ignore delay time 12ms */
#define OB_RST_EN_DT1ms ((uint16_t)0x0008) /* Reset IO enable (PD7) and Ignore delay time 1ms */
#define OB_RST_EN_DT128ms ((uint16_t)0x0000) /* Reset IO enable (PD7) and Ignore delay time 128ms */
#define OB_RST_EN_DT128us ((uint16_t)0x0000) /* Reset IO enable (PD7) and Ignore delay time 128us */
#define OB_STARTMODE_BOOT ((uint16_t)0x0020) /* Start in BOOT area */
#define OB_STARTMODE_USER ((uint16_t)0x0000) /* Start in user area */
@ -12586,6 +12468,349 @@ typedef volatile unsigned long *PUINT32V;
#endif
/* ch32x035_usbpd.h ----------------------------------------------------------*/
/* Register Bit Definition */
/* USBPD->CONFIG */
#define PD_FILT_ED (1<<0) /* PD pin input filter enable */
#define PD_ALL_CLR (1<<1) /* Clear all interrupt flags */
#if defined(CH32X03x)
#define CC_SEL (1<<2) /* Select PD communication port */
#define PD_DMA_EN (1<<3) /* Enable DMA for USBPD */
#define PD_RST_EN (1<<4) /* PD mode reset command enable */
#define WAKE_POLAR (1<<5) /* PD port wake-up level */
#elif defined(CH641)
#define CC_ALL_SEL (3<<2) /* Select PD communication port ALL*/
#define CC_SEL_Mask (3<<2) /* Clear PD communication port */
#define CC_SEL_1 (0<<2) /* Select PD communication port1 */
#define CC_SEL_2 (1<<2) /* Select PD communication port2 */
#define CC_SEL_3 (2<<2) /* Select PD communication port3 */
#define PD_DMA_EN (1<<4) /* Enable DMA for USBPD */
#define PD_RST_EN (1<<5) /* PD mode reset command enable */
#define WAKE_POLAR (1<<6) /* PD port wake-up level */
#endif
#define IE_PD_IO (1<<10) /* PD IO interrupt enable */
#define IE_RX_BIT (1<<11) /* Receive bit interrupt enable */
#define IE_RX_BYTE (1<<12) /* Receive byte interrupt enable */
#define IE_RX_ACT (1<<13) /* Receive completion interrupt enable */
#define IE_RX_RESET (1<<14) /* Reset interrupt enable */
#define IE_TX_END (1<<15) /* Transfer completion interrupt enable */
/* USBPD->CONTROL */
#define PD_TX_EN (1<<0) /* USBPD transceiver mode and transmit enable */
#define BMC_START (1<<1) /* BMC send start signal */
#define RX_STATE_0 (1<<2) /* PD received state bit 0 */
#define RX_STATE_1 (1<<3) /* PD received state bit 1 */
#define RX_STATE_2 (1<<4) /* PD received state bit 2 */
#define DATA_FLAG (1<<5) /* Cache data valid flag bit */
#define TX_BIT_BACK (1<<6) /* Indicates the current bit status of the BMC when sending the code */
#define BMC_BYTE_HI (1<<7) /* Indicates the current half-byte status of the PD data being sent and received */
/* USBPD->TX_SEL */
#define TX_SEL1 (0<<0)
#define TX_SEL1_SYNC1 (0<<0) /* 0-SYNC1 */
#define TX_SEL1_RST1 (1<<0) /* 1-RST1 */
#define TX_SEL2_Mask (3<<2)
#define TX_SEL2_SYNC1 (0<<2) /* 00-SYNC1 */
#define TX_SEL2_SYNC3 (1<<2) /* 01-SYNC3 */
#define TX_SEL2_RST1 (2<<2) /* 1x-RST1 */
#define TX_SEL3_Mask (3<<4)
#define TX_SEL3_SYNC1 (0<<4) /* 00-SYNC1 */
#define TX_SEL3_SYNC3 (1<<4) /* 01-SYNC3 */
#define TX_SEL3_RST1 (2<<4) /* 1x-RST1 */
#define TX_SEL4_Mask (3<<6)
#define TX_SEL4_SYNC2 (0<<6) /* 00-SYNC2 */
#define TX_SEL4_SYNC3 (1<<6) /* 01-SYNC3 */
#define TX_SEL4_RST2 (2<<6) /* 1x-RST2 */
/* USBPD->STATUS */
#define BMC_AUX_Mask (3<<0) /* Clear BMC auxiliary information */
#define BMC_AUX_INVALID (0<<0) /* 00-Invalid */
#define BMC_AUX_SOP0 (1<<0) /* 01-SOP0 */
#define BMC_AUX_SOP1_HRST (2<<0) /* 10-SOP1 hard reset */
#define BMC_AUX_SOP2_CRST (3<<0) /* 11-SOP2 cable reset */
#define BUF_ERR (1<<2) /* BUFFER or DMA error interrupt flag */
#define IF_RX_BIT (1<<3) /* Receive bit or 5bit interrupt flag */
#define IF_RX_BYTE (1<<4) /* Receive byte or SOP interrupt flag */
#define IF_RX_ACT (1<<5) /* Receive completion interrupt flag */
#define IF_RX_RESET (1<<6) /* Receive reset interrupt flag */
#define IF_TX_END (1<<7) /* Transfer completion interrupt flag */
/* USBPD->PORT_CC1 */
/* USBPD->PORT_CC2 */
#if defined(CH641)
/* USBPD->PORT_CC3 */
#define CC_CMPO (1<<0) /* CC port comparator analog input */
#elif defined(CH32X03x)
#define PA_CC_AI (1<<0) /* CC port comparator analogue input */
#endif
#define CC_PD (1<<1) /* CC port pull-down resistor enable */
#define CC_PU_Mask (3<<2) /* Clear CC port pull-up current */
#define CC_NO_PU (0<<2) /* 00-Prohibit pull-up current */
#define CC_PU_330 (1<<2) /* 01-330uA */
#define CC_PU_180 (2<<2) /* 10-180uA */
#define CC_PU_80 (3<<2) /* 11-80uA */
#define CC_LVE (1<<4) /* CC port output low voltage enable */
#if defined(CH32X03x)
#define CC_CMP_Mask (7<<5) /* Clear CC_CMP*/
#define CC_NO_CMP (0<<5) /* 000-closed */
#define CC_CMP_22 (2<<5) /* 010-0.22V */
#define CC_CMP_45 (3<<5) /* 011-0.45V */
#define CC_CMP_55 (4<<5) /* 100-0.55V */
#define CC_CMP_66 (5<<5) /* 101-0.66V */
#define CC_CMP_95 (6<<5) /* 110-0.95V */
#define CC_CMP_123 (7<<5) /* 111-1.23V */
#define USBPD_IN_HVT (1<<9)
#elif defined(CH641)
#define CC_CVS_Mask (3<<5) /* clear CC_CVS*/
#define CC_CVS_55 (0<<5) /* 00-0.55V */
#define CC_CVS_22 (1<<5) /* 01-0.22V */
#define CC_CVS_66 (2<<5) /* 10-0.66V */
#define CC_CVS_123 (3<<5) /* 11-1.23V */
#define CC_CE (1<<7) /* Enable the voltage comparator on port CC */
#endif
#if defined(CH32X03x)
/*********************************************************
* PD pin PC14/PC15 high threshold input mode:
* 1-High threshold input (2.2V typical), to reduce the I/O power consumption during PD communication
* 0-Normal GPIO threshold input
* *******************************************************/
#define USBPD_PHY_V33 (1<<8)
/**********************************************************
* PD transceiver PHY pull-up limit configuration bits:
* 1-Direct use of VDD for GPIO applications or PD applications with VDD voltage of 3.3V
* 0-LDO buck enabled, limited to approx 3.3V, for PD applications with VDD more than 4V
* ********************************************************/
#elif defined(CH641)
/*********************************************************
* PD pin PB0/PB1/PB9 high threshold input mode:
* 1-High threshold input (2.2V typical), to reduce the I/O power consumption during PD communication
* 0-Normal GPIO threshold input
* *******************************************************/
#define USBPD_HVT (1<<19)
#endif
/* Control Message Types */
#define DEF_TYPE_RESERVED 0x00
#define DEF_TYPE_GOODCRC 0x01 /* Send By: Source,Sink,Cable Plug */
#define DEF_TYPE_GOTOMIN 0x02 /* Send By: Source */
#define DEF_TYPE_ACCEPT 0x03 /* Send By: Source,Sink,Cable Plug */
#define DEF_TYPE_REJECT 0x04 /* Send By: Source,Sink,Cable Plug */
#define DEF_TYPE_PING 0x05 /* Send By: Source */
#define DEF_TYPE_PS_RDY 0x06 /* Send By: Source,Sink */
#define DEF_TYPE_GET_SRC_CAP 0x07 /* Send By: Sink,DRP */
#define DEF_TYPE_GET_SNK_CAP 0x08 /* Send By: Source,DRP */
#define DEF_TYPE_DR_SWAP 0x09 /* Send By: Source,Sink */
#define DEF_TYPE_PR_SWAP 0x0A /* Send By: Source,Sink */
#define DEF_TYPE_VCONN_SWAP 0x0B /* Send By: Source,Sink */
#define DEF_TYPE_WAIT 0x0C /* Send By: Source,Sink */
#define DEF_TYPE_SOFT_RESET 0x0D /* Send By: Source,Sink */
#define DEF_TYPE_DATA_RESET 0x0E /* Send By: Source,Sink */
#define DEF_TYPE_DATA_RESET_CMP 0x0F /* Send By: Source,Sink */
#define DEF_TYPE_NOT_SUPPORT 0x10 /* Send By: Source,Sink,Cable Plug */
#define DEF_TYPE_GET_SRC_CAP_EX 0x11 /* Send By: Sink,DRP */
#define DEF_TYPE_GET_STATUS 0x12 /* Send By: Source,Sink */
#define DEF_TYPE_GET_STATUS_R 0X02 /* ext=1 */
#define DEF_TYPE_FR_SWAP 0x13 /* Send By: Sink */
#define DEF_TYPE_GET_PPS_STATUS 0x14 /* Send By: Sink */
#define DEF_TYPE_GET_CTY_CODES 0x15 /* Send By: Source,Sink */
#define DEF_TYPE_GET_SNK_CAP_EX 0x16 /* Send By: Source,DRP */
#define DEF_TYPE_GET_SRC_INFO 0x17 /* Send By: Sink,DRP */
#define DEF_TYPE_GET_REVISION 0x18 /* Send By: Source,Sink */
/* Data Message Types */
#define DEF_TYPE_SRC_CAP 0x01 /* Send By: Source,Dual-Role Power */
#define DEF_TYPE_REQUEST 0x02 /* Send By: Sink */
#define DEF_TYPE_BIST 0x03 /* Send By: Tester,Source,Sink */
#define DEF_TYPE_SNK_CAP 0x04 /* Send By: Sink,Dual-Role Power */
#define DEF_TYPE_BAT_STATUS 0x05 /* Send By: Source,Sink */
#define DEF_TYPE_ALERT 0x06 /* Send By: Source,Sink */
#define DEF_TYPE_GET_CTY_INFO 0x07 /* Send By: Source,Sink */
#define DEF_TYPE_ENTER_USB 0x08 /* Send By: DFP */
#define DEF_TYPE_EPR_REQUEST 0x09 /* Send By: Sink */
#define DEF_TYPE_EPR_MODE 0x0A /* Send By: Source,Sink */
#define DEF_TYPE_SRC_INFO 0x0B /* Send By: Source */
#define DEF_TYPE_REVISION 0x0C /* Send By: Source,Sink,Cable Plug */
#define DEF_TYPE_VENDOR_DEFINED 0x0F /* Send By: Source,Sink,Cable Plug */
/* Vendor Define Message Command */
#define DEF_VDM_DISC_IDENT 0x01
#define DEF_VDM_DISC_SVID 0x02
#define DEF_VDM_DISC_MODE 0x03
#define DEF_VDM_ENTER_MODE 0x04
#define DEF_VDM_EXIT_MODE 0x05
#define DEF_VDM_ATTENTION 0x06
#define DEF_VDM_DP_S_UPDATE 0x10
#define DEF_VDM_DP_CONFIG 0x11
/* PD Revision */
#define DEF_PD_REVISION_10 0x00
#define DEF_PD_REVISION_20 0x01
#define DEF_PD_REVISION_30 0x02
/* PD PHY Channel */
#if defined(CH32X03x)
#define DEF_PD_CC1 0x00
#define DEF_PD_CC2 0x01
#define PIN_CC1 GPIO_Pin_14
#define PIN_CC2 GPIO_Pin_15
#elif defined(CH641)
#define PIN_CC1 GPIO_Pin_0
#define PIN_CC2 GPIO_Pin_1
#define PIN_CC3 GPIO_Pin_9
#endif
/* PD Tx Status */
#define DEF_PD_TX_OK 0x00
#define DEF_PD_TX_FAIL 0x01
/* PDO INDEX */
#define PDO_INDEX_1 1
#define PDO_INDEX_2 2
#define PDO_INDEX_3 3
#define PDO_INDEX_4 4
#define PDO_INDEX_5 5
/******************************************************************************/
#define UPD_TMR_TX_48M (80-1) /* timer value for USB PD BMC transmittal @Fsys=48MHz */
#define UPD_TMR_RX_48M (120-1) /* timer value for USB PD BMC receiving @Fsys=48MHz */
#define UPD_TMR_TX_24M (40-1) /* timer value for USB PD BMC transmittal @Fsys=24MHz */
#define UPD_TMR_RX_24M (60-1) /* timer value for USB PD BMC receiving @Fsys=24MHz */
#define UPD_TMR_TX_12M (20-1) /* timer value for USB PD BMC transmittal @Fsys=12MHz */
#define UPD_TMR_RX_12M (30-1) /* timer value for USB PD BMC receiving @Fsys=12MHz */
#define MASK_PD_STAT 0x03 /* Bit mask for current PD status */
#define PD_RX_SOP0 0x01 /* SOP0 received */
#define PD_RX_SOP1_HRST 0x02 /* SOP1 or Hard Reset received */
#define PD_RX_SOP2_CRST 0x03 /* SOP2 or Cable Reset received */
#define UPD_SOP0 ( TX_SEL1_SYNC1 | TX_SEL2_SYNC1 | TX_SEL3_SYNC1 | TX_SEL4_SYNC2 ) /* SOP1 */
#define UPD_SOP1 ( TX_SEL1_SYNC1 | TX_SEL2_SYNC1 | TX_SEL3_SYNC3 | TX_SEL4_SYNC3 ) /* SOP2 */
#define UPD_SOP2 ( TX_SEL1_SYNC1 | TX_SEL2_SYNC3 | TX_SEL3_SYNC1 | TX_SEL4_SYNC3 ) /* SOP3 */
#define UPD_HARD_RESET ( TX_SEL1_RST1 | TX_SEL2_RST1 | TX_SEL3_RST1 | TX_SEL4_RST2 ) /* Hard Reset*/
#define UPD_CABLE_RESET ( TX_SEL1_RST1 | TX_SEL2_SYNC1 | TX_SEL3_RST1 | TX_SEL4_SYNC3 ) /* Cable Reset*/
#define bCC_CMP_22 0X01
#define bCC_CMP_45 0X02
#define bCC_CMP_55 0X04
#define bCC_CMP_66 0X08
#define bCC_CMP_95 0X10
#define bCC_CMP_123 0X20
#define bCC_CMP_220 0X40
/******************************************************************************/
/* PD State Machine */
typedef enum
{
STA_IDLE = 0, /* 0: No task status */
STA_DISCONNECT, /* 1: Disconnection */
STA_SRC_CONNECT, /* 2: SRC connect */
STA_RX_SRC_CAP_WAIT, /* 3: Waiting to receive SRC_CAP */
STA_RX_SRC_CAP, /* 4: SRC_CAP received */
STA_TX_REQ, /* 5: Send REQUEST */
STA_RX_ACCEPT_WAIT, /* 6: Waiting to receive ACCEPT */
STA_RX_ACCEPT, /* 7: ACCEPT received */
STA_RX_REJECT, /* 8: REJECT received */
STA_RX_PS_RDY_WAIT, /* 9: Waiting to receive PS_RDY */
STA_RX_PS_RDY, /* 10: PS_RDY received */
STA_SINK_CONNECT, /* 11: SNK access */
STA_TX_SRC_CAP, /* 12: Send SRC_CAP */
STA_RX_REQ_WAIT, /* 13: Waiting to receive REQUEST */
STA_RX_REQ, /* 14: REQUEST received */
STA_TX_ACCEPT, /* 15: Send ACCEPT */
STA_TX_REJECT, /* 16: Send REJECT */
STA_ADJ_VOL, /* 17: Adjustment of output voltage and current */
STA_TX_PS_RDY, /* 18: Send PS_RDY */
STA_TX_DR_SWAP, /* 19: Send DR_SWAP */
STA_RX_DR_SWAP_ACCEPT, /* 20: Waiting to receive the answer ACCEPT from DR_SWAP */
STA_TX_PR_SWAP, /* 21: Send PR_SWAP */
STA_RX_PR_SWAP_ACCEPT, /* 22: Waiting to receive the answer ACCEPT from PR_SWAP */
STA_RX_PR_SWAP_PS_RDY, /* 23: Waiting to receive the answer PS_RDY from PR_SWAP */
STA_TX_PR_SWAP_PS_RDY, /* 24: Send answer PS_RDY for PR_SWAP */
STA_PR_SWAP_RECON_WAIT, /* 25: Wait for PR_SWAP before reconnecting */
STA_SRC_RECON_WAIT, /* 26: Waiting for SRC to reconnect */
STA_SINK_RECON_WAIT, /* 27: Waiting for SNK to reconnect */
STA_RX_APD_PS_RDY_WAIT, /* 28: Waiting for PS_RDY from the receiving adapter */
STA_RX_APD_PS_RDY, /* 29: PS_RDY received from the adapter */
STA_MODE_SWITCH, /* 30: Mode switching */
STA_TX_SOFTRST, /* 31: Sending a software reset */
STA_TX_HRST, /* 32: Send hardware reset */
STA_PHY_RST, /* 33: PHY reset */
STA_APD_IDLE_WAIT, /* 34: Waiting for the adapter to become idle */
} CC_STATUS;
/******************************************************************************/
/* PD Message Header Struct */
typedef union
{
struct _Message_Header
{
UINT8 MsgType: 5; /* Message Type */
UINT8 PDRole: 1; /* 0-UFP; 1-DFP */
UINT8 SpecRev: 2; /* 00-Rev1.0; 01-Rev2.0; 10-Rev3.0; */
UINT8 PRRole: 1; /* 0-Sink; 1-Source */
UINT8 MsgID: 3;
UINT8 NumDO: 3;
UINT8 Ext: 1;
}Message_Header;
UINT16 Data;
}_Message_Header;
/******************************************************************************/
/* Bit definition */
typedef union
{
struct _BITS_
{
UINT8 Msg_Recvd: 1; /* Notify the main program of the receipt of a PD packet */
UINT8 Connected: 1; /* PD Physical Layer Connected Flag */
UINT8 Stop_Det_Chk: 1; /* 0-Enable detection; 1-Disable disconnection detection */
UINT8 PD_Role: 1; /* 0-UFP; 1-DFP */
UINT8 PR_Role: 1; /* 0-Sink; 1-Source */
UINT8 Auto_Ack_PRRole: 1; /* Role used by auto-responder 0:SINK; 1:SOURCE */
UINT8 PD_Version: 1; /* PD version 0-PD2.0; 1-PD3.0 */
UINT8 VDM_Version: 1; /* VDM Version 0-1.0 1-2.0 */
UINT8 HPD_Connected: 1; /* HPD Physical Layer Connected Flag */
UINT8 HPD_Det_Chk: 1; /* 0-turn off HPD connection detection; 1-turn on HPD connection detection */
UINT8 CC_Sel_En: 1; /* 0-CC channel selection toggle enable; 1-CC channel selection toggle disable */
UINT8 CC_Sel_State: 1; /* 0-CC channel selection switches to 0; 1-CC channel selection switches to 1 */
UINT8 PD_Comm_Succ: 1; /* 0-PD communication unsuccessful; 1-PD communication successful; */
UINT8 Recv: 3;
}Bit;
UINT16 Bit_Flag;
}_BIT_FLAG;
/* PD control-related structures */
typedef struct _PD_CONTROL
{
CC_STATUS PD_State; /* PD communication status machine */
CC_STATUS PD_State_Last; /* PD communication status machine (last value) */
UINT8 Msg_ID; /* ID of the message sent */
UINT8 Det_Timer; /* PD connection status detection timing */
UINT8 Det_Cnt; /* Number of PD connection status detections */
UINT8 Det_Sel_Cnt; /* Number of SEL toggles for PD connection status detection */
UINT8 HPD_Det_Timer; /* HPD connection detection timing */
UINT8 HPD_Det_Cnt; /* HPD pin connection status detection count */
UINT16 PD_Comm_Timer; /* PD shared timing variables */
UINT8 ReqPDO_Idx; /* Index of the requested PDO, valid values 1-7 */
UINT16 PD_BusIdle_Timer; /* Bus Idle Time Timer */
UINT8 Mode_Try_Cnt; /* Number of retries for current mode, highest bit marks mode */
UINT8 Err_Op_Cnt; /* Exception operation count */
UINT8 Adapter_Idle_Cnt; /* Adapter communication idle timing */
_BIT_FLAG Flag; /* Flag byte bit definition */
}PD_CONTROL, *pPD_CONTROL;
/* ch32v00x_wwdg.h -----------------------------------------------------------*/

View file

@ -1,244 +1,226 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
const char *yes[] = {"SENTINEL_WILL_BE_REPLACED_BY_CMDLINE"}; // "CH32X03x", etc. element 0 is filled in by command-line
const char *no[] = {"CH32V10x", "CH32V30x", "CH32V20x", "CH32X03x", "CH32V003"};
const char * yes[] = { "SENTINEL_WILL_BE_REPLACED_BY_CMDLINE" }; // "CH32X03x", etc. element 0 is filled in by command-line
const char * no[] = { "CH32V10x", "CH32V30x", "CH32V20x", "CH32X03x", "CH32V003" };
char *WhitePull(const char **sti)
char * WhitePull( const char ** sti )
{
const char *st = *sti;
int len = 0;
while ((*st == ' ' || *st == '\t' || *st == '(') && *st)
{
st++;
}
const char *sts = st;
while (*st != ' ' && *st != '\t' && *st != '\n' && *st != ')' && *st != '(' && *st != 0)
{
st++;
len++;
}
if (*st == ')') { st++; }
char *ret = malloc(len + 1);
memcpy(ret, sts, len);
ret[len] = 0;
*sti = st;
return ret;
const char * st = *sti;
int len = 0;
while( ( *st == ' ' || *st == '\t' || *st == '(' ) && *st ) { st++; }
const char * sts = st;
while( *st != ' ' && *st != '\t' && *st != '\n' && *st != ')' && *st != '(' && *st != 0 ) { st++; len++; }
if( *st == ')' ) { st++; }
char * ret = malloc( len + 1 );
memcpy( ret, sts, len );
ret[len] = 0;
*sti = st;
return ret;
}
int NYI(const char *s)
int NYI( const char * s )
{
int ret = 2;
char *wp = WhitePull(&s);
int i;
for (i = 0; i < sizeof(yes) / sizeof(yes[0]); i++)
if (strcmp(yes[i], wp) == 0) ret = 1;
if (ret != 1)
for (i = 0; i < sizeof(no) / sizeof(no[0]); i++)
if (strcmp(no[i], wp) == 0) ret = 0;
free(wp);
return ret;
int ret = 2;
char * wp = WhitePull( &s );
int i;
for( i = 0; i < sizeof(yes)/sizeof(yes[0]); i++ )
if( strcmp( yes[i], wp ) == 0 ) ret = 1;
if( ret != 1 )
for( i = 0; i < sizeof(no)/sizeof(no[0]); i++ )
if( strcmp( no[i], wp ) == 0 ) ret = 0;
free( wp );
return ret;
}
int EvalSpec(const char *spl)
int EvalSpec( const char * spl )
{
int rsofar = 0;
int i;
int lastv = 0;
int lasto = -1;
int ret = 0;
int rsofar = 0;
int i;
int lastv = 0;
int lasto = -1;
int ret = 0;
cont:
char *wp = WhitePull(&spl);
int def = -1;
if (strcmp(wp, "defined") == 0) def = 1;
if (strcmp(wp, "!defined") == 0) def = 2;
if (def < 0) return 2;
char *wpn = WhitePull(&spl);
i = NYI(wpn);
// printf( "SPIN: %s/%s/%d/%d/%d\n", wp, wpn, i, def, lasto );
if (i == 2) return 2;
char * wp = WhitePull( &spl );
int def = -1;
if( strcmp( wp, "defined" ) == 0 ) def = 1;
if( strcmp( wp, "!defined" ) == 0 ) def = 2;
if( def < 0 ) return 2;
char * wpn = WhitePull( &spl );
i = NYI( wpn );
//printf( "SPIN: %s/%s/%d/%d/%d\n", wp, wpn, i, def, lasto );
if( i == 2 ) return 2;
if (def == 2) i = !i;
if( def == 2 ) i = !i;
if (lasto == 1)
{
ret = lastv || i;
}
else if (lasto == 2)
ret = lastv && i;
else
ret = i;
if( lasto == 1 )
{
ret = lastv || i;
}
else if( lasto == 2 )
ret = lastv && i;
else
ret = i;
char *wpa = WhitePull(&spl);
// printf( "WPA: \"%s\"\n", wpa );
lastv = ret;
lasto = -1;
// printf( "RET: %d\n", ret );
if (strcmp(wpa, "||") == 0)
{
lasto = 1;
goto cont;
}
else if (strcmp(wpa, "&&") == 0)
{
lasto = 2;
goto cont;
}
else
return ret;
char * wpa = WhitePull( &spl );
//printf( "WPA: \"%s\"\n", wpa );
lastv = ret;
lasto = -1;
//printf( "RET: %d\n", ret );
if( strcmp( wpa, "||" ) == 0 ) { lasto = 1; goto cont; }
else if( strcmp( wpa, "&&" ) == 0 ) { lasto = 2; goto cont; }
else return ret;
}
// 0 for no
// 1 for yes
// 2 for indeterminate
int NoYesInd(const char *preprocc)
int NoYesInd( const char * preprocc )
{
int ret;
int ofs = 0;
if (strncmp(preprocc, "#if ", 4) == 0) ofs = 4;
if (strncmp(preprocc, "#elif ", 6) == 0) ofs = 6;
if (ofs)
{
ret = EvalSpec(preprocc + ofs);
// printf( "SPEC: %d\n", ret );
}
else if (strncmp(preprocc, "#ifdef ", 7) == 0)
{
const char *ep = preprocc + 6;
char *wp = WhitePull(&ep);
ret = NYI(wp);
free(wp);
}
else if (strncmp(preprocc, "#ifndef ", 8) == 0)
{
const char *ep = preprocc + 6;
char *wp = WhitePull(&ep);
ret = NYI(wp);
if (ret < 2) ret = !ret;
free(wp);
}
else
ret = 2;
// printf( "%d-> %s\n", ret, preprocc );
return ret;
int ret;
int ofs = 0;
if( strncmp( preprocc, "#if ", 4 ) == 0 ) ofs = 4;
if( strncmp( preprocc, "#elif ", 6 ) == 0 ) ofs = 6;
if( ofs )
{
ret = EvalSpec( preprocc + ofs );
//printf( "SPEC: %d\n", ret );
}
else if( strncmp( preprocc, "#ifdef ", 7 ) == 0 )
{
const char * ep = preprocc + 6;
char * wp = WhitePull( &ep );
ret = NYI( wp );
free( wp );
}
else if( strncmp( preprocc, "#ifndef ", 8 ) == 0 )
{
const char * ep = preprocc + 6;
char * wp = WhitePull( &ep );
ret = NYI( wp );
if( ret < 2 ) ret = !ret;
free( wp );
}
else
ret = 2;
//printf( "%d-> %s\n", ret, preprocc );
return ret;
}
const char *sslineis(const char *line, const char *match)
const char * sslineis( const char * line, const char * match )
{
while (*line == ' ' || *line == '\t')
line++;
const char *linestart = line;
while (*line && *match == *line)
{
line++;
match++;
}
if (*match == 0)
return linestart;
else
return 0;
while( *line == ' ' || *line == '\t' ) line++;
const char * linestart = line;
while( *line && *match == *line ) { line++; match++; }
if( *match == 0 )
return linestart;
else
return 0;
}
int main(int argc, char **argv)
int main( int argc, char ** argv )
{
if (argc != 3)
{
fprintf(stderr, "Syntax: transition [#define to trigger on] [file to convert]\nNo'd architectures:\n");
int i;
for (i = 0; i < sizeof(no) / sizeof(no[0]); i++)
{
fprintf(stderr, "\t%s\n", no[i]);
}
return -1;
}
if( argc != 3 )
{
fprintf( stderr, "Syntax: transition [#define to trigger on] [file to convert]\nNo'd architectures:\n" );
int i;
for( i = 0; i < sizeof(no)/sizeof(no[0]); i++ )
{
fprintf( stderr, "\t%s\n", no[i] );
}
return -1;
}
yes[0] = argv[1];
yes[0] = argv[1];
FILE *f = fopen(argv[2], "r");
if (!f)
{
fprintf(stderr, "Error: Could not open \"%s\"\n", argv[2]);
return -2;
}
char line[1024];
char *l;
FILE * f = fopen( argv[2], "r" );
if( !f )
{
fprintf( stderr, "Error: Could not open \"%s\"\n", argv[2] );
return -2;
}
char line[1024];
char * l;
int depth = 0;
// 0 = no
// 1 = yes
// 2 = indeterminate
// 3 = super no. (I.e. after a true #if clause)
int yesnoind[1024];
yesnoind[0] = 1;
int depth = 0;
while (l = fgets(line, sizeof(line) - 1, f))
{
const char *ss = 0;
int nyi = yesnoind[depth];
int waspre = 0;
// 0 = no
// 1 = yes
// 2 = indeterminate
// 3 = super no. (I.e. after a true #if clause)
int yesnoind[1024];
yesnoind[0] = 1;
if ((ss = sslineis(line, "#if ")) || (ss = sslineis(line, "#ifdef ")) || (ss = sslineis(line, "#ifndef ")))
{
waspre = 1;
// printf( "CHECK: %d/%s\n", depth, l );
nyi = NoYesInd(ss);
depth++;
yesnoind[depth] = nyi;
}
else if ((ss = sslineis(line, "#elif ")))
{
if (nyi != 2)
{
waspre = 1;
if (nyi == 1)
{
nyi = 3;
}
else
{
nyi = NoYesInd(ss);
}
// printf( "ELIF check: %s %d\n", ss, nyi );
yesnoind[depth] = nyi;
}
}
else if ((ss = sslineis(line, "#else")))
{
if (nyi != 2)
{
waspre = 1;
if (yesnoind[depth] == 1)
nyi = 3;
else
nyi = !yesnoind[depth];
yesnoind[depth] = nyi;
}
}
else if ((ss = sslineis(line, "#endif")))
{
waspre = 1;
depth--;
if (depth < 0)
{
fprintf(stderr, "UNTERMD IF\n");
}
}
while( l = fgets( line, sizeof(line)-1, f ) )
{
const char * ss = 0;
int nyi = yesnoind[depth];
int waspre = 0;
int thisv = nyi;
int i;
for (i = 0; i <= depth; i++)
{
// printf( "%d", yesnoind[i] );
if (yesnoind[i] == 0 || yesnoind[i] == 3) thisv = 0;
}
// printf( ">>%s", l );
if( (ss = sslineis( line, "#if " ) ) || (ss = sslineis( line, "#ifdef " ) ) || (ss = sslineis( line, "#ifndef " ) ) )
{
waspre = 1;
//printf( "CHECK: %d/%s\n", depth, l );
nyi = NoYesInd( ss );
depth++;
yesnoind[depth] = nyi;
}
else if( (ss = sslineis( line, "#elif " ) ) )
{
if( nyi != 2 )
{
waspre = 1;
if( nyi == 1 )
{
nyi = 3;
}
else
{
nyi = NoYesInd( ss );
}
//printf( "ELIF check: %s %d\n", ss, nyi );
yesnoind[depth] = nyi;
}
}
else if( (ss = sslineis( line, "#else" ) ) )
{
if( nyi != 2 )
{
waspre = 1;
if( yesnoind[depth] == 1 )
nyi = 3;
else
nyi = !yesnoind[depth];
yesnoind[depth] = nyi;
}
}
else if( (ss = sslineis( line, "#endif" ) ) )
{
waspre = 1;
depth--;
if( depth < 0 )
{
fprintf( stderr, "UNTERMD IF\n" );
}
}
if (thisv != 0 && thisv != 3 && (thisv != 1 || !waspre))
{
printf("%s", l);
}
}
int thisv = nyi;
int i;
for( i = 0; i <= depth; i++ )
{
//printf( "%d", yesnoind[i] );
if( yesnoind[i] == 0 || yesnoind[i] == 3 ) thisv = 0;
}
//printf( ">>%s", l );
if( thisv != 0 && thisv != 3 && ( thisv != 1 || !waspre ) )
{
printf( "%s", l );
}
}
}

View file

@ -0,0 +1,11 @@
This only works for the WCH LinkE in RISC-V mode. If your programmer has the BLUE light ON shortly after boot your programmer is in ARM mode.
Please follow instructions here to convert your programmer to RISC-V mode from ARM mode.
Basically press-and-hold the ModeS button while plugging in the USB.
Once powered, it will store that to default.
The blue light should be OFF.
Once you are in RISC-V mode, you can install this driver by right-clicking on the driver and saying install.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,190 @@
using namespace System.IO;
using namespace System.IO.Compression.FileSystem;
[CmdletBinding(DefaultParameterSetName = 'DestPreset')]
param (
# Uses one of the preset locations to install to
[Parameter(ParameterSetName = 'DestPreset', Position = 0)]
[ValidateSet('User', 'System')]
[string]$Destination = 'User',
# If a preset location is not used, then the user must specify a path to install to
[Parameter(ParameterSetName = 'DestPath', Position = 0, Mandatory = $true)]
[string]$Path,
# If enabled, will prevent modification of your PATH environment variable
[switch]$NoPath,
# If enabled, will not install our make
[switch]$NoMake,
# Skips all prompts and just installs, mainly intended for use if we need to re-run as admin.
[switch]$SkipPrompts
);
$ErrorActionPreference = 'Stop';
[string] $XpackVersion = '14.2.0-3';
[string] $XpackEdition = 'riscv-none-elf-gcc';
[string] $XpackArch = 'win32-x64';
[string] $XpackNameEd = "xpack-$XpackEdition";
[string] $XpackNameEdVer = "$XpackNameEd-$XpackVersion";
[string] $XpackNameEdVerArch = "$XpackNameEdVer-$XpackArch"
[string] $XpackDownloadFilename = "$XpackNameEdVerArch.zip";
[string] $XpackDownloadURL = "https://github.com/xpack-dev-tools/$XpackEdition-xpack/releases/download/v$XpackVersion/$XpackDownloadFilename";
[string] $MakeVersion = '4.4.1';
[string] $MakeEdition = 'without-guile';
[string] $MakeArch = 'w32';
[string] $MakeName = "make-$MakeVersion-$MakeEdition"
[string] $MakeNameFull = "$MakeName-$MakeArch";
[string] $MakeDownloadFilename = "$MakeNameFull-bin.zip";
[string] $MakeDownloadURL = "https://sourceforge.net/projects/ezwinports/files/$MakeDownloadFilename/download";
[EnvironmentVariableTarget] $PathScope = [EnvironmentVariableTarget]::User;
[string] $TempFolder = [Path]::GetTempPath();
[bool] $NoClearTemp = $false;
if ($PSCmdlet.ParameterSetName -EQ 'DestPreset')
{
if ($Destination -EQ 'User') { $Path = Join-Path $ENV:LocalAppData $XpackNameEd; }
elseif ($Destination -EQ 'System')
{
$Path = Join-Path $ENV:ProgramFiles $XpackNameEd;
$PathScope = [EnvironmentVariableTarget]::Machine;
}
else { throw 'Unknown Preset Destination'; }
}
if ([string]::IsNullOrEmpty($Path)) { throw 'Destination path must be provided'; }
[string] $TempFolder = Join-Path $TempFolder 'ch32fun';
[string] $XpackDownloadFilePath = Join-Path $TempFolder $XpackDownloadFilename;
[string] $XpackPath = Join-Path $Path $XpackVersion;
[string] $XpackBinPath = Join-Path $XpackPath 'bin';
[string] $MakeDownloadFilePath = Join-Path $TempFolder $MakeDownloadFilename;
Write-Host "${XpackNameEdVer}:";
Write-Host " will be downloaded from " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $XpackDownloadURL -ForegroundColor 'DarkGreen';
Write-Host " to " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $XpackDownloadFilePath -ForegroundColor 'DarkGreen';
Write-Host " then installed to " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $XpackPath -ForegroundColor 'DarkGreen';
Write-Host;
if (-NOT $NoMake)
{
Write-Host "${MakeName}:"
Write-Host " will be downloaded from " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $MakeDownloadURL -ForegroundColor 'DarkGreen';
Write-Host " to " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $MakeDownloadFilePath -ForegroundColor 'DarkGreen';
Write-Host " then installed to " -NoNewline -ForegroundColor 'DarkGray';
Write-Host $XpackBinPath -ForegroundColor 'DarkGreen';
Write-Host;
}
if ($NoPath) { Write-Host "Your environment variables will not be edited.`n"; }
else
{
Write-Host "xpack will be added to your " -NoNewline;
Write-Host $PathScope.ToString().ToLower() -ForegroundColor 'DarkGreen' -NoNewline;
Write-Host " PATH environment variable.`n";
}
if (-NOT $SkipPrompts)
{
[string] $Answer = Read-Host -Prompt "Is this correct? (y/n)";
if ($Answer -NE 'y') { Exit; }
}
try
{ # Check if we need elevation
[bool] $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator');
if ((-NOT $NoPath) -AND ($PathScope -EQ [EnvironmentVariableTarget]::Machine) -AND (-NOT $IsAdmin)) { throw; } # If we want to set the system PATH variable, we need to elevate
if (-NOT (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path -ErrorAction SilentlyContinue -ErrorVariable PermissionError | Out-Null; }
if ($PermissionError) { throw; }
[string] $TestFile = Join-Path $Path 'TestingFile.txt';
Set-Content -Path $TestFile -Value 'Checking permissions.' -ErrorAction SilentlyContinue -ErrorVariable PermissionError;
if ($PermissionError) { throw; }
Remove-Item -Path $TestFile -ErrorAction SilentlyContinue -ErrorVariable PermissionError;
if ($PermissionError) { throw; }
}
catch
{
Write-Host 'Administrator permissions are required, requesting elevation...';
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator'))
{
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -GE 6000)
{
$CommandLine = "-File `"$($MyInvocation.MyCommand.Path)`" -SkipPrompts";
if ($PSCmdlet.ParameterSetName -EQ 'DestPreset') { $CommandLine += " -Destination `"$Destination`""; }
else { $CommandLine += " -Path `"$Path`""; }
if ($NoPath) { $CommandLine += ' -NoPath'; }
if ($NoMake) { $CommandLine += ' -NoMake'; }
try { Start-Process -FilePath 'PowerShell.exe' -Verb RunAs -ArgumentList $CommandLine; }
catch { Write-Error 'Failed to elevate to administrator.'; Exit; }
}
}
else { Write-Error 'Could not access folder, but am already administrator!'; Exit; }
if (-NOT $NoPath) { Write-Host 'You may need to restart your terminal before changes apply.'; }
Exit;
}
[void](New-Item -Path $TempFolder -ItemType Directory -Force);
[void](New-Item -Path $Path -ItemType Directory -Force);
if ($PSVersionTable.PSVersion.Major -LT 7) { $ProgressPreference = 'SilentlyContinue'; } # Showing progress makes it download very slowly on old PowerShell https://github.com/PowerShell/PowerShell/issues/2138
Write-Host "Downloading $XpackDownloadFilename" -NoNewline;
try { Write-Host $(' ({0:F2} MB)...' -F ([long]::Parse((Invoke-WebRequest -UseBasicParsing -Uri $XpackDownloadURL -Method Head).Headers['Content-Length']) / (1024.0 * 1024.0))); }
catch { Write-Host '...'; }
Invoke-WebRequest -UseBasicParsing -Uri $XpackDownloadURL -OutFile $XpackDownloadFilePath;
if (-NOT $NoMake)
{
Write-Host "Downloading $MakeDownloadFilename...";
Invoke-WebRequest -UseBasicParsing -UserAgent 'Wget' -Uri $MakeDownloadURL -OutFile $MakeDownloadFilePath;
}
Write-Host "Extracting $XpackDownloadFilename...";
[string] $XpackTempPath = Join-Path $TempFolder 'xpath';
if (Test-Path $XpackTempPath) { Remove-Item -Recurse -Force $XpackTempPath; }
Expand-Archive $XpackDownloadFilePath -DestinationPath $XpackTempPath;
if (-NOT (Test-Path $XpackPath)) { [void](New-Item -Path $XpackPath -ItemType Directory -Force); }
Copy-Item -Path $(Join-Path $(Join-Path $XpackTempPath $XpackNameEdVer) '*') -Destination $XpackPath -Recurse -Force;
if (-NOT $NoMake)
{
[string] $MakeTempPath = Join-Path $TempFolder 'make';
Write-Host "Extracting $MakeDownloadFilename...";
Expand-Archive -Force $MakeDownloadFilePath -DestinationPath $MakeTempPath;
Move-Item -Force -Path $(Join-Path $MakeTempPath 'bin/make.exe') -Destination $XpackBinPath;
}
if (-NOT $NoClearTemp)
{
Write-Host "Deleting $TempFolder...";
Remove-Item $TempFolder -Force -Recurse;
}
if (-NOT $NoPath)
{
Write-Host "Adding to $PathScope PATH environment variable...";
$CurrentPATH = [Environment]::GetEnvironmentVariable('PATH', $PathScope);
if ([string]::IsNullOrWhitespace($CurrentPATH)) { Write-Error 'Could not retrieve the current PATH, not editing'; Exit; }
if ($CurrentPATH.Contains($XpackBinPath.TrimEnd(('\', '/')))) # If the install dir is on the path, regardless of trailing slash or not
{
Write-Host ' It looks like this xpack installation is already in your user PATH, so it will not be edited.';
}
else {
$NewPATH = "$CurrentPATH;$XpackBinPath";
[Environment]::SetEnvironmentVariable('PATH', $NewPATH, $PathScope);
Write-Host ' You may need to restart your terminal before you can use xpack gcc.';
}
# Detect GitHub Actions runner and export for future steps
if ($env:GITHUB_ACTIONS -eq 'true') {
Write-Host "Detected GitHub Actions runner. Prepending xPack bin to PATH for workflow steps..."
Add-Content -Path $env:GITHUB_ENV -Value "PATH=$XpackBinPath`:$env:PATH"
}
}
Write-Host 'Finished!';

View file

@ -0,0 +1,37 @@
# Minichlink live command server
Minichlink now supports a command server on port 4444.
Currently, there are only two commands implemented:
- `-s` Set/Write command
- `-m` Read command
The format of these commands is the same as the command line interface commands with the same name:
```sh
-s [debug register] [value]
-m [debug register]
```
> [!WARNING]
> All values MUST be expressed in hexadecimal:
```sh
-s 0x20 0xc02a717d
```
Multiple commands can be chained together, the results of the read operations will be returned as a key value pair with the key being the register address:
```sh
04: 1234bees5
04: f00cac1a
```
The QingKe V4 Processor manual describes how to build these commands, but there are also 4 example scripts that you can use:
```sh
./read.sh dma_count # reads contents of memory of `debug_count` symbol
./write.sh dma_count 123 # writes the value 123 at `dma_count` symbol address
./batch_read.sh dma_buffer # reads the contents of the symbol `dma_buffer`
# size is deduced automatically, but can also be specified with a second argument
./plot.py dma_count # continuously read a value an plot it
```
> [!WARNING]
> All of these scripts are examples, not tools, treat appropriately

View file

@ -0,0 +1,64 @@
#!/usr/bin/env bash
SYMBOL=$1
COUNT=$2
if [ -z "$SYMBOL" ]; then
echo "Usage: $0 <symbol> [<count>]"
exit 1
fi
echo "Searching for symbol: '$SYMBOL'"
ADDR="$(riscv64-unknown-elf-objdump -t *.elf | grep $SYMBOL | awk '{print "0x"$1}')"
if [ -z "$COUNT" ]; then
WORDS=$(riscv64-unknown-elf-objdump -t *.elf | grep $SYMBOL | awk '{print int(("0x"$5 + 3) / 4)}')
else
WORDS=$COUNT
fi
if [ -z "$ADDR" ]; then
echo "Error: Symbol '$SYMBOL' not found in the object file."
exit 1
fi
if ! [[ $ADDR =~ ^0x[0-9a-fA-F]+$ ]]; then
echo "Error: Address must be in hexadecimal format (e.g., 0x20000000)."
exit 1
fi
echo "Reading $WORDS words from $SYMBOL@$ADDR"
commands=" -s 0x10 0x80000001" # Make the debug module work properly.
# Prelude
commands+=" -s 0x20 0xc02a717d" # add sp,sp,-16; sw a0,0(sp)
commands+=" -s 0x21 0x9002c22e" # sw a1,4(sp); ebreak
commands+=" -s 0x17 0x00270000" # Abstact cmd exec progbuf
# commands+=" -m 0x16" # Read ABSTRACTCS
# Batch Read
commands+=" -s 0x20 0x05914188" # lw a0,0(a1); add a1,a1,4
commands+=" -s 0x21 0x90029002" # ebreak
commands+=" -s 0x04 $ADDR" # Write address 0x20000000 to DATA0
commands+=" -s 0x17 0x0027100b" # Abstract cmd data0->x11 and exec progbuf
# commands+=" -m 0x16" # Read ABSTRACTCS
for i in $(seq 0 $((WORDS - 2))); do
commands+=" -s 0x17 0x0026100a" # Abstract exec and x10->data0
# commands+=" -m 0x16" # Read ABSTRACTCS
commands+=" -m 0x04" # Read DATA0
done
commands+=" -s 0x17 0x0022100a" # Abstract cmd x10->data0
# commands+=" -m 0x16" # Read ABSTRACTCS
commands+=" -m 0x04" # Read DATA0
# Postlude
commands+=" -s 0x20 0x45924502" # lw a0,0(sp); lw a1,4(sp)
commands+=" -s 0x21 0x90026141" # add sp,sp,16; ebreak
# commands+=" -s 0x17 0x00270000" # Abstact cmd exec progbuf
commands+=" -s 0x10 0x40000001" # Resume(1<<30) without reset(1<<0)
echo $commands | nc localhost 4444

102
inc/misc/minichlink-live/plot.py Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import sys
import subprocess
import socket
PERIOD_MS = 1000 / 30
SAMPLES = 200
symbol = sys.argv[1]
fig, ax = plt.subplots()
xdata, ydata = range(SAMPLES), [0] * SAMPLES
(ln,) = plt.plot([], [])
# find elf in current directory
result = subprocess.run(["find", ".", "-name", "*.elf"], capture_output=True, text=True)
if result.returncode != 0:
print(f"Error: {result.stderr.strip()}")
sys.exit(1)
elf = result.stdout.splitlines()[0]
print(f"Using ELF file: {elf}")
result = subprocess.run(
["riscv64-unknown-elf-objdump", "-t", elf], capture_output=True, text=True
)
if result.returncode != 0:
print(f"Error: {result.stderr.strip()}")
sys.exit(1)
symbol_address = -1
for line in result.stdout.splitlines()[4:]:
name = line.split()[-1]
if symbol == name:
symbol_address = int(line.split()[0], 16)
break
if symbol_address == -1:
print(f"Error: Symbol '{symbol}' not found in ELF file.")
sys.exit(1)
print(f"Symbol '{symbol}' found at address: {symbol_address:#x}")
commands = " -s 0x10 0x80000001" # Make the debug module work properly.
commands += " -s 0x20 0xc02a717d" # add sp,sp,-16; sw a0,0(sp)
commands += " -s 0x21 0xc432c22e" # sw a1,4(sp); sw a2,8(sp)
commands += " -s 0x22 0xe0000637" # lui a2,0xe0000
commands += " -s 0x23 0x0f460613" # add a2,a2,244 # e00000f4 <data0>
commands += " -s 0x24 0x410c4208" # lw a0,0(a2); lw a1,0(a0)
commands += " -s 0x25 0x4502c20c" # sw a1,0(a2); lw a0,0(sp)
commands += " -s 0x26 0x46224592" # lw a1,4(sp); lw a2,8(sp)
commands += " -s 0x27 0x90026141" # add sp,sp,16; ebreak
commands += f" -s 0x04 {symbol_address:#x}" # Write address 0x20000000 to DATA0
commands += " -s 0x17 0x00270000" # Abstact cmd exec progbuf
commands += " -m 0x16" # Read ABSTRACTCS
commands += " -m 0x04" # Read DATA0
commands += " -s 0x10 0x40000001" # Resume(1<<30) without reset(1<<0)
command = commands.encode()
def read_memory():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 4444))
s.send(command)
data = s.recv(1024).decode()
s.close()
return data
def get_value():
value = int(read_memory().splitlines()[-1].split()[-1], 16)
print(f"Received value: {value}")
return value
def init():
ax.set_xlim(0, SAMPLES)
ax.set_ylim(0, 1024)
return (ln,)
def update(frame):
global ydata
value = get_value()
ydata = ydata[1:] + [value]
ln.set_data(xdata, ydata)
return (ln,)
ani = FuncAnimation(
fig, update, frames=SAMPLES, init_func=init, blit=True, interval=PERIOD_MS
)
plt.show()

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
SYMBOL=$1
if [ -z "$SYMBOL" ]; then
echo "Usage: $0 <symbol>"
exit 1
fi
echo "Searching for symbol: '$SYMBOL'"
ADDR=$(riscv64-unknown-elf-objdump -t *.elf | grep $SYMBOL | awk '{print "0x"$1}')
if [ -z "$ADDR" ]; then
echo "Error: Symbol '$SYMBOL' not found in the object file."
exit 1
fi
if ! [[ $ADDR =~ ^0x[0-9a-fA-F]+$ ]]; then
echo "Error: Address must be in hexadecimal format (e.g., 0x20000000)."
exit 1
fi
echo "Reading $SYMBOL@$ADDR"
commands=" -s 0x10 0x80000001" # Make the debug module work properly.
commands+=" -s 0x20 0xc02a717d" # add sp,sp,-16; sw a0,0(sp)
commands+=" -s 0x21 0xc432c22e" # sw a1,4(sp); sw a2,8(sp)
commands+=" -s 0x22 0xe0000637" # lui a2,0xe0000
commands+=" -s 0x23 0x0f460613" # add a2,a2,244 # e00000f4 <data0>
commands+=" -s 0x24 0x410c4208" # lw a0,0(a2); lw a1,0(a0)
commands+=" -s 0x25 0x4502c20c" # sw a1,0(a2); lw a0,0(sp)
commands+=" -s 0x26 0x46224592" # lw a1,4(sp); lw a2,8(sp)
commands+=" -s 0x27 0x90026141" # add sp,sp,16; ebreak
commands+=" -s 0x04 $ADDR" # Write address 0x20000000 to DATA0
commands+=" -s 0x17 0x00270000" # Abstact cmd exec progbuf
commands+=" -m 0x16" # Read ABSTRACTCS
commands+=" -m 0x04" # Read DATA0
commands+=" -s 0x10 0x40000001" # Resume(1<<30) without reset(1<<0)
echo $commands | nc localhost 4444

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
SYMBOL=$1
VAL=$2
if [ -z "$SYMBOL" ] || [ -z "$VAL" ]; then
echo "Usage: $0 <symbol> <value>"
exit 1
fi
echo "Searching for symbol: '$SYMBOL'"
ADDR="0x$(riscv64-unknown-elf-objdump -t *.elf | grep $SYMBOL | awk '{print $1}')"
if [ -z "$ADDR" ]; then
echo "Error: Symbol '$SYMBOL' not found in the object file."
exit 1
fi
if ! [[ $ADDR =~ ^0x[0-9a-fA-F]+$ ]]; then
echo "Error: Address must be in hexadecimal format (e.g., 0x20000000)."
exit 1
fi
# Convert the value to hexadecimal format
if ! [[ $VAL =~ ^0x[0-9a-fA-F]+$ ]]; then
VAL="0x$(printf '%x' "$VAL")"
fi
echo "Setting $SYMBOL@$ADDR -> $VAL"
commands=" -s 0x10 0x80000001" # Make the debug module work properly.
commands+=" -s 0x20 0x0072a023" # Write wcode of sw x7,0(x5)
commands+=" -s 0x21 0x00100073" # Write wcode of ebreak
commands+=" -s 0x04 $ADDR" # Write address 0x20000000 to DATA0
commands+=" -s 0x17 0x00271005" # Abstract cmd data0->x5 and exec progbuf
commands+=" -s 0x04 $VAL" # Write value to DATA0
commands+=" -s 0x17 0x00271007" # Abstract cmd data0->x7
commands+=" -m 0x16" # Read ABSTRACTCS
commands+=" -s 0x10 0x40000001" # Resume(1<<30) without reset(1<<0)
echo $commands | nc localhost 4444

23
inc/misc/tests/Makefile Normal file
View file

@ -0,0 +1,23 @@
all : ci
EXAMPLES := $(wildcard ../../examples/*/.) $(wildcard ../../examples_v10x/*/.) $(wildcard ../../examples_v20x/*/.) $(wildcard ../../examples_v30x/*/.) $(wildcard ../../examples_x035/*/.)
.PHONY: ci tests all $(EXAMPLES) clean
results :
mkdir -p results
$(EXAMPLES) : results
echo $(shell basename $(realpath $(lastword $@)))
($(MAKE) -C $@ build > results/$(subst .,,$(subst /,_,$@)).txt 2> results/$(subst .,,$(subst /,_,$@)).warning && echo "success" > results/$(subst .,,$(subst /,_,$@)).result) || echo "failure" > results/$(subst .,,$(subst /,_,$@)).result
echo $(shell basename $(realpath $(lastword $@))).bin > results/$(subst .,,$(subst /,_,$@)).stat
sha1sum $@/$(shell basename $(realpath $(lastword $@))).bin | cut -d' ' -f 1 >> results/$(subst .,,$(subst /,_,$@)).stat
wc --bytes $@/$(shell basename $(realpath $(lastword $@))).bin | cut -d' ' -f 1 >> results/$(subst .,,$(subst /,_,$@)).stat
tests : $(EXAMPLES)
ci : install tests
clean :
rm -rf results

File diff suppressed because it is too large Load diff