removed uneeded files.

This commit is contained in:
Jake Goodwin 2025-03-08 17:09:58 -08:00
parent 1f17f8dcdf
commit 32a3dcad12
25 changed files with 6911 additions and 20039 deletions

View file

@ -1,7 +0,0 @@
add_library(attic STATIC
temp_transition_helper.c
)
target_include_directories(attic PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)

File diff suppressed because it is too large Load diff

View file

@ -1,226 +0,0 @@
#include <stdio.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" };
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;
}
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 EvalSpec( const char * spl )
{
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;
if( def == 2 ) i = !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;
}
// 0 for no
// 1 for yes
// 2 for indeterminate
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;
}
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;
}
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;
}
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;
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;
while( l = fgets( line, sizeof(line)-1, f ) )
{
const char * ss = 0;
int nyi = yesnoind[depth];
int waspre = 0;
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" );
}
}
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,5 @@
{
"DisableFormat": true,
"SortIncludes": "Never"
}

View file

@ -1,26 +1,22 @@
// 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.
@ -98,78 +94,81 @@ 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)
//######## 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_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)
@ -202,72 +201,66 @@ static inline void GPIO_tim2_init();
#define GPIO_tim1_analogWrite(channel, value)
#define GPIO_tim2_analogWrite(channel, value)
// ######## internal function declarations
// ######## internal variables
//######## internal function declarations
// ######## preprocessor macros
//######## internal variables
//######## preprocessor macros
#define CONCAT(a, b) a ## b
#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)
@ -287,91 +280,74 @@ 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
//######## small function definitions, static inline
// ######## define requirements / maintenance defines
// ######## 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
@ -381,133 +357,137 @@ 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,10 +47,8 @@ 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
@ -67,14 +65,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)
@ -87,31 +85,25 @@ 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();
// ######## 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();
static inline void SPI_wait_RX_available();
//######## internal variables
// ######## 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)
@ -120,233 +112,249 @@ 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;
//######## 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);
// set prescaler
SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER<<3);
// 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 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
// 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;
// set data direction and configure data pins
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX)
SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex;
// CH32V003 is master
SPI1->CTLR1 |= SPI_Mode_Master;
// 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;
// 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);
#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);
// 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
}
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,44 +3,42 @@
/** 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\
@ -50,169 +48,172 @@
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;
// If we run multiple times with slightly different wait times, we can
// reduce the impact of the ADC's DNL.
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 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
@ -221,4 +222,3 @@ uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterat
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

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

File diff suppressed because it is too large Load diff

View file

@ -7,75 +7,74 @@
This is referenced in Chapter 22 USB Host/Device Controller (USBHD) of CH32FV2x_V3xRM.pdf
*/
#include <stdint.h>
#include "ch32fun.h"
#include "usb_defines.h"
#include "usb_config.h"
#include "usb_defines.h"
#include <stdint.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

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,14 +34,13 @@
// 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
@ -51,63 +50,59 @@ 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
@ -115,40 +110,39 @@ 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

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;
#endif
// Enable I2C
I2C1->CTLR1 |= I2C_CTLR1_PE;
// enable IRQ driven operation
NVIC_EnableIRQ(I2C1_EV_IRQn);
// set ACK mode
I2C1->CTLR1 |= I2C_CTLR1_ACK;
// initialize the state
ssd1306_i2c_irq_state = 0;
#endif
// Enable I2C
I2C1->CTLR1 |= I2C_CTLR1_PE;
// 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]);
// reset & initialize I2C
ssd1306_i2c_setup();
// report error
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
return 1;
// reset & initialize I2C
ssd1306_i2c_setup();
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,63 +135,67 @@ 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;
#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;
int32_t timeout;
#ifdef IRQ_DIAG
GPIOC->BSHR = (1<<(16+4));
GPIOC->BSHR = (1 << (3));
#endif
// exit
return 0;
// 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;
}
/*
@ -200,42 +204,43 @@ 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;
/* check for TXE */
if(STAR1 & I2C_STAR1_TXE)
{
/* check for remaining data */
if(ssd1306_i2c_send_sz--)
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
// read status, clear any events
STAR1 = I2C1->STAR1;
STAR2 = I2C1->STAR2;
/* 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));
/* check for TXE */
if (STAR1 & I2C_STAR1_TXE)
{
/* check for remaining data */
if (ssd1306_i2c_send_sz--)
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
}
}
/* 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;
}
}
#ifdef IRQ_DIAG
GPIOC->BSHR = (1<<(16+4));
GPIOC->BSHR = (1 << (16 + 4));
#endif
}
#else
@ -244,56 +249,61 @@ void I2C1_EV_IRQHandler(void)
*/
uint8_t ssd1306_i2c_send(uint8_t addr, const uint8_t *data, int sz)
{
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);
int32_t timeout;
// 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 not busy
timeout = TIMEOUT_MAX;
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--))
;
if (timeout == -1)
return ssd1306_i2c_error(0);
// 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);
// Set START condition
I2C1->CTLR1 |= I2C_CTLR1_START;
// 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 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);
// 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);
// send 7-bit address + write flag
I2C1->DATAR = addr << 1;
// set STOP condition
I2C1->CTLR1 |= I2C_CTLR1_STOP;
// we're happy
return 0;
// 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;
}
#endif
@ -302,20 +312,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);
}
/*
@ -323,51 +333,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,111 +26,115 @@
*/
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_IN funDigitalRead(SSD1306_I2C_BITBANG_SDA);
#define I2CSPEEDBASE 1
#define I2CDELAY_FUNC(x) ADD_N_NOPS(x*1)
//Delay_Us(x*1);
#define I2CDELAY_FUNC(x) ADD_N_NOPS(x * 1)
// Delay_Us(x*1);
static void ssd1306_i2c_sendstart()
{
SCL_HIGH
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SDA_LOW
I2CDELAY_FUNC( 1 * I2CSPEEDBASE );
SCL_LOW
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 );
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);
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;
}
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,7 +24,7 @@
#endif
#ifndef SSD1306_SCK_PIN
#define SSD1306_SCK_PIN PC5
#define SSD1306_SCK_PIN PC5
#endif
#ifndef SSD1306_BAUD_RATE_PRESCALER
@ -36,31 +36,31 @@
*/
uint8_t ssd1306_spi_init(void)
{
// Enable GPIOC and SPI
RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
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 );
// Enable GPIOC and SPI
RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
funDigitalWrite( SSD1306_RST_PIN, FUN_HIGH );
funDigitalWrite( SSD1306_CS_PIN, FUN_HIGH );
funDigitalWrite( SSD1306_DC_PIN, FUN_LOW );
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);
// 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;
funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
// enable SPI port
SPI1->CTLR1 |= CTLR1_SPE_Set;
// always succeed
return 0;
// 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;
}
/*
@ -68,9 +68,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,34 +78,35 @@ 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 );
// send data
while(sz--)
{
// wait for TXE
while(!(SPI1->STATR & SPI_STATR_TXE));
// 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_LOW);
// send data
while (sz--)
{
// wait for TXE
while (!(SPI1->STATR & SPI_STATR_TXE))
;
// 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;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -2,28 +2,28 @@
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**
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 +32,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,230 +46,241 @@ 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;
// 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, };
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;
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++;
}
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
while( place < 0 && ptr != end )
{
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
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;
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++;
}
// 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;
}
break;
}
#ifdef WSRAW
uint32_t ledval32bit = WS2812BLEDCallback( place++ );
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[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;
ptr += 8;
i += 8;
#else
// Use a LUT to figure out how we should set the SPI line.
uint32_t ledval24bit = WS2812BLEDCallback( place++ );
// 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 )
void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt));
void DMA1_Channel3_IRQHandler(void)
{
//GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
// 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
}
void WS2812BDMAStart( int leds )
void WS2812BDMAStart(int leds)
{
// 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();
// 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();
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 0 );
WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN, 0);
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
}
void WS2812BDMAInit( )
void WS2812BDMAInit()
{
// Enable DMA + Peripherals
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
// Enable DMA + Peripherals
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
// MOSI, Configure GPIO Pin
GPIOC->CFGLR &= ~(0xf<<(4*6));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6);
// MOSI, Configure GPIO Pin
GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
// 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)
// 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)
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
SPI1->HSCR = 1;
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
SPI1->HSCR = 1;
SPI1->CTLR1 |= CTLR1_SPE_Set;
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

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,59 +25,58 @@ void WS2812BSimpleSend( GPIO_TypeDef * port, int pin, uint8_t * data, int len_in
#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

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

View file

@ -1,226 +1,244 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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;
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;
// 0 = no
// 1 = yes
// 2 = indeterminate
// 3 = super no. (I.e. after a true #if clause)
int yesnoind[1024];
yesnoind[0] = 1;
while (l = fgets(line, sizeof(line) - 1, f))
{
const char *ss = 0;
int nyi = yesnoind[depth];
int waspre = 0;
while( l = fgets( line, sizeof(line)-1, f ) )
{
const char * ss = 0;
int nyi = yesnoind[depth];
int waspre = 0;
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( (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" );
}
}
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 );
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 );
}
}
if (thisv != 0 && thisv != 3 && (thisv != 1 || !waspre))
{
printf("%s", l);
}
}
}

View file

@ -1,11 +0,0 @@
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.

View file

@ -1,23 +0,0 @@
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