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 // 2023-06-26 recallmenot
//######## necessities // ######## necessities
// include guards // include guards
#ifndef CH32V003_GPIO_BR_H #ifndef CH32V003_GPIO_BR_H
#define CH32V003_GPIO_BR_H #define CH32V003_GPIO_BR_H
// includes // includes
#include <stdint.h> //uintN_t support
#include "../ch32fun/ch32fun.h" #include "../ch32fun/ch32fun.h"
#include <stdint.h> //uintN_t support
/*######## library description /*######## library description
This is a speedy and light GPIO library due to This is a speedy and light GPIO library due to
static inlining of most functions static inlining of most functions
compile-time abstraction compile-time abstraction
branchless where it counts branchless where it counts
*/ */
/*######## library usage and configuration /*######## library usage and configuration
first, enable the desired port. 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! enum GPIO_port_n
{
#define GPIOv_from_PORT_PIN( GPIO_port_n, pin ) GPIO_port_A = 0b00,
GPIO_port_C = 0b10,
enum GPIO_port_n { GPIO_port_D = 0b11,
GPIO_port_A = 0b00,
GPIO_port_C = 0b10,
GPIO_port_D = 0b11,
}; };
enum GPIO_pinModes { enum GPIO_pinModes
GPIO_pinMode_I_floating, {
GPIO_pinMode_I_pullUp, GPIO_pinMode_I_floating,
GPIO_pinMode_I_pullDown, GPIO_pinMode_I_pullUp,
GPIO_pinMode_I_analog, GPIO_pinMode_I_pullDown,
GPIO_pinMode_O_pushPull, GPIO_pinMode_I_analog,
GPIO_pinMode_O_openDrain, GPIO_pinMode_O_pushPull,
GPIO_pinMode_O_pushPullMux, GPIO_pinMode_O_openDrain,
GPIO_pinMode_O_openDrainMux, GPIO_pinMode_O_pushPullMux,
GPIO_pinMode_O_openDrainMux,
}; };
enum lowhigh { enum lowhigh
low, {
high, low,
high,
}; };
// analog inputs // analog inputs
enum GPIO_analog_inputs { enum GPIO_analog_inputs
GPIO_Ain0_A2, {
GPIO_Ain1_A1, GPIO_Ain0_A2,
GPIO_Ain2_C4, GPIO_Ain1_A1,
GPIO_Ain3_D2, GPIO_Ain2_C4,
GPIO_Ain4_D3, GPIO_Ain3_D2,
GPIO_Ain5_D5, GPIO_Ain4_D3,
GPIO_Ain6_D6, GPIO_Ain5_D5,
GPIO_Ain7_D4, GPIO_Ain6_D6,
GPIO_AinVref, GPIO_Ain7_D4,
GPIO_AinVcal, GPIO_AinVref,
GPIO_AinVcal,
}; };
// how many cycles the ADC shall sample the input for (speed vs precision) // how many cycles the ADC shall sample the input for (speed vs precision)
enum GPIO_ADC_sampletimes { enum GPIO_ADC_sampletimes
GPIO_ADC_sampletime_3cy, {
GPIO_ADC_sampletime_9cy, GPIO_ADC_sampletime_3cy,
GPIO_ADC_sampletime_15cy, GPIO_ADC_sampletime_9cy,
GPIO_ADC_sampletime_30cy, GPIO_ADC_sampletime_15cy,
GPIO_ADC_sampletime_43cy, GPIO_ADC_sampletime_30cy,
GPIO_ADC_sampletime_57cy, GPIO_ADC_sampletime_43cy,
GPIO_ADC_sampletime_73cy, GPIO_ADC_sampletime_57cy,
GPIO_ADC_sampletime_241cy_default, GPIO_ADC_sampletime_73cy,
GPIO_ADC_sampletime_241cy_default,
}; };
enum GPIO_tim1_output_sets { 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_0__D2_A1_C3_C4__D0_A2_D1,
GPIO_tim1_output_set_2__D2_A1_C3_C4__D0_A2_D1, GPIO_tim1_output_set_1__C6_C7_C0_D3__C3_C4_D1,
GPIO_tim1_output_set_3__C4_C7_C5_D4__C3_D2_C6, 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 { 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_0__D4_D3_C0_D7,
GPIO_tim2_output_set_2__C1_D3_C0_D7, GPIO_tim2_output_set_1__C5_C2_D2_C1,
GPIO_tim2_output_set_3__C1_C7_D6_D5, 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 // setup
#define GPIO_port_enable(GPIO_port_n) #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_tim1_analogWrite(channel, value)
#define GPIO_tim2_analogWrite(channel, value) #define GPIO_tim2_analogWrite(channel, value)
// ######## internal function declarations
// ######## internal variables
//######## internal function declarations // ######## preprocessor macros
#define CONCAT(a, b) a##b
//######## internal variables
//######## preprocessor macros
#define CONCAT(a, b) a ## b
#define CONCAT_INDIRECT(a, b) CONCAT(a, b) #define CONCAT_INDIRECT(a, b) CONCAT(a, b)
#undef GPIOv_from_PORT_PIN #undef GPIOv_from_PORT_PIN
#define GPIOv_from_PORT_PIN( GPIO_port_n, pin ) ((GPIO_port_n << 4 ) | (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_PORT(GPIOv) (GPIOv >> 4)
#define GPIOv_to_PIN( GPIOv ) (GPIOv & 0b1111) #define GPIOv_to_PIN(GPIOv) (GPIOv & 0b1111)
#define GPIOv_to_GPIObase( GPIOv ) ((GPIO_TypeDef*)(uintptr_t)((GPIOA_BASE + (0x400 * (GPIOv >> 4))))) #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_n2(GPIOx) GPIOx_to_port_n_##GPIOx
#define GPIOx_to_port_n(GPIOx) GPIOx_to_port_n2(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_A 0b00
#define GPIOx_to_port_n_GPIO_port_C 0b10 #define GPIOx_to_port_n_GPIO_port_C 0b10
#define GPIOx_to_port_n_GPIO_port_D 0b11 #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_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_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_A GPIOA
#define GPIO_port_n_to_GPIOx_GPIO_port_C GPIOC #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_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_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_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_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_C RCC_APB2Periph_GPIOC
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_D RCC_APB2Periph_GPIOD #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_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, 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_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_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_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_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_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_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_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_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_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, 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_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_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_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_I_analog(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(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_openDrain(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIOv) #define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIOv)
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrainMux(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_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, 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_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_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_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_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_pushPull(GPIO_port_n)
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(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 #endif
#if !defined(GPIO_timer_prescaler) #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 #endif
//######## define requirements / maintenance defines // ######## define requirements / maintenance defines
//######## small function definitions, static inline
// ######## small function definitions, static inline
#undef GPIO_port_enable #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_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) ({ \ #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_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 * 1)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 2)) | \ (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 * 3)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 4)) | \ (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 * 5)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 6)) | \ (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 6)) | \
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 7)); \ (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 7)); \
GPIO_port_pinMode_set_PUPD(pinMode, GPIO_port_n); \ GPIO_port_pinMode_set_PUPD(pinMode, GPIO_port_n); \
}) })
#undef GPIO_port_digitalWrite #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 #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 #undef GPIO_pinMode
#define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed) ({ \ #define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed) ({ \
GPIOv_to_GPIObase(GPIOv)->CFGLR &= ~(0b1111 << (4 * GPIOv_to_PIN(GPIOv))); \ 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))); \ GPIOv_to_GPIObase(GPIOv)->CFGLR |= (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * GPIOv_to_PIN(GPIOv))); \
GPIO_pinMode_set_PUPD(pinMode, GPIOv); \ GPIO_pinMode_set_PUPD(pinMode, GPIOv); \
}) })
#undef GPIO_digitalWrite_hi #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 #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 #undef GPIO_digitalWrite
#define GPIO_digitalWrite(GPIOv, lowhigh) GPIO_digitalWrite_##lowhigh(GPIOv) #define GPIO_digitalWrite(GPIOv, lowhigh) GPIO_digitalWrite_##lowhigh(GPIOv)
#define GPIO_digitalWrite_low(GPIOv) GPIO_digitalWrite_lo(GPIOv) #define GPIO_digitalWrite_low(GPIOv) GPIO_digitalWrite_lo(GPIOv)
#define GPIO_digitalWrite_0(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_high(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#define GPIO_digitalWrite_1(GPIOv) GPIO_digitalWrite_hi(GPIOv) #define GPIO_digitalWrite_1(GPIOv) GPIO_digitalWrite_hi(GPIOv)
#undef GPIO_digitalWrite_branching #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 #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 #undef GPIO_ADC_set_sampletime
// 0:7 => 3/9/15/30/43/57/73/241 cycles // 0:7 => 3/9/15/30/43/57/73/241 cycles
#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \ #define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \ ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \
ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \ ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \
}) })
#undef GPIO_ADC_set_sampletimes_all #undef GPIO_ADC_set_sampletimes_all
#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \ #define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \
ADC1->SAMPTR2 &= 0; \ ADC1->SAMPTR2 &= 0; \
ADC1->SAMPTR2 |= \ ADC1->SAMPTR2 |= \
GPIO_ADC_sampletime << (0 * 3) \ 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); \
| GPIO_ADC_sampletime << (1 * 3) \ ADC1->SAMPTR1 &= 0; \
| GPIO_ADC_sampletime << (2 * 3) \ ADC1->SAMPTR1 |= \
| GPIO_ADC_sampletime << (3 * 3) \ 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 << (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 #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) #define GPIO_ADC_set_power_0 ADC1->CTLR2 &= ~(ADC_ADON)
#undef GPIO_ADC_calibrate #undef GPIO_ADC_calibrate
#define GPIO_ADC_calibrate() ({ \ #define GPIO_ADC_calibrate() ({ \
ADC1->CTLR2 |= ADC_RSTCAL; \ ADC1->CTLR2 |= ADC_RSTCAL; \
while(ADC1->CTLR2 & ADC_RSTCAL); \ while (ADC1->CTLR2 & ADC_RSTCAL) \
ADC1->CTLR2 |= ADC_CAL; \ ; \
while(ADC1->CTLR2 & ADC_CAL); \ ADC1->CTLR2 |= ADC_CAL; \
while (ADC1->CTLR2 & ADC_CAL) \
; \
}) })
// large but will likely only ever be called once // large but will likely only ever be called once
static inline void GPIO_ADCinit() { static inline void GPIO_ADCinit()
// select ADC clock source {
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2 // select ADC clock source
RCC->CFGR0 &= ~(0x1F<<11); // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2
RCC->CFGR0 &= ~(0x1F << 11);
// enable clock to the ADC // enable clock to the ADC
RCC->APB2PCENR |= RCC_APB2Periph_ADC1; RCC->APB2PCENR |= RCC_APB2Periph_ADC1;
// Reset the ADC to init all regs // Reset the ADC to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1; RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1; RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
// set sampling time for all inputs to 241 cycles // set sampling time for all inputs to 241 cycles
GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime); GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime);
// set trigger to software // set trigger to software
ADC1->CTLR2 |= ADC_EXTSEL; ADC1->CTLR2 |= ADC_EXTSEL;
// pre-clear conversion queue // pre-clear conversion queue
ADC1->RSQR1 = 0; ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0; ADC1->RSQR2 = 0;
ADC1->RSQR3 = 0; ADC1->RSQR3 = 0;
// power the ADC // power the ADC
GPIO_ADC_set_power(1); GPIO_ADC_set_power(1);
GPIO_ADC_calibrate(); GPIO_ADC_calibrate();
} }
static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input) { static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input)
// set mux to selected input {
ADC1->RSQR3 = input; // set mux to selected input
// allow everything to precharge ADC1->RSQR3 = input;
Delay_Us(GPIO_ADC_MUX_DELAY); // allow everything to precharge
// start sw conversion (auto clears) Delay_Us(GPIO_ADC_MUX_DELAY);
ADC1->CTLR2 |= ADC_SWSTART; // start sw conversion (auto clears)
// wait for conversion complete ADC1->CTLR2 |= ADC_SWSTART;
while(!(ADC1->STATR & ADC_EOC)) {} // wait for conversion complete
// get result while (!(ADC1->STATR & ADC_EOC)) {}
return ADC1->RDATAR; // get result
return ADC1->RDATAR;
} }
#undef GPIO_tim1_map #undef GPIO_tim1_map
#define GPIO_tim1_map(GPIO_tim1_output_set) ({ \ #define GPIO_tim1_map(GPIO_tim1_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \ RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \ AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \
}) })
#undef GPIO_tim2_map #undef GPIO_tim2_map
#define GPIO_tim2_map(GPIO_tim2_output_set) ({ \ #define GPIO_tim2_map(GPIO_tim2_output_set) ({ \
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \ RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \ AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \
}) })
static inline void GPIO_tim1_init() { static inline void GPIO_tim1_init()
// enable TIM1 {
RCC->APB2PCENR |= RCC_APB2Periph_TIM1; // enable TIM1
// reset TIM1 to init all regs RCC->APB2PCENR |= RCC_APB2Periph_TIM1;
RCC->APB2PRSTR |= 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 RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
// set clock prescaler divider // SMCFGR: default clk input is CK_INT
TIM1->PSC = GPIO_timer_prescaler; // set clock prescaler divider
// set PWM total cycle width TIM1->PSC = GPIO_timer_prescaler;
TIM1->ATRLR = GPIO_timer_resolution; // set PWM total cycle width
// CTLR1: default is up, events generated, edge align TIM1->ATRLR = GPIO_timer_resolution;
// enable auto-reload of preload // CTLR1: default is up, events generated, edge align
TIM1->CTLR1 |= TIM_ARPE; // enable auto-reload of preload
// initialize counter TIM1->CTLR1 |= TIM_ARPE;
TIM1->SWEVGR |= TIM_UG; // initialize counter
// disengage brake TIM1->SWEVGR |= TIM_UG;
TIM1->BDTR |= TIM_MOE; // disengage brake
// Enable TIM1 TIM1->BDTR |= TIM_MOE;
TIM1->CTLR1 |= TIM_CEN; // Enable TIM1
TIM1->CTLR1 |= TIM_CEN;
} }
static inline void GPIO_tim2_init() { static inline void GPIO_tim2_init()
// enable TIM2 {
RCC->APB1PCENR |= RCC_APB1Periph_TIM2; // enable TIM2
// reset TIM2 to init all regs RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
RCC->APB1PRSTR |= 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 RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
// set clock prescaler divider // SMCFGR: default clk input is CK_INT
TIM2->PSC = GPIO_timer_prescaler; // set clock prescaler divider
// set PWM total cycle width TIM2->PSC = GPIO_timer_prescaler;
TIM2->ATRLR = GPIO_timer_resolution; // set PWM total cycle width
// CTLR1: default is up, events generated, edge align TIM2->ATRLR = GPIO_timer_resolution;
// enable auto-reload of preload // CTLR1: default is up, events generated, edge align
TIM2->CTLR1 |= TIM_ARPE; // enable auto-reload of preload
// initialize counter TIM2->CTLR1 |= TIM_ARPE;
TIM2->SWEVGR |= TIM_UG; // initialize counter
// Enable TIM2 TIM2->SWEVGR |= TIM_UG;
TIM2->CTLR1 |= TIM_CEN; // Enable TIM2
TIM2->CTLR1 |= TIM_CEN;
} }
#define GPIO_timer_channel_set2(timer, channel) GPIO_timer_channel_set_##channel(timer) #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(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_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_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_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_set_4(timer) timer->CHCTLR2 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
#undef GPIO_tim1_enableCH #undef GPIO_tim1_enableCH
#define GPIO_tim1_enableCH(channel) ({ \ #define GPIO_tim1_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM1, channel); \ GPIO_timer_channel_set(TIM1, channel); \
TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \ TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
}) })
#undef GPIO_tim2_enableCH #undef GPIO_tim2_enableCH
#define GPIO_tim2_enableCH(channel) ({ \ #define GPIO_tim2_enableCH(channel) ({ \
GPIO_timer_channel_set(TIM2, channel); \ GPIO_timer_channel_set(TIM2, channel); \
TIM2->CCER |= (TIM_OutputState_Enable ) << (4 * (channel - 1)); \ 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 #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 #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 #endif // CH32V003_GPIO_BR_H

View file

@ -1,15 +1,15 @@
//######## necessities // ######## necessities
// include guards // include guards
#ifndef CH32V003_SPI_H #ifndef CH32V003_SPI_H
#define CH32V003_SPI_H #define CH32V003_SPI_H
// includes // includes
#include<stdint.h> //uintN_t support
#include "ch32fun.h" #include "ch32fun.h"
#include <stdint.h> //uintN_t support
#ifndef APB_CLOCK #ifndef APB_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK #define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#endif #endif
/*######## library usage and configuration /*######## 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 #ifndef APB_CLOCK
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK #define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
#endif #endif
to enable using the functions of this library: 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! #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(); static inline void SPI_init();
// establish / end a connection to the SPI device // establish / end a connection to the SPI device
@ -67,14 +65,14 @@ static inline void SPI_NSS_software_high();
// read / write the SPI device // read / write the SPI device
// these commands are raw, you'll have to consider all other steps in SPI_transfer! // 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 uint16_t SPI_read_16();
static inline void SPI_write_8(uint8_t data); static inline void SPI_write_8(uint8_t data);
static inline void SPI_write_16(uint16_t data); static inline void SPI_write_16(uint16_t data);
// send a command and get a response from the SPI device // send a command and get a response from the SPI device
// you'll use this for most devices // 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); static inline uint16_t SPI_transfer_16(uint16_t data);
// SPI peripheral power enable / disable (default off, init() automatically enables) // 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 kill_interrrupts();
static inline void restore_interrupts(); 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 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; static uint16_t EXT1_INTENR_backup;
// ######## preprocessor macros
// min and max helper macros
//######## preprocessor macros #define MIN(a, b) (((a) < (b)) ? (a) : (b))
// min and max helper macros #define MAX(a, b) (((a) > (b)) ? (a) : (b))
#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 // stringify for displaying what #defines evaluated to at preprocessor stage
#define VALUE_TO_STRING(x) #x #define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(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)) #define LOG2(x) ((x) == 0 ? -1 : __builtin_ctz(x))
// compile-time clock prescaler calculation: log2(APB_CLOCK/SPEED_BUS) // 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 // 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)"); _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) #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 #endif
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && defined(CH32V003_SPI_DIRECTION_1LINE_TX) #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 #endif
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ #if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 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_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1 (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!" #warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!"
#endif #endif
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ #if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 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_PHA0) ? 1 : 0) + \
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0 (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!" #warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!"
#endif #endif
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ #if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1 (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1
#warning "more than one of the CH32V003_SPI_NSS_ options were defined!" #warning "more than one of the CH32V003_SPI_NSS_ options were defined!"
#endif #endif
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ #if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0 (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0
#warning "none of the CH32V003_SPI_NSS_ options were defined!" #warning "none of the CH32V003_SPI_NSS_ options were defined!"
#endif #endif
// ######## small function definitions, static inline
static inline void SPI_init()
{
SPI_poweron();
// reset control register
SPI1->CTLR1 = 0;
//######## small function definitions, static inline // set prescaler
static inline void SPI_init() { SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER << 3);
SPI_poweron();
// reset control register // set clock polarity and phase
SPI1->CTLR1 = 0; #if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL0_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA0)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge);
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA1)
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge);
#endif
// set prescaler // configure NSS pin, master mode
SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER<<3); #if defined(CH32V003_SPI_NSS_HARDWARE_PC0)
// _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf << (4 * 0));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 0);
AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0)
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_HARDWARE_PC1)
// NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
GPIOC->CFGLR &= ~(0xf << (4 * 1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 1);
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf << (4 * 3));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 3);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
GPIOC->CFGLR &= ~(0xf << (4 * 4));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
#elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL)
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
#endif
// set clock polarity and phase // SCK on PC5, 10MHz Output, alt func, push-pull
#if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) GPIOC->CFGLR &= ~(0xf << (4 * 5));
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge); GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5);
#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 // CH32V003 is master
#if defined(CH32V003_SPI_NSS_HARDWARE_PC0) SPI1->CTLR1 |= SPI_Mode_Master;
// _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 // set data direction and configure data pins
GPIOC->CFGLR &= ~(0xf<<(4*5)); #if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX)
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*5); SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex;
// CH32V003 is master // MOSI on PC6, 10MHz Output, alt func, push-pull
SPI1->CTLR1 |= SPI_Mode_Master; GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
// set data direction and configure data pins // MISO on PC7, 10MHz input, floating
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) GPIOC->CFGLR &= ~(0xf << (4 * 7));
SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex; 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 // MOSI on PC6, 10MHz Output, alt func, push-pull
GPIOC->CFGLR &= ~(0xf<<(4*6)); GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
#endif
// 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() { 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; 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() { 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; 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() { static inline void SPI_end()
SPI1->CTLR1 &= ~(SPI_CTLR1_SPE); {
SPI1->CTLR1 &= ~(SPI_CTLR1_SPE);
} }
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
static inline void SPI_NSS_software_high() { static inline void SPI_NSS_software_high()
GPIOC->BSHR = (1<<3); {
GPIOC->BSHR = (1 << 3);
} }
static inline void SPI_NSS_software_low() { static inline void SPI_NSS_software_low()
GPIOC->BSHR = (1<<(16+3)); {
GPIOC->BSHR = (1 << (16 + 3));
} }
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4) #elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
static inline void SPI_NSS_software_high() { static inline void SPI_NSS_software_high()
GPIOC->BSHR = (1<<4); {
GPIOC->BSHR = (1 << 4);
} }
static inline void SPI_NSS_software_low() { static inline void SPI_NSS_software_low()
GPIOC->BSHR = (1<<(16+4)); {
GPIOC->BSHR = (1 << (16 + 4));
} }
#endif #endif
static inline uint8_t SPI_read_8() { static inline uint8_t SPI_read_8()
return SPI1->DATAR; {
return SPI1->DATAR;
} }
static inline uint16_t SPI_read_16() { static inline uint16_t SPI_read_16()
return SPI1->DATAR; {
return SPI1->DATAR;
} }
static inline void SPI_write_8(uint8_t data) { static inline void SPI_write_8(uint8_t data)
SPI1->DATAR = data; {
SPI1->DATAR = data;
} }
static inline void SPI_write_16(uint16_t data) { static inline void SPI_write_16(uint16_t data)
SPI1->DATAR = data; {
SPI1->DATAR = data;
} }
static inline uint8_t SPI_transfer_8(uint8_t 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(); #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
#endif SPI_NSS_software_high();
SPI_write_8(data); #endif
SPI_wait_TX_complete(); SPI_write_8(data);
asm volatile("nop"); SPI_wait_TX_complete();
SPI_wait_RX_available(); asm volatile("nop");
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) SPI_wait_RX_available();
SPI_NSS_software_low(); #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
#endif SPI_NSS_software_low();
return SPI_read_8(); #endif
return SPI_read_8();
} }
static inline uint16_t SPI_transfer_16(uint16_t data) { 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(); #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
#endif SPI_NSS_software_high();
SPI_write_16(data); #endif
SPI_wait_TX_complete(); SPI_write_16(data);
asm volatile("nop"); SPI_wait_TX_complete();
SPI_wait_RX_available(); asm volatile("nop");
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) SPI_wait_RX_available();
SPI_NSS_software_low(); #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
#endif SPI_NSS_software_low();
return SPI_read_16(); #endif
return SPI_read_16();
} }
static inline void SPI_poweroff() { static inline void SPI_poweroff()
SPI_end(); {
RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1; SPI_end();
RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1;
} }
static inline void SPI_poweron() { static inline void SPI_poweron()
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; {
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
} }
static inline void kill_interrrupts() { static inline void kill_interrrupts()
EXT1_INTENR_backup = EXTI->INTENR; {
// zero the interrupt enable register to disable all interrupts EXT1_INTENR_backup = EXTI->INTENR;
EXTI->INTENR = 0; // zero the interrupt enable register to disable all interrupts
EXTI->INTENR = 0;
} }
static inline void restore_interrupts() { static inline void restore_interrupts()
EXTI->INTENR = EXT1_INTENR_backup; {
EXTI->INTENR = EXT1_INTENR_backup;
} }
// ######## small internal function definitions, static inline
static inline void SPI_wait_TX_complete()
//######## small internal function definitions, static inline {
static inline void SPI_wait_TX_complete() { while (!(SPI1->STATR & SPI_STATR_TXE)) {}
while(!(SPI1->STATR & SPI_STATR_TXE)) {}
} }
static inline uint8_t SPI_is_RX_empty() { static inline uint8_t SPI_is_RX_empty()
return SPI1->STATR & SPI_STATR_RXNE; {
return SPI1->STATR & SPI_STATR_RXNE;
} }
static inline void SPI_wait_RX_available() { static inline void SPI_wait_RX_available()
while(!(SPI1->STATR & SPI_STATR_RXNE)) {} {
while (!(SPI1->STATR & SPI_STATR_RXNE)) {}
} }
static inline void SPI_wait_not_busy() { static inline void SPI_wait_not_busy()
while((SPI1->STATR & SPI_STATR_BSY) != 0) {} {
while ((SPI1->STATR & SPI_STATR_BSY) != 0) {}
} }
static inline void SPI_wait_transmit_finished() { static inline void SPI_wait_transmit_finished()
SPI_wait_TX_complete(); {
SPI_wait_not_busy(); SPI_wait_TX_complete();
SPI_wait_not_busy();
} }
// ######## implementation block
//######## 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
//#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) #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_IMPLEMENTATION
#endif // CH32V003_SPI_H #endif // CH32V003_SPI_H

View file

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

View file

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

View file

@ -1,30 +1,30 @@
/****************************************************************************** /******************************************************************************
* Psuedo Random Number Generator using a Linear Feedback Shift Register * Psuedo Random Number Generator using a Linear Feedback Shift Register
* See the GitHub for more information: * See the GitHub for more information:
* https://github.com/ADBeta/CH32V003_lib_rand * https://github.com/ADBeta/CH32V003_lib_rand
* *
* Ver 1.1 09 Sep 2024 * Ver 1.1 09 Sep 2024
* *
* Released under the MIT Licence * Released under the MIT Licence
* Copyright ADBeta (c) 2024 * Copyright ADBeta (c) 2024
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to * of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the * deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * 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 * sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 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 * 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 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE. * USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/ ******************************************************************************/
#ifndef CH32V003_LIB_RAND #ifndef CH32V003_LIB_RAND
#define CH32V003_LIB_RAND #define CH32V003_LIB_RAND
@ -35,13 +35,12 @@
// Example: #define RANDOM_STRENGTH 2 // Example: #define RANDOM_STRENGTH 2
#ifndef RANDOM_STRENGTH #ifndef RANDOM_STRENGTH
#error "Error in lib_rand. Must define RANDOM_STRENGTH" #error "Error in lib_rand. Must define RANDOM_STRENGTH"
#endif #endif
// @brief set the random LFSR values seed by default to a known-good value // @brief set the random LFSR values seed by default to a known-good value
static uint32_t _rand_lfsr = 0x747AA32F; static uint32_t _rand_lfsr = 0x747AA32F;
/*** Library specific Functions - Do Not Use *********************************/ /*** Library specific Functions - Do Not Use *********************************/
/****************************************************************************/ /****************************************************************************/
/// @brief Updates the LFSR by getting a new tap bit, for MSB, then shifting /// @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 /// @return 0x01 or 0x00, as a LSB translation of the tapped MSB for the LFSR
uint8_t _rand_lfsr_update(void) uint8_t _rand_lfsr_update(void)
{ {
// Shifting to MSB to make calculations more efficient later // Shifting to MSB to make calculations more efficient later
uint32_t bit_31 = _rand_lfsr & 0x80000000; uint32_t bit_31 = _rand_lfsr & 0x80000000;
uint32_t bit_21 = (_rand_lfsr << 10) & 0x80000000; uint32_t bit_21 = (_rand_lfsr << 10) & 0x80000000;
uint32_t bit_01 = (_rand_lfsr << 30) & 0x80000000; uint32_t bit_01 = (_rand_lfsr << 30) & 0x80000000;
uint32_t bit_00 = (_rand_lfsr << 31) & 0x80000000; uint32_t bit_00 = (_rand_lfsr << 31) & 0x80000000;
// Calculate the MSB to be put into the LFSR // Calculate the MSB to be put into the LFSR
uint32_t msb = bit_31 ^ bit_21 ^ bit_01 ^ bit_00; uint32_t msb = bit_31 ^ bit_21 ^ bit_01 ^ bit_00;
// Shift the lfsr and append the MSB to it // Shift the lfsr and append the MSB to it
_rand_lfsr = (_rand_lfsr >> 1) | msb; _rand_lfsr = (_rand_lfsr >> 1) | msb;
// Return the LSB instead of MSB // Return the LSB instead of MSB
return msb >> 31; return msb >> 31;
} }
/// @brief Generates a Random 32-bit number, using the LFSR - by generating /// @brief Generates a Random 32-bit number, using the LFSR - by generating
/// a random bit from LFSR taps, 32 times. /// a random bit from LFSR taps, 32 times.
/// @param None /// @param None
/// @return a (psuedo)random 32-bit value /// @return a (psuedo)random 32-bit value
uint32_t _rand_gen_32b(void) uint32_t _rand_gen_32b(void)
{ {
uint32_t rand_out = 0; uint32_t rand_out = 0;
uint8_t bits = 32; uint8_t bits = 32;
while(bits--) while (bits--)
{ {
// Shift the current rand value for the new LSB // Shift the current rand value for the new LSB
rand_out = rand_out << 1; rand_out = rand_out << 1;
// Append the LSB // Append the LSB
rand_out |= _rand_lfsr_update(); rand_out |= _rand_lfsr_update();
} }
return rand_out; return rand_out;
} }
/// @brief Generates a Random n-bit number, using the LFSR - by generating /// @brief Generates a Random n-bit number, using the LFSR - by generating
/// a random bit from LFSR taps, n times. /// a random bit from LFSR taps, n times.
/// @param None /// @param None
/// @return a (psuedo)random n-bit value /// @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--) while (bits--)
{ {
// Shift the current rand value for the new LSB // Shift the current rand value for the new LSB
rand_out = rand_out << 1; rand_out = rand_out << 1;
// Append the LSB // Append the LSB
rand_out |= _rand_lfsr_update(); rand_out |= _rand_lfsr_update();
} }
return rand_out; return rand_out;
} }
/*** API Functions ***********************************************************/ /*** API Functions ***********************************************************/
/*****************************************************************************/ /*****************************************************************************/
/// @brief seeds the Random LFSR to the value passed /// @brief seeds the Random LFSR to the value passed
@ -115,40 +110,39 @@ uint32_t _rand_gen_nb( int bits)
/// @return None /// @return None
void seed(const uint32_t seed_val) 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 /// @brief Generates a Random (32-bit) Number, based on the RANDOM_STRENGTH
/// you have selected /// you have selected
/// @param None /// @param None
/// @return 32bit Random value /// @return 32bit Random value
uint32_t rand(void) 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 is level 1, Update LFSR Once, then return it
#if RANDOM_STRENGTH == 1 #if RANDOM_STRENGTH == 1
// Update the LFSR, discard result, and return _lsfr raw // Update the LFSR, discard result, and return _lsfr raw
(void)_rand_lfsr_update(); (void)_rand_lfsr_update();
rand_out = _rand_lfsr; rand_out = _rand_lfsr;
#endif #endif
// If RANDOM_STRENGTH is level 2, generate a 32-bit output, using 32 random // If RANDOM_STRENGTH is level 2, generate a 32-bit output, using 32 random
// bits from the LFSR // bits from the LFSR
#if RANDOM_STRENGTH == 2 #if RANDOM_STRENGTH == 2
rand_out = _rand_gen_32b(); rand_out = _rand_gen_32b();
#endif #endif
// If RANDOM_STRENGTH is level 3, generate 2 32-bit outputs, then XOR them // If RANDOM_STRENGTH is level 3, generate 2 32-bit outputs, then XOR them
// together // together
#if RANDOM_STRENGTH == 3 #if RANDOM_STRENGTH == 3
uint32_t rand_a = _rand_gen_32b(); uint32_t rand_a = _rand_gen_32b();
uint32_t rand_b = _rand_gen_32b(); uint32_t rand_b = _rand_gen_32b();
rand_out = rand_a ^ rand_b; rand_out = rand_a ^ rand_b;
#endif #endif
return rand_out; return rand_out;
} }
#endif #endif

View file

@ -6,19 +6,19 @@
#ifndef _SSD1306_H #ifndef _SSD1306_H
#define _SSD1306_H #define _SSD1306_H
#include "font_8x8.h"
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "font_8x8.h"
// comfortable packet size for this OLED // comfortable packet size for this OLED
#define SSD1306_PSZ 32 #define SSD1306_PSZ 32
#if defined (SSD1306_CUSTOM) #if defined(SSD1306_CUSTOM)
// Let the caller configure the OLED. // Let the caller configure the OLED.
#else #else
// characteristics of each type // characteristics of each type
#if !defined (SSD1306_64X32) && !defined (SSD1306_128X32) && !defined (SSD1306_128X64) && !defined (SH1107_128x128) && !(defined(SSD1306_W) && defined(SSD1306_H) && defined(SSD1306_OFFSET) ) #if !defined(SSD1306_64X32) && !defined(SSD1306_128X32) && !defined(SSD1306_128X64) && !defined(SH1107_128x128) && !(defined(SSD1306_W) && defined(SSD1306_H) && defined(SSD1306_OFFSET))
#error "Please define the SSD1306_WXH resolution used in your application" #error "Please define the SSD1306_WXH resolution used in your application"
#endif #endif
#ifdef SSD1306_64X32 #ifdef SSD1306_64X32
@ -57,7 +57,7 @@
*/ */
uint8_t ssd1306_cmd(uint8_t cmd) uint8_t ssd1306_cmd(uint8_t cmd)
{ {
return ssd1306_pkt_send(&cmd, 1, 1); return ssd1306_pkt_send(&cmd, 1, 1);
} }
/* /*
@ -65,7 +65,7 @@ uint8_t ssd1306_cmd(uint8_t cmd)
*/ */
uint8_t ssd1306_data(uint8_t *data, int sz) uint8_t ssd1306_data(uint8_t *data, int sz)
{ {
return ssd1306_pkt_send(data, sz, 0); return ssd1306_pkt_send(data, sz, 0);
} }
#define SSD1306_SETCONTRAST 0x81 #define SSD1306_SETCONTRAST 0x81
@ -87,7 +87,7 @@ uint8_t ssd1306_data(uint8_t *data, int sz)
#define SSD1306_SETSTARTLINE 0x40 #define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20 #define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21 #define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR 0x22 #define SSD1306_PAGEADDR 0x22
#define SSD1306_COMSCANINC 0xC0 #define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8 #define SSD1306_COMSCANDEC 0xC8
#define SSD1306_CHARGEPUMP 0x8D #define SSD1306_CHARGEPUMP 0x8D
@ -98,81 +98,81 @@ uint8_t ssd1306_data(uint8_t *data, int sz)
/* choose VCC mode */ /* choose VCC mode */
#define SSD1306_EXTERNALVCC 0x1 #define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2 #define SSD1306_SWITCHCAPVCC 0x2
//#define vccstate SSD1306_EXTERNALVCC // #define vccstate SSD1306_EXTERNALVCC
#define vccstate SSD1306_SWITCHCAPVCC #define vccstate SSD1306_SWITCHCAPVCC
#if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY #if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY
// OLED initialization commands for 128x32 // OLED initialization commands for 128x32
const uint8_t ssd1306_init_array[] = const uint8_t ssd1306_init_array[] =
{ {
#ifdef SH1107 #ifdef SH1107
SSD1306_DISPLAYOFF, // Turn OLED off SSD1306_DISPLAYOFF, // Turn OLED off
0x00, // Low column 0x00, // Low column
0x10, // High column 0x10, // High column
0xb0, // Page address 0xb0, // Page address
0xdc, 0x00, // Set Display Start Line (Where in memory it reads from) 0xdc, 0x00, // Set Display Start Line (Where in memory it reads from)
SSD1306_SETCONTRAST, 0x6f, // Set constrast SSD1306_SETCONTRAST, 0x6f, // Set constrast
SSD1306_COLUMNADDR, // Set memory addressing mode SSD1306_COLUMNADDR, // Set memory addressing mode
SSD1306_DISPLAYALLON_RESUME, // normal (as opposed to invert colors, always on or off.) SSD1306_DISPLAYALLON_RESUME, // normal (as opposed to invert colors, always on or off.)
SSD1306_SETMULTIPLEX, (SSD1306_H-1), // Iterate over all 128 rows (Multiplex Ratio) SSD1306_SETMULTIPLEX, (SSD1306_H - 1), // Iterate over all 128 rows (Multiplex Ratio)
SSD1306_SETDISPLAYOFFSET, 0x00, // Set display offset // Where this appears on-screen (Some displays will be different) SSD1306_SETDISPLAYOFFSET, 0x00, // Set display offset // Where this appears on-screen (Some displays will be different)
SSD1306_SETDISPLAYCLOCKDIV, 0xf0, // Set precharge properties. THIS IS A LIE This has todo with timing. <<< This makes it go brrrrrrrrr SSD1306_SETDISPLAYCLOCKDIV, 0xf0, // Set precharge properties. THIS IS A LIE This has todo with timing. <<< This makes it go brrrrrrrrr
SSD1306_SETPRECHARGE, 0x1d, // Set pre-charge period (This controls brightness) SSD1306_SETPRECHARGE, 0x1d, // Set pre-charge period (This controls brightness)
SSD1306_SETVCOMDETECT, 0x35, // Set vcomh SSD1306_SETVCOMDETECT, 0x35, // Set vcomh
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
0xad, 0x80, // Set Charge pump 0xad, 0x80, // Set Charge pump
SSD1306_SEGREMAP, 0x01, // Default mapping SSD1306_SEGREMAP, 0x01, // Default mapping
SSD1306_SETPRECHARGE, 0x06, // ???? No idea what this does, but this looks best. SSD1306_SETPRECHARGE, 0x06, // ???? No idea what this does, but this looks best.
SSD1306_SETCONTRAST, 0xfe, // Set constrast SSD1306_SETCONTRAST, 0xfe, // Set constrast
SSD1306_SETVCOMDETECT, 0xfe, // Set vcomh SSD1306_SETVCOMDETECT, 0xfe, // Set vcomh
SSD1306_SETMULTIPLEX, (SSD1306_H-1), // 128-wide. SSD1306_SETMULTIPLEX, (SSD1306_H - 1), // 128-wide.
SSD1306_DISPLAYON, // Display on. SSD1306_DISPLAYON, // Display on.
#else #else
SSD1306_DISPLAYOFF, // 0xAE SSD1306_DISPLAYOFF, // 0xAE
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
0x80, // the suggested ratio 0x80 0x80, // the suggested ratio 0x80
SSD1306_SETMULTIPLEX, // 0xA8 SSD1306_SETMULTIPLEX, // 0xA8
#ifdef SSD1306_64X32 #ifdef SSD1306_64X32
0x1F, // for 64-wide displays 0x1F, // for 64-wide displays
#else #else
0x3F, // for 128-wide displays 0x3F, // for 128-wide displays
#endif #endif
SSD1306_SETDISPLAYOFFSET, // 0xD3 SSD1306_SETDISPLAYOFFSET, // 0xD3
0x00, // no offset 0x00, // no offset
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
SSD1306_CHARGEPUMP, // 0x8D SSD1306_CHARGEPUMP, // 0x8D
0x14, // enable? 0x14, // enable?
SSD1306_MEMORYMODE, // 0x20 SSD1306_MEMORYMODE, // 0x20
0x00, // 0x0 act like ks0108 0x00, // 0x0 act like ks0108
SSD1306_SEGREMAP | 0x1, // 0xA0 | bit SSD1306_SEGREMAP | 0x1, // 0xA0 | bit
SSD1306_COMSCANDEC, SSD1306_COMSCANDEC,
SSD1306_SETCOMPINS, // 0xDA SSD1306_SETCOMPINS, // 0xDA
0x12, // 0x12, //
SSD1306_SETCONTRAST, // 0x81 SSD1306_SETCONTRAST, // 0x81
0x8F, 0x8F,
SSD1306_SETPRECHARGE, // 0xd9 SSD1306_SETPRECHARGE, // 0xd9
0xF1, 0xF1,
SSD1306_SETVCOMDETECT, // 0xDB SSD1306_SETVCOMDETECT, // 0xDB
0x40, 0x40,
SSD1306_DISPLAYALLON_RESUME, // 0xA4 SSD1306_DISPLAYALLON_RESUME, // 0xA4
#ifndef SSD1327 #ifndef SSD1327
SSD1306_NORMALDISPLAY, // 0xA6 SSD1306_NORMALDISPLAY, // 0xA6
#endif #endif
SSD1306_DISPLAYON, // 0xAF --turn on oled panel SSD1306_DISPLAYON, // 0xAF --turn on oled panel
#endif #endif
SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end
}; };
#endif #endif
// the display buffer // the display buffer
uint8_t ssd1306_buffer[SSD1306_W*SSD1306_H/8]; uint8_t ssd1306_buffer[SSD1306_W * SSD1306_H / 8];
/* /*
* set the buffer to a color * set the buffer to a color
*/ */
void ssd1306_setbuf(uint8_t color) void ssd1306_setbuf(uint8_t color)
{ {
memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer)); memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
} }
#ifndef SSD1306_FULLUSE #ifndef SSD1306_FULLUSE
@ -180,11 +180,23 @@ void ssd1306_setbuf(uint8_t color)
* expansion array for OLED with every other row unused * expansion array for OLED with every other row unused
*/ */
const uint8_t expand[16] = const uint8_t expand[16] =
{ {
0x00,0x02,0x08,0x0a, 0x00,
0x20,0x22,0x28,0x2a, 0x02,
0x80,0x82,0x88,0x8a, 0x08,
0xa0,0xa2,0xa8,0xaa, 0x0a,
0x20,
0x22,
0x28,
0x2a,
0x80,
0x82,
0x88,
0x8a,
0xa0,
0xa2,
0xa8,
0xaa,
}; };
#endif #endif
@ -193,66 +205,65 @@ const uint8_t expand[16] =
*/ */
void ssd1306_refresh(void) void ssd1306_refresh(void)
{ {
uint16_t i; uint16_t i;
#ifdef SH1107 #ifdef SH1107
ssd1306_cmd(SSD1306_MEMORYMODE); // vertical addressing mode. ssd1306_cmd(SSD1306_MEMORYMODE); // vertical addressing mode.
for(i=0;i<SSD1306_H/8;i++) for (i = 0; i < SSD1306_H / 8; i++)
{ {
ssd1306_cmd(0xb0 | i); ssd1306_cmd(0xb0 | i);
ssd1306_cmd( 0x00 | (0&0xf) ); ssd1306_cmd(0x00 | (0 & 0xf));
ssd1306_cmd( 0x10 | (0>>4) ); ssd1306_cmd(0x10 | (0 >> 4));
ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+0*SSD1306_PSZ], SSD1306_PSZ); ssd1306_data(&ssd1306_buffer[i * 4 * SSD1306_PSZ + 0 * SSD1306_PSZ], SSD1306_PSZ);
ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+1*SSD1306_PSZ], SSD1306_PSZ); ssd1306_data(&ssd1306_buffer[i * 4 * SSD1306_PSZ + 1 * SSD1306_PSZ], SSD1306_PSZ);
ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+2*SSD1306_PSZ], SSD1306_PSZ); ssd1306_data(&ssd1306_buffer[i * 4 * SSD1306_PSZ + 2 * SSD1306_PSZ], SSD1306_PSZ);
ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+3*SSD1306_PSZ], SSD1306_PSZ); ssd1306_data(&ssd1306_buffer[i * 4 * SSD1306_PSZ + 3 * SSD1306_PSZ], SSD1306_PSZ);
} }
#else #else
ssd1306_cmd(SSD1306_COLUMNADDR); ssd1306_cmd(SSD1306_COLUMNADDR);
ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset) ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset)
ssd1306_cmd(SSD1306_OFFSET+SSD1306_W-1); // Column end address (127 = reset) ssd1306_cmd(SSD1306_OFFSET + SSD1306_W - 1); // Column end address (127 = reset)
ssd1306_cmd(SSD1306_PAGEADDR); ssd1306_cmd(SSD1306_PAGEADDR);
ssd1306_cmd(0); // Page start address (0 = reset) ssd1306_cmd(0); // Page start address (0 = reset)
ssd1306_cmd(7); // Page end address ssd1306_cmd(7); // Page end address
#ifdef SSD1306_FULLUSE #ifdef SSD1306_FULLUSE
/* for fully used rows just plow thru everything */ /* for fully used rows just plow thru everything */
for(i=0;i<sizeof(ssd1306_buffer);i+=SSD1306_PSZ) for (i = 0; i < sizeof(ssd1306_buffer); i += SSD1306_PSZ)
{ {
/* send PSZ block of data */ /* send PSZ block of data */
ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ); ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
} }
#else #else
/* for displays with odd rows unused expand bytes */ /* for displays with odd rows unused expand bytes */
uint8_t tbuf[SSD1306_PSZ], j, k; uint8_t tbuf[SSD1306_PSZ], j, k;
for(i=0;i<sizeof(ssd1306_buffer);i+=128) for (i = 0; i < sizeof(ssd1306_buffer); i += 128)
{ {
/* low nybble */ /* low nybble */
for(j=0;j<128;j+=SSD1306_PSZ) for (j = 0; j < 128; j += SSD1306_PSZ)
{ {
for(k=0;k<SSD1306_PSZ;k++) for (k = 0; k < SSD1306_PSZ; k++)
tbuf[k] = expand[ssd1306_buffer[i+j+k]&0xf]; tbuf[k] = expand[ssd1306_buffer[i + j + k] & 0xf];
/* send PSZ block of data */ /* send PSZ block of data */
ssd1306_data(tbuf, SSD1306_PSZ); ssd1306_data(tbuf, SSD1306_PSZ);
} }
/* high nybble */ /* high nybble */
for(j=0;j<128;j+=SSD1306_PSZ) for (j = 0; j < 128; j += SSD1306_PSZ)
{ {
for(k=0;k<SSD1306_PSZ;k++) for (k = 0; k < SSD1306_PSZ; k++)
tbuf[k] = expand[(ssd1306_buffer[i+j+k]>>4)&0xf]; tbuf[k] = expand[(ssd1306_buffer[i + j + k] >> 4) & 0xf];
/* send PSZ block of data */ /* send PSZ block of data */
ssd1306_data(tbuf, SSD1306_PSZ); ssd1306_data(tbuf, SSD1306_PSZ);
} }
} }
#endif #endif
#endif #endif
} }
/* /*
@ -260,22 +271,22 @@ void ssd1306_refresh(void)
*/ */
void ssd1306_drawPixel(uint32_t x, uint32_t y, int color) void ssd1306_drawPixel(uint32_t x, uint32_t y, int color)
{ {
uint32_t addr; uint32_t addr;
/* clip */ /* clip */
if(x >= SSD1306_W) if (x >= SSD1306_W)
return; return;
if(y >= SSD1306_H) if (y >= SSD1306_H)
return; return;
/* compute buffer address */ /* compute buffer address */
addr = x + SSD1306_W*(y/8); addr = x + SSD1306_W * (y / 8);
/* set/clear bit in buffer */ /* set/clear bit in buffer */
if(color) if (color)
ssd1306_buffer[addr] |= (1<<(y&7)); ssd1306_buffer[addr] |= (1 << (y & 7));
else else
ssd1306_buffer[addr] &= ~(1<<(y&7)); ssd1306_buffer[addr] &= ~(1 << (y & 7));
} }
/* /*
@ -283,89 +294,96 @@ void ssd1306_drawPixel(uint32_t x, uint32_t y, int color)
*/ */
void ssd1306_xorPixel(uint32_t x, uint32_t y) void ssd1306_xorPixel(uint32_t x, uint32_t y)
{ {
uint32_t addr; uint32_t addr;
/* clip */ /* clip */
if(x >= SSD1306_W) if (x >= SSD1306_W)
return; return;
if(y >= SSD1306_H) if (y >= SSD1306_H)
return; return;
/* compute buffer address */ /* compute buffer address */
addr = x + SSD1306_W*(y/8); addr = x + SSD1306_W * (y / 8);
ssd1306_buffer[addr] ^= (1<<(y&7)); ssd1306_buffer[addr] ^= (1 << (y & 7));
} }
/* /*
* draw a an image from an array, directly into to the display buffer * draw a an image from an array, directly into to the display buffer
* the color modes allow for overwriting and even layering (sprites!) * the color modes allow for overwriting and even layering (sprites!)
*/ */
void ssd1306_drawImage(uint32_t x, uint32_t y, const unsigned char* input, uint32_t width, uint32_t height, uint32_t color_mode) { void ssd1306_drawImage(uint32_t x, uint32_t y, const unsigned char *input, uint32_t width, uint32_t height, uint32_t color_mode)
uint32_t x_absolute; {
uint32_t y_absolute; uint32_t x_absolute;
uint32_t pixel; uint32_t y_absolute;
uint32_t bytes_to_draw = width / 8; uint32_t pixel;
uint32_t buffer_addr; uint32_t bytes_to_draw = width / 8;
uint32_t buffer_addr;
for (uint32_t line = 0; line < height; line++) { for (uint32_t line = 0; line < height; line++)
y_absolute = y + line; {
if (y_absolute >= SSD1306_H) { y_absolute = y + line;
break; if (y_absolute >= SSD1306_H)
} {
break;
}
// SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data // SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data
// bitmask for current pixel in vertical (output) byte // bitmask for current pixel in vertical (output) byte
uint32_t v_mask = 1 << (y_absolute & 7); uint32_t v_mask = 1 << (y_absolute & 7);
for (uint32_t byte = 0; byte < bytes_to_draw; byte++) { for (uint32_t byte = 0; byte < bytes_to_draw; byte++)
uint32_t input_byte = input[byte + line * bytes_to_draw]; {
uint32_t input_byte = input[byte + line * bytes_to_draw];
for (pixel = 0; pixel < 8; pixel++) { for (pixel = 0; pixel < 8; pixel++)
x_absolute = x + 8 * (bytes_to_draw - byte) + pixel; {
if (x_absolute >= SSD1306_W) { x_absolute = x + 8 * (bytes_to_draw - byte) + pixel;
break; if (x_absolute >= SSD1306_W)
} {
// looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8 break;
buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8); }
// state of current pixel // looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8
uint8_t input_pixel = input_byte & (1 << pixel); buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8);
// state of current pixel
uint8_t input_pixel = input_byte & (1 << pixel);
switch (color_mode) { switch (color_mode)
case 0: {
// write pixels as they are case 0:
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0); // write pixels as they are
break; ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0);
case 1: break;
// write pixels after inversion case 1:
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0); // write pixels after inversion
break; ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0);
case 2: break;
// 0 clears pixel case 2:
ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask; // 0 clears pixel
break; ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask;
case 3: break;
// 1 sets pixel case 3:
ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0; // 1 sets pixel
break; ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0;
case 4: break;
// 0 sets pixel case 4:
ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0; // 0 sets pixel
break; ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0;
case 5: break;
// 1 clears pixel case 5:
ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF; // 1 clears pixel
break; ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF;
} break;
} }
#if SSD1306_LOG_IMAGE == 1 }
printf("%02x ", input_byte); #if SSD1306_LOG_IMAGE == 1
#endif printf("%02x ", input_byte);
} #endif
#if SSD1306_LOG_IMAGE == 1 }
printf("\n\r"); #if SSD1306_LOG_IMAGE == 1
#endif printf("\n\r");
} #endif
}
} }
/* /*
@ -373,13 +391,13 @@ void ssd1306_drawImage(uint32_t x, uint32_t y, const unsigned char* input, uint3
*/ */
void ssd1306_drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) void ssd1306_drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
{ {
// clipping // clipping
if((x >= SSD1306_W) || (y >= SSD1306_H)) return; if ((x >= SSD1306_W) || (y >= SSD1306_H)) return;
if((y+h-1) >= SSD1306_H) h = SSD1306_H-y; if ((y + h - 1) >= SSD1306_H) h = SSD1306_H - y;
while(h--) while (h--)
{ {
ssd1306_drawPixel(x, y++, color); ssd1306_drawPixel(x, y++, color);
} }
} }
/* /*
@ -387,14 +405,14 @@ void ssd1306_drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
*/ */
void ssd1306_drawFastHLine(uint32_t x, uint32_t y, uint32_t w, uint32_t color) void ssd1306_drawFastHLine(uint32_t x, uint32_t y, uint32_t w, uint32_t color)
{ {
// clipping // clipping
if((x >= SSD1306_W) || (y >= SSD1306_H)) return; if ((x >= SSD1306_W) || (y >= SSD1306_H)) return;
if((x+w-1) >= SSD1306_W) w = SSD1306_W-x; if ((x + w - 1) >= SSD1306_W) w = SSD1306_W - x;
while (w--) while (w--)
{ {
ssd1306_drawPixel(x++, y, color); ssd1306_drawPixel(x++, y, color);
} }
} }
/* /*
@ -402,7 +420,7 @@ void ssd1306_drawFastHLine(uint32_t x, uint32_t y, uint32_t w, uint32_t color)
*/ */
int gfx_abs(int x) int gfx_abs(int x)
{ {
return (x<0) ? -x : x; return (x < 0) ? -x : x;
} }
/* /*
@ -410,9 +428,9 @@ int gfx_abs(int x)
*/ */
void gfx_swap(int *z0, int *z1) void gfx_swap(int *z0, int *z1)
{ {
uint16_t temp = *z0; uint16_t temp = *z0;
*z0 = *z1; *z0 = *z1;
*z1 = temp; *z1 = temp;
} }
/* /*
@ -420,56 +438,56 @@ void gfx_swap(int *z0, int *z1)
*/ */
void ssd1306_drawLine(int x0, int y0, int x1, int y1, uint32_t color) void ssd1306_drawLine(int x0, int y0, int x1, int y1, uint32_t color)
{ {
int32_t steep; int32_t steep;
int32_t deltax, deltay, error, ystep, x, y; int32_t deltax, deltay, error, ystep, x, y;
/* flip sense 45deg to keep error calc in range */ /* flip sense 45deg to keep error calc in range */
steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0)); steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0));
if(steep) if (steep)
{ {
gfx_swap(&x0, &y0); gfx_swap(&x0, &y0);
gfx_swap(&x1, &y1); gfx_swap(&x1, &y1);
} }
/* run low->high */ /* run low->high */
if(x0 > x1) if (x0 > x1)
{ {
gfx_swap(&x0, &x1); gfx_swap(&x0, &x1);
gfx_swap(&y0, &y1); gfx_swap(&y0, &y1);
} }
/* set up loop initial conditions */ /* set up loop initial conditions */
deltax = x1 - x0; deltax = x1 - x0;
deltay = gfx_abs(y1 - y0); deltay = gfx_abs(y1 - y0);
error = deltax/2; error = deltax / 2;
y = y0; y = y0;
if(y0 < y1) if (y0 < y1)
ystep = 1; ystep = 1;
else else
ystep = -1; ystep = -1;
/* loop x */ /* loop x */
for(x=x0;x<=x1;x++) for (x = x0; x <= x1; x++)
{ {
/* plot point */ /* plot point */
if(steep) if (steep)
/* flip point & plot */ /* flip point & plot */
ssd1306_drawPixel(y, x, color); ssd1306_drawPixel(y, x, color);
else else
/* just plot */ /* just plot */
ssd1306_drawPixel(x, y, color); ssd1306_drawPixel(x, y, color);
/* update error */ /* update error */
error = error - deltay; error = error - deltay;
/* update y */ /* update y */
if(error < 0) if (error < 0)
{ {
y = y + ystep; y = y + ystep;
error = error + deltax; error = error + deltax;
} }
} }
} }
/* /*
@ -480,22 +498,26 @@ void ssd1306_drawCircle(int x, int y, int radius, int color)
/* Bresenham algorithm */ /* Bresenham algorithm */
int x_pos = -radius; int x_pos = -radius;
int y_pos = 0; int y_pos = 0;
int err = 2 - 2 * radius; int err = 2 - 2 * radius;
int e2; int e2;
do { do
{
ssd1306_drawPixel(x - x_pos, y + y_pos, color); ssd1306_drawPixel(x - x_pos, y + y_pos, color);
ssd1306_drawPixel(x + x_pos, y + y_pos, color); ssd1306_drawPixel(x + x_pos, y + y_pos, color);
ssd1306_drawPixel(x + x_pos, y - y_pos, color); ssd1306_drawPixel(x + x_pos, y - y_pos, color);
ssd1306_drawPixel(x - x_pos, y - y_pos, color); ssd1306_drawPixel(x - x_pos, y - y_pos, color);
e2 = err; e2 = err;
if (e2 <= y_pos) { if (e2 <= y_pos)
{
err += ++y_pos * 2 + 1; err += ++y_pos * 2 + 1;
if(-x_pos == y_pos && e2 <= x_pos) { if (-x_pos == y_pos && e2 <= x_pos)
e2 = 0; {
e2 = 0;
} }
} }
if (e2 > x_pos) { if (e2 > x_pos)
{
err += ++x_pos * 2 + 1; err += ++x_pos * 2 + 1;
} }
} while (x_pos <= 0); } while (x_pos <= 0);
@ -509,10 +531,11 @@ void ssd1306_fillCircle(int x, int y, int radius, int color)
/* Bresenham algorithm */ /* Bresenham algorithm */
int x_pos = -radius; int x_pos = -radius;
int y_pos = 0; int y_pos = 0;
int err = 2 - 2 * radius; int err = 2 - 2 * radius;
int e2; int e2;
do { do
{
ssd1306_drawPixel(x - x_pos, y + y_pos, color); ssd1306_drawPixel(x - x_pos, y + y_pos, color);
ssd1306_drawPixel(x + x_pos, y + y_pos, color); ssd1306_drawPixel(x + x_pos, y + y_pos, color);
ssd1306_drawPixel(x + x_pos, y - y_pos, color); ssd1306_drawPixel(x + x_pos, y - y_pos, color);
@ -520,16 +543,19 @@ void ssd1306_fillCircle(int x, int y, int radius, int color)
ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color); ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color);
ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color); ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color);
e2 = err; e2 = err;
if (e2 <= y_pos) { if (e2 <= y_pos)
{
err += ++y_pos * 2 + 1; err += ++y_pos * 2 + 1;
if(-x_pos == y_pos && e2 <= x_pos) { if (-x_pos == y_pos && e2 <= x_pos)
{
e2 = 0; e2 = 0;
} }
} }
if(e2 > x_pos) { if (e2 > x_pos)
{
err += ++x_pos * 2 + 1; err += ++x_pos * 2 + 1;
} }
} while(x_pos <= 0); } while (x_pos <= 0);
} }
/* /*
@ -537,10 +563,10 @@ void ssd1306_fillCircle(int x, int y, int radius, int color)
*/ */
void ssd1306_drawRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color) void ssd1306_drawRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t color)
{ {
ssd1306_drawFastVLine(x, y, h, color); ssd1306_drawFastVLine(x, y, h, color);
ssd1306_drawFastVLine(x+w-1, y, h, color); ssd1306_drawFastVLine(x + w - 1, y, h, color);
ssd1306_drawFastHLine(x, y, w, color); ssd1306_drawFastHLine(x, y, w, color);
ssd1306_drawFastHLine(x, y+h-1, w, color); ssd1306_drawFastHLine(x, y + h - 1, w, color);
} }
/* /*
@ -548,21 +574,21 @@ void ssd1306_drawRect(int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t col
*/ */
void ssd1306_fillRect(uint32_t x, uint32_t y, uint8_t w, uint32_t h, uint32_t color) void ssd1306_fillRect(uint32_t x, uint32_t y, uint8_t w, uint32_t h, uint32_t color)
{ {
uint32_t m, n=y, iw = w; uint32_t m, n = y, iw = w;
/* scan vertical */ /* scan vertical */
while(h--) while (h--)
{ {
m=x; m = x;
w=iw; w = iw;
/* scan horizontal */ /* scan horizontal */
while(w--) while (w--)
{ {
/* invert pixels */ /* invert pixels */
ssd1306_drawPixel(m++, n, color); ssd1306_drawPixel(m++, n, color);
} }
n++; n++;
} }
} }
/* /*
@ -570,21 +596,21 @@ void ssd1306_fillRect(uint32_t x, uint32_t y, uint8_t w, uint32_t h, uint32_t co
*/ */
void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{ {
uint8_t m, n=y, iw = w; uint8_t m, n = y, iw = w;
/* scan vertical */ /* scan vertical */
while(h--) while (h--)
{ {
m=x; m = x;
w=iw; w = iw;
/* scan horizontal */ /* scan horizontal */
while(w--) while (w--)
{ {
/* invert pixels */ /* invert pixels */
ssd1306_xorPixel(m++, n); ssd1306_xorPixel(m++, n);
} }
n++; n++;
} }
} }
/* /*
@ -592,25 +618,25 @@ void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
*/ */
void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color) void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
{ {
uint16_t i, j, col; uint16_t i, j, col;
uint8_t d; uint8_t d;
for(i=0;i<8;i++) for (i = 0; i < 8; i++)
{ {
d = fontdata[(chr<<3)+i]; d = fontdata[(chr << 3) + i];
for(j=0;j<8;j++) for (j = 0; j < 8; j++)
{ {
if(d&0x80) if (d & 0x80)
col = color; col = color;
else else
col = (~color)&1; col = (~color) & 1;
ssd1306_drawPixel(x+j, y+i, col); ssd1306_drawPixel(x + j, y + i, col);
// next bit // next bit
d <<= 1; d <<= 1;
} }
} }
} }
/* /*
@ -618,25 +644,26 @@ void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
*/ */
void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color) void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color)
{ {
uint8_t c; uint8_t c;
while((c=*str++)) while ((c = *str++))
{ {
ssd1306_drawchar(x, y, c, color); ssd1306_drawchar(x, y, c, color);
x += 8; x += 8;
if(x>120) if (x > 120)
break; break;
} }
} }
/* /*
* enum for font size * enum for font size
*/ */
typedef enum { typedef enum
fontsize_8x8 = 1, {
fontsize_8x8 = 1,
fontsize_16x16 = 2, fontsize_16x16 = 2,
fontsize_32x32 = 4, fontsize_32x32 = 4,
fontsize_64x64 = 8, fontsize_64x64 = 8,
} font_size_t; } font_size_t;
/* /*
@ -645,7 +672,7 @@ typedef enum {
void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size) void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size)
{ {
uint16_t i, j, col; uint16_t i, j, col;
uint8_t d; uint8_t d;
// Determine the font scale factor based on the font_size parameter // Determine the font scale factor based on the font_size parameter
uint8_t font_scale = (uint8_t)font_size; uint8_t font_scale = (uint8_t)font_size;
@ -666,8 +693,10 @@ void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_
col = (~color) & 1; col = (~color) & 1;
// Draw the pixel at the original size and scaled size using nested for-loops // Draw the pixel at the original size and scaled size using nested for-loops
for (uint8_t k = 0; k < font_scale; k++) { for (uint8_t k = 0; k < font_scale; k++)
for (uint8_t l = 0; l < font_scale; l++) { {
for (uint8_t l = 0; l < font_scale; l++)
{
ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col); ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col);
} }
} }
@ -683,15 +712,15 @@ void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_
*/ */
void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size) void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size)
{ {
uint8_t c; uint8_t c;
while((c=*str++)) while ((c = *str++))
{ {
ssd1306_drawchar_sz(x, y, c, color, font_size); ssd1306_drawchar_sz(x, y, c, color, font_size);
x += 8 * font_size; x += 8 * font_size;
if(x>128 - 8 * font_size) if (x > 128 - 8 * font_size)
break; break;
} }
} }
/* /*
@ -699,25 +728,25 @@ void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_siz
*/ */
uint8_t ssd1306_init(void) uint8_t ssd1306_init(void)
{ {
// pulse reset // pulse reset
ssd1306_rst(); ssd1306_rst();
ssd1306_setbuf(0); ssd1306_setbuf(0);
// initialize OLED // initialize OLED
#if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY #if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY
uint8_t *cmd_list = (uint8_t *)ssd1306_init_array; uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
while(*cmd_list != SSD1306_TERMINATE_CMDS) while (*cmd_list != SSD1306_TERMINATE_CMDS)
{ {
if(ssd1306_cmd(*cmd_list++)) if (ssd1306_cmd(*cmd_list++))
return 1; return 1;
} }
// clear display // clear display
ssd1306_refresh(); ssd1306_refresh();
#endif #endif
return 0; return 0;
} }
#endif #endif

View file

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

View file

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

View file

@ -24,7 +24,7 @@
#endif #endif
#ifndef SSD1306_SCK_PIN #ifndef SSD1306_SCK_PIN
#define SSD1306_SCK_PIN PC5 #define SSD1306_SCK_PIN PC5
#endif #endif
#ifndef SSD1306_BAUD_RATE_PRESCALER #ifndef SSD1306_BAUD_RATE_PRESCALER
@ -36,31 +36,31 @@
*/ */
uint8_t ssd1306_spi_init(void) uint8_t ssd1306_spi_init(void)
{ {
// Enable GPIOC and SPI // Enable GPIOC and SPI
RCC->APB2PCENR |= RCC_APB2Periph_SPI1; RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
funGpioInitAll(); funGpioInitAll();
funPinMode( SSD1306_RST_PIN, GPIO_CFGLR_OUT_50Mhz_PP ); funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
funPinMode( SSD1306_CS_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_DC_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
funPinMode( SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP ); funPinMode(SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
funPinMode( SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP ); funPinMode(SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
funDigitalWrite( SSD1306_RST_PIN, FUN_HIGH ); funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
funDigitalWrite( SSD1306_CS_PIN, FUN_HIGH ); funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
funDigitalWrite( SSD1306_DC_PIN, FUN_LOW ); funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
// Configure SPI // Configure SPI
SPI1->CTLR1 = SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b | SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b |
SPI_Mode_Master | SPI_Direction_1Line_Tx | SPI_Mode_Master | SPI_Direction_1Line_Tx |
SSD1306_BAUD_RATE_PRESCALER; SSD1306_BAUD_RATE_PRESCALER;
// enable SPI port // enable SPI port
SPI1->CTLR1 |= CTLR1_SPE_Set; SPI1->CTLR1 |= CTLR1_SPE_Set;
// always succeed // always succeed
return 0; return 0;
} }
/* /*
@ -68,9 +68,9 @@ uint8_t ssd1306_spi_init(void)
*/ */
void ssd1306_rst(void) void ssd1306_rst(void)
{ {
funDigitalWrite( SSD1306_RST_PIN, FUN_LOW ); funDigitalWrite(SSD1306_RST_PIN, FUN_LOW);
Delay_Ms(10); Delay_Ms(10);
funDigitalWrite( SSD1306_RST_PIN, FUN_HIGH ); 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) uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
{ {
if(cmd) if (cmd)
{ {
funDigitalWrite( SSD1306_DC_PIN, FUN_LOW ); funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
} }
else else
{ {
funDigitalWrite( SSD1306_DC_PIN, FUN_HIGH ); funDigitalWrite(SSD1306_DC_PIN, FUN_HIGH);
} }
funDigitalWrite( SSD1306_CS_PIN, FUN_LOW ); funDigitalWrite(SSD1306_CS_PIN, FUN_LOW);
// send data // send data
while(sz--) while (sz--)
{ {
// wait for TXE // wait for TXE
while(!(SPI1->STATR & SPI_STATR_TXE)); while (!(SPI1->STATR & SPI_STATR_TXE))
;
// Send byte // Send byte
SPI1->DATAR = *data++; SPI1->DATAR = *data++;
} }
// wait for not busy before exiting // wait for not busy before exiting
while(SPI1->STATR & SPI_STATR_BSY) { } while (SPI1->STATR & SPI_STATR_BSY) {}
funDigitalWrite( SSD1306_CS_PIN, FUN_HIGH ); funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
// we're happy // we're happy
return 0; return 0;
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -8,22 +8,22 @@
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose! Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
If you are including this in main, simply If you are including this in main, simply
#define WS2812DMA_IMPLEMENTATION #define WS2812DMA_IMPLEMENTATION
Other defines inclue: Other defines inclue:
#define WSRAW #define WSRAW
#define WSRBG #define WSRBG
#define WSGRB #define WSGRB
#define WS2812B_ALLOW_INTERRUPT_NESTING #define WS2812B_ALLOW_INTERRUPT_NESTING
You will need to implement the following two functions, as callbacks from the ISR. 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 You willalso need to call
WS2812BDMAInit(); WS2812BDMAInit();
Then, whenyou want to update the LEDs, call: Then, whenyou want to update the LEDs, call:
WS2812BDMAStart( int num_leds ); WS2812BDMAStart( int num_leds );
*/ */
#ifndef _WS2812_LED_DRIVER_H #ifndef _WS2812_LED_DRIVER_H
@ -32,11 +32,11 @@
#include <stdint.h> #include <stdint.h>
// Use DMA and SPI to stream out WS2812B LED Data via the MOSI pin. // Use DMA and SPI to stream out WS2812B LED Data via the MOSI pin.
void WS2812BDMAInit( ); void WS2812BDMAInit();
void WS2812BDMAStart( int leds ); void WS2812BDMAStart(int leds);
// Callbacks that you must implement. // Callbacks that you must implement.
uint32_t WS2812BLEDCallback( int ledno ); uint32_t WS2812BLEDCallback(int ledno);
#ifdef WS2812DMA_IMPLEMENTATION #ifdef WS2812DMA_IMPLEMENTATION
@ -51,225 +51,236 @@ uint32_t WS2812BLEDCallback( int ledno );
#define WS2812B_RESET_PERIOD 2 #define WS2812B_RESET_PERIOD 2
#ifdef WSRAW #ifdef WSRAW
#define DMA_BUFFER_LEN (((DMALEDS)/2)*8) #define DMA_BUFFER_LEN (((DMALEDS) / 2) * 8)
#else #else
#define DMA_BUFFER_LEN (((DMALEDS)/2)*6) #define DMA_BUFFER_LEN (((DMALEDS) / 2) * 6)
#endif #endif
static uint16_t WS2812dmabuff[DMA_BUFFER_LEN]; static uint16_t WS2812dmabuff[DMA_BUFFER_LEN];
static volatile int WS2812LEDs; static volatile int WS2812LEDs;
static volatile int WS2812LEDPlace; static volatile int WS2812LEDPlace;
static volatile int WS2812BLEDInUse; static volatile int WS2812BLEDInUse;
// This is the code that updates a portion of the WS2812dmabuff with new data. // This is the code that updates a portion of the WS2812dmabuff with new data.
// This effectively creates the bitstream that outputs to the LEDs. // 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] = { const static uint16_t bitquartets[16] = {
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, 0b1000100010001000,
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, 0b1000100010001110,
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, 0b1000100011101000,
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, }; 0b1000100011101110,
0b1000111010001000,
0b1000111010001110,
0b1000111011101000,
0b1000111011101110,
0b1110100010001000,
0b1110100010001110,
0b1110100011101000,
0b1110100011101110,
0b1110111010001000,
0b1110111010001110,
0b1110111011101000,
0b1110111011101110,
};
int i; int i;
uint16_t * end = ptr + numhalfwords; uint16_t *end = ptr + numhalfwords;
int ledcount = WS2812LEDs; int ledcount = WS2812LEDs;
int place = WS2812LEDPlace; int place = WS2812LEDPlace;
#ifdef WSRAW #ifdef WSRAW
while( place < 0 && ptr != end ) while (place < 0 && ptr != end)
{ {
uint32_t * lptr = (uint32_t *)ptr; uint32_t *lptr = (uint32_t *)ptr;
lptr[0] = 0; lptr[0] = 0;
lptr[1] = 0; lptr[1] = 0;
lptr[2] = 0; lptr[2] = 0;
lptr[3] = 0; lptr[3] = 0;
ptr += 8; ptr += 8;
place++; place++;
} }
#else #else
while( place < 0 && ptr != end ) while (place < 0 && ptr != end)
{ {
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
place++; place++;
} }
#endif #endif
while( ptr != end ) while (ptr != end)
{ {
if( place >= ledcount ) if (place >= ledcount)
{ {
// Optionally, leave line high. // Optionally, leave line high.
while( ptr != end ) while (ptr != end)
(*ptr++) = 0;//0xffff; (*ptr++) = 0; // 0xffff;
// Only safe to do this when we're on the second leg. // Only safe to do this when we're on the second leg.
if( tce ) if (tce)
{ {
if( place == ledcount ) if (place == ledcount)
{ {
// Take the DMA out of circular mode and let it expire. // Take the DMA out of circular mode and let it expire.
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular; DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
WS2812BLEDInUse = 0; WS2812BLEDInUse = 0;
} }
place++; place++;
} }
break; break;
} }
#ifdef WSRAW #ifdef WSRAW
uint32_t ledval32bit = WS2812BLEDCallback( place++ ); uint32_t ledval32bit = WS2812BLEDCallback(place++);
ptr[6] = bitquartets[(ledval32bit>>28)&0xf]; ptr[6] = bitquartets[(ledval32bit >> 28) & 0xf];
ptr[7] = bitquartets[(ledval32bit>>24)&0xf]; ptr[7] = bitquartets[(ledval32bit >> 24) & 0xf];
ptr[4] = bitquartets[(ledval32bit>>20)&0xf]; ptr[4] = bitquartets[(ledval32bit >> 20) & 0xf];
ptr[5] = bitquartets[(ledval32bit>>16)&0xf]; ptr[5] = bitquartets[(ledval32bit >> 16) & 0xf];
ptr[2] = bitquartets[(ledval32bit>>12)&0xf]; ptr[2] = bitquartets[(ledval32bit >> 12) & 0xf];
ptr[3] = bitquartets[(ledval32bit>>8)&0xf]; ptr[3] = bitquartets[(ledval32bit >> 8) & 0xf];
ptr[0] = bitquartets[(ledval32bit>>4)&0xf]; ptr[0] = bitquartets[(ledval32bit >> 4) & 0xf];
ptr[1] = bitquartets[(ledval32bit>>0)&0xf]; ptr[1] = bitquartets[(ledval32bit >> 0) & 0xf];
ptr += 8; ptr += 8;
i += 8; i += 8;
#else #else
// Use a LUT to figure out how we should set the SPI line. // Use a LUT to figure out how we should set the SPI line.
uint32_t ledval24bit = WS2812BLEDCallback( place++ ); uint32_t ledval24bit = WS2812BLEDCallback(place++);
#ifdef WSRBG #ifdef WSRBG
ptr[0] = bitquartets[(ledval24bit>>12)&0xf]; ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[1] = bitquartets[(ledval24bit>>8)&0xf]; ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[2] = bitquartets[(ledval24bit>>20)&0xf]; ptr[2] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[3] = bitquartets[(ledval24bit>>16)&0xf]; ptr[3] = bitquartets[(ledval24bit >> 16) & 0xf];
ptr[4] = bitquartets[(ledval24bit>>4)&0xf]; ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[5] = bitquartets[(ledval24bit>>0)&0xf]; ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
#elif defined( WSGRB ) #elif defined(WSGRB)
ptr[0] = bitquartets[(ledval24bit>>12)&0xf]; ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[1] = bitquartets[(ledval24bit>>8)&0xf]; ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[2] = bitquartets[(ledval24bit>>4)&0xf]; ptr[2] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[3] = bitquartets[(ledval24bit>>0)&0xf]; ptr[3] = bitquartets[(ledval24bit >> 0) & 0xf];
ptr[4] = bitquartets[(ledval24bit>>20)&0xf]; ptr[4] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[5] = bitquartets[(ledval24bit>>16)&0xf]; ptr[5] = bitquartets[(ledval24bit >> 16) & 0xf];
#else #else
ptr[0] = bitquartets[(ledval24bit>>20)&0xf]; ptr[0] = bitquartets[(ledval24bit >> 20) & 0xf];
ptr[1] = bitquartets[(ledval24bit>>16)&0xf]; ptr[1] = bitquartets[(ledval24bit >> 16) & 0xf];
ptr[2] = bitquartets[(ledval24bit>>12)&0xf]; ptr[2] = bitquartets[(ledval24bit >> 12) & 0xf];
ptr[3] = bitquartets[(ledval24bit>>8)&0xf]; ptr[3] = bitquartets[(ledval24bit >> 8) & 0xf];
ptr[4] = bitquartets[(ledval24bit>>4)&0xf]; ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
ptr[5] = bitquartets[(ledval24bit>>0)&0xf]; ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
#endif #endif
ptr += 6; ptr += 6;
i += 6; i += 6;
#endif #endif
}
} WS2812LEDPlace = place;
WS2812LEDPlace = place;
} }
void DMA1_Channel3_IRQHandler( void ) __attribute__((interrupt)); void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt));
void DMA1_Channel3_IRQHandler( void ) void DMA1_Channel3_IRQHandler(void)
{ {
//GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling // GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
// Backup flags. // Backup flags.
volatile int intfr = DMA1->INTFR; volatile int intfr = DMA1->INTFR;
do do
{ {
// Clear all possible flags. // Clear all possible flags.
DMA1->INTFCR = DMA1_IT_GL3; DMA1->INTFCR = DMA1_IT_GL3;
// Strange note: These are backwards. DMA1_IT_HT3 should be HALF and // 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 // DMA1_IT_TC3 should be COMPLETE. But for some reason, doing this causes
// LED jitter. I am henseforth flipping the order. // LED jitter. I am henseforth flipping the order.
if( intfr & DMA1_IT_HT3 ) if (intfr & DMA1_IT_HT3)
{ {
// Halfwaay (Fill in first part) // Halfwaay (Fill in first part)
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN / 2, 1 ); WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN / 2, 1);
} }
if( intfr & DMA1_IT_TC3 ) if (intfr & DMA1_IT_TC3)
{ {
// Complete (Fill in second part) // Complete (Fill in second part)
WS2812FillBuffSec( WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0 ); WS2812FillBuffSec(WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0);
} }
intfr = DMA1->INTFR; intfr = DMA1->INTFR;
} while( intfr & DMA1_IT_GL3 ); } 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. // Enter critical section.
__disable_irq(); __disable_irq();
WS2812BLEDInUse = 1; WS2812BLEDInUse = 1;
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular; DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
DMA1_Channel3->CNTR = 0; DMA1_Channel3->CNTR = 0;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff; DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
WS2812LEDs = leds; WS2812LEDs = leds;
WS2812LEDPlace = -WS2812B_RESET_PERIOD; WS2812LEDPlace = -WS2812B_RESET_PERIOD;
__enable_irq(); __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->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular; DMA1_Channel3->CFGR |= DMA_Mode_Circular;
} }
void WS2812BDMAInit( ) void WS2812BDMAInit()
{ {
// Enable DMA + Peripherals // Enable DMA + Peripherals
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
// MOSI, Configure GPIO Pin // MOSI, Configure GPIO Pin
GPIOC->CFGLR &= ~(0xf<<(4*6)); GPIOC->CFGLR &= ~(0xf << (4 * 6));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
// Configure SPI // Configure SPI
SPI1->CTLR1 = SPI1->CTLR1 =
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_16b | SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_16b |
SPI_Mode_Master | SPI_Direction_1Line_Tx | SPI_Mode_Master | SPI_Direction_1Line_Tx |
3<<3; // Divisior = 16 (48/16 = 3MHz) 3 << 3; // Divisior = 16 (48/16 = 3MHz)
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN; SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
SPI1->HSCR = 1; 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 is for SPI1TX
DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR; DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff; DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
DMA1_Channel3->CNTR = 0;// sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!) DMA1_Channel3->CNTR = 0; // sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!)
DMA1_Channel3->CFGR = DMA1_Channel3->CFGR =
DMA_M2M_Disable | DMA_M2M_Disable |
DMA_Priority_VeryHigh | DMA_Priority_VeryHigh |
DMA_MemoryDataSize_HalfWord | DMA_MemoryDataSize_HalfWord |
DMA_PeripheralDataSize_HalfWord | DMA_PeripheralDataSize_HalfWord |
DMA_MemoryInc_Enable | DMA_MemoryInc_Enable |
DMA_Mode_Normal | // OR DMA_Mode_Circular or DMA_Mode_Normal DMA_Mode_Normal | // OR DMA_Mode_Circular or DMA_Mode_Normal
DMA_DIR_PeripheralDST | DMA_DIR_PeripheralDST |
DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts. 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_SetPriority( DMA1_Channel3_IRQn, 0<<4 ); //We don't need to tweak priority.
NVIC_EnableIRQ( DMA1_Channel3_IRQn ); NVIC_EnableIRQ(DMA1_Channel3_IRQn);
DMA1_Channel3->CFGR |= DMA_CFGR1_EN; DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
#ifdef WS2812B_ALLOW_INTERRUPT_NESTING #ifdef WS2812B_ALLOW_INTERRUPT_NESTING
__set_INTSYSCR( __get_INTSYSCR() | 2 ); // Enable interrupt nesting. __set_INTSYSCR(__get_INTSYSCR() | 2); // Enable interrupt nesting.
PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3 PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3
#endif #endif
} }
#endif #endif
#endif #endif

View file

@ -3,10 +3,10 @@
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose! Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
If you are including this in main, simply If you are including this in main, simply
#define WS2812BSIMPLE_IMPLEMENTATION #define WS2812BSIMPLE_IMPLEMENTATION
You may also want to define You may also want to define
#define WS2812BSIMPLE_NO_IRQ_TWEAKING #define WS2812BSIMPLE_NO_IRQ_TWEAKING
*/ */
@ -15,7 +15,7 @@
#include <stdint.h> #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 #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 #error WS2812B Driver Requires FUNCONF_SYSTICK_USE_HCLK
#endif #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); int port_id = (((intptr_t)port - (intptr_t)GPIOA) >> 10);
RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA<<port_id); // Make sure port is enabled. RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA << port_id); // Make sure port is enabled.
int poffset = (pin*4); int poffset = (pin * 4);
port->CFGLR = ( port->CFGLR & (~(0xf<<poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP)<<(poffset)); port->CFGLR = (port->CFGLR & (~(0xf << poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP) << (poffset));
int maskon = 1<<pin; int maskon = 1 << pin;
int maskoff = 1<<(16+pin); int maskoff = 1 << (16 + pin);
port->BSHR = maskoff; port->BSHR = maskoff;
uint8_t * end = data + len_in_bytes; uint8_t *end = data + len_in_bytes;
while( data != end ) while (data != end)
{ {
uint8_t byte = *data; uint8_t byte = *data;
int i; int i;
for( i = 0; i < 8; i++ ) for (i = 0; i < 8; i++)
{ {
if( byte & 0x80 ) if (byte & 0x80)
{ {
// WS2812B's need AT LEAST 625ns for a logical "1" // WS2812B's need AT LEAST 625ns for a logical "1"
port->BSHR = maskon; port->BSHR = maskon;
DelaySysTick(25); DelaySysTick(25);
port->BSHR = maskoff; port->BSHR = maskoff;
DelaySysTick(1); DelaySysTick(1);
} }
else else
{ {
// WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0" // WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0"
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING #ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
__disable_irq(); __disable_irq();
#endif #endif
port->BSHR = maskon; port->BSHR = maskon;
asm volatile( "nop\nnop\nnop\nnop" ); asm volatile("nop\nnop\nnop\nnop");
port->BSHR = maskoff; port->BSHR = maskoff;
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING #ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
__enable_irq(); __enable_irq();
#endif #endif
DelaySysTick(15); DelaySysTick(15);
} }
byte <<= 1; byte <<= 1;
} }
data++; data++;
} }
port->BSHR = maskoff; port->BSHR = maskoff;
} }
#endif #endif
#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 <stdint.h>
#include <string.h> #include <stdio.h>
#include <stdlib.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 *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 *no[] = {"CH32V10x", "CH32V30x", "CH32V20x", "CH32X03x", "CH32V003"};
char * WhitePull( const char ** sti ) char *WhitePull(const char **sti)
{ {
const char * st = *sti; const char *st = *sti;
int len = 0; int len = 0;
while( ( *st == ' ' || *st == '\t' || *st == '(' ) && *st ) { st++; } while ((*st == ' ' || *st == '\t' || *st == '(') && *st)
const char * sts = st; {
while( *st != ' ' && *st != '\t' && *st != '\n' && *st != ')' && *st != '(' && *st != 0 ) { st++; len++; } st++;
if( *st == ')' ) { st++; } }
char * ret = malloc( len + 1 ); const char *sts = st;
memcpy( ret, sts, len ); while (*st != ' ' && *st != '\t' && *st != '\n' && *st != ')' && *st != '(' && *st != 0)
ret[len] = 0; {
*sti = st; st++;
return ret; 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; int ret = 2;
char * wp = WhitePull( &s ); char *wp = WhitePull(&s);
int i; int i;
for( i = 0; i < sizeof(yes)/sizeof(yes[0]); i++ ) for (i = 0; i < sizeof(yes) / sizeof(yes[0]); i++)
if( strcmp( yes[i], wp ) == 0 ) ret = 1; if (strcmp(yes[i], wp) == 0) ret = 1;
if( ret != 1 ) if (ret != 1)
for( i = 0; i < sizeof(no)/sizeof(no[0]); i++ ) for (i = 0; i < sizeof(no) / sizeof(no[0]); i++)
if( strcmp( no[i], wp ) == 0 ) ret = 0; if (strcmp(no[i], wp) == 0) ret = 0;
free( wp ); free(wp);
return ret; return ret;
} }
int EvalSpec( const char * spl ) int EvalSpec(const char *spl)
{ {
int rsofar = 0; int rsofar = 0;
int i; int i;
int lastv = 0; int lastv = 0;
int lasto = -1; int lasto = -1;
int ret = 0; int ret = 0;
cont: cont:
char * wp = WhitePull( &spl ); char *wp = WhitePull(&spl);
int def = -1; int def = -1;
if( strcmp( wp, "defined" ) == 0 ) def = 1; if (strcmp(wp, "defined") == 0) def = 1;
if( strcmp( wp, "!defined" ) == 0 ) def = 2; if (strcmp(wp, "!defined") == 0) def = 2;
if( def < 0 ) return 2; if (def < 0) return 2;
char * wpn = WhitePull( &spl ); char *wpn = WhitePull(&spl);
i = NYI( wpn ); i = NYI(wpn);
//printf( "SPIN: %s/%s/%d/%d/%d\n", wp, wpn, i, def, lasto ); // printf( "SPIN: %s/%s/%d/%d/%d\n", wp, wpn, i, def, lasto );
if( i == 2 ) return 2; if (i == 2) return 2;
if( def == 2 ) i = !i; if (def == 2) i = !i;
if( lasto == 1 ) if (lasto == 1)
{ {
ret = lastv || i; ret = lastv || i;
} }
else if( lasto == 2 ) else if (lasto == 2)
ret = lastv && i; ret = lastv && i;
else else
ret = i; ret = i;
char * wpa = WhitePull( &spl ); char *wpa = WhitePull(&spl);
//printf( "WPA: \"%s\"\n", wpa ); // printf( "WPA: \"%s\"\n", wpa );
lastv = ret; lastv = ret;
lasto = -1; lasto = -1;
//printf( "RET: %d\n", ret ); // printf( "RET: %d\n", ret );
if( strcmp( wpa, "||" ) == 0 ) { lasto = 1; goto cont; } if (strcmp(wpa, "||") == 0)
else if( strcmp( wpa, "&&" ) == 0 ) { lasto = 2; goto cont; } {
else return ret; lasto = 1;
goto cont;
}
else if (strcmp(wpa, "&&") == 0)
{
lasto = 2;
goto cont;
}
else
return ret;
} }
// 0 for no // 0 for no
// 1 for yes // 1 for yes
// 2 for indeterminate // 2 for indeterminate
int NoYesInd( const char * preprocc ) int NoYesInd(const char *preprocc)
{ {
int ret; int ret;
int ofs = 0; int ofs = 0;
if( strncmp( preprocc, "#if ", 4 ) == 0 ) ofs = 4; if (strncmp(preprocc, "#if ", 4) == 0) ofs = 4;
if( strncmp( preprocc, "#elif ", 6 ) == 0 ) ofs = 6; if (strncmp(preprocc, "#elif ", 6) == 0) ofs = 6;
if( ofs ) if (ofs)
{ {
ret = EvalSpec( preprocc + ofs ); ret = EvalSpec(preprocc + ofs);
//printf( "SPEC: %d\n", ret ); // printf( "SPEC: %d\n", ret );
} }
else if( strncmp( preprocc, "#ifdef ", 7 ) == 0 ) else if (strncmp(preprocc, "#ifdef ", 7) == 0)
{ {
const char * ep = preprocc + 6; const char *ep = preprocc + 6;
char * wp = WhitePull( &ep ); char *wp = WhitePull(&ep);
ret = NYI( wp ); ret = NYI(wp);
free( wp ); free(wp);
} }
else if( strncmp( preprocc, "#ifndef ", 8 ) == 0 ) else if (strncmp(preprocc, "#ifndef ", 8) == 0)
{ {
const char * ep = preprocc + 6; const char *ep = preprocc + 6;
char * wp = WhitePull( &ep ); char *wp = WhitePull(&ep);
ret = NYI( wp ); ret = NYI(wp);
if( ret < 2 ) ret = !ret; if (ret < 2) ret = !ret;
free( wp ); free(wp);
} }
else else
ret = 2; ret = 2;
//printf( "%d-> %s\n", ret, preprocc ); // printf( "%d-> %s\n", ret, preprocc );
return ret; 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++; while (*line == ' ' || *line == '\t')
const char * linestart = line; line++;
while( *line && *match == *line ) { line++; match++; } const char *linestart = line;
if( *match == 0 ) while (*line && *match == *line)
return linestart; {
else line++;
return 0; match++;
}
if (*match == 0)
return linestart;
else
return 0;
} }
int main( int argc, char ** argv ) int main(int argc, char **argv)
{ {
if( argc != 3 ) if (argc != 3)
{ {
fprintf( stderr, "Syntax: transition [#define to trigger on] [file to convert]\nNo'd architectures:\n" ); fprintf(stderr, "Syntax: transition [#define to trigger on] [file to convert]\nNo'd architectures:\n");
int i; int i;
for( i = 0; i < sizeof(no)/sizeof(no[0]); i++ ) for (i = 0; i < sizeof(no) / sizeof(no[0]); i++)
{ {
fprintf( stderr, "\t%s\n", no[i] ); fprintf(stderr, "\t%s\n", no[i]);
} }
return -1; return -1;
} }
yes[0] = argv[1]; yes[0] = argv[1];
FILE * f = fopen( argv[2], "r" ); FILE *f = fopen(argv[2], "r");
if( !f ) if (!f)
{ {
fprintf( stderr, "Error: Could not open \"%s\"\n", argv[2] ); fprintf(stderr, "Error: Could not open \"%s\"\n", argv[2]);
return -2; return -2;
} }
char line[1024]; char line[1024];
char * l; 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 while (l = fgets(line, sizeof(line) - 1, f))
// 1 = yes {
// 2 = indeterminate const char *ss = 0;
// 3 = super no. (I.e. after a true #if clause) int nyi = yesnoind[depth];
int yesnoind[1024]; int waspre = 0;
yesnoind[0] = 1;
while( l = fgets( line, sizeof(line)-1, f ) ) if ((ss = sslineis(line, "#if ")) || (ss = sslineis(line, "#ifdef ")) || (ss = sslineis(line, "#ifndef ")))
{ {
const char * ss = 0; waspre = 1;
int nyi = yesnoind[depth]; // printf( "CHECK: %d/%s\n", depth, l );
int waspre = 0; 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 " ) ) ) int thisv = nyi;
{ int i;
waspre = 1; for (i = 0; i <= depth; i++)
//printf( "CHECK: %d/%s\n", depth, l ); {
nyi = NoYesInd( ss ); // printf( "%d", yesnoind[i] );
depth++; if (yesnoind[i] == 0 || yesnoind[i] == 3) thisv = 0;
yesnoind[depth] = nyi; }
} // printf( ">>%s", l );
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; if (thisv != 0 && thisv != 3 && (thisv != 1 || !waspre))
int i; {
for( i = 0; i <= depth; i++ ) printf("%s", l);
{ }
//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

@ -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