Added stuff from ch32fun dirs
This commit is contained in:
parent
0e9243a7e0
commit
7ef358200b
33 changed files with 31847 additions and 0 deletions
7
src/attic/CMakeLists.txt
Normal file
7
src/attic/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
add_library(attic STATIC
|
||||||
|
temp_transition_helper.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(attic PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
12914
src/attic/hardware_header_all_combined.h
Normal file
12914
src/attic/hardware_header_all_combined.h
Normal file
File diff suppressed because it is too large
Load diff
226
src/attic/temp_transition_helper.c
Normal file
226
src/attic/temp_transition_helper.c
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
#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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
31
src/ch32fun/CMakeLists.txt
Normal file
31
src/ch32fun/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Compiler flags
|
||||||
|
add_compile_options(
|
||||||
|
-g
|
||||||
|
-Os
|
||||||
|
-flto
|
||||||
|
-ffunction-sections
|
||||||
|
-fdata-sections
|
||||||
|
-fmessage-length=0
|
||||||
|
-msmall-data-limit=8
|
||||||
|
-march=rv32ec
|
||||||
|
-mabi=ilp32e
|
||||||
|
-DCH32V003=1
|
||||||
|
-static-libgcc
|
||||||
|
-nostdlib
|
||||||
|
-Wall
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ch32fun OBJECT
|
||||||
|
ch32fun.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(ch32fun PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/inc
|
||||||
|
${CMAKE_SOURCE_DIR}/src/extralibs
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ch32fun
|
||||||
|
)
|
||||||
|
|
||||||
|
#target_link_libraries(ch32fun PRIVATE
|
||||||
|
# ${CMAKE_SOURCE_DIR}/libgcc.a
|
||||||
|
#)
|
||||||
44
src/ch32fun/README.md
Normal file
44
src/ch32fun/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
## Update Status Overview
|
||||||
|
|PERIPHERAL |V003|V00x|V10x|V20x|V30x|X035|L103|M030|
|
||||||
|
|:-------------|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||||
|
|DPAL Header\* |2.0 | x |2.7 | x | x | √ | × | × |
|
||||||
|
|ADC |1.9 | x |2.1 | x | x |1.3 | × | × |
|
||||||
|
|AWU |N/A |N/A |N/A |N/A |N/A | √ |N/A |N/A |
|
||||||
|
|BKP |N/A |N/A |2.1 | x | x |N/A | × |N/A |
|
||||||
|
|CAN |N/A |N/A |N/A | x | x |N/A | × |N/A |
|
||||||
|
|CRC |N/A |N/A |2.1 | x | x |N/A | × |N/A |
|
||||||
|
|DAC |N/A |N/A |N/A |N/A | x |N/A |N/A |N/A |
|
||||||
|
|DBGMCU |1.5 | x |2.1 | x | x | √ | × | × |
|
||||||
|
|DMA | √ | x | √ | x | x | √ | × | × |
|
||||||
|
|DVP |N/A |N/A |N/A |N/A | x |N/A |N/A |N/A |
|
||||||
|
|ETH |N/A |N/A |N/A |N/A | x |N/A |N/A |N/A |
|
||||||
|
|EXIT | √ | x |2.4 | x | x | √ | × | × |
|
||||||
|
|FLASH | √ | x |2.7 | x | x |1.4 | × | × |
|
||||||
|
|FSMC |N/A |N/A |N/A |N/A | x |N/A |N/A |N/A |
|
||||||
|
|GPIO |2.0 | x |2.7 | x | x |1.6 | × | × |
|
||||||
|
|I2C | √ | x | √ | x | x |1.7 | × | × |
|
||||||
|
|IWDG | √ | x | √ | x | x | √ | × |N/A |
|
||||||
|
|LPTIM |N/A |N/A |N/A |N/A |N/A |N/A | × |N/A |
|
||||||
|
|MISC | √ | x |2.4 | x | x |1.6 | × |N/A |
|
||||||
|
|OPA | √ | x |N/A | x | x |1.3 | × | × |
|
||||||
|
|PWR |1.9 | x |2.6 | x | x |1.7 | × | × |
|
||||||
|
|RCC |1.8 | x |2.7 | x | x | √ | × | × |
|
||||||
|
|RNG |N/A |N/A |N/A |N/A | x |N/A |N/A |N/A |
|
||||||
|
|RTC |N/A |N/A | √ | x | x |N/A | × |N/A |
|
||||||
|
|SPI |1.9 | x |2.7 | x | x |1.7 | × | × |
|
||||||
|
|TIM |1.6 | x | √ | x | x | √ | × | × |
|
||||||
|
|USART | √ | x |2.4 | x | x | √ | × | × |
|
||||||
|
|USB |N/A |N/A | √ | x | x |1.8 | × | × |
|
||||||
|
|USB_HOST |N/A |N/A | √ |N/A |N/A |N/A |N/A |N/A |
|
||||||
|
|USBPD |N/A |N/A |N/A |N/A |N/A | x | × | × |
|
||||||
|
|WWWDG | √ | x | √ | x | x | √ | × | × |
|
||||||
|
|**chxxxhw.h** | √ | x | √ | √ | √ | √ | x | x |
|
||||||
|
|**minichlink**| √ | x | √ | √ | √ | √ | x | x |
|
||||||
|
|
||||||
|
* n.m: Last commit message of the header file in ch32xxx/EVT/EXAM/SRC/Peripheral/inc
|
||||||
|
* √: Merged in , version unspecified
|
||||||
|
* ×: Not merged / Unchecked
|
||||||
|
* +: Work in progress
|
||||||
|
* N/A: No header file with this suffix in EVT, does not mean that the feature is not supported
|
||||||
|
|
||||||
|
\* DPAL Header: Device Peripheral Access Layer Header File, normally named as ch32xxx.h
|
||||||
1684
src/ch32fun/ch32fun.c
Executable file
1684
src/ch32fun/ch32fun.c
Executable file
File diff suppressed because it is too large
Load diff
1052
src/ch32fun/ch32fun.h
Normal file
1052
src/ch32fun/ch32fun.h
Normal file
File diff suppressed because it is too large
Load diff
222
src/ch32fun/ch32fun.ld
Normal file
222
src/ch32fun/ch32fun.ld
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
ENTRY( InterruptVector )
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
#if TARGET_MCU_LD == 0
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
|
||||||
|
#elif TARGET_MCU_LD == 1
|
||||||
|
#if MCU_PACKAGE == 1
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
#elif MCU_PACKAGE == 2
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 32K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 10K
|
||||||
|
#else
|
||||||
|
#error "Unknown MCU package"
|
||||||
|
#endif
|
||||||
|
#elif TARGET_MCU_LD == 2
|
||||||
|
#if MCU_PACKAGE == 1
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
#elif MCU_PACKAGE == 2
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 32K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 10K
|
||||||
|
#elif MCU_PACKAGE == 3
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||||
|
#else
|
||||||
|
#error "Unknown MCU package"
|
||||||
|
#endif
|
||||||
|
#elif TARGET_MCU_LD == 3
|
||||||
|
#if MCU_PACKAGE == 1
|
||||||
|
#if TARGET_MCU_MEMORY_SPLIT == 1
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 224K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
|
||||||
|
#elif TARGET_MCU_MEMORY_SPLIT == 2
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||||
|
#elif TARGET_MCU_MEMORY_SPLIT == 3
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
|
#else
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 192K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
#endif
|
||||||
|
#elif MCU_PACKAGE == 2
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
|
#else
|
||||||
|
#error "Unknown MCU package"
|
||||||
|
#endif
|
||||||
|
#elif TARGET_MCU_LD == 4
|
||||||
|
#if MCU_PACKAGE == 1
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 62K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
#else
|
||||||
|
#error "Unknown MCU package"
|
||||||
|
#endif
|
||||||
|
#elif TARGET_MCU_LD == 5
|
||||||
|
/* CH32V002 */
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
|
||||||
|
#elif TARGET_MCU_LD == 6
|
||||||
|
/* CH32V004 */
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 32K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 6K
|
||||||
|
#elif TARGET_MCU_LD == 7
|
||||||
|
/* CH32V005, CH32V006, CH32V007 */
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 62K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
|
||||||
|
#else
|
||||||
|
#error "Unknown MCU target"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
_sinit = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(SORT_NONE(.init)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
_einit = .;
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
*(.gnu.linkonce.t.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini :
|
||||||
|
{
|
||||||
|
KEEP(*(SORT_NONE(.fini)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _etext = . );
|
||||||
|
PROVIDE( _eitcm = . );
|
||||||
|
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||||
|
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||||
|
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.ctors :
|
||||||
|
{
|
||||||
|
/* gcc uses crtbegin.o to find the start of
|
||||||
|
the constructors, so we make sure it is
|
||||||
|
first. Because this is a wildcard, it
|
||||||
|
doesn't matter if the user does not
|
||||||
|
actually link against crtbegin.o; the
|
||||||
|
linker won't look for a file to match a
|
||||||
|
wildcard. The wildcard also means that it
|
||||||
|
doesn't matter which directory crtbegin.o
|
||||||
|
is in. */
|
||||||
|
KEEP (*crtbegin.o(.ctors))
|
||||||
|
KEEP (*crtbegin?.o(.ctors))
|
||||||
|
/* We don't want to include the .ctor section from
|
||||||
|
the crtend.o file until after the sorted ctors.
|
||||||
|
The .ctor section from the crtend file contains the
|
||||||
|
end of ctors marker and it must be last */
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||||
|
KEEP (*(SORT(.ctors.*)))
|
||||||
|
KEEP (*(.ctors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dtors :
|
||||||
|
{
|
||||||
|
KEEP (*crtbegin.o(.dtors))
|
||||||
|
KEEP (*crtbegin?.o(.dtors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||||
|
KEEP (*(SORT(.dtors.*)))
|
||||||
|
KEEP (*(.dtors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_vma = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.dlalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_lma = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__global_pointer$ = . + 0x3fc; /* This gets set in the startup code. This allows -mrelax'd code to be smaller by acting as a sort of quick reference in the gp register. */
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
*(.gnu.linkonce.d.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.sdata2*)
|
||||||
|
*(.gnu.linkonce.s.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.srodata.cst16)
|
||||||
|
*(.srodata.cst8)
|
||||||
|
*(.srodata.cst4)
|
||||||
|
*(.srodata.cst2)
|
||||||
|
*(.srodata .srodata.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _edata = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _sbss = .);
|
||||||
|
*(.sbss*)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.gnu.linkonce.b.*)
|
||||||
|
*(COMMON*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _ebss = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _end = _ebss);
|
||||||
|
PROVIDE( end = . );
|
||||||
|
|
||||||
|
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.note .note.*)
|
||||||
|
*(.eh_frame .eh_frame.*)
|
||||||
|
*(.comment .comment.*)
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
262
src/ch32fun/ch32fun.mk
Normal file
262
src/ch32fun/ch32fun.mk
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
# Default prefix for Windows
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
PREFIX?=riscv64-unknown-elf
|
||||||
|
# Check if riscv64-unknown-elf-gcc exists
|
||||||
|
else ifneq ($(shell which riscv64-unknown-elf-gcc),)
|
||||||
|
PREFIX?=riscv64-unknown-elf
|
||||||
|
# We used to check if riscv64-linux-gnu-gcc exists, because it would still produce valid output with -ffreestanding.
|
||||||
|
# It was different enough that we decided not to automatically fallback to it.
|
||||||
|
# Default prefix
|
||||||
|
else
|
||||||
|
PREFIX?=riscv64-elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Fedora places newlib in a different location
|
||||||
|
ifneq ($(wildcard /etc/fedora-release),)
|
||||||
|
NEWLIB?=/usr/arm-none-eabi/include
|
||||||
|
else
|
||||||
|
NEWLIB?=/usr/include/newlib
|
||||||
|
endif
|
||||||
|
|
||||||
|
CH32FUN?=$(shell dirname $(lastword $(MAKEFILE_LIST)))
|
||||||
|
#TARGET_MCU?=CH32V003 # Because we are now opening up to more processors, don't assume this.
|
||||||
|
|
||||||
|
TARGET_EXT?=c
|
||||||
|
|
||||||
|
CH32FUN?=$(dir $(lastword $(MAKEFILE_LIST)))
|
||||||
|
MINICHLINK?=$(CH32FUN)/../minichlink
|
||||||
|
|
||||||
|
WRITE_SECTION?=flash
|
||||||
|
SYSTEM_C?=$(CH32FUN)/ch32fun.c
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),1)
|
||||||
|
EXTRA_CFLAGS+=-DFUNCONF_DEBUG=1
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS?=-g -Os -flto -ffunction-sections -fdata-sections -fmessage-length=0 -msmall-data-limit=8
|
||||||
|
LDFLAGS+=-Wl,--print-memory-usage -Wl,-Map=$(TARGET).map
|
||||||
|
|
||||||
|
ifeq ($(TARGET_MCU),CH32V003)
|
||||||
|
CFLAGS_ARCH+=-march=rv32ec -mabi=ilp32e -DCH32V003=1
|
||||||
|
GENERATED_LD_FILE?=$(CH32FUN)/generated_ch32v003.ld
|
||||||
|
TARGET_MCU_LD:=0
|
||||||
|
LINKER_SCRIPT?=$(GENERATED_LD_FILE)
|
||||||
|
LDFLAGS+=-L$(CH32FUN)/../misc -lgcc
|
||||||
|
else
|
||||||
|
MCU_PACKAGE?=1
|
||||||
|
ifeq ($(findstring CH32V00,$(TARGET_MCU)),CH32V00) # CH32V002, 4, 5, 6, 7
|
||||||
|
# Note: The CH32V003 is not a CH32V00x.
|
||||||
|
CFLAGS_ARCH+=-march=rv32eczmmul -mabi=ilp32e -DCH32V00x=1
|
||||||
|
ifeq ($(findstring CH32V002, $(TARGET_MCU)), CH32V002)
|
||||||
|
TARGET_MCU_LD:=5
|
||||||
|
else ifeq ($(findstring CH32V004, $(TARGET_MCU)), CH32V004)
|
||||||
|
TARGET_MCU_LD:=6
|
||||||
|
else ifeq ($(findstring CH32V005, $(TARGET_MCU)), CH32V005)
|
||||||
|
TARGET_MCU_LD:=7
|
||||||
|
else ifeq ($(findstring CH32V006, $(TARGET_MCU)), CH32V006)
|
||||||
|
TARGET_MCU_LD:=7
|
||||||
|
else ifeq ($(findstring CH32V007, $(TARGET_MCU)), CH32V007)
|
||||||
|
TARGET_MCU_LD:=7
|
||||||
|
else
|
||||||
|
ERROR:=$(error Unknown MCU $(TARGET_MCU))
|
||||||
|
endif
|
||||||
|
else ifeq ($(findstring CH32V10,$(TARGET_MCU)),CH32V10) # CH32V103
|
||||||
|
TARGET_MCU_PACKAGE?=CH32V103R8T6
|
||||||
|
CFLAGS_ARCH+= -march=rv32imac \
|
||||||
|
-mabi=ilp32 \
|
||||||
|
-DCH32V10x=1
|
||||||
|
|
||||||
|
# MCU Flash/RAM split
|
||||||
|
ifeq ($(findstring R8, $(TARGET_MCU_PACKAGE)), R8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring C8, $(TARGET_MCU_PACKAGE)), C8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring C6, $(TARGET_MCU_PACKAGE)), C6)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET_MCU_LD:=1
|
||||||
|
else ifeq ($(findstring CH32X03,$(TARGET_MCU)),CH32X03) # CH32X033, X035
|
||||||
|
TARGET_MCU_PACKAGE?=CH32X035F8U6
|
||||||
|
CFLAGS_ARCH+=-march=rv32imac \
|
||||||
|
-mabi=ilp32 \
|
||||||
|
-DCH32X03x=1
|
||||||
|
|
||||||
|
# MCU Flash/RAM split
|
||||||
|
ifeq ($(findstring F8, $(TARGET_MCU_PACKAGE)), F8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring R8, $(TARGET_MCU_PACKAGE)), R8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring K8, $(TARGET_MCU_PACKAGE)), K8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring C8, $(TARGET_MCU_PACKAGE)), C8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring G8, $(TARGET_MCU_PACKAGE)), G8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring G6, $(TARGET_MCU_PACKAGE)), G6)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring F7, $(TARGET_MCU_PACKAGE)), F7)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET_MCU_LD:=4
|
||||||
|
else ifeq ($(findstring CH32V20,$(TARGET_MCU)),CH32V20) # CH32V203
|
||||||
|
TARGET_MCU_PACKAGE?=CH32V203F6P6
|
||||||
|
CFLAGS_ARCH+= -march=rv32imac \
|
||||||
|
-mabi=ilp32 \
|
||||||
|
-DCH32V20x=1
|
||||||
|
|
||||||
|
# MCU Flash/RAM split
|
||||||
|
|
||||||
|
|
||||||
|
# Package
|
||||||
|
ifeq ($(findstring 203RB, $(TARGET_MCU_PACKAGE)), 203RB)
|
||||||
|
CFLAGS+=-DCH32V20x_D8
|
||||||
|
else ifeq ($(findstring 208, $(TARGET_MCU_PACKAGE)), 208)
|
||||||
|
CFLAGS+=-DCH32V20x_D8W
|
||||||
|
MCU_PACKAGE:=3
|
||||||
|
else ifeq ($(findstring F8, $(TARGET_MCU_PACKAGE)), F8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring G8, $(TARGET_MCU_PACKAGE)), G8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring K8, $(TARGET_MCU_PACKAGE)), K8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring C8, $(TARGET_MCU_PACKAGE)), C8)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring F6, $(TARGET_MCU_PACKAGE)), F6)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring G6, $(TARGET_MCU_PACKAGE)), G6)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring K6, $(TARGET_MCU_PACKAGE)), K6)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring C6, $(TARGET_MCU_PACKAGE)), C6)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring RB, $(TARGET_MCU_PACKAGE)), RB)
|
||||||
|
MCU_PACKAGE:=3
|
||||||
|
else ifeq ($(findstring GB, $(TARGET_MCU_PACKAGE)), GB)
|
||||||
|
MCU_PACKAGE:=3
|
||||||
|
else ifeq ($(findstring CB, $(TARGET_MCU_PACKAGE)), CB)
|
||||||
|
MCU_PACKAGE:=3
|
||||||
|
else ifeq ($(findstring WB, $(TARGET_MCU_PACKAGE)), WB)
|
||||||
|
MCU_PACKAGE:=3
|
||||||
|
else
|
||||||
|
CFLAGS+=-DCH32V20x_D6
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET_MCU_LD:=2
|
||||||
|
else ifeq ($(findstring CH32V30,$(TARGET_MCU)),CH32V30) #CH32V307
|
||||||
|
TARGET_MCU_PACKAGE?=CH32V307VCT6
|
||||||
|
MCU_PACKAGE?=1
|
||||||
|
TARGET_MCU_MEMORY_SPLIT?=3
|
||||||
|
ENABLE_FPU?=1
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_FPU), 1)
|
||||||
|
CFLAGS_ARCH+= -march=rv32imafc -mabi=ilp32f
|
||||||
|
else
|
||||||
|
CFLAGS_ARCH+= -march=rv32imac -mabi=ilp32 -DDISABLED_FLOAT
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS_ARCH+= \
|
||||||
|
-DCH32V30x=1 \
|
||||||
|
-DTARGET_MCU_MEMORY_SPLIT=$(TARGET_MCU_MEMORY_SPLIT)
|
||||||
|
|
||||||
|
# MCU Flash/RAM split
|
||||||
|
ifeq ($(findstring RC, $(TARGET_MCU_PACKAGE)), RC)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring VC, $(TARGET_MCU_PACKAGE)), VC)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring WC, $(TARGET_MCU_PACKAGE)), WC)
|
||||||
|
MCU_PACKAGE:=1
|
||||||
|
else ifeq ($(findstring CB, $(TARGET_MCU_PACKAGE)), CB)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring FB, $(TARGET_MCU_PACKAGE)), FB)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
else ifeq ($(findstring RB, $(TARGET_MCU_PACKAGE)), RB)
|
||||||
|
MCU_PACKAGE:=2
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Package
|
||||||
|
ifeq ($(findstring 303, $(TARGET_MCU_PACKAGE)), 303)
|
||||||
|
CFLAGS+=-DCH32V30x_D8
|
||||||
|
else
|
||||||
|
CFLAGS+=-DCH32V30x_D8C
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET_MCU_LD:=3
|
||||||
|
else
|
||||||
|
ERROR:=$(error Unknown MCU $(TARGET_MCU))
|
||||||
|
endif
|
||||||
|
|
||||||
|
LDFLAGS+=-lgcc
|
||||||
|
GENERATED_LD_FILE:=$(CH32FUN)/generated_$(TARGET_MCU_PACKAGE)_$(TARGET_MCU_MEMORY_SPLIT).ld
|
||||||
|
LINKER_SCRIPT:=$(GENERATED_LD_FILE)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS+= \
|
||||||
|
$(CFLAGS_ARCH) -static-libgcc \
|
||||||
|
-I$(NEWLIB) \
|
||||||
|
-I$(CH32FUN)/../extralibs \
|
||||||
|
-I$(CH32FUN) \
|
||||||
|
-nostdlib \
|
||||||
|
-I. -Wall $(EXTRA_CFLAGS)
|
||||||
|
|
||||||
|
LDFLAGS+=-T $(LINKER_SCRIPT) -Wl,--gc-sections
|
||||||
|
FILES_TO_COMPILE:=$(SYSTEM_C) $(TARGET).$(TARGET_EXT) $(ADDITIONAL_C_FILES)
|
||||||
|
|
||||||
|
$(TARGET).bin : $(TARGET).elf
|
||||||
|
$(PREFIX)-objdump -S $^ > $(TARGET).lst
|
||||||
|
$(PREFIX)-objcopy -O binary $< $(TARGET).bin
|
||||||
|
$(PREFIX)-objcopy -O ihex $< $(TARGET).hex
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
closechlink :
|
||||||
|
-taskkill /F /IM minichlink.exe /T
|
||||||
|
else
|
||||||
|
closechlink :
|
||||||
|
-killall minichlink
|
||||||
|
endif
|
||||||
|
|
||||||
|
terminal : monitor
|
||||||
|
|
||||||
|
monitor :
|
||||||
|
$(MINICHLINK)/minichlink -T
|
||||||
|
|
||||||
|
unbrick :
|
||||||
|
$(MINICHLINK)/minichlink -u
|
||||||
|
|
||||||
|
gdbserver :
|
||||||
|
-$(MINICHLINK)/minichlink -baG
|
||||||
|
|
||||||
|
gdbclient :
|
||||||
|
gdb-multiarch $(TARGET).elf -ex "target remote :3333"
|
||||||
|
|
||||||
|
clangd :
|
||||||
|
make clean
|
||||||
|
bear -- make build
|
||||||
|
|
||||||
|
clangd_clean :
|
||||||
|
rm -f compile_commands.json
|
||||||
|
rm -rf .cache
|
||||||
|
|
||||||
|
FLASH_COMMAND?=$(MINICHLINK)/minichlink -w $< $(WRITE_SECTION) -b
|
||||||
|
|
||||||
|
.PHONY : $(GENERATED_LD_FILE)
|
||||||
|
$(GENERATED_LD_FILE) :
|
||||||
|
$(PREFIX)-gcc -E -P -x c -DTARGET_MCU=$(TARGET_MCU) -DMCU_PACKAGE=$(MCU_PACKAGE) -DTARGET_MCU_LD=$(TARGET_MCU_LD) -DTARGET_MCU_MEMORY_SPLIT=$(TARGET_MCU_MEMORY_SPLIT) $(CH32FUN)/ch32fun.ld > $(GENERATED_LD_FILE)
|
||||||
|
|
||||||
|
$(TARGET).elf : $(FILES_TO_COMPILE) $(LINKER_SCRIPT) $(EXTRA_ELF_DEPENDENCIES)
|
||||||
|
$(PREFIX)-gcc -o $@ $(FILES_TO_COMPILE) $(CFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
|
# Rule for independently building ch32fun.o indirectly, instead of recompiling it from source every time.
|
||||||
|
# Not used in the default 003fun toolchain, but used in more sophisticated toolchains.
|
||||||
|
ch32fun.o : $(SYSTEM_C)
|
||||||
|
$(PREFIX)-gcc -c -o $@ $(SYSTEM_C) $(CFLAGS)
|
||||||
|
|
||||||
|
cv_flash : $(TARGET).bin
|
||||||
|
make -C $(MINICHLINK) all
|
||||||
|
$(FLASH_COMMAND)
|
||||||
|
|
||||||
|
cv_clean :
|
||||||
|
rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map $(TARGET).hex $(GENERATED_LD_FILE) || true
|
||||||
|
|
||||||
|
build : $(TARGET).bin
|
||||||
148
src/ch32fun/ch32v003fun-bootloader.ld
Normal file
148
src/ch32fun/ch32v003fun-bootloader.ld
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
ENTRY( InterruptVector )
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* Actually at 0x1FFFF000 but the system maps it to 0x00000000 */
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1920
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
_sinit = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(SORT_NONE(.init)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
_einit = .;
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
*(.gnu.linkonce.t.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini :
|
||||||
|
{
|
||||||
|
KEEP(*(SORT_NONE(.fini)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _etext = . );
|
||||||
|
PROVIDE( _eitcm = . );
|
||||||
|
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||||
|
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||||
|
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.ctors :
|
||||||
|
{
|
||||||
|
/* gcc uses crtbegin.o to find the start of
|
||||||
|
the constructors, so we make sure it is
|
||||||
|
first. Because this is a wildcard, it
|
||||||
|
doesn't matter if the user does not
|
||||||
|
actually link against crtbegin.o; the
|
||||||
|
linker won't look for a file to match a
|
||||||
|
wildcard. The wildcard also means that it
|
||||||
|
doesn't matter which directory crtbegin.o
|
||||||
|
is in. */
|
||||||
|
KEEP (*crtbegin.o(.ctors))
|
||||||
|
KEEP (*crtbegin?.o(.ctors))
|
||||||
|
/* We don't want to include the .ctor section from
|
||||||
|
the crtend.o file until after the sorted ctors.
|
||||||
|
The .ctor section from the crtend file contains the
|
||||||
|
end of ctors marker and it must be last */
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||||
|
KEEP (*(SORT(.ctors.*)))
|
||||||
|
KEEP (*(.ctors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dtors :
|
||||||
|
{
|
||||||
|
KEEP (*crtbegin.o(.dtors))
|
||||||
|
KEEP (*crtbegin?.o(.dtors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||||
|
KEEP (*(SORT(.dtors.*)))
|
||||||
|
KEEP (*(.dtors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_vma = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.dlalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_lma = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
*(.gnu.linkonce.d.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.sdata2*)
|
||||||
|
*(.gnu.linkonce.s.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.srodata.cst16)
|
||||||
|
*(.srodata.cst8)
|
||||||
|
*(.srodata.cst4)
|
||||||
|
*(.srodata.cst2)
|
||||||
|
*(.srodata .srodata.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _edata = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _sbss = .);
|
||||||
|
*(.sbss*)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.gnu.linkonce.b.*)
|
||||||
|
*(COMMON*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _ebss = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _end = _ebss);
|
||||||
|
PROVIDE( end = . );
|
||||||
|
|
||||||
|
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6109
src/ch32fun/ch32x03xhw.h
Normal file
6109
src/ch32fun/ch32x03xhw.h
Normal file
File diff suppressed because it is too large
Load diff
192
src/ch32v003.ld
Normal file
192
src/ch32v003.ld
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
/* Filename: ch32v003.ld
|
||||||
|
* Author: Jake Goodwin Email: jakegoodwin@gorge.works
|
||||||
|
* Date: 2025-02-28
|
||||||
|
* Description: A linker file for the WCH uC `ch32v003` that links with
|
||||||
|
* the newlib C library for functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I've added tons of comments so that when I come back to this file in a
|
||||||
|
* couple of months I remember what I was doing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*Sets the Entry point address in header of final ELF file. */
|
||||||
|
ENTRY( InterruptVector )
|
||||||
|
|
||||||
|
/*The CH32V003 has 16KiB of "Code Flash" it actually has more I think.*/
|
||||||
|
/*The CH32V003 has 2048Bytes of SRAM*/
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/*Attributes:*/
|
||||||
|
/*R -- Read-Only Sections*/
|
||||||
|
/*W -- Read and Write Sections*/
|
||||||
|
/*X -- Sections contain executable code*/
|
||||||
|
/*A -- Allocated sections */
|
||||||
|
/*I or L -- Initialized sections */
|
||||||
|
/*! -- Invert the Meaning of any attribute */
|
||||||
|
|
||||||
|
/*Syntax:*/
|
||||||
|
/*<name> (attr) : ORIGIN=<origin addr>, LENGTH = <sizeK> */
|
||||||
|
|
||||||
|
/*The Flash is usally listed as it's origin being at zero in linker scripts
|
||||||
|
* that you can find. The refernce manual shows it starting at 0x0800 0000
|
||||||
|
* but this is actually an alias that could also point to system memory of
|
||||||
|
* the area for the boot flash.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||||
|
BOOT1 (rx) : ORIGIN = 0x1FFFF000, LENGTH = 1920 /*System flash/boot*/
|
||||||
|
VENDR (rx) : ORIGIN = 0x1FFFF7C0, LENGTH = 128 /*Vendor Bytes*/
|
||||||
|
OPTIB (rx) : ORIGIN = 0x1FFFF800, LENGTH = 64 /*Option Bytes*/
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Sections command is used to create differnt output sections in the final elf
|
||||||
|
* executable */
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
_sinit = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(SORT_NONE(.init)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
_einit = .;
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
*(.gnu.linkonce.t.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini :
|
||||||
|
{
|
||||||
|
KEEP(*(SORT_NONE(.fini)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _etext = . );
|
||||||
|
PROVIDE( _eitcm = . );
|
||||||
|
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||||
|
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||||
|
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.ctors :
|
||||||
|
{
|
||||||
|
/* gcc uses crtbegin.o to find the start of
|
||||||
|
the constructors, so we make sure it is
|
||||||
|
first. Because this is a wildcard, it
|
||||||
|
doesn't matter if the user does not
|
||||||
|
actually link against crtbegin.o; the
|
||||||
|
linker won't look for a file to match a
|
||||||
|
wildcard. The wildcard also means that it
|
||||||
|
doesn't matter which directory crtbegin.o
|
||||||
|
is in. */
|
||||||
|
KEEP (*crtbegin.o(.ctors))
|
||||||
|
KEEP (*crtbegin?.o(.ctors))
|
||||||
|
/* We don't want to include the .ctor section from
|
||||||
|
the crtend.o file until after the sorted ctors.
|
||||||
|
The .ctor section from the crtend file contains the
|
||||||
|
end of ctors marker and it must be last */
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||||
|
KEEP (*(SORT(.ctors.*)))
|
||||||
|
KEEP (*(.ctors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dtors :
|
||||||
|
{
|
||||||
|
KEEP (*crtbegin.o(.dtors))
|
||||||
|
KEEP (*crtbegin?.o(.dtors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||||
|
KEEP (*(SORT(.dtors.*)))
|
||||||
|
KEEP (*(.dtors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.dalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_vma = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.dlalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_lma = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__global_pointer$ = . + 0x3fc; /* This gets set in the startup code. This allows -mrelax'd code to be smaller by acting as a sort of quick reference in the gp register. */
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
*(.gnu.linkonce.d.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.sdata2*)
|
||||||
|
*(.gnu.linkonce.s.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.srodata.cst16)
|
||||||
|
*(.srodata.cst8)
|
||||||
|
*(.srodata.cst4)
|
||||||
|
*(.srodata.cst2)
|
||||||
|
*(.srodata .srodata.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _edata = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _sbss = .);
|
||||||
|
*(.sbss*)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.gnu.linkonce.b.*)
|
||||||
|
*(COMMON*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _ebss = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
|
||||||
|
PROVIDE( _end = _ebss);
|
||||||
|
PROVIDE( end = . );
|
||||||
|
|
||||||
|
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.note .note.*)
|
||||||
|
*(.eh_frame .eh_frame.*)
|
||||||
|
*(.comment .comment.*)
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
7
src/extralibs/CMakeLists.txt
Normal file
7
src/extralibs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
add_library(hsusb_v30x STATIC
|
||||||
|
hsusb_v30x.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(extralibs PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
493
src/extralibs/ch32v003_GPIO_branchless.h
Normal file
493
src/extralibs/ch32v003_GPIO_branchless.h
Normal file
|
|
@ -0,0 +1,493 @@
|
||||||
|
// 2023-06-26 recallmenot
|
||||||
|
|
||||||
|
// ######## necessities
|
||||||
|
|
||||||
|
// include guards
|
||||||
|
#ifndef CH32V003_GPIO_BR_H
|
||||||
|
#define CH32V003_GPIO_BR_H
|
||||||
|
|
||||||
|
// includes
|
||||||
|
#include "../ch32fun/ch32fun.h"
|
||||||
|
#include <stdint.h> //uintN_t support
|
||||||
|
|
||||||
|
/*######## library description
|
||||||
|
This is a speedy and light GPIO library due to
|
||||||
|
static inlining of most functions
|
||||||
|
compile-time abstraction
|
||||||
|
branchless where it counts
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*######## library usage and configuration
|
||||||
|
|
||||||
|
first, enable the desired port.
|
||||||
|
|
||||||
|
digital usage is quite Arduino-like:
|
||||||
|
pinMode
|
||||||
|
digitalWrite
|
||||||
|
digitalWrite_lo
|
||||||
|
digitalWrite_hi
|
||||||
|
digitalRead
|
||||||
|
|
||||||
|
pins are referenced as a single byte, ST-style:
|
||||||
|
the upper 4 bytes specify the port, where A is 0, C is 2 etc.
|
||||||
|
the lower 4 bytes specify the pin
|
||||||
|
both are used as uint4_t, not as 4 individual bits, allowing to address up to 16 pins on up to 16 ports.
|
||||||
|
this style of referencing a pin is called "GPIOv" in this library.
|
||||||
|
|
||||||
|
additionally, there are functions to operate an entire port at once
|
||||||
|
this can be useful where setting all pins one by one would be too inefficient / unnecessary
|
||||||
|
an example where this may be useful: https://www.youtube.com/watch?v=cy6o8TrDUFU
|
||||||
|
GPIO_port_digitalWrite
|
||||||
|
GPIO_port_digitalRead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
analog-to-digital usage is almost Arduino-like:
|
||||||
|
pinMode
|
||||||
|
ADCinit
|
||||||
|
analogRead
|
||||||
|
|
||||||
|
By default, this library inserts a delay of 300 µs between configuration of the ADC input mux and the time the conversion starts.
|
||||||
|
This serves to counteract the high input impedance of the ADC, especially if it is increased by external resistors.
|
||||||
|
The input impedance of port A appears to be especially large.
|
||||||
|
You may modify it to your liking using the following define before including this library.
|
||||||
|
#define GPIO_ADC_MUX_DELAY 1200
|
||||||
|
|
||||||
|
GPIO_ADC_sampletime controls the time each conversion is granted, by default it is GPIO_ADC_sampletime_241cy_default, all options originate from the GPIO_ADC_sampletimes enum.
|
||||||
|
To alter it, you have 3 options:
|
||||||
|
* `#define GPIO_ADC_sampletime GPIO_ADC_sampletime_43cy` before including this library
|
||||||
|
* call the GPIO_ADC_set_sampletime function-like macro to momentarrily set it for one channel
|
||||||
|
* call the GPIO_ADC_set_sampletimes_all function-like macro to to momentarrily set it for all channels
|
||||||
|
|
||||||
|
You may also disable the ADC to save power between infrequent measurements.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
digital-to-analog (PWM) usage is quite different:
|
||||||
|
pinMode
|
||||||
|
GPIO_timX_map
|
||||||
|
GPIO_timX_init
|
||||||
|
GPIO_timX_enableCH
|
||||||
|
GPIO_timX_analogWrite
|
||||||
|
|
||||||
|
This is due to the fact that the CH32V003 has 2 timers, which each can be connected to 4 pre-defined sets (mappings) of pins.
|
||||||
|
Then you address the 4 channels of the timers, instead of the pins.
|
||||||
|
|
||||||
|
By default, the timers will be configured to count up to 2^10, which is 10 bits or 1024 discrete steps.
|
||||||
|
You may alter this to suit your needs, for example to an 8 bit resolution (256 discrete steps).
|
||||||
|
Insert this before including this library:
|
||||||
|
#define GPIO_timer_resolution (1 << 8)
|
||||||
|
|
||||||
|
By default, the timers will operate with a clock prescaler of 2 but you may choose 1 or 4 if you wish to alter the speed.
|
||||||
|
Insert this before including this library:
|
||||||
|
#define GPIO_timer_prescaler TIM_CKD_DIV1; // APB_CLOCK / 1024 / 1 = 46.9kHz
|
||||||
|
|
||||||
|
You may calculate the base frequency of the timer (the rate of repetition of your signal) like follows:
|
||||||
|
fpwm = APB_CLOCK / resolution / prescaler
|
||||||
|
This puts the defaults at an inaudible 23.4kHz.
|
||||||
|
The higher the frequency, the greater the EMI radiation will be.
|
||||||
|
With low frequencies, say below 1000Hz, LEDs may exhibit perceivable flicker.
|
||||||
|
|
||||||
|
Since this library enables compare capture preload (OCxPE of CHCTLRy), writing a value into the compare register using analogWrite will automatically apply it (=load into shadow register) when the timer starts its next cycle.
|
||||||
|
This avoids a bug whereby writing a compare value lower than the current counter value, the output will glitch high for the next cycle, resulting in flickery updates.
|
||||||
|
Writing `TIMx->SWEVGR |= TIM_UG` will immediately update the shadow register and cause the same issue.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ######## ports, pins and states: use these for the functions below!
|
||||||
|
|
||||||
|
#define GPIOv_from_PORT_PIN(GPIO_port_n, pin)
|
||||||
|
|
||||||
|
enum GPIO_port_n
|
||||||
|
{
|
||||||
|
GPIO_port_A = 0b00,
|
||||||
|
GPIO_port_C = 0b10,
|
||||||
|
GPIO_port_D = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GPIO_pinModes
|
||||||
|
{
|
||||||
|
GPIO_pinMode_I_floating,
|
||||||
|
GPIO_pinMode_I_pullUp,
|
||||||
|
GPIO_pinMode_I_pullDown,
|
||||||
|
GPIO_pinMode_I_analog,
|
||||||
|
GPIO_pinMode_O_pushPull,
|
||||||
|
GPIO_pinMode_O_openDrain,
|
||||||
|
GPIO_pinMode_O_pushPullMux,
|
||||||
|
GPIO_pinMode_O_openDrainMux,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum lowhigh
|
||||||
|
{
|
||||||
|
low,
|
||||||
|
high,
|
||||||
|
};
|
||||||
|
|
||||||
|
// analog inputs
|
||||||
|
enum GPIO_analog_inputs
|
||||||
|
{
|
||||||
|
GPIO_Ain0_A2,
|
||||||
|
GPIO_Ain1_A1,
|
||||||
|
GPIO_Ain2_C4,
|
||||||
|
GPIO_Ain3_D2,
|
||||||
|
GPIO_Ain4_D3,
|
||||||
|
GPIO_Ain5_D5,
|
||||||
|
GPIO_Ain6_D6,
|
||||||
|
GPIO_Ain7_D4,
|
||||||
|
GPIO_AinVref,
|
||||||
|
GPIO_AinVcal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// how many cycles the ADC shall sample the input for (speed vs precision)
|
||||||
|
enum GPIO_ADC_sampletimes
|
||||||
|
{
|
||||||
|
GPIO_ADC_sampletime_3cy,
|
||||||
|
GPIO_ADC_sampletime_9cy,
|
||||||
|
GPIO_ADC_sampletime_15cy,
|
||||||
|
GPIO_ADC_sampletime_30cy,
|
||||||
|
GPIO_ADC_sampletime_43cy,
|
||||||
|
GPIO_ADC_sampletime_57cy,
|
||||||
|
GPIO_ADC_sampletime_73cy,
|
||||||
|
GPIO_ADC_sampletime_241cy_default,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GPIO_tim1_output_sets
|
||||||
|
{
|
||||||
|
GPIO_tim1_output_set_0__D2_A1_C3_C4__D0_A2_D1,
|
||||||
|
GPIO_tim1_output_set_1__C6_C7_C0_D3__C3_C4_D1,
|
||||||
|
GPIO_tim1_output_set_2__D2_A1_C3_C4__D0_A2_D1,
|
||||||
|
GPIO_tim1_output_set_3__C4_C7_C5_D4__C3_D2_C6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GPIO_tim2_output_sets
|
||||||
|
{
|
||||||
|
GPIO_tim2_output_set_0__D4_D3_C0_D7,
|
||||||
|
GPIO_tim2_output_set_1__C5_C2_D2_C1,
|
||||||
|
GPIO_tim2_output_set_2__C1_D3_C0_D7,
|
||||||
|
GPIO_tim2_output_set_3__C1_C7_D6_D5,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ######## interface function overview: use these!
|
||||||
|
// most functions have been reduced to function-like macros, actual definitions downstairs
|
||||||
|
|
||||||
|
// setup
|
||||||
|
#define GPIO_port_enable(GPIO_port_n)
|
||||||
|
#define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed)
|
||||||
|
|
||||||
|
// digital
|
||||||
|
#define GPIO_digitalWrite_hi(GPIOv)
|
||||||
|
#define GPIO_digitalWrite_lo(GPIOv)
|
||||||
|
#define GPIO_digitalWrite(GPIOv, lowhigh)
|
||||||
|
#define GPIO_digitalWrite_branching(GPIOv, lowhigh)
|
||||||
|
#define GPIO_digitalRead(GPIOv)
|
||||||
|
#define GPIO_port_digitalWrite(GPIO_port_n, byte)
|
||||||
|
#define GPIO_port_digitalRead(GPIO_port_n)
|
||||||
|
|
||||||
|
// analog to digital
|
||||||
|
static inline void GPIO_ADCinit();
|
||||||
|
#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime)
|
||||||
|
#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime)
|
||||||
|
#define GPIO_ADC_set_power(enable)
|
||||||
|
#define GPIO_ADC_calibrate()
|
||||||
|
static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input);
|
||||||
|
|
||||||
|
// digital to analog (PWM)
|
||||||
|
#define GPIO_tim1_map(GPIO_tim1_output_set)
|
||||||
|
#define GPIO_tim2_map(GPIO_tim2_output_set)
|
||||||
|
static inline void GPIO_tim1_init();
|
||||||
|
static inline void GPIO_tim2_init();
|
||||||
|
#define GPIO_tim1_enableCH(channel)
|
||||||
|
#define GPIO_tim2_enableCH(channel)
|
||||||
|
#define GPIO_tim1_analogWrite(channel, value)
|
||||||
|
#define GPIO_tim2_analogWrite(channel, value)
|
||||||
|
|
||||||
|
// ######## internal function declarations
|
||||||
|
|
||||||
|
// ######## internal variables
|
||||||
|
|
||||||
|
// ######## preprocessor macros
|
||||||
|
|
||||||
|
#define CONCAT(a, b) a##b
|
||||||
|
#define CONCAT_INDIRECT(a, b) CONCAT(a, b)
|
||||||
|
|
||||||
|
#undef GPIOv_from_PORT_PIN
|
||||||
|
#define GPIOv_from_PORT_PIN(GPIO_port_n, pin) ((GPIO_port_n << 4) | (pin))
|
||||||
|
#define GPIOv_to_PORT(GPIOv) (GPIOv >> 4)
|
||||||
|
#define GPIOv_to_PIN(GPIOv) (GPIOv & 0b1111)
|
||||||
|
#define GPIOv_to_GPIObase(GPIOv) ((GPIO_TypeDef *)(uintptr_t)((GPIOA_BASE + (0x400 * (GPIOv >> 4)))))
|
||||||
|
|
||||||
|
#define GPIOx_to_port_n2(GPIOx) GPIOx_to_port_n_##GPIOx
|
||||||
|
#define GPIOx_to_port_n(GPIOx) GPIOx_to_port_n2(GPIOx)
|
||||||
|
#define GPIOx_to_port_n_GPIO_port_A 0b00
|
||||||
|
#define GPIOx_to_port_n_GPIO_port_C 0b10
|
||||||
|
#define GPIOx_to_port_n_GPIO_port_D 0b11
|
||||||
|
|
||||||
|
#define GPIO_port_n_to_GPIOx2(GPIO_port_n) GPIO_port_n_to_GPIOx_##GPIO_port_n
|
||||||
|
#define GPIO_port_n_to_GPIOx(GPIO_port_n) GPIO_port_n_to_GPIOx2(GPIO_port_n)
|
||||||
|
#define GPIO_port_n_to_GPIOx_GPIO_port_A GPIOA
|
||||||
|
#define GPIO_port_n_to_GPIOx_GPIO_port_C GPIOC
|
||||||
|
#define GPIO_port_n_to_GPIOx_GPIO_port_D GPIOD
|
||||||
|
|
||||||
|
#define GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph_##GPIO_port_n
|
||||||
|
#define GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n)
|
||||||
|
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_A RCC_APB2Periph_GPIOA
|
||||||
|
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_C RCC_APB2Periph_GPIOC
|
||||||
|
#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_D RCC_APB2Periph_GPIOD
|
||||||
|
|
||||||
|
#define GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG_##GPIO_pinMode(GPIO_Speed)
|
||||||
|
#define GPIO_pinMode_to_CFG(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_floating(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_FLOATING)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullUp(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullDown(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_PUPD)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_analog(GPIO_Speed) (GPIO_Speed_In | GPIO_CNF_IN_ANALOG)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPull(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrain(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_OD)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPullMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP_AF)
|
||||||
|
#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrainMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_IN_ANALOG)
|
||||||
|
|
||||||
|
#define GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD_##GPIO_pinMode(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD(GPIO_pinMode, GPIOv) GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_floating(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (GPIOv_to_PIN(GPIOv) + 16))
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_analog(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIOv)
|
||||||
|
#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrainMux(GPIOv)
|
||||||
|
|
||||||
|
#define GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD_##GPIO_pinMode(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD(GPIO_pinMode, GPIO_port_n) GPIO_port_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_floating(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b11111111
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIO_port_n) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = 0b00000000
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_I_analog(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIO_port_n)
|
||||||
|
#define GPIO_port_pinMode_set_PUPD_GPIO_pinMode_O_openDrainMux(GPIO_port_n)
|
||||||
|
|
||||||
|
#if !defined(GPIO_ADC_MUX_DELAY)
|
||||||
|
#define GPIO_ADC_MUX_DELAY 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(GPIO_ADC_sampletime)
|
||||||
|
#define GPIO_ADC_sampletime GPIO_ADC_sampletime_241cy_default
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(GPIO_timer_resolution)
|
||||||
|
#define GPIO_timer_resolution (1 << 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(GPIO_timer_prescaler)
|
||||||
|
#define GPIO_timer_prescaler TIM_CKD_DIV2 // APB_CLOCK / 1024 / 2 = 23.4kHz
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ######## define requirements / maintenance defines
|
||||||
|
|
||||||
|
// ######## small function definitions, static inline
|
||||||
|
|
||||||
|
#undef GPIO_port_enable
|
||||||
|
#define GPIO_port_enable(GPIO_port_n) RCC->APB2PCENR |= GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n);
|
||||||
|
|
||||||
|
#define GPIO_port_pinMode(GPIO_port_n, pinMode, GPIO_Speed) ({ \
|
||||||
|
GPIO_port_n_to_GPIOx(GPIO_port_n)->CFGLR = (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 0)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 1)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 2)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 3)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 4)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 5)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 6)) | \
|
||||||
|
(GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * 7)); \
|
||||||
|
GPIO_port_pinMode_set_PUPD(pinMode, GPIO_port_n); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef GPIO_port_digitalWrite
|
||||||
|
#define GPIO_port_digitalWrite(GPIO_port_n, byte) GPIO_port_n_to_GPIOx(GPIO_port_n)->OUTDR = byte
|
||||||
|
|
||||||
|
#undef GPIO_port_digitalRead
|
||||||
|
#define GPIO_port_digitalRead(GPIO_port_n) (GPIO_port_n_to_GPIOx(GPIO_port_n)->INDR & 0b11111111)
|
||||||
|
|
||||||
|
#undef GPIO_pinMode
|
||||||
|
#define GPIO_pinMode(GPIOv, pinMode, GPIO_Speed) ({ \
|
||||||
|
GPIOv_to_GPIObase(GPIOv)->CFGLR &= ~(0b1111 << (4 * GPIOv_to_PIN(GPIOv))); \
|
||||||
|
GPIOv_to_GPIObase(GPIOv)->CFGLR |= (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * GPIOv_to_PIN(GPIOv))); \
|
||||||
|
GPIO_pinMode_set_PUPD(pinMode, GPIOv); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef GPIO_digitalWrite_hi
|
||||||
|
#define GPIO_digitalWrite_hi(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << GPIOv_to_PIN(GPIOv))
|
||||||
|
#undef GPIO_digitalWrite_lo
|
||||||
|
#define GPIO_digitalWrite_lo(GPIOv) GPIOv_to_GPIObase(GPIOv)->BSHR = (1 << (16 + GPIOv_to_PIN(GPIOv)))
|
||||||
|
|
||||||
|
#undef GPIO_digitalWrite
|
||||||
|
#define GPIO_digitalWrite(GPIOv, lowhigh) GPIO_digitalWrite_##lowhigh(GPIOv)
|
||||||
|
#define GPIO_digitalWrite_low(GPIOv) GPIO_digitalWrite_lo(GPIOv)
|
||||||
|
#define GPIO_digitalWrite_0(GPIOv) GPIO_digitalWrite_lo(GPIOv)
|
||||||
|
#define GPIO_digitalWrite_high(GPIOv) GPIO_digitalWrite_hi(GPIOv)
|
||||||
|
#define GPIO_digitalWrite_1(GPIOv) GPIO_digitalWrite_hi(GPIOv)
|
||||||
|
|
||||||
|
#undef GPIO_digitalWrite_branching
|
||||||
|
#define GPIO_digitalWrite_branching(GPIOv, lowhigh) (lowhigh ? GPIO_digitalWrite_hi(GPIOv) : GPIO_digitalWrite_lo(GPIOv))
|
||||||
|
|
||||||
|
#undef GPIO_digitalRead
|
||||||
|
#define GPIO_digitalRead(GPIOv) ((GPIOv_to_GPIObase(GPIOv)->INDR >> GPIOv_to_PIN(GPIOv)) & 0b1)
|
||||||
|
|
||||||
|
#undef GPIO_ADC_set_sampletime
|
||||||
|
// 0:7 => 3/9/15/30/43/57/73/241 cycles
|
||||||
|
#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \
|
||||||
|
ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \
|
||||||
|
ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef GPIO_ADC_set_sampletimes_all
|
||||||
|
#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \
|
||||||
|
ADC1->SAMPTR2 &= 0; \
|
||||||
|
ADC1->SAMPTR2 |= \
|
||||||
|
GPIO_ADC_sampletime << (0 * 3) | GPIO_ADC_sampletime << (1 * 3) | GPIO_ADC_sampletime << (2 * 3) | GPIO_ADC_sampletime << (3 * 3) | GPIO_ADC_sampletime << (4 * 3) | GPIO_ADC_sampletime << (5 * 3) | GPIO_ADC_sampletime << (6 * 3) | GPIO_ADC_sampletime << (7 * 3) | GPIO_ADC_sampletime << (8 * 3) | GPIO_ADC_sampletime << (9 * 3); \
|
||||||
|
ADC1->SAMPTR1 &= 0; \
|
||||||
|
ADC1->SAMPTR1 |= \
|
||||||
|
GPIO_ADC_sampletime << (0 * 3) | GPIO_ADC_sampletime << (1 * 3) | GPIO_ADC_sampletime << (2 * 3) | GPIO_ADC_sampletime << (3 * 3) | GPIO_ADC_sampletime << (4 * 3) | GPIO_ADC_sampletime << (5 * 3); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef GPIO_ADC_set_power
|
||||||
|
#define GPIO_ADC_set_power2(enable) GPIO_ADC_set_power_##enable
|
||||||
|
#define GPIO_ADC_set_power(enable) GPIO_ADC_set_power2(enable)
|
||||||
|
#define GPIO_ADC_set_power_1 ADC1->CTLR2 |= ADC_ADON
|
||||||
|
#define GPIO_ADC_set_power_0 ADC1->CTLR2 &= ~(ADC_ADON)
|
||||||
|
|
||||||
|
#undef GPIO_ADC_calibrate
|
||||||
|
#define GPIO_ADC_calibrate() ({ \
|
||||||
|
ADC1->CTLR2 |= ADC_RSTCAL; \
|
||||||
|
while (ADC1->CTLR2 & ADC_RSTCAL) \
|
||||||
|
; \
|
||||||
|
ADC1->CTLR2 |= ADC_CAL; \
|
||||||
|
while (ADC1->CTLR2 & ADC_CAL) \
|
||||||
|
; \
|
||||||
|
})
|
||||||
|
|
||||||
|
// large but will likely only ever be called once
|
||||||
|
static inline void GPIO_ADCinit()
|
||||||
|
{
|
||||||
|
// select ADC clock source
|
||||||
|
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2
|
||||||
|
RCC->CFGR0 &= ~(0x1F << 11);
|
||||||
|
|
||||||
|
// enable clock to the ADC
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_ADC1;
|
||||||
|
|
||||||
|
// Reset the ADC to init all regs
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
|
||||||
|
|
||||||
|
// set sampling time for all inputs to 241 cycles
|
||||||
|
GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime);
|
||||||
|
|
||||||
|
// set trigger to software
|
||||||
|
ADC1->CTLR2 |= ADC_EXTSEL;
|
||||||
|
|
||||||
|
// pre-clear conversion queue
|
||||||
|
ADC1->RSQR1 = 0;
|
||||||
|
ADC1->RSQR2 = 0;
|
||||||
|
ADC1->RSQR3 = 0;
|
||||||
|
|
||||||
|
// power the ADC
|
||||||
|
GPIO_ADC_set_power(1);
|
||||||
|
GPIO_ADC_calibrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input)
|
||||||
|
{
|
||||||
|
// set mux to selected input
|
||||||
|
ADC1->RSQR3 = input;
|
||||||
|
// allow everything to precharge
|
||||||
|
Delay_Us(GPIO_ADC_MUX_DELAY);
|
||||||
|
// start sw conversion (auto clears)
|
||||||
|
ADC1->CTLR2 |= ADC_SWSTART;
|
||||||
|
// wait for conversion complete
|
||||||
|
while (!(ADC1->STATR & ADC_EOC)) {}
|
||||||
|
// get result
|
||||||
|
return ADC1->RDATAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef GPIO_tim1_map
|
||||||
|
#define GPIO_tim1_map(GPIO_tim1_output_set) ({ \
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
|
||||||
|
AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#undef GPIO_tim2_map
|
||||||
|
#define GPIO_tim2_map(GPIO_tim2_output_set) ({ \
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \
|
||||||
|
AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \
|
||||||
|
})
|
||||||
|
|
||||||
|
static inline void GPIO_tim1_init()
|
||||||
|
{
|
||||||
|
// enable TIM1
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_TIM1;
|
||||||
|
// reset TIM1 to init all regs
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
|
||||||
|
// SMCFGR: default clk input is CK_INT
|
||||||
|
// set clock prescaler divider
|
||||||
|
TIM1->PSC = GPIO_timer_prescaler;
|
||||||
|
// set PWM total cycle width
|
||||||
|
TIM1->ATRLR = GPIO_timer_resolution;
|
||||||
|
// CTLR1: default is up, events generated, edge align
|
||||||
|
// enable auto-reload of preload
|
||||||
|
TIM1->CTLR1 |= TIM_ARPE;
|
||||||
|
// initialize counter
|
||||||
|
TIM1->SWEVGR |= TIM_UG;
|
||||||
|
// disengage brake
|
||||||
|
TIM1->BDTR |= TIM_MOE;
|
||||||
|
// Enable TIM1
|
||||||
|
TIM1->CTLR1 |= TIM_CEN;
|
||||||
|
}
|
||||||
|
static inline void GPIO_tim2_init()
|
||||||
|
{
|
||||||
|
// enable TIM2
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
|
||||||
|
// reset TIM2 to init all regs
|
||||||
|
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
|
||||||
|
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
|
||||||
|
// SMCFGR: default clk input is CK_INT
|
||||||
|
// set clock prescaler divider
|
||||||
|
TIM2->PSC = GPIO_timer_prescaler;
|
||||||
|
// set PWM total cycle width
|
||||||
|
TIM2->ATRLR = GPIO_timer_resolution;
|
||||||
|
// CTLR1: default is up, events generated, edge align
|
||||||
|
// enable auto-reload of preload
|
||||||
|
TIM2->CTLR1 |= TIM_ARPE;
|
||||||
|
// initialize counter
|
||||||
|
TIM2->SWEVGR |= TIM_UG;
|
||||||
|
// Enable TIM2
|
||||||
|
TIM2->CTLR1 |= TIM_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GPIO_timer_channel_set2(timer, channel) GPIO_timer_channel_set_##channel(timer)
|
||||||
|
#define GPIO_timer_channel_set(timer, channel) GPIO_timer_channel_set2(timer, channel)
|
||||||
|
#define GPIO_timer_channel_set_1(timer) timer->CHCTLR1 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
|
||||||
|
#define GPIO_timer_channel_set_2(timer) timer->CHCTLR1 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
|
||||||
|
#define GPIO_timer_channel_set_3(timer) timer->CHCTLR2 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable)
|
||||||
|
#define GPIO_timer_channel_set_4(timer) timer->CHCTLR2 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8)
|
||||||
|
|
||||||
|
#undef GPIO_tim1_enableCH
|
||||||
|
#define GPIO_tim1_enableCH(channel) ({ \
|
||||||
|
GPIO_timer_channel_set(TIM1, channel); \
|
||||||
|
TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
|
||||||
|
})
|
||||||
|
#undef GPIO_tim2_enableCH
|
||||||
|
#define GPIO_tim2_enableCH(channel) ({ \
|
||||||
|
GPIO_timer_channel_set(TIM2, channel); \
|
||||||
|
TIM2->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define GPIO_timer_CVR(channel) CONCAT_INDIRECT(CH, CONCAT_INDIRECT(channel, CVR))
|
||||||
|
|
||||||
|
#undef GPIO_tim1_analogWrite
|
||||||
|
#define GPIO_tim1_analogWrite(channel, value) TIM1->GPIO_timer_CVR(channel) = value;
|
||||||
|
#undef GPIO_tim2_analogWrite
|
||||||
|
#define GPIO_tim2_analogWrite(channel, value) TIM2->GPIO_timer_CVR(channel) = value;
|
||||||
|
|
||||||
|
#endif // CH32V003_GPIO_BR_H
|
||||||
360
src/extralibs/ch32v003_SPI.h
Normal file
360
src/extralibs/ch32v003_SPI.h
Normal file
|
|
@ -0,0 +1,360 @@
|
||||||
|
// ######## necessities
|
||||||
|
|
||||||
|
// include guards
|
||||||
|
#ifndef CH32V003_SPI_H
|
||||||
|
#define CH32V003_SPI_H
|
||||||
|
|
||||||
|
// includes
|
||||||
|
#include "ch32fun.h"
|
||||||
|
#include <stdint.h> //uintN_t support
|
||||||
|
|
||||||
|
#ifndef APB_CLOCK
|
||||||
|
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*######## library usage and configuration
|
||||||
|
|
||||||
|
in the .c files that use this library, you'll need to #define some configuration options _before_ the #include "ch32v003_SPI.h"
|
||||||
|
|
||||||
|
SYSTEM_CORE_CLOCK and APB_CLOCK should be defined already as APB_CLOCK is used by this library
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APB_CLOCK
|
||||||
|
#define APB_CLOCK FUNCONF_SYSTEM_CORE_CLOCK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
to enable using the functions of this library:
|
||||||
|
#define CH32V003_SPI_IMPLEMENTATION
|
||||||
|
|
||||||
|
to configure the settings of the SPI bus, first, declare the desired bus speed
|
||||||
|
|
||||||
|
#define CH32V003_SPI_SPEED_HZ 1000000
|
||||||
|
|
||||||
|
then pick the desired setting of each group:
|
||||||
|
|
||||||
|
#define CH32V003_SPI_DIRECTION_2LINE_TXRX
|
||||||
|
#define CH32V003_SPI_DIRECTION_1LINE_TX
|
||||||
|
|
||||||
|
#define CH32V003_SPI_CLK_MODE_POL0_PHA0 //leading = rising trailing = falling sample on leading default if you're unsure
|
||||||
|
#define CH32V003_SPI_CLK_MODE_POL0_PHA1 //leading = rising trailing = falling sample on trailing
|
||||||
|
#define CH32V003_SPI_CLK_MODE_POL1_PHA0 //leading = falling trailing = rising sample on leading
|
||||||
|
#define CH32V003_SPI_CLK_MODE_POL1_PHA1 //leading = falling trailing = rising sample on trailing
|
||||||
|
|
||||||
|
#define CH32V003_SPI_NSS_HARDWARE_PC0 // _NSS toggled by hardware, automatic
|
||||||
|
#define CH32V003_SPI_NSS_HARDWARE_PC1 // NSS toggled by hardware, automatic
|
||||||
|
#define CH32V003_SPI_NSS_SOFTWARE_PC3 // PC3 toggled by software, automatic, manual setters available
|
||||||
|
#define CH32V003_SPI_NSS_SOFTWARE_PC4 // PC4 toggled by software, automatic, manual setters available
|
||||||
|
#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually!
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ######## function overview (declarations): use these!
|
||||||
|
// initialize and configure the SPI peripheral
|
||||||
|
static inline void SPI_init();
|
||||||
|
|
||||||
|
// establish / end a connection to the SPI device
|
||||||
|
static inline void SPI_begin_8();
|
||||||
|
static inline void SPI_begin_16();
|
||||||
|
static inline void SPI_end();
|
||||||
|
|
||||||
|
// manually set the NSS (chip select) pin high / low
|
||||||
|
// "SPI_NSS_HIGH_FN" and "SPI_NSS_LOW_FN" only become available functions if the selected NSS is software PC3 or PC4
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
static inline void SPI_NSS_software_low();
|
||||||
|
static inline void SPI_NSS_software_high();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// read / write the SPI device
|
||||||
|
// these commands are raw, you'll have to consider all other steps in SPI_transfer!
|
||||||
|
static inline uint8_t SPI_read_8();
|
||||||
|
static inline uint16_t SPI_read_16();
|
||||||
|
static inline void SPI_write_8(uint8_t data);
|
||||||
|
static inline void SPI_write_16(uint16_t data);
|
||||||
|
|
||||||
|
// send a command and get a response from the SPI device
|
||||||
|
// you'll use this for most devices
|
||||||
|
static inline uint8_t SPI_transfer_8(uint8_t data);
|
||||||
|
static inline uint16_t SPI_transfer_16(uint16_t data);
|
||||||
|
|
||||||
|
// SPI peripheral power enable / disable (default off, init() automatically enables)
|
||||||
|
// send SPI peripheral to sleep
|
||||||
|
static inline void SPI_poweroff();
|
||||||
|
// wake SPI peripheral from sleep
|
||||||
|
static inline void SPI_poweron();
|
||||||
|
|
||||||
|
// helper: kill / restore all interrupts on the CH32V003
|
||||||
|
static inline void kill_interrrupts();
|
||||||
|
static inline void restore_interrupts();
|
||||||
|
|
||||||
|
// ######## internal function declarations
|
||||||
|
static inline void SPI_wait_TX_complete();
|
||||||
|
static inline uint8_t SPI_is_RX_empty();
|
||||||
|
static inline void SPI_wait_RX_available();
|
||||||
|
|
||||||
|
// ######## internal variables
|
||||||
|
static uint16_t EXT1_INTENR_backup;
|
||||||
|
|
||||||
|
// ######## preprocessor macros
|
||||||
|
// min and max helper macros
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
// stringify for displaying what #defines evaluated to at preprocessor stage
|
||||||
|
#define VALUE_TO_STRING(x) #x
|
||||||
|
#define VALUE(x) VALUE_TO_STRING(x)
|
||||||
|
#define VAR_NAME_VALUE(var) #var "=" VALUE(var)
|
||||||
|
|
||||||
|
// compile-time log2
|
||||||
|
#define LOG2(x) ((x) == 0 ? -1 : __builtin_ctz(x))
|
||||||
|
|
||||||
|
// compile-time clock prescaler calculation: log2(APB_CLOCK/SPEED_BUS)
|
||||||
|
#define SPI_CLK_RATIO (APB_CLOCK / CH32V003_SPI_SPEED_HZ)
|
||||||
|
#define SPI_CLK_PRESCALER LOG2(SPI_CLK_RATIO)
|
||||||
|
|
||||||
|
// ensure that CLOCK_PRESCALER_VALUE is within the range of 0..7
|
||||||
|
_Static_assert(SPI_CLK_PRESCALER >= 0 && SPI_CLK_PRESCALER <= 7, "SPI_CLK_PRESCALER is out of range (0..7). Please set a different SPI bus speed. prescaler = log2(f_CPU/f_SPI)");
|
||||||
|
// #pragma message(VAR_NAME_VALUE(SPI_CLK_PRESCALER))
|
||||||
|
|
||||||
|
// ######## preprocessor #define requirements
|
||||||
|
|
||||||
|
#if !defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && !defined(CH32V003_SPI_DIRECTION_1LINE_TX)
|
||||||
|
#warning "none of the CH32V003_SPI_DIRECTION_ options were defined!"
|
||||||
|
#endif
|
||||||
|
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && defined(CH32V003_SPI_DIRECTION_1LINE_TX)
|
||||||
|
#warning "both CH32V003_SPI_DIRECTION_ options were defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1
|
||||||
|
#warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!"
|
||||||
|
#endif
|
||||||
|
#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0
|
||||||
|
#warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1
|
||||||
|
#warning "more than one of the CH32V003_SPI_NSS_ options were defined!"
|
||||||
|
#endif
|
||||||
|
#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \
|
||||||
|
(defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0
|
||||||
|
#warning "none of the CH32V003_SPI_NSS_ options were defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ######## small function definitions, static inline
|
||||||
|
static inline void SPI_init()
|
||||||
|
{
|
||||||
|
SPI_poweron();
|
||||||
|
|
||||||
|
// reset control register
|
||||||
|
SPI1->CTLR1 = 0;
|
||||||
|
|
||||||
|
// set prescaler
|
||||||
|
SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER << 3);
|
||||||
|
|
||||||
|
// set clock polarity and phase
|
||||||
|
#if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0)
|
||||||
|
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge);
|
||||||
|
#elif defined(CH32V003_SPI_CLK_MODE_POL0_PHA1)
|
||||||
|
SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge);
|
||||||
|
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA0)
|
||||||
|
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge);
|
||||||
|
#elif defined(CH32V003_SPI_CLK_MODE_POL1_PHA1)
|
||||||
|
SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// configure NSS pin, master mode
|
||||||
|
#if defined(CH32V003_SPI_NSS_HARDWARE_PC0)
|
||||||
|
// _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1
|
||||||
|
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 0));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 0);
|
||||||
|
AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0)
|
||||||
|
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
|
||||||
|
#elif defined(CH32V003_SPI_NSS_HARDWARE_PC1)
|
||||||
|
// NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1
|
||||||
|
SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 1));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 1);
|
||||||
|
SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high
|
||||||
|
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
|
||||||
|
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 3));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 3);
|
||||||
|
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 4));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
|
||||||
|
#elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL)
|
||||||
|
SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SCK on PC5, 10MHz Output, alt func, push-pull
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 5));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5);
|
||||||
|
|
||||||
|
// CH32V003 is master
|
||||||
|
SPI1->CTLR1 |= SPI_Mode_Master;
|
||||||
|
|
||||||
|
// set data direction and configure data pins
|
||||||
|
#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX)
|
||||||
|
SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex;
|
||||||
|
|
||||||
|
// MOSI on PC6, 10MHz Output, alt func, push-pull
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 6));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
|
||||||
|
|
||||||
|
// MISO on PC7, 10MHz input, floating
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 7));
|
||||||
|
GPIOC->CFGLR |= GPIO_CNF_IN_FLOATING << (4 * 7);
|
||||||
|
#elif defined(CH32V003_SPI_DIRECTION_1LINE_TX)
|
||||||
|
SPI1->CTLR1 |= SPI_Direction_1Line_Tx;
|
||||||
|
|
||||||
|
// MOSI on PC6, 10MHz Output, alt func, push-pull
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 6));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void SPI_begin_8()
|
||||||
|
{
|
||||||
|
SPI1->CTLR1 &= ~(SPI_CTLR1_DFF); // DFF 16bit data-length enable, writable only when SPE is 0
|
||||||
|
SPI1->CTLR1 |= SPI_CTLR1_SPE;
|
||||||
|
}
|
||||||
|
static inline void SPI_begin_16()
|
||||||
|
{
|
||||||
|
SPI1->CTLR1 |= SPI_CTLR1_DFF; // DFF 16bit data-length enable, writable only when SPE is 0
|
||||||
|
SPI1->CTLR1 |= SPI_CTLR1_SPE;
|
||||||
|
}
|
||||||
|
static inline void SPI_end()
|
||||||
|
{
|
||||||
|
SPI1->CTLR1 &= ~(SPI_CTLR1_SPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3)
|
||||||
|
static inline void SPI_NSS_software_high()
|
||||||
|
{
|
||||||
|
GPIOC->BSHR = (1 << 3);
|
||||||
|
}
|
||||||
|
static inline void SPI_NSS_software_low()
|
||||||
|
{
|
||||||
|
GPIOC->BSHR = (1 << (16 + 3));
|
||||||
|
}
|
||||||
|
#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
static inline void SPI_NSS_software_high()
|
||||||
|
{
|
||||||
|
GPIOC->BSHR = (1 << 4);
|
||||||
|
}
|
||||||
|
static inline void SPI_NSS_software_low()
|
||||||
|
{
|
||||||
|
GPIOC->BSHR = (1 << (16 + 4));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline uint8_t SPI_read_8()
|
||||||
|
{
|
||||||
|
return SPI1->DATAR;
|
||||||
|
}
|
||||||
|
static inline uint16_t SPI_read_16()
|
||||||
|
{
|
||||||
|
return SPI1->DATAR;
|
||||||
|
}
|
||||||
|
static inline void SPI_write_8(uint8_t data)
|
||||||
|
{
|
||||||
|
SPI1->DATAR = data;
|
||||||
|
}
|
||||||
|
static inline void SPI_write_16(uint16_t data)
|
||||||
|
{
|
||||||
|
SPI1->DATAR = data;
|
||||||
|
}
|
||||||
|
static inline uint8_t SPI_transfer_8(uint8_t data)
|
||||||
|
{
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
SPI_NSS_software_high();
|
||||||
|
#endif
|
||||||
|
SPI_write_8(data);
|
||||||
|
SPI_wait_TX_complete();
|
||||||
|
asm volatile("nop");
|
||||||
|
SPI_wait_RX_available();
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
SPI_NSS_software_low();
|
||||||
|
#endif
|
||||||
|
return SPI_read_8();
|
||||||
|
}
|
||||||
|
static inline uint16_t SPI_transfer_16(uint16_t data)
|
||||||
|
{
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
SPI_NSS_software_high();
|
||||||
|
#endif
|
||||||
|
SPI_write_16(data);
|
||||||
|
SPI_wait_TX_complete();
|
||||||
|
asm volatile("nop");
|
||||||
|
SPI_wait_RX_available();
|
||||||
|
#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4)
|
||||||
|
SPI_NSS_software_low();
|
||||||
|
#endif
|
||||||
|
return SPI_read_16();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void SPI_poweroff()
|
||||||
|
{
|
||||||
|
SPI_end();
|
||||||
|
RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1;
|
||||||
|
}
|
||||||
|
static inline void SPI_poweron()
|
||||||
|
{
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kill_interrrupts()
|
||||||
|
{
|
||||||
|
EXT1_INTENR_backup = EXTI->INTENR;
|
||||||
|
// zero the interrupt enable register to disable all interrupts
|
||||||
|
EXTI->INTENR = 0;
|
||||||
|
}
|
||||||
|
static inline void restore_interrupts()
|
||||||
|
{
|
||||||
|
EXTI->INTENR = EXT1_INTENR_backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######## small internal function definitions, static inline
|
||||||
|
static inline void SPI_wait_TX_complete()
|
||||||
|
{
|
||||||
|
while (!(SPI1->STATR & SPI_STATR_TXE)) {}
|
||||||
|
}
|
||||||
|
static inline uint8_t SPI_is_RX_empty()
|
||||||
|
{
|
||||||
|
return SPI1->STATR & SPI_STATR_RXNE;
|
||||||
|
}
|
||||||
|
static inline void SPI_wait_RX_available()
|
||||||
|
{
|
||||||
|
while (!(SPI1->STATR & SPI_STATR_RXNE)) {}
|
||||||
|
}
|
||||||
|
static inline void SPI_wait_not_busy()
|
||||||
|
{
|
||||||
|
while ((SPI1->STATR & SPI_STATR_BSY) != 0) {}
|
||||||
|
}
|
||||||
|
static inline void SPI_wait_transmit_finished()
|
||||||
|
{
|
||||||
|
SPI_wait_TX_complete();
|
||||||
|
SPI_wait_not_busy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ######## implementation block
|
||||||
|
// #define CH32V003_SPI_IMPLEMENTATION //enable so LSP can give you text colors while working on the implementation block, disable for normal use of the library
|
||||||
|
#if defined(CH32V003_SPI_IMPLEMENTATION)
|
||||||
|
|
||||||
|
// no functions here because I think all of the functions are small enough to static inline
|
||||||
|
|
||||||
|
#endif // CH32V003_SPI_IMPLEMENTATION
|
||||||
|
#endif // CH32V003_SPI_H
|
||||||
224
src/extralibs/ch32v003_touch.h
Normal file
224
src/extralibs/ch32v003_touch.h
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
#ifndef _CH32V003_TOUCH_H
|
||||||
|
#define _CH32V003_TOUCH_H
|
||||||
|
|
||||||
|
/** ADC-based Capactive Touch Control.
|
||||||
|
|
||||||
|
see cap_touch_adc.c for an example.
|
||||||
|
|
||||||
|
// Enable GPIOD, C and ADC
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1;
|
||||||
|
InitTouchADC();
|
||||||
|
|
||||||
|
|
||||||
|
// Then do this any time you want to read some touches.
|
||||||
|
sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations );
|
||||||
|
sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations );
|
||||||
|
sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations );
|
||||||
|
sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations );
|
||||||
|
sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations );
|
||||||
|
sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations );
|
||||||
|
sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations );
|
||||||
|
sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations );
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TOUCH_ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation.
|
||||||
|
|
||||||
|
// Can either be 0 or 1.
|
||||||
|
// If 0: Measurement low and rises high. So more pressed is smaller number.
|
||||||
|
// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT.
|
||||||
|
// If you are doing more prox, use mode 0, otherwise, use mode 1.
|
||||||
|
#define TOUCH_SLOPE 1
|
||||||
|
|
||||||
|
// If you set this to 1, it will glitch the line, so it will only read
|
||||||
|
// anything reasonable if the capacitance can overcome that initial spike.
|
||||||
|
// Typically, it seems if you use this you probbly don't need to do
|
||||||
|
// any pre-use calibration.
|
||||||
|
#define TOUCH_FLAT 0
|
||||||
|
|
||||||
|
// Macro used for force-alingining ADC timing
|
||||||
|
#define FORCEALIGNADC \
|
||||||
|
asm volatile( \
|
||||||
|
"\n\
|
||||||
|
.balign 4\n\
|
||||||
|
andi a2, %[cyccnt], 3\n\
|
||||||
|
c.slli a2, 1\n\
|
||||||
|
c.addi a2, 12\n\
|
||||||
|
auipc a1, 0\n\
|
||||||
|
c.add a2, a1\n\
|
||||||
|
jalr a2, 1\n\
|
||||||
|
.long 0x00010001\n\
|
||||||
|
.long 0x00010001\n\
|
||||||
|
" ::[cyccnt] "r"(SysTick->CNT) : "a1", "a2");
|
||||||
|
|
||||||
|
static void InitTouchADC();
|
||||||
|
void InitTouchADC()
|
||||||
|
{
|
||||||
|
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2
|
||||||
|
RCC->CFGR0 &= ~(0x1F << 11);
|
||||||
|
|
||||||
|
// Set up single conversion on chl 2
|
||||||
|
ADC1->RSQR1 = 0;
|
||||||
|
ADC1->RSQR2 = 0;
|
||||||
|
|
||||||
|
// turn on ADC and set rule group to sw trig
|
||||||
|
ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
|
||||||
|
|
||||||
|
// Reset calibration
|
||||||
|
ADC1->CTLR2 |= ADC_RSTCAL;
|
||||||
|
while (ADC1->CTLR2 & ADC_RSTCAL)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Calibrate
|
||||||
|
ADC1->CTLR2 |= ADC_CAL;
|
||||||
|
while (ADC1->CTLR2 & ADC_CAL)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run from RAM to get even more stable timing.
|
||||||
|
// This function call takes about 8.1uS to execute.
|
||||||
|
static uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
|
||||||
|
uint32_t ReadTouchPin(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
|
||||||
|
{
|
||||||
|
uint32_t ret = 0;
|
||||||
|
|
||||||
|
__disable_irq();
|
||||||
|
FORCEALIGNADC
|
||||||
|
ADC1->RSQR3 = adcno;
|
||||||
|
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
|
||||||
|
__enable_irq();
|
||||||
|
|
||||||
|
uint32_t CFGBASE = io->CFGLR & (~(0xf << (4 * portpin)));
|
||||||
|
uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | CFGBASE;
|
||||||
|
uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | CFGBASE;
|
||||||
|
|
||||||
|
// If we run multiple times with slightly different wait times, we can
|
||||||
|
// reduce the impact of the ADC's DNL.
|
||||||
|
|
||||||
|
#if TOUCH_FLAT == 1
|
||||||
|
#define RELEASEIO \
|
||||||
|
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
|
||||||
|
io->CFGLR = CFGFLOAT;
|
||||||
|
#else
|
||||||
|
#define RELEASEIO \
|
||||||
|
io->CFGLR = CFGFLOAT; \
|
||||||
|
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define INNER_LOOP(n) \
|
||||||
|
{ \
|
||||||
|
/* Only lock IRQ for a very narrow window. */ \
|
||||||
|
__disable_irq(); \
|
||||||
|
FORCEALIGNADC \
|
||||||
|
\
|
||||||
|
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
|
||||||
|
this We are catching it onthe slope much more effectively. */ \
|
||||||
|
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
|
||||||
|
\
|
||||||
|
ADD_N_NOPS(n) \
|
||||||
|
\
|
||||||
|
RELEASEIO \
|
||||||
|
\
|
||||||
|
/* Sampling actually starts here, somewhere, so we can let other \
|
||||||
|
interrupts run */ \
|
||||||
|
__enable_irq(); \
|
||||||
|
while (!(ADC1->STATR & ADC_EOC)) \
|
||||||
|
; \
|
||||||
|
io->CFGLR = CFGDRIVE; \
|
||||||
|
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
|
||||||
|
ret += ADC1->RDATAR; \
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < iterations; i++)
|
||||||
|
{
|
||||||
|
// Wait a variable amount of time based on loop iteration, in order
|
||||||
|
// to get a variety of RC points and minimize DNL.
|
||||||
|
|
||||||
|
INNER_LOOP(0);
|
||||||
|
INNER_LOOP(2);
|
||||||
|
INNER_LOOP(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run from RAM to get even more stable timing.
|
||||||
|
// This function call takes about 8.1uS to execute.
|
||||||
|
static uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations) __attribute__((noinline, section(".srodata")));
|
||||||
|
uint32_t ReadTouchPinSafe(GPIO_TypeDef *io, int portpin, int adcno, int iterations)
|
||||||
|
{
|
||||||
|
uint32_t ret = 0;
|
||||||
|
|
||||||
|
ADC1->RSQR3 = adcno;
|
||||||
|
ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME << (3 * adcno);
|
||||||
|
|
||||||
|
// If we run multiple times with slightly different wait times, we can
|
||||||
|
// reduce the impact of the ADC's DNL.
|
||||||
|
|
||||||
|
#define INNER_LOOP_SAFE(n) \
|
||||||
|
{ \
|
||||||
|
/* Only lock IRQ for a very narrow window. */ \
|
||||||
|
__disable_irq(); \
|
||||||
|
\
|
||||||
|
FORCEALIGNADC \
|
||||||
|
\
|
||||||
|
/* Tricky - we start the ADC BEFORE we transition the pin. By doing \
|
||||||
|
this We are catching it onthe slope much more effectively. */ \
|
||||||
|
ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \
|
||||||
|
\
|
||||||
|
ADD_N_NOPS(n) \
|
||||||
|
\
|
||||||
|
io->CFGLR = ((GPIO_CFGLR_IN_PUPD) << (4 * portpin)) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
|
||||||
|
io->BSHR = 1 << (portpin + 16 * TOUCH_SLOPE); \
|
||||||
|
\
|
||||||
|
/* Sampling actually starts here, somewhere, so we can let other \
|
||||||
|
interrupts run */ \
|
||||||
|
__enable_irq(); \
|
||||||
|
while (!(ADC1->STATR & ADC_EOC)) \
|
||||||
|
; \
|
||||||
|
__disable_irq(); \
|
||||||
|
io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP) << (4 * portpin) | (io->CFGLR & (~(0xf << (4 * portpin)))); \
|
||||||
|
__enable_irq(); \
|
||||||
|
io->BSHR = 1 << (portpin + (16 * (1 - TOUCH_SLOPE))); \
|
||||||
|
ret += ADC1->RDATAR; \
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < iterations; i++)
|
||||||
|
{
|
||||||
|
// Wait a variable amount of time based on loop iteration, in order
|
||||||
|
// to get a variety of RC points and minimize DNL.
|
||||||
|
|
||||||
|
INNER_LOOP_SAFE(0);
|
||||||
|
INNER_LOOP_SAFE(2);
|
||||||
|
INNER_LOOP_SAFE(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Valve Corporation
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
548
src/extralibs/ch32v307gigabit.h
Normal file
548
src/extralibs/ch32v307gigabit.h
Normal file
|
|
@ -0,0 +1,548 @@
|
||||||
|
#ifndef _CH32V307GIGABIT_H
|
||||||
|
#define _CH32V307GIGABIT_H
|
||||||
|
|
||||||
|
/* This file is written against the RTL8211E
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #define CH32V307GIGABIT_MCO25 1
|
||||||
|
// #define CH32V307GIGABIT_PHYADDRESS 0
|
||||||
|
|
||||||
|
#define CH32V307GIGABIT_RXBUFNB 8
|
||||||
|
#define CH32V307GIGABIT_TXBUFNB 8
|
||||||
|
#define CH32V307GIGABIT_BUFFSIZE 1524 // 1518 + 4, Rounded up.
|
||||||
|
|
||||||
|
#define CH32V307GIGABIT_CFG_CLOCK_DELAY 4 // 0..7
|
||||||
|
#define CH32V307GIGABIT_CFG_CLOCK_PHASE 0
|
||||||
|
|
||||||
|
#ifndef CH32V307GIGABIT_MCO25
|
||||||
|
#define CH32V307GIGABIT_MCO25 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CH32V307GIGABIT_PHYADDRESS
|
||||||
|
#define CH32V307GIGABIT_PHYADDRESS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CH32V307GIGABIT_PHY_TIMEOUT
|
||||||
|
#define CH32V307GIGABIT_PHY_TIMEOUT 0x10000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Additional definitions, not part of ch32v003fun.h
|
||||||
|
#ifndef CH32V307GIGABIT_PHY_RSTB
|
||||||
|
#define CH32V307GIGABIT_PHY_RSTB PA10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ETH DMA structure definition (From ch32v30x_eth.c
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t volatile Status; /* Status */
|
||||||
|
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
|
||||||
|
uint32_t Buffer1Addr; /* Buffer1 address pointer */
|
||||||
|
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
|
||||||
|
} ETH_DMADESCTypeDef;
|
||||||
|
|
||||||
|
// You must provide:
|
||||||
|
|
||||||
|
void ch32v307ethHandleReconfig(int link, int speed, int duplex);
|
||||||
|
|
||||||
|
// Return non-zero to suppress OWN return (for if you are still holding onto the buffer)
|
||||||
|
int ch32v307ethInitHandlePacket(uint8_t *data, int frame_length, ETH_DMADESCTypeDef *dmadesc);
|
||||||
|
|
||||||
|
void ch32v307ethInitHandleTXC(void);
|
||||||
|
|
||||||
|
// This library provides:
|
||||||
|
static void ch32v307ethGetMacInUC(uint8_t *mac);
|
||||||
|
static int ch32v307ethInit(void);
|
||||||
|
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc); // Does not copy.
|
||||||
|
static int ch32v307ethTickPhy(void);
|
||||||
|
|
||||||
|
// Data pursuent to ethernet.
|
||||||
|
uint8_t ch32v307eth_mac[6] = {0};
|
||||||
|
uint16_t ch32v307eth_phyid = 0; // 0xc916 = RTL8211FS / 0xc915 = RTL8211E-VB
|
||||||
|
ETH_DMADESCTypeDef ch32v307eth_DMARxDscrTab[CH32V307GIGABIT_RXBUFNB] __attribute__((aligned(4))); // MAC receive descriptor, 4-byte aligned
|
||||||
|
ETH_DMADESCTypeDef ch32v307eth_DMATxDscrTab[CH32V307GIGABIT_TXBUFNB] __attribute__((aligned(4))); // MAC send descriptor, 4-byte aligned
|
||||||
|
uint8_t ch32v307eth_MACRxBuf[CH32V307GIGABIT_RXBUFNB * CH32V307GIGABIT_BUFFSIZE] __attribute__((aligned(4))); // MAC receive buffer, 4-byte aligned
|
||||||
|
ETH_DMADESCTypeDef *pDMARxGet;
|
||||||
|
ETH_DMADESCTypeDef *pDMATxSet;
|
||||||
|
|
||||||
|
// Internal functions
|
||||||
|
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val);
|
||||||
|
static int ch32v307ethPHYRegAsyncRead(int reg, int *value);
|
||||||
|
static int ch32v307ethPHYRegRead(uint32_t reg);
|
||||||
|
|
||||||
|
static int ch32v307ethPHYRegAsyncRead(int reg, int *value)
|
||||||
|
{
|
||||||
|
static uint8_t reg_request_count = 0;
|
||||||
|
|
||||||
|
uint32_t miiar = ETH->MACMIIAR;
|
||||||
|
if (miiar & ETH_MACMIIAR_MB)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (((miiar & ETH_MACMIIAR_MR) >> 6) != reg || reg_request_count < 2)
|
||||||
|
{
|
||||||
|
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
|
||||||
|
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
|
||||||
|
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
|
||||||
|
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
|
||||||
|
reg_request_count++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
reg_request_count = 0;
|
||||||
|
*value = ETH->MACMIIDR;
|
||||||
|
ETH->MACMIIAR |= ETH_MACMIIAR_MR; // Poison register.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ch32v307ethTickPhy(void)
|
||||||
|
{
|
||||||
|
int speed, linked, duplex;
|
||||||
|
const int reg = (ch32v307eth_phyid == 0xc916) ? 0x1a : 0x11; // PHYSR (different on each part)
|
||||||
|
int miidr;
|
||||||
|
if (ch32v307ethPHYRegAsyncRead(reg, &miidr)) return -1;
|
||||||
|
|
||||||
|
printf("REG: %02x / %04x / %04x\n", reg, miidr, ch32v307eth_phyid);
|
||||||
|
|
||||||
|
if (reg == 0x1a)
|
||||||
|
{
|
||||||
|
speed = ((miidr >> 4) & 3);
|
||||||
|
linked = ((miidr >> 2) & 1);
|
||||||
|
duplex = ((miidr >> 3) & 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
speed = ((miidr >> 14) & 3);
|
||||||
|
linked = ((miidr >> 10) & 1);
|
||||||
|
duplex = ((miidr >> 13) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("LINK INFO: %d %d %d\n", speed, linked, duplex);
|
||||||
|
if (linked)
|
||||||
|
{
|
||||||
|
uint32_t oldmaccr = ETH->MACCR;
|
||||||
|
uint32_t newmaccr = (oldmaccr & ~((1 << 11) | (3 << 14))) | (speed << 14) | (duplex << 11);
|
||||||
|
if (newmaccr != oldmaccr)
|
||||||
|
{
|
||||||
|
ETH->MACCR = newmaccr;
|
||||||
|
ch32v307ethHandleReconfig(linked, speed, duplex);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on ETH_WritePHYRegister
|
||||||
|
static int ch32v307ethPHYRegWrite(uint32_t reg, uint32_t val)
|
||||||
|
{
|
||||||
|
ETH->MACMIIDR = val;
|
||||||
|
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
|
||||||
|
(((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11)) | // ETH_MACMIIAR_PA
|
||||||
|
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
|
||||||
|
ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
|
||||||
|
|
||||||
|
uint32_t timeout = 0x100000;
|
||||||
|
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
|
||||||
|
;
|
||||||
|
|
||||||
|
// If timeout = 0, is an error.
|
||||||
|
return timeout ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ch32v307ethPHYRegRead(uint32_t reg)
|
||||||
|
{
|
||||||
|
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42 /* = 0, per 27.1.8.1.4 */ |
|
||||||
|
((uint32_t)CH32V307GIGABIT_PHYADDRESS << 11) | // ETH_MACMIIAR_PA
|
||||||
|
(((uint32_t)reg << 6) & ETH_MACMIIAR_MR) |
|
||||||
|
(0 /*!ETH_MACMIIAR_MW*/) | ETH_MACMIIAR_MB;
|
||||||
|
|
||||||
|
uint32_t timeout = 0x100000;
|
||||||
|
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) && --timeout)
|
||||||
|
;
|
||||||
|
|
||||||
|
// If timeout = 0, is an error.
|
||||||
|
return timeout ? ETH->MACMIIDR : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ch32v307ethGetMacInUC(uint8_t *mac)
|
||||||
|
{
|
||||||
|
// Mac is backwards.
|
||||||
|
const uint8_t *macaddr = (const uint8_t *)(ROM_CFG_USERADR_ID + 5);
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
mac[i] = *(macaddr--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ch32v307ethInit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#ifdef CH32V307GIGABIT_PHY_RSTB
|
||||||
|
funPinMode(CH32V307GIGABIT_PHY_RSTB, GPIO_CFGLR_OUT_50Mhz_PP); // PHY_RSTB (For reset)
|
||||||
|
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_LOW);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Configure strapping.
|
||||||
|
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
|
||||||
|
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
|
||||||
|
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
|
||||||
|
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
|
||||||
|
funDigitalWrite(PA1, FUN_HIGH);
|
||||||
|
funDigitalWrite(PA0, FUN_HIGH);
|
||||||
|
funDigitalWrite(PC3, FUN_HIGH); // No TX Delay
|
||||||
|
funDigitalWrite(PC2, FUN_HIGH);
|
||||||
|
|
||||||
|
// Pull-up MDIO
|
||||||
|
funPinMode(PD9, GPIO_CFGLR_OUT_50Mhz_PP); // Pull-up control (DO NOT CHECK IN, ADD RESISTOR)
|
||||||
|
funDigitalWrite(PD9, FUN_HIGH);
|
||||||
|
|
||||||
|
// Will be required later.
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
|
||||||
|
|
||||||
|
// https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8c&HSI=1,8&HSE=0,8&PREDIV2=1,1&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=0,4&PLLVCO=1,144&SYSCLK=1,2&
|
||||||
|
// Clock Tree:
|
||||||
|
// 8MHz Input
|
||||||
|
// PREDIV2 = 2 (1 in register) = 4MHz
|
||||||
|
// PLL2 = 9 (7 in register) = 36MHz / PLL2VCO = 72MHz
|
||||||
|
// PLL3CLK = 12.5 (1 in register) = 50MHz = 100MHz VCO
|
||||||
|
// PREDIV1SRC = HSE (1 in register) = 8MHz
|
||||||
|
// PREDIV1 = 2 (1 in register).
|
||||||
|
// PLLSRC = PREDIV1 (0 in register) = 4MHz
|
||||||
|
// PLL = 18 (0 in register) = 72MHz
|
||||||
|
// PLLVCO = 144MHz
|
||||||
|
// SYSCLK = PLLVCO = 144MHz
|
||||||
|
// Use EXT_125M (ETH1G_SRC)
|
||||||
|
|
||||||
|
// Switch processor back to HSI so we don't eat dirt.
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
|
||||||
|
|
||||||
|
// Setup clock tree.
|
||||||
|
RCC->CFGR2 |=
|
||||||
|
(1 << RCC_PREDIV2_OFFSET) | // PREDIV = /2; Prediv Freq = 4MHz
|
||||||
|
(1 << RCC_PLL3MUL_OFFSET) | // PLL3 = x12.5 (PLL3 = 50MHz)
|
||||||
|
(2 << RCC_ETH1GSRC_OFFSET) | // External source for RGMII
|
||||||
|
(7 << RCC_PLL2MUL_OFFSET) | // PLL2 = x9 (PLL2 = 36MHz)
|
||||||
|
(1 << RCC_PREDIV1_OFFSET) | // PREDIV1 = /2; Prediv freq = 50MHz
|
||||||
|
0;
|
||||||
|
|
||||||
|
// Power on PLLs
|
||||||
|
RCC->CTLR |= RCC_PLL3ON | RCC_PLL2ON;
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
for (timeout = 10000; timeout > 0; timeout--)
|
||||||
|
if (RCC->CTLR & RCC_PLL3RDY) break;
|
||||||
|
if (timeout == 0) return -5;
|
||||||
|
for (timeout = 10000; timeout > 0; timeout--)
|
||||||
|
if (RCC->CTLR & RCC_PLL2RDY) break;
|
||||||
|
if (timeout == 0) return -6;
|
||||||
|
|
||||||
|
// PLL = x18 (0 in register)
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~(0xf << 18)) | 0;
|
||||||
|
RCC->CTLR |= RCC_PLLON;
|
||||||
|
|
||||||
|
for (timeout = 10000; timeout > 0; timeout--)
|
||||||
|
if (RCC->CTLR & RCC_PLLRDY) break;
|
||||||
|
if (timeout == 0) return -7;
|
||||||
|
|
||||||
|
// Switch to PLL.
|
||||||
|
#ifdef CH32V307GIGABIT_MCO25
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL | (9 << 24); // And output clock on PA8
|
||||||
|
#else
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For clock in.
|
||||||
|
funPinMode(PB1, GPIO_CFGLR_IN_FLOAT); // GMII_CLK125
|
||||||
|
|
||||||
|
RCC->CFGR2 |= RCC_ETH1G_125M_EN; // Enable 125MHz clock.
|
||||||
|
|
||||||
|
// Power on and reset.
|
||||||
|
RCC->AHBPCENR |= RCC_ETHMACEN | RCC_ETHMACTXEN | RCC_ETHMACRXEN;
|
||||||
|
RCC->AHBRSTR |= RCC_ETHMACRST;
|
||||||
|
RCC->AHBRSTR &= ~RCC_ETHMACRST;
|
||||||
|
|
||||||
|
ETH->DMABMR |= ETH_DMABMR_SR;
|
||||||
|
|
||||||
|
// Wait for reset to complete.
|
||||||
|
for (timeout = 10000; timeout > 0 && (ETH->DMABMR & ETH_DMABMR_SR); timeout--)
|
||||||
|
{
|
||||||
|
Delay_Us(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use RGMII
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL; // EXTEN_ETH_RGMII_SEL;
|
||||||
|
|
||||||
|
funPinMode(PB13, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDIO
|
||||||
|
funPinMode(PB12, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_MDC
|
||||||
|
|
||||||
|
// For clock output to Ethernet module.
|
||||||
|
funPinMode(PA8, GPIO_CFGLR_OUT_50Mhz_AF_PP); // PHY_CKTAL
|
||||||
|
|
||||||
|
// Release PHY from reset.
|
||||||
|
#ifdef CH32V307GIGABIT_PHY_RSTB
|
||||||
|
funDigitalWrite(CH32V307GIGABIT_PHY_RSTB, FUN_HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 23ms (But only on the RTL8211FS) None is needed on the RTL8211E
|
||||||
|
|
||||||
|
funPinMode(PB0, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD3
|
||||||
|
funPinMode(PC5, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD2
|
||||||
|
funPinMode(PC4, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD1
|
||||||
|
funPinMode(PA7, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXD0
|
||||||
|
funPinMode(PA3, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXCTL
|
||||||
|
funPinMode(PA2, GPIO_CFGLR_OUT_50Mhz_AF_PP); // GMII_TXC
|
||||||
|
funPinMode(PA1, GPIO_CFGLR_IN_PUPD); // GMII_RXD3
|
||||||
|
funPinMode(PA0, GPIO_CFGLR_IN_PUPD); // GMII_RXD2
|
||||||
|
funPinMode(PC3, GPIO_CFGLR_IN_PUPD); // GMII_RXD1
|
||||||
|
funPinMode(PC2, GPIO_CFGLR_IN_PUPD); // GMII_RXD0
|
||||||
|
funPinMode(PC1, GPIO_CFGLR_IN_PUPD); // GMII_RXCTL
|
||||||
|
funPinMode(PC0, GPIO_CFGLR_IN_FLOAT); // GMII_RXC
|
||||||
|
|
||||||
|
funDigitalWrite(PA1, FUN_HIGH); // SELGRV = 3.3V
|
||||||
|
funDigitalWrite(PA0, FUN_HIGH); // TXDelay = 1
|
||||||
|
funDigitalWrite(PC3, FUN_HIGH); // AN[0] = 1
|
||||||
|
funDigitalWrite(PC2, FUN_HIGH); // AN[1] = 1
|
||||||
|
funDigitalWrite(PC1, FUN_LOW); // PHYAD[0]
|
||||||
|
|
||||||
|
// Configure MDC/MDIO
|
||||||
|
// Conflicting notes - some say /42, others don't.
|
||||||
|
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42;
|
||||||
|
|
||||||
|
// Update MACCR
|
||||||
|
ETH->MACCR =
|
||||||
|
(CH32V307GIGABIT_CFG_CLOCK_DELAY << 29) | // No clock delay
|
||||||
|
(0 << 23) | // Max RX = 2kB (Revisit if looking into jumbo frames)
|
||||||
|
(0 << 22) | // Max TX = 2kB (Revisit if looking into jumbo frames)
|
||||||
|
(0 << 21) | // Rated Drive (instead of energy savings mode) (10M PHY only)
|
||||||
|
(1 << 20) | // Bizarre re-use of termination resistor terminology? (10M PHY Only)
|
||||||
|
(0 << 17) | // IFG = 0, 96-bit guard time.
|
||||||
|
(0 << 14) | // FES = 2 = GBE, 1=100MBit/s (UNSET TO START)
|
||||||
|
(0 << 12) | // Self Loop = 0
|
||||||
|
(0 << 11) | // Full-Duplex Mode (UNSET TO START)
|
||||||
|
(1 << 10) | // IPCO = 1, Check TCP, UDP, ICMP header checksums.
|
||||||
|
(1 << 7) | // APCS (automatically strip frames)
|
||||||
|
(1 << 3) | // TE (Transmit enable!)
|
||||||
|
(1 << 2) | // RE (Receive Enable)
|
||||||
|
(CH32V307GIGABIT_CFG_CLOCK_PHASE << 1) | // TCF = 0 (Potentailly change if clocking is wrong)
|
||||||
|
0;
|
||||||
|
|
||||||
|
Delay_Ms(25); // Waiting for PHY to exit sleep. This is inconsistent at 19ms.
|
||||||
|
|
||||||
|
// Reset the physical layer
|
||||||
|
ch32v307ethPHYRegWrite(PHY_BCR,
|
||||||
|
PHY_Reset |
|
||||||
|
1 << 12 | // Auto negotiate
|
||||||
|
1 << 8 | // Duplex
|
||||||
|
1 << 6 | // Speed Bit.
|
||||||
|
0);
|
||||||
|
|
||||||
|
// De-assert reset.
|
||||||
|
ch32v307ethPHYRegWrite(PHY_BCR,
|
||||||
|
1 << 12 | // Auto negotiate
|
||||||
|
1 << 8 | // Duplex
|
||||||
|
1 << 6 | // Speed Bit.
|
||||||
|
0);
|
||||||
|
|
||||||
|
ch32v307ethPHYRegRead(0x03);
|
||||||
|
ch32v307eth_phyid = ch32v307ethPHYRegRead(0x03); // Read twice to be safe.
|
||||||
|
if (ch32v307eth_phyid == 0xc916)
|
||||||
|
ch32v307ethPHYRegWrite(0x1F, 0x0a43); // RTL8211FS needs page select.
|
||||||
|
|
||||||
|
ch32v307ethGetMacInUC(ch32v307eth_mac);
|
||||||
|
|
||||||
|
ETH->MACA0HR = (uint32_t)((ch32v307eth_mac[5] << 8) | ch32v307eth_mac[4]);
|
||||||
|
ETH->MACA0LR = (uint32_t)(ch32v307eth_mac[0] | (ch32v307eth_mac[1] << 8) | (ch32v307eth_mac[2] << 16) | (ch32v307eth_mac[3] << 24));
|
||||||
|
|
||||||
|
ETH->MACFFR = (uint32_t)(ETH_ReceiveAll_Disable |
|
||||||
|
ETH_SourceAddrFilter_Disable |
|
||||||
|
ETH_PassControlFrames_BlockAll |
|
||||||
|
ETH_BroadcastFramesReception_Enable |
|
||||||
|
ETH_DestinationAddrFilter_Normal |
|
||||||
|
ETH_PromiscuousMode_Disable |
|
||||||
|
ETH_MulticastFramesFilter_Perfect |
|
||||||
|
ETH_UnicastFramesFilter_Perfect);
|
||||||
|
|
||||||
|
ETH->MACHTHR = (uint32_t)0;
|
||||||
|
ETH->MACHTLR = (uint32_t)0;
|
||||||
|
ETH->MACVLANTR = (uint32_t)(ETH_VLANTagComparison_16Bit);
|
||||||
|
|
||||||
|
ETH->MACFCR = 0; // No pause frames.
|
||||||
|
|
||||||
|
// Configure RX/TX chains.
|
||||||
|
ETH_DMADESCTypeDef *tdesc;
|
||||||
|
for (i = 0; i < CH32V307GIGABIT_TXBUFNB; i++)
|
||||||
|
{
|
||||||
|
tdesc = ch32v307eth_DMATxDscrTab + i;
|
||||||
|
tdesc->ControlBufferSize = 0;
|
||||||
|
tdesc->Status = ETH_DMATxDesc_TCH | ETH_DMATxDesc_IC | ETH_DMATxDesc_FS;
|
||||||
|
tdesc->Buffer1Addr = (uint32_t)0; // Populate with data.
|
||||||
|
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_TXBUFNB - 1) ? ((uint32_t)(ch32v307eth_DMATxDscrTab + i + 1)) : (uint32_t)ch32v307eth_DMATxDscrTab;
|
||||||
|
}
|
||||||
|
ETH->DMATDLAR = (uint32_t)ch32v307eth_DMATxDscrTab;
|
||||||
|
for (i = 0; i < CH32V307GIGABIT_RXBUFNB; i++)
|
||||||
|
{
|
||||||
|
tdesc = ch32v307eth_DMARxDscrTab + i;
|
||||||
|
tdesc->Status = ETH_DMARxDesc_OWN;
|
||||||
|
tdesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)CH32V307GIGABIT_BUFFSIZE;
|
||||||
|
tdesc->Buffer1Addr = (uint32_t)(&ch32v307eth_MACRxBuf[i * CH32V307GIGABIT_BUFFSIZE]);
|
||||||
|
tdesc->Buffer2NextDescAddr = (i < CH32V307GIGABIT_RXBUFNB - 1) ? (uint32_t)(ch32v307eth_DMARxDscrTab + i + 1) : (uint32_t)(ch32v307eth_DMARxDscrTab);
|
||||||
|
}
|
||||||
|
ETH->DMARDLAR = (uint32_t)ch32v307eth_DMARxDscrTab;
|
||||||
|
|
||||||
|
pDMARxGet = ch32v307eth_DMARxDscrTab;
|
||||||
|
pDMATxSet = ch32v307eth_DMATxDscrTab;
|
||||||
|
|
||||||
|
// Receive a good frame half interrupt mask.
|
||||||
|
// Receive CRC error frame half interrupt mask.
|
||||||
|
// For the future: Why do we want this?
|
||||||
|
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
|
||||||
|
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;
|
||||||
|
|
||||||
|
ETH->DMAIER = ETH_DMA_IT_NIS | // Normal interrupt enable.
|
||||||
|
ETH_DMA_IT_R | // Receive
|
||||||
|
ETH_DMA_IT_T | // Transmit
|
||||||
|
ETH_DMA_IT_AIS | // Abnormal interrupt
|
||||||
|
ETH_DMA_IT_RBU; // Receive buffer unavailable interrupt enable
|
||||||
|
|
||||||
|
NVIC_EnableIRQ(ETH_IRQn);
|
||||||
|
|
||||||
|
// Actually enable receiving process.
|
||||||
|
ETH->DMAOMR = ETH_DMAOMR_SR | ETH_DMAOMR_ST | ETH_DMAOMR_TSF | ETH_DMAOMR_FEF;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ETH_IRQHandler(void) __attribute__((interrupt));
|
||||||
|
void ETH_IRQHandler(void)
|
||||||
|
{
|
||||||
|
uint32_t int_sta;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int_sta = ETH->DMASR;
|
||||||
|
if ((int_sta & (ETH_DMA_IT_AIS | ETH_DMA_IT_NIS)) == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off nominal situations.
|
||||||
|
if (int_sta & ETH_DMA_IT_AIS)
|
||||||
|
{
|
||||||
|
// Receive buffer unavailable interrupt enable.
|
||||||
|
if (int_sta & ETH_DMA_IT_RBU)
|
||||||
|
{
|
||||||
|
ETH->DMASR = ETH_DMA_IT_RBU;
|
||||||
|
if ((INFO->CHIPID & 0xf0) == 0x10)
|
||||||
|
{
|
||||||
|
((ETH_DMADESCTypeDef *)(((ETH_DMADESCTypeDef *)(ETH->DMACHRDR))->Buffer2NextDescAddr))->Status = ETH_DMARxDesc_OWN;
|
||||||
|
ETH->DMARPDR = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ETH->DMASR = ETH_DMA_IT_AIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nominal interrupts.
|
||||||
|
if (int_sta & ETH_DMA_IT_NIS)
|
||||||
|
{
|
||||||
|
if (int_sta & ETH_DMA_IT_R)
|
||||||
|
{
|
||||||
|
// Received a packet, normally.
|
||||||
|
// Status is in Table 27-17 Definitions of RDes0
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// XXX TODO: Is this a good place to acknowledge? REVISIT: Should this go lower?
|
||||||
|
// XXX TODO: Restructure this to allow for
|
||||||
|
ETH->DMASR = ETH_DMA_IT_R;
|
||||||
|
|
||||||
|
uint32_t status = pDMARxGet->Status;
|
||||||
|
if (status & ETH_DMARxDesc_OWN) break;
|
||||||
|
|
||||||
|
// We only have a valid packet in a specific situation.
|
||||||
|
// So, we take the status, then mask off the bits we care about
|
||||||
|
// And see if they're equal to the ones that need to be set/unset.
|
||||||
|
const uint32_t mask =
|
||||||
|
ETH_DMARxDesc_OWN |
|
||||||
|
ETH_DMARxDesc_LS |
|
||||||
|
ETH_DMARxDesc_ES |
|
||||||
|
ETH_DMARxDesc_FS;
|
||||||
|
const uint32_t eq =
|
||||||
|
0 |
|
||||||
|
ETH_DMARxDesc_LS |
|
||||||
|
0 |
|
||||||
|
ETH_DMARxDesc_FS;
|
||||||
|
|
||||||
|
int suppress_own = 0;
|
||||||
|
|
||||||
|
if ((status & mask) == eq)
|
||||||
|
{
|
||||||
|
int32_t frame_length = ((status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT) - 4;
|
||||||
|
if (frame_length > 0)
|
||||||
|
{
|
||||||
|
uint8_t *data = (uint8_t *)pDMARxGet->Buffer1Addr;
|
||||||
|
suppress_own = ch32v307ethInitHandlePacket(data, frame_length, pDMARxGet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, Invalid Packet
|
||||||
|
|
||||||
|
// Relinquish control back to underlying hardware.
|
||||||
|
if (!suppress_own)
|
||||||
|
pDMARxGet->Status = ETH_DMARxDesc_OWN;
|
||||||
|
|
||||||
|
// Tricky logic for figuring out the next packet. Originally
|
||||||
|
// discussed in ch32v30x_eth.c in ETH_DropRxPkt
|
||||||
|
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET)
|
||||||
|
pDMARxGet = (ETH_DMADESCTypeDef *)(pDMARxGet->Buffer2NextDescAddr);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((pDMARxGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET)
|
||||||
|
pDMARxGet = (ETH_DMADESCTypeDef *)(ETH->DMARDLAR);
|
||||||
|
else
|
||||||
|
pDMARxGet = (ETH_DMADESCTypeDef *)((uint32_t)pDMARxGet + 0x10 + ((ETH->DMABMR & ETH_DMABMR_DSL) >> 2));
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
if (int_sta & ETH_DMA_IT_T)
|
||||||
|
{
|
||||||
|
ch32v307ethInitHandleTXC();
|
||||||
|
ETH->DMASR = ETH_DMA_IT_T;
|
||||||
|
}
|
||||||
|
ETH->DMASR = ETH_DMA_IT_NIS;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ch32v307ethTransmitStatic(uint8_t *buffer, uint32_t length, int enable_txc)
|
||||||
|
{
|
||||||
|
// The official SDK waits until ETH_DMATxDesc_TTSS is set.
|
||||||
|
// This also provides a transmit timestamp, which could be
|
||||||
|
// used for PTP.
|
||||||
|
// But we don't want to do that.
|
||||||
|
// We just want to go. If anyone cares, they can check later.
|
||||||
|
|
||||||
|
if (pDMATxSet->Status & ETH_DMATxDesc_OWN)
|
||||||
|
{
|
||||||
|
ETH->DMATPDR = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDMATxSet->ControlBufferSize = (length & ETH_DMATxDesc_TBS1);
|
||||||
|
pDMATxSet->Buffer1Addr = (uint32_t)buffer;
|
||||||
|
|
||||||
|
// Status is in Table 27-12 "Definitions of TDes0 bits"
|
||||||
|
enable_txc = enable_txc ? ETH_DMATxDesc_IC : 0;
|
||||||
|
pDMATxSet->Status =
|
||||||
|
ETH_DMATxDesc_LS | // Last Segment (This is all you need to have to transmit)
|
||||||
|
ETH_DMATxDesc_FS | // First Segment (Beginning of transmission)
|
||||||
|
enable_txc | // Interrupt when complete
|
||||||
|
ETH_DMATxDesc_TCH | // Next Descriptor Address Valid
|
||||||
|
ETH_DMATxDesc_CIC_TCPUDPICMP_Full | // Do all header checksums.
|
||||||
|
ETH_DMATxDesc_OWN; // Own back to hardware
|
||||||
|
|
||||||
|
pDMATxSet = (ETH_DMADESCTypeDef *)pDMATxSet->Buffer2NextDescAddr;
|
||||||
|
|
||||||
|
ETH->DMASR = ETH_DMASR_TBUS; // This resets the transmit process (or "starts" it)
|
||||||
|
ETH->DMATPDR = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
2569
src/extralibs/font_8x8.h
Normal file
2569
src/extralibs/font_8x8.h
Normal file
File diff suppressed because it is too large
Load diff
651
src/extralibs/hsusb_v30x.c
Normal file
651
src/extralibs/hsusb_v30x.c
Normal file
|
|
@ -0,0 +1,651 @@
|
||||||
|
#include "hsusb_v30x.h"
|
||||||
|
#include "ch32fun.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define UEP_CTRL_H(n) (((uint16_t *)&USBHSD->UEP0_TX_CTRL)[n * 2])
|
||||||
|
|
||||||
|
struct _USBState HSUSBCTX;
|
||||||
|
|
||||||
|
// based on https://github.com/openwch/ch32v307/blob/main/EVT/EXAM/USB/USBHS/DEVICE/CompositeKM
|
||||||
|
|
||||||
|
// Mask for the combined USBHSD->INT_FG + USBHSD->INT_ST
|
||||||
|
#define CRB_U_IS_NAK (1 << 7)
|
||||||
|
#define CTOG_MATCH_SYNC (1 << 6)
|
||||||
|
#define CRB_UIF_SETUP_ACT (1 << 5) // CRB_U_SIE_FREE on USBFS
|
||||||
|
#define CRB_UIF_FIFO_OV (1 << 4)
|
||||||
|
#define CRB_UIF_HST_SOF (1 << 3)
|
||||||
|
#define CRB_UIF_SUSPEND (1 << 2)
|
||||||
|
#define CRB_UIF_TRANSFER (1 << 1)
|
||||||
|
#define CRB_UIF_BUS_RST (1 << 0)
|
||||||
|
#define CSETUP_ACT (1 << 15)
|
||||||
|
#define CRB_UIS_TOG_OK (1 << 14)
|
||||||
|
#define CMASK_UIS_TOKEN (3 << 12)
|
||||||
|
#define CMASK_UIS_ENDP (0xf << 8)
|
||||||
|
|
||||||
|
#define CUIS_TOKEN_OUT 0x0
|
||||||
|
#define CUIS_TOKEN_SOF 0x1
|
||||||
|
#define CUIS_TOKEN_IN 0x2
|
||||||
|
#define CUIS_TOKEN_SETUP 0x3
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static inline void DMA7FastCopy( uint8_t * dest, const uint8_t * src, int len )
|
||||||
|
{
|
||||||
|
while( DMA1_Channel7->CNTR );
|
||||||
|
DMA1_Channel7->CFGR = 0;
|
||||||
|
DMA1_Channel7->MADDR = (uintptr_t)src;
|
||||||
|
DMA1_Channel7->PADDR = (uintptr_t)dest;
|
||||||
|
DMA1_Channel7->CNTR = (len+3)/4;
|
||||||
|
DMA1_Channel7->CFGR =
|
||||||
|
DMA_M2M_Enable |
|
||||||
|
DMA_DIR_PeripheralDST |
|
||||||
|
DMA_Priority_Low |
|
||||||
|
DMA_MemoryDataSize_Word |
|
||||||
|
DMA_PeripheralDataSize_Word |
|
||||||
|
DMA_MemoryInc_Enable |
|
||||||
|
DMA_PeripheralInc_Enable |
|
||||||
|
DMA_Mode_Normal | DMA_CFGR1_EN;
|
||||||
|
#if !(FUSB_CURSED_TURBO_DMA == 1)
|
||||||
|
// Somehow, it seems to work (unsafely) without this.
|
||||||
|
// Really, though, it's probably fine.
|
||||||
|
while( DMA1_Channel7->CNTR );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void DMA7FastCopyComplete() { while( DMA1_Channel7->CNTR ); }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void USBHS_InternalFinishSetup();
|
||||||
|
|
||||||
|
// void USBHSWakeUp_IRQHandler(void) __attribute((interrupt));
|
||||||
|
// void USBHSWakeUp_IRQHandler(void)
|
||||||
|
//{
|
||||||
|
// printf( "USBHSWakeUp MSTATUS:%08x MTVAL:%08x MCAUSE:%08x MEPC:%08x\n", (int)__get_MSTATUS(), (int)__get_MTVAL(), (int)__get_MCAUSE(), (int)__get_MEPC() );
|
||||||
|
// }
|
||||||
|
extern uint8_t scratchpad[];
|
||||||
|
|
||||||
|
void USBHS_IRQHandler(void) __attribute((interrupt));
|
||||||
|
void USBHS_IRQHandler(void)
|
||||||
|
{
|
||||||
|
// Based on https://github.com/openwch/ch32v307/blob/main/EVT/EXAM/USB/USBHS/DEVICE/CompositeKM/User/ch32v30x_usbhs_device.c
|
||||||
|
// Combined FG + ST flag
|
||||||
|
uint16_t intfgst = *(uint16_t *)(&USBHSD->INT_FG);
|
||||||
|
int len = 0;
|
||||||
|
struct _USBState *ctx = &HSUSBCTX;
|
||||||
|
uint8_t *ctrl0buff = CTRL0BUFF;
|
||||||
|
|
||||||
|
if (intfgst & (CRB_UIF_SETUP_ACT))
|
||||||
|
{
|
||||||
|
// On the Chapter 22 USB, SETUP Requests are handled here instead of in UIF_TRANSFER, with TOKEN_SETUP.
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_ACK;
|
||||||
|
USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
|
||||||
|
|
||||||
|
/* Store All Setup Values */
|
||||||
|
int USBHS_SetupReqType = HSUSBCTX.USBHS_SetupReqType = pUSBHS_SetupReqPak->bmRequestType;
|
||||||
|
int USBHS_SetupReqCode = HSUSBCTX.USBHS_SetupReqCode = pUSBHS_SetupReqPak->bRequest;
|
||||||
|
int USBHS_SetupReqLen = HSUSBCTX.USBHS_SetupReqLen = pUSBHS_SetupReqPak->wLength;
|
||||||
|
int USBHS_SetupReqIndex = pUSBHS_SetupReqPak->wIndex;
|
||||||
|
int USBHS_IndexValue = HSUSBCTX.USBHS_IndexValue = (pUSBHS_SetupReqPak->wIndex << 16) | pUSBHS_SetupReqPak->wValue;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
// printf( "Setup: %d %d %d %d %d\n", USBHS_SetupReqType, USBHS_SetupReqCode, USBHS_SetupReqLen,
|
||||||
|
// USBHS_SetupReqIndex, USBHS_IndexValue );
|
||||||
|
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD)
|
||||||
|
{
|
||||||
|
#if HUSB_HID_INTERFACES > 0
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_TYP_MASK) == USB_REQ_TYP_CLASS)
|
||||||
|
{
|
||||||
|
/* Class Request */
|
||||||
|
// printf( "REQ: %d [%02x %02x %04x %04x]\n", USBHS_SetupReqCode, pUSBHS_SetupReqPak->bmRequestType, pUSBHS_SetupReqPak->bRequest, pUSBHS_SetupReqPak->wValue, pUSBHS_SetupReqPak->wLength );
|
||||||
|
switch (USBHS_SetupReqCode)
|
||||||
|
{
|
||||||
|
case HID_SET_REPORT:
|
||||||
|
#if HUSB_HID_USER_REPORTS
|
||||||
|
len = HandleHidUserSetReportSetup(ctx, pUSBHS_SetupReqPak);
|
||||||
|
if (len < 0) goto sendstall;
|
||||||
|
ctx->USBHS_SetupReqLen = len;
|
||||||
|
USBHSD->UEP0_TX_LEN = 0;
|
||||||
|
USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1;
|
||||||
|
goto replycomplete;
|
||||||
|
case HID_GET_REPORT:
|
||||||
|
len = HandleHidUserGetReportSetup(ctx, pUSBHS_SetupReqPak);
|
||||||
|
if (len < 0) goto sendstall;
|
||||||
|
ctx->USBHS_SetupReqLen = len;
|
||||||
|
len = len >= DEF_USBD_UEP0_SIZE ? DEF_USBD_UEP0_SIZE : len;
|
||||||
|
if (!ctx->pCtrlPayloadPtr)
|
||||||
|
{
|
||||||
|
len = HandleHidUserReportDataIn(ctx, ctrl0buff, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// DMA7FastCopy( ctrl0buff, ctx->pCtrlPayloadPtr, len );
|
||||||
|
memcpy(ctrl0buff, ctx->pCtrlPayloadPtr, len);
|
||||||
|
ctx->pCtrlPayloadPtr += len;
|
||||||
|
}
|
||||||
|
USBHSD->UEP0_TX_LEN = len;
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_ACK;
|
||||||
|
ctx->USBHS_SetupReqLen -= len;
|
||||||
|
goto replycomplete;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_SET_IDLE:
|
||||||
|
if (USBHS_SetupReqIndex < HUSB_HID_INTERFACES)
|
||||||
|
HSUSBCTX.USBHS_HidIdle[USBHS_SetupReqIndex] = (uint8_t)(USBHS_IndexValue >> 8);
|
||||||
|
break;
|
||||||
|
case HID_SET_PROTOCOL:
|
||||||
|
if (USBHS_SetupReqIndex < HUSB_HID_INTERFACES)
|
||||||
|
HSUSBCTX.USBHS_HidProtocol[USBHS_SetupReqIndex] = (uint8_t)USBHS_IndexValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_GET_IDLE:
|
||||||
|
if (USBHS_SetupReqIndex < HUSB_HID_INTERFACES)
|
||||||
|
{
|
||||||
|
ctrl0buff[0] = HSUSBCTX.USBHS_HidIdle[USBHS_SetupReqIndex];
|
||||||
|
len = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_GET_PROTOCOL:
|
||||||
|
if (USBHS_SetupReqIndex < HUSB_HID_INTERFACES)
|
||||||
|
{
|
||||||
|
ctrl0buff[0] = HSUSBCTX.USBHS_HidProtocol[USBHS_SetupReqIndex];
|
||||||
|
len = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto sendstall;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* usb standard request processing */
|
||||||
|
switch (USBHS_SetupReqCode)
|
||||||
|
{
|
||||||
|
/* get device/configuration/string/report/... descriptors */
|
||||||
|
case USB_GET_DESCRIPTOR:
|
||||||
|
{
|
||||||
|
const struct descriptor_list_struct *e = descriptor_list;
|
||||||
|
const struct descriptor_list_struct *e_end = e + DESCRIPTOR_LIST_ENTRIES;
|
||||||
|
for (; e != e_end; e++)
|
||||||
|
{
|
||||||
|
if (e->lIndexValue == USBHS_IndexValue)
|
||||||
|
{
|
||||||
|
ctx->pCtrlPayloadPtr = (uint8_t *)e->addr;
|
||||||
|
len = e->length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e == e_end)
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy Descriptors to Endp0 DMA buffer */
|
||||||
|
int totalLen = USBHS_SetupReqLen;
|
||||||
|
if (totalLen > len)
|
||||||
|
{
|
||||||
|
totalLen = len;
|
||||||
|
}
|
||||||
|
len = (totalLen >= DEF_USBD_UEP0_SIZE) ? DEF_USBD_UEP0_SIZE : totalLen;
|
||||||
|
// DMA7FastCopy( ctrl0buff, ctx->pCtrlPayloadPtr, len ); //memcpy( CTRL0BUFF, ctx->pCtrlPayloadPtr, len );
|
||||||
|
memcpy(ctrl0buff, ctx->pCtrlPayloadPtr, len);
|
||||||
|
ctx->USBHS_SetupReqLen = totalLen - len;
|
||||||
|
ctx->pCtrlPayloadPtr += len;
|
||||||
|
USBHSD->UEP0_TX_LEN = len;
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_ACK;
|
||||||
|
goto replycomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set usb address */
|
||||||
|
case USB_SET_ADDRESS:
|
||||||
|
ctx->USBHS_DevAddr = (uint16_t)(ctx->USBHS_IndexValue & 0xFF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Get usb configuration now set */
|
||||||
|
case USB_GET_CONFIGURATION:
|
||||||
|
ctrl0buff[0] = ctx->USBHS_DevConfig;
|
||||||
|
if (ctx->USBHS_SetupReqLen > 1)
|
||||||
|
ctx->USBHS_SetupReqLen = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Set usb configuration to use */
|
||||||
|
case USB_SET_CONFIGURATION:
|
||||||
|
ctx->USBHS_DevConfig = (uint8_t)(ctx->USBHS_IndexValue & 0xFF);
|
||||||
|
ctx->USBHS_DevEnumStatus = 0x01;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Clear or disable one usb feature */
|
||||||
|
case USB_CLEAR_FEATURE:
|
||||||
|
#if HUSB_SUPPORTS_SLEEP
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
|
||||||
|
{
|
||||||
|
/* clear one device feature */
|
||||||
|
if ((uint8_t)(USBHS_IndexValue & 0xFF) == USB_REQ_FEAT_REMOTE_WAKEUP)
|
||||||
|
{
|
||||||
|
/* clear usb sleep status, device not prepare to sleep */
|
||||||
|
ctx->USBHS_DevSleepStatus &= ~0x01;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
|
||||||
|
{
|
||||||
|
if ((uint8_t)(USBHS_IndexValue & 0xFF) == USB_REQ_FEAT_ENDP_HALT)
|
||||||
|
{
|
||||||
|
/* Clear End-point Feature */
|
||||||
|
int ep = USBHS_SetupReqIndex & 0xf;
|
||||||
|
if ((USBHS_SetupReqIndex & DEF_UEP_IN) && ep < HUSB_CONFIG_EPS)
|
||||||
|
{
|
||||||
|
UEP_CTRL_H(ep) = USBHS_UEP_T_TOG_DATA0 | USBHS_UEP_T_RES_NAK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* set or enable one usb feature */
|
||||||
|
case USB_SET_FEATURE:
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
|
||||||
|
{
|
||||||
|
#if HUSB_SUPPORTS_SLEEP
|
||||||
|
/* Set Device Feature */
|
||||||
|
if ((uint8_t)(USBHS_IndexValue & 0xFF) == USB_REQ_FEAT_REMOTE_WAKEUP)
|
||||||
|
{
|
||||||
|
/* Set Wake-up flag, device prepare to sleep */
|
||||||
|
USBHS_DevSleepStatus |= 0x01;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
|
||||||
|
{
|
||||||
|
/* Set Endpoint Feature */
|
||||||
|
if ((uint8_t)(USBHS_IndexValue & 0xFF) == USB_REQ_FEAT_ENDP_HALT)
|
||||||
|
{
|
||||||
|
int ep = USBHS_SetupReqIndex & 0xf;
|
||||||
|
if ((USBHS_SetupReqIndex & DEF_UEP_IN) && ep < HUSB_CONFIG_EPS)
|
||||||
|
UEP_CTRL_H(ep) = (UEP_CTRL_H(ep) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_STALL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto sendstall;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* This request allows the host to select another setting for the specified interface */
|
||||||
|
case USB_GET_INTERFACE:
|
||||||
|
ctrl0buff[0] = 0x00;
|
||||||
|
if (USBHS_SetupReqLen > 1) USBHS_SetupReqLen = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_SET_INTERFACE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* host get status of specified device/interface/end-points */
|
||||||
|
case USB_GET_STATUS:
|
||||||
|
ctrl0buff[0] = 0x00;
|
||||||
|
ctrl0buff[1] = 0x00;
|
||||||
|
if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE)
|
||||||
|
{
|
||||||
|
#if FUSB_SUPPORTS_SLEEP
|
||||||
|
ctrl0buff[0] = (ctx->USBHS_DevSleepStatus & 0x01) << 1;
|
||||||
|
#else
|
||||||
|
ctrl0buff[0] = 0x00;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if ((USBHS_SetupReqType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
|
||||||
|
{
|
||||||
|
int ep = USBHS_SetupReqIndex & 0xf;
|
||||||
|
if ((USBHS_SetupReqIndex & DEF_UEP_IN) && ep < HUSB_CONFIG_EPS)
|
||||||
|
ctrl0buff[0] = (UEP_CTRL_H(ep) & USBHS_UEP_T_RES_MASK) == USBHS_UEP_T_RES_STALL;
|
||||||
|
else
|
||||||
|
goto sendstall;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto sendstall;
|
||||||
|
if (USBHS_SetupReqLen > 2)
|
||||||
|
USBHS_SetupReqLen = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto sendstall;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* end-point 0 data Tx/Rx */
|
||||||
|
if (USBHS_SetupReqType & DEF_UEP_IN)
|
||||||
|
{
|
||||||
|
len = (USBHS_SetupReqLen > DEF_USBD_UEP0_SIZE) ? DEF_USBD_UEP0_SIZE : USBHS_SetupReqLen;
|
||||||
|
USBHS_SetupReqLen -= len;
|
||||||
|
USBHSD->UEP0_TX_LEN = len;
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_ACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (USBHS_SetupReqLen == 0)
|
||||||
|
{
|
||||||
|
USBHSD->UEP0_TX_LEN = 0;
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_ACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto replycomplete;
|
||||||
|
|
||||||
|
// This might look a little weird, for error handling but it saves a nontrivial amount of storage, and simplifies
|
||||||
|
// control flow to hard-abort here.
|
||||||
|
sendstall:
|
||||||
|
// if one request not support, return stall. Stall means permanent error.
|
||||||
|
USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_STALL;
|
||||||
|
USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_STALL;
|
||||||
|
replycomplete:;
|
||||||
|
}
|
||||||
|
if (intfgst & (CRB_UIF_TRANSFER))
|
||||||
|
{
|
||||||
|
int token = (intfgst & CMASK_UIS_TOKEN) >> 12;
|
||||||
|
int ep = (intfgst & CMASK_UIS_ENDP) >> 8;
|
||||||
|
switch (token)
|
||||||
|
{
|
||||||
|
case CUIS_TOKEN_IN:
|
||||||
|
if (ep)
|
||||||
|
{
|
||||||
|
if (ep < HUSB_CONFIG_EPS)
|
||||||
|
{
|
||||||
|
UEP_CTRL_H(ep) = (UEP_CTRL_H(ep) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_NAK;
|
||||||
|
UEP_CTRL_H(ep) ^= USBHS_UEP_T_TOG_DATA1;
|
||||||
|
ctx->USBHS_Endp_Busy[ep] = 0;
|
||||||
|
// Don't set EP in here. Wait for out.
|
||||||
|
// Optimization: Could we set EP here?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* end-point 0 data in interrupt */
|
||||||
|
if (ctx->USBHS_SetupReqLen == 0)
|
||||||
|
{
|
||||||
|
USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->pCtrlPayloadPtr)
|
||||||
|
{
|
||||||
|
// Shortcut mechanism, for descriptors or if the user wants it.
|
||||||
|
len = ctx->USBHS_SetupReqLen >= DEF_USBD_UEP0_SIZE ? DEF_USBD_UEP0_SIZE : ctx->USBHS_SetupReqLen;
|
||||||
|
// DMA7FastCopy( ctrl0buff, ctx->pCtrlPayloadPtr, len ); // FYI -> Would need to do this if using DMA
|
||||||
|
memcpy(ctrl0buff, ctx->pCtrlPayloadPtr, len);
|
||||||
|
ctx->USBHS_SetupReqLen -= len;
|
||||||
|
if (ctx->USBHS_SetupReqLen > 0)
|
||||||
|
ctx->pCtrlPayloadPtr += len;
|
||||||
|
else
|
||||||
|
ctx->pCtrlPayloadPtr = 0;
|
||||||
|
|
||||||
|
USBHSD->UEP0_TX_LEN = len;
|
||||||
|
USBHSD->UEP0_TX_CTRL ^= USBHS_UEP_T_TOG_DATA1;
|
||||||
|
}
|
||||||
|
else if ((ctx->USBHS_SetupReqType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD)
|
||||||
|
{
|
||||||
|
#if HUSB_HID_USER_REPORTS
|
||||||
|
len = ctx->USBHS_SetupReqLen >= DEF_USBD_UEP0_SIZE ? DEF_USBD_UEP0_SIZE : ctx->USBHS_SetupReqLen;
|
||||||
|
if (len && HSUSBCTX.USBHS_SetupReqCode == HID_GET_REPORT)
|
||||||
|
{
|
||||||
|
len = HandleHidUserReportDataIn(ctx, ctrl0buff, len);
|
||||||
|
USBHSD->UEP0_TX_LEN = len;
|
||||||
|
USBHSD->UEP0_TX_CTRL ^= USBHS_UEP_T_TOG_DATA1;
|
||||||
|
ctx->USBHS_SetupReqLen -= len;
|
||||||
|
ctx->pCtrlPayloadPtr += len;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (HSUSBCTX.USBHS_SetupReqCode)
|
||||||
|
{
|
||||||
|
case USB_GET_DESCRIPTOR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_SET_ADDRESS:
|
||||||
|
USBHSD->DEV_AD = ctx->USBHS_DevAddr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* data-out stage processing */
|
||||||
|
case CUIS_TOKEN_OUT:
|
||||||
|
switch (ep)
|
||||||
|
{
|
||||||
|
/* end-point 0 data out interrupt */
|
||||||
|
case DEF_UEP0:
|
||||||
|
{
|
||||||
|
// XXX WARNINGS:
|
||||||
|
// 1. intfgst & CRB_UIS_TOG_OK is not set for non-odd transactions, i.e. first, third, etc, are all fine.
|
||||||
|
// 2. HandleHidUserReportOutComplete doesn't seem to work.
|
||||||
|
// if( intfgst & CRB_UIS_TOG_OK )
|
||||||
|
|
||||||
|
#if HUSB_HID_USER_REPORTS
|
||||||
|
int len = USBHSD->RX_LEN;
|
||||||
|
uint8_t *cptr = ctx->pCtrlPayloadPtr;
|
||||||
|
if (!cptr)
|
||||||
|
{
|
||||||
|
HandleHidUserReportDataOut(ctx, ctrl0buff, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int remain = ctx->USBHS_SetupReqLen - len;
|
||||||
|
if (remain < 0)
|
||||||
|
{
|
||||||
|
len += remain;
|
||||||
|
remain = 0;
|
||||||
|
}
|
||||||
|
// DMA7FastCopy( cptr, ctrl0buff, len );
|
||||||
|
memcpy(cptr, ctrl0buff, len);
|
||||||
|
ctx->USBHS_SetupReqLen = remain;
|
||||||
|
if (remain > 0)
|
||||||
|
ctx->pCtrlPayloadPtr = cptr + len;
|
||||||
|
else
|
||||||
|
ctx->pCtrlPayloadPtr = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ctx->USBHS_SetupReqLen == 0)
|
||||||
|
{
|
||||||
|
#if HUSB_HID_USER_REPORTS
|
||||||
|
// DMA7FastCopyComplete();
|
||||||
|
HandleHidUserReportOutComplete(ctx);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// See above comment
|
||||||
|
// //USBHSD->UEP0_RX_CTRL ^= USBFS_UEP_R_TOG;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Any other out. (also happens with In)
|
||||||
|
HSUSBCTX.USBHS_Endp_Busy[ep] = 0x02;
|
||||||
|
USBHSD_UEP_RXCTRL(ep) = ((USBHSD_UEP_RXCTRL(ep)) & ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_NAK;
|
||||||
|
#if HUSB_BULK_USER_REPORTS
|
||||||
|
HandleGotEPComplete(ctx, ep);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CUIS_TOKEN_SETUP: // Not actually used on this chip (It's done as a separate flag)
|
||||||
|
case CUIS_TOKEN_SOF: // Sof pack processing
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (intfgst & USBHS_UIF_BUS_RST)
|
||||||
|
{
|
||||||
|
/* usb reset interrupt processing */
|
||||||
|
ctx->USBHS_DevConfig = 0;
|
||||||
|
ctx->USBHS_DevAddr = 0;
|
||||||
|
ctx->USBHS_DevSleepStatus = 0;
|
||||||
|
ctx->USBHS_DevEnumStatus = 0;
|
||||||
|
|
||||||
|
USBHSD->DEV_AD = 0;
|
||||||
|
USBHS_InternalFinishSetup();
|
||||||
|
}
|
||||||
|
if (intfgst & USBHS_UIF_SUSPEND)
|
||||||
|
{
|
||||||
|
USBHSD->INT_FG = USBHS_UIF_SUSPEND;
|
||||||
|
Delay_Us(10);
|
||||||
|
|
||||||
|
// USB suspend interrupt processing
|
||||||
|
if (USBHSD->MIS_ST & USBHS_UMS_SUSPEND)
|
||||||
|
{
|
||||||
|
HSUSBCTX.USBHS_DevSleepStatus |= 0x02;
|
||||||
|
if (HSUSBCTX.USBHS_DevSleepStatus == 0x03)
|
||||||
|
{
|
||||||
|
// TODO: Handle usb sleep here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HSUSBCTX.USBHS_DevSleepStatus &= ~0x02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
USBHSD->INT_FG = intfgst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBHS_InternalFinishSetup()
|
||||||
|
{
|
||||||
|
// To reconfigure your endpoints for TX/RX do it here.
|
||||||
|
|
||||||
|
#if HUSB_CONFIG_EPS > 5
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN | USBHS_UEP1_T_EN | USBHS_UEP2_T_EN | USBHS_UEP3_T_EN | USBHS_UEP4_T_EN | USBHS_UEP5_R_EN;
|
||||||
|
#elif HUSB_CONFIG_EPS > 4
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN | USBHS_UEP1_T_EN | USBHS_UEP2_T_EN | USBHS_UEP3_T_EN | USBHS_UEP4_T_EN;
|
||||||
|
#elif HUSB_CONFIG_EPS > 3
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN | USBHS_UEP1_T_EN | USBHS_UEP2_T_EN | USBHS_UEP3_T_EN;
|
||||||
|
#elif HUSB_CONFIG_EPS > 2
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN | USBHS_UEP1_T_EN | USBHS_UEP2_T_EN;
|
||||||
|
#elif HUSB_CONFIG_EPS > 1
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN | USBHS_UEP1_T_EN;
|
||||||
|
#else
|
||||||
|
USBHSD->ENDP_CONFIG = USBHS_UEP0_T_EN | USBHS_UEP0_R_EN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This is really cursed. Somehow it doesn't explode.
|
||||||
|
// But, normally the USB wants a separate buffer here.
|
||||||
|
|
||||||
|
#if HUSB_CONFIG_EPS > 5
|
||||||
|
// Feel free to override any of these.
|
||||||
|
USBHSD->UEP5_MAX_LEN = 64;
|
||||||
|
USBHSD->UEP5_RX_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[5];
|
||||||
|
USBHSD->UEP5_RX_CTRL = USBHS_UEP_R_RES_ACK | USBHS_UEP_R_TOG_AUTO; // For bulk-out, I think you need to do this.
|
||||||
|
#endif
|
||||||
|
#if HUSB_CONFIG_EPS > 4
|
||||||
|
USBHSD->UEP4_MAX_LEN = 64; // TODO: change to dynamic size, as USB HS supports more than 64?
|
||||||
|
USBHSD->UEP4_TX_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[4];
|
||||||
|
#endif
|
||||||
|
#if HUSB_CONFIG_EPS > 3
|
||||||
|
USBHSD->UEP3_MAX_LEN = 64; // TODO: change to dynamic size, as USB HS supports more than 64?
|
||||||
|
USBHSD->UEP3_TX_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[3];
|
||||||
|
#endif
|
||||||
|
#if HUSB_CONFIG_EPS > 2
|
||||||
|
USBHSD->UEP2_MAX_LEN = 64; // TODO: change to dynamic size, as USB HS supports more than 64?
|
||||||
|
USBHSD->UEP2_TX_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[2];
|
||||||
|
#endif
|
||||||
|
#if HUSB_CONFIG_EPS > 1
|
||||||
|
USBHSD->UEP1_MAX_LEN = 64; // TODO: change to dynamic size, as USB HS supports more than 64?
|
||||||
|
USBHSD->UEP1_TX_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[1];
|
||||||
|
#endif
|
||||||
|
#if HUSB_CONFIG_EPS > 0
|
||||||
|
USBHSD->UEP0_MAX_LEN = 64;
|
||||||
|
USBHSD->UEP0_DMA = (uintptr_t)HSUSBCTX.ENDPOINTS[0];
|
||||||
|
#else
|
||||||
|
#error You must have at least EP0!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UEP_CTRL_H(0) = USBHS_UEP_R_RES_ACK | USBHS_UEP_T_RES_NAK;
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < HUSB_CONFIG_EPS; i++)
|
||||||
|
UEP_CTRL_H(i) = USBFS_UEP_T_RES_NAK;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(HSUSBCTX.USBHS_Endp_Busy) / sizeof(HSUSBCTX.USBHS_Endp_Busy[0]); i++)
|
||||||
|
{
|
||||||
|
HSUSBCTX.USBHS_Endp_Busy[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int HSUSBSetup()
|
||||||
|
{
|
||||||
|
// Set USB clock source to USBPHY
|
||||||
|
RCC->CFGR2 &= ~(1 << 31);
|
||||||
|
RCC->CFGR2 |= RCC_USBCLK48MCLKSource_USBPHY << 31;
|
||||||
|
|
||||||
|
// Set PLL clock source to HSE
|
||||||
|
RCC->CFGR2 &= ~(1 << 27);
|
||||||
|
RCC->CFGR2 |= RCC_HSBHSPLLCLKSource_HSE << 27;
|
||||||
|
|
||||||
|
// Configure PLL for USB
|
||||||
|
RCC->CFGR2 &= ~(7 << 24);
|
||||||
|
RCC->CFGR2 |= RCC_USBPLL_Div2 << 24;
|
||||||
|
|
||||||
|
// Configure reference clock
|
||||||
|
RCC->CFGR2 &= ~(3 << 28);
|
||||||
|
RCC->CFGR2 |= RCC_USBHSPLLCKREFCLK_4M << 28;
|
||||||
|
|
||||||
|
// Enable USB high-speed peripheral
|
||||||
|
RCC->CFGR2 |= (1 << 30);
|
||||||
|
RCC->AHBPCENR |= RCC_AHBPeriph_USBHS | RCC_AHBPeriph_DMA1;
|
||||||
|
|
||||||
|
// Initialize USB module
|
||||||
|
USBHSD->CONTROL = USBHS_UC_CLR_ALL | USBHS_UC_RESET_SIE;
|
||||||
|
Delay_Us(10);
|
||||||
|
USBHSD->CONTROL = 0;
|
||||||
|
|
||||||
|
// Initialize USB device config
|
||||||
|
USBHSD->HOST_CTRL = USBHS_UH_PHY_SUSPENDM;
|
||||||
|
USBHSD->CONTROL = USBHS_UC_DMA_EN | USBHS_UC_INT_BUSY | USBHS_UC_SPEED_HIGH;
|
||||||
|
USBHSD->INT_EN = USBHS_UIE_SETUP_ACT | USBHS_UIE_TRANSFER | USBHS_UIE_DETECT | USBHS_UIE_SUSPEND;
|
||||||
|
|
||||||
|
USBHS_InternalFinishSetup();
|
||||||
|
|
||||||
|
USBHSD->CONTROL |= USBHS_UC_DEV_PU_EN;
|
||||||
|
NVIC_EnableIRQ(USBHS_IRQn);
|
||||||
|
|
||||||
|
// Go on-bus.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
80
src/extralibs/hsusb_v30x.h
Normal file
80
src/extralibs/hsusb_v30x.h
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#ifndef _HSUSB_H
|
||||||
|
#define _HSUSB_H
|
||||||
|
|
||||||
|
/* High speed USB infrastructure for CH32V30x.
|
||||||
|
Based off of the official USB stack and the current CH32X035 FS implementation.
|
||||||
|
|
||||||
|
This is referenced in Chapter 22 USB Host/Device Controller (USBHD) of CH32FV2x_V3xRM.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ch32fun.h"
|
||||||
|
#include "usb_config.h"
|
||||||
|
#include "usb_defines.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct _USBState
|
||||||
|
{
|
||||||
|
// Setup Request
|
||||||
|
uint8_t USBHS_SetupReqCode;
|
||||||
|
uint8_t USBHS_SetupReqType;
|
||||||
|
uint16_t USBHS_SetupReqLen; // Used for tracking place along send.
|
||||||
|
uint32_t USBHS_IndexValue;
|
||||||
|
|
||||||
|
// USB Device Status
|
||||||
|
uint16_t USBHS_DevConfig;
|
||||||
|
uint16_t USBHS_DevAddr;
|
||||||
|
uint8_t USBHS_DevSleepStatus;
|
||||||
|
uint8_t USBHS_DevEnumStatus;
|
||||||
|
|
||||||
|
uint8_t *pCtrlPayloadPtr;
|
||||||
|
|
||||||
|
uint8_t ENDPOINTS[HUSB_CONFIG_EPS][64];
|
||||||
|
|
||||||
|
#define CTRL0BUFF (HSUSBCTX.ENDPOINTS[0])
|
||||||
|
#define pUSBHS_SetupReqPak ((tusb_control_request_t *)CTRL0BUFF)
|
||||||
|
|
||||||
|
#if HUSB_HID_INTERFACES > 0
|
||||||
|
uint8_t USBHS_HidIdle[HUSB_HID_INTERFACES];
|
||||||
|
uint8_t USBHS_HidProtocol[HUSB_HID_INTERFACES];
|
||||||
|
#endif
|
||||||
|
volatile uint8_t USBHS_Endp_Busy[HUSB_CONFIG_EPS];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Provided functions:
|
||||||
|
int HSUSBSetup();
|
||||||
|
uint8_t USBHS_Endp_DataUp(uint8_t endp, const uint8_t *pbuf, uint16_t len, uint8_t mod);
|
||||||
|
|
||||||
|
// Implement the following:
|
||||||
|
#if HUSB_HID_USER_REPORTS
|
||||||
|
int HandleHidUserGetReportSetup(struct _USBState *ctx, tusb_control_request_t *req);
|
||||||
|
int HandleHidUserSetReportSetup(struct _USBState *ctx, tusb_control_request_t *req);
|
||||||
|
void HandleHidUserReportDataOut(struct _USBState *ctx, uint8_t *data, int len);
|
||||||
|
int HandleHidUserReportDataIn(struct _USBState *ctx, uint8_t *data, int len);
|
||||||
|
void HandleHidUserReportOutComplete(struct _USBState *ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HUSB_BULK_USER_REPORTS
|
||||||
|
void HandleGotEPComplete(struct _USBState *ctx, int ep);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct _USBState HSUSBCTX;
|
||||||
|
|
||||||
|
// To TX, you can use USBFS_GetEPBufferIfAvailable or USBHSD_UEP_TXBUF( endp )
|
||||||
|
|
||||||
|
static inline uint8_t *USBHS_GetEPBufferIfAvailable(int endp)
|
||||||
|
{
|
||||||
|
if (HSUSBCTX.USBHS_Endp_Busy[endp]) return 0;
|
||||||
|
return USBHSD_UEP_TXBUF(endp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USBHS_SendEndpoint(int endp, int len, const uint8_t *data)
|
||||||
|
{
|
||||||
|
if (endp)
|
||||||
|
{
|
||||||
|
(((uint32_t *)(&USBHSD->UEP1_TX_DMA))[2 - 1]) = (uintptr_t)data;
|
||||||
|
}
|
||||||
|
USBHSD_UEP_TLEN(endp) = len;
|
||||||
|
USBHSD_UEP_TXCTRL(endp) = (USBHSD_UEP_TXCTRL(endp) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_ACK;
|
||||||
|
HSUSBCTX.USBHS_Endp_Busy[endp] = 0x01;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
148
src/extralibs/lib_rand.h
Normal file
148
src/extralibs/lib_rand.h
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* Psuedo Random Number Generator using a Linear Feedback Shift Register
|
||||||
|
* See the GitHub for more information:
|
||||||
|
* https://github.com/ADBeta/CH32V003_lib_rand
|
||||||
|
*
|
||||||
|
* Ver 1.1 09 Sep 2024
|
||||||
|
*
|
||||||
|
* Released under the MIT Licence
|
||||||
|
* Copyright ADBeta (c) 2024
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef CH32V003_LIB_RAND
|
||||||
|
#define CH32V003_LIB_RAND
|
||||||
|
|
||||||
|
// Define the strength of the random generation. Do this in funconfig.h
|
||||||
|
// Strength 1: Tap and shift the LFSR, then returns the LFSR value as is
|
||||||
|
// Strength 2: Generate 32 random bits using the LFSR
|
||||||
|
// Strength 3: Genetate two 32bit values using the LFSR, then XOR them together
|
||||||
|
// Example: #define RANDOM_STRENGTH 2
|
||||||
|
|
||||||
|
#ifndef RANDOM_STRENGTH
|
||||||
|
#error "Error in lib_rand. Must define RANDOM_STRENGTH"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// @brief set the random LFSR values seed by default to a known-good value
|
||||||
|
static uint32_t _rand_lfsr = 0x747AA32F;
|
||||||
|
|
||||||
|
/*** Library specific Functions - Do Not Use *********************************/
|
||||||
|
/****************************************************************************/
|
||||||
|
/// @brief Updates the LFSR by getting a new tap bit, for MSB, then shifting
|
||||||
|
/// the LFSR >> 1, appending the new MSB.
|
||||||
|
/// Taps bits 0, 1, 21 and 31
|
||||||
|
/// @param None
|
||||||
|
/// @return 0x01 or 0x00, as a LSB translation of the tapped MSB for the LFSR
|
||||||
|
uint8_t _rand_lfsr_update(void)
|
||||||
|
{
|
||||||
|
// Shifting to MSB to make calculations more efficient later
|
||||||
|
uint32_t bit_31 = _rand_lfsr & 0x80000000;
|
||||||
|
uint32_t bit_21 = (_rand_lfsr << 10) & 0x80000000;
|
||||||
|
uint32_t bit_01 = (_rand_lfsr << 30) & 0x80000000;
|
||||||
|
uint32_t bit_00 = (_rand_lfsr << 31) & 0x80000000;
|
||||||
|
|
||||||
|
// Calculate the MSB to be put into the LFSR
|
||||||
|
uint32_t msb = bit_31 ^ bit_21 ^ bit_01 ^ bit_00;
|
||||||
|
// Shift the lfsr and append the MSB to it
|
||||||
|
_rand_lfsr = (_rand_lfsr >> 1) | msb;
|
||||||
|
// Return the LSB instead of MSB
|
||||||
|
return msb >> 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generates a Random 32-bit number, using the LFSR - by generating
|
||||||
|
/// a random bit from LFSR taps, 32 times.
|
||||||
|
/// @param None
|
||||||
|
/// @return a (psuedo)random 32-bit value
|
||||||
|
uint32_t _rand_gen_32b(void)
|
||||||
|
{
|
||||||
|
uint32_t rand_out = 0;
|
||||||
|
|
||||||
|
uint8_t bits = 32;
|
||||||
|
while (bits--)
|
||||||
|
{
|
||||||
|
// Shift the current rand value for the new LSB
|
||||||
|
rand_out = rand_out << 1;
|
||||||
|
// Append the LSB
|
||||||
|
rand_out |= _rand_lfsr_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generates a Random n-bit number, using the LFSR - by generating
|
||||||
|
/// a random bit from LFSR taps, n times.
|
||||||
|
/// @param None
|
||||||
|
/// @return a (psuedo)random n-bit value
|
||||||
|
uint32_t _rand_gen_nb(int bits)
|
||||||
|
{
|
||||||
|
uint32_t rand_out = 0;
|
||||||
|
|
||||||
|
while (bits--)
|
||||||
|
{
|
||||||
|
// Shift the current rand value for the new LSB
|
||||||
|
rand_out = rand_out << 1;
|
||||||
|
// Append the LSB
|
||||||
|
rand_out |= _rand_lfsr_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** API Functions ***********************************************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
/// @brief seeds the Random LFSR to the value passed
|
||||||
|
/// @param uint32_t seed
|
||||||
|
/// @return None
|
||||||
|
void seed(const uint32_t seed_val)
|
||||||
|
{
|
||||||
|
_rand_lfsr = seed_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Generates a Random (32-bit) Number, based on the RANDOM_STRENGTH
|
||||||
|
/// you have selected
|
||||||
|
/// @param None
|
||||||
|
/// @return 32bit Random value
|
||||||
|
uint32_t rand(void)
|
||||||
|
{
|
||||||
|
uint32_t rand_out = 0;
|
||||||
|
|
||||||
|
// If RANDOM_STRENGTH is level 1, Update LFSR Once, then return it
|
||||||
|
#if RANDOM_STRENGTH == 1
|
||||||
|
// Update the LFSR, discard result, and return _lsfr raw
|
||||||
|
(void)_rand_lfsr_update();
|
||||||
|
rand_out = _rand_lfsr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If RANDOM_STRENGTH is level 2, generate a 32-bit output, using 32 random
|
||||||
|
// bits from the LFSR
|
||||||
|
#if RANDOM_STRENGTH == 2
|
||||||
|
rand_out = _rand_gen_32b();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If RANDOM_STRENGTH is level 3, generate 2 32-bit outputs, then XOR them
|
||||||
|
// together
|
||||||
|
#if RANDOM_STRENGTH == 3
|
||||||
|
uint32_t rand_a = _rand_gen_32b();
|
||||||
|
uint32_t rand_b = _rand_gen_32b();
|
||||||
|
rand_out = rand_a ^ rand_b;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return rand_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
752
src/extralibs/ssd1306.h
Normal file
752
src/extralibs/ssd1306.h
Normal file
|
|
@ -0,0 +1,752 @@
|
||||||
|
/*
|
||||||
|
* Single-File-Header for using SPI OLED
|
||||||
|
* 05-05-2023 E. Brombaugh
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SSD1306_H
|
||||||
|
#define _SSD1306_H
|
||||||
|
|
||||||
|
#include "font_8x8.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// comfortable packet size for this OLED
|
||||||
|
#define SSD1306_PSZ 32
|
||||||
|
|
||||||
|
#if defined(SSD1306_CUSTOM)
|
||||||
|
// Let the caller configure the OLED.
|
||||||
|
#else
|
||||||
|
// 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))
|
||||||
|
#error "Please define the SSD1306_WXH resolution used in your application"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSD1306_64X32
|
||||||
|
#define SSD1306_W 64
|
||||||
|
#define SSD1306_H 32
|
||||||
|
#define SSD1306_FULLUSE
|
||||||
|
#define SSD1306_OFFSET 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSD1306_128X32
|
||||||
|
#define SSD1306_W 128
|
||||||
|
#define SSD1306_H 32
|
||||||
|
#define SSD1306_OFFSET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SSD1306_128X64
|
||||||
|
#define SSD1306_W 128
|
||||||
|
#define SSD1306_H 64
|
||||||
|
#define SSD1306_FULLUSE
|
||||||
|
#define SSD1306_OFFSET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SH1107_128x128
|
||||||
|
#define SH1107
|
||||||
|
#define SSD1306_FULLUSE
|
||||||
|
#define SSD1306_W 128
|
||||||
|
#define SSD1306_H 128
|
||||||
|
#define SSD1306_FULLUSE
|
||||||
|
#define SSD1306_OFFSET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send OLED command byte
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_cmd(uint8_t cmd)
|
||||||
|
{
|
||||||
|
return ssd1306_pkt_send(&cmd, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send OLED data packet (up to 32 bytes)
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_data(uint8_t *data, int sz)
|
||||||
|
{
|
||||||
|
return ssd1306_pkt_send(data, sz, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SSD1306_SETCONTRAST 0x81
|
||||||
|
#define SSD1306_SEGREMAP 0xA0
|
||||||
|
#define SSD1306_DISPLAYALLON_RESUME 0xA4
|
||||||
|
#define SSD1306_DISPLAYALLON 0xA5
|
||||||
|
#define SSD1306_NORMALDISPLAY 0xA6
|
||||||
|
#define SSD1306_INVERTDISPLAY 0xA7
|
||||||
|
#define SSD1306_DISPLAYOFF 0xAE
|
||||||
|
#define SSD1306_DISPLAYON 0xAF
|
||||||
|
#define SSD1306_SETDISPLAYOFFSET 0xD3
|
||||||
|
#define SSD1306_SETCOMPINS 0xDA
|
||||||
|
#define SSD1306_SETVCOMDETECT 0xDB
|
||||||
|
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
|
||||||
|
#define SSD1306_SETPRECHARGE 0xD9
|
||||||
|
#define SSD1306_SETMULTIPLEX 0xA8
|
||||||
|
#define SSD1306_SETLOWCOLUMN 0x00
|
||||||
|
#define SSD1306_SETHIGHCOLUMN 0x10
|
||||||
|
#define SSD1306_SETSTARTLINE 0x40
|
||||||
|
#define SSD1306_MEMORYMODE 0x20
|
||||||
|
#define SSD1306_COLUMNADDR 0x21
|
||||||
|
#define SSD1306_PAGEADDR 0x22
|
||||||
|
#define SSD1306_COMSCANINC 0xC0
|
||||||
|
#define SSD1306_COMSCANDEC 0xC8
|
||||||
|
#define SSD1306_CHARGEPUMP 0x8D
|
||||||
|
#define SSD1306_EXTERNALVCC 0x1
|
||||||
|
#define SSD1306_SWITCHCAPVCC 0x2
|
||||||
|
#define SSD1306_TERMINATE_CMDS 0xFF
|
||||||
|
|
||||||
|
/* choose VCC mode */
|
||||||
|
#define SSD1306_EXTERNALVCC 0x1
|
||||||
|
#define SSD1306_SWITCHCAPVCC 0x2
|
||||||
|
// #define vccstate SSD1306_EXTERNALVCC
|
||||||
|
#define vccstate SSD1306_SWITCHCAPVCC
|
||||||
|
|
||||||
|
#if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY
|
||||||
|
// OLED initialization commands for 128x32
|
||||||
|
const uint8_t ssd1306_init_array[] =
|
||||||
|
{
|
||||||
|
#ifdef SH1107
|
||||||
|
SSD1306_DISPLAYOFF, // Turn OLED off
|
||||||
|
0x00, // Low column
|
||||||
|
0x10, // High column
|
||||||
|
0xb0, // Page address
|
||||||
|
0xdc, 0x00, // Set Display Start Line (Where in memory it reads from)
|
||||||
|
SSD1306_SETCONTRAST, 0x6f, // Set constrast
|
||||||
|
SSD1306_COLUMNADDR, // Set memory addressing mode
|
||||||
|
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_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_SETPRECHARGE, 0x1d, // Set pre-charge period (This controls brightness)
|
||||||
|
SSD1306_SETVCOMDETECT, 0x35, // Set vcomh
|
||||||
|
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
|
||||||
|
0xad, 0x80, // Set Charge pump
|
||||||
|
SSD1306_SEGREMAP, 0x01, // Default mapping
|
||||||
|
SSD1306_SETPRECHARGE, 0x06, // ???? No idea what this does, but this looks best.
|
||||||
|
SSD1306_SETCONTRAST, 0xfe, // Set constrast
|
||||||
|
SSD1306_SETVCOMDETECT, 0xfe, // Set vcomh
|
||||||
|
SSD1306_SETMULTIPLEX, (SSD1306_H - 1), // 128-wide.
|
||||||
|
SSD1306_DISPLAYON, // Display on.
|
||||||
|
#else
|
||||||
|
SSD1306_DISPLAYOFF, // 0xAE
|
||||||
|
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
|
||||||
|
0x80, // the suggested ratio 0x80
|
||||||
|
SSD1306_SETMULTIPLEX, // 0xA8
|
||||||
|
#ifdef SSD1306_64X32
|
||||||
|
0x1F, // for 64-wide displays
|
||||||
|
#else
|
||||||
|
0x3F, // for 128-wide displays
|
||||||
|
#endif
|
||||||
|
SSD1306_SETDISPLAYOFFSET, // 0xD3
|
||||||
|
0x00, // no offset
|
||||||
|
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
|
||||||
|
SSD1306_CHARGEPUMP, // 0x8D
|
||||||
|
0x14, // enable?
|
||||||
|
SSD1306_MEMORYMODE, // 0x20
|
||||||
|
0x00, // 0x0 act like ks0108
|
||||||
|
SSD1306_SEGREMAP | 0x1, // 0xA0 | bit
|
||||||
|
SSD1306_COMSCANDEC,
|
||||||
|
SSD1306_SETCOMPINS, // 0xDA
|
||||||
|
0x12, //
|
||||||
|
SSD1306_SETCONTRAST, // 0x81
|
||||||
|
0x8F,
|
||||||
|
SSD1306_SETPRECHARGE, // 0xd9
|
||||||
|
0xF1,
|
||||||
|
SSD1306_SETVCOMDETECT, // 0xDB
|
||||||
|
0x40,
|
||||||
|
SSD1306_DISPLAYALLON_RESUME, // 0xA4
|
||||||
|
#ifndef SSD1327
|
||||||
|
SSD1306_NORMALDISPLAY, // 0xA6
|
||||||
|
#endif
|
||||||
|
SSD1306_DISPLAYON, // 0xAF --turn on oled panel
|
||||||
|
#endif
|
||||||
|
SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the display buffer
|
||||||
|
uint8_t ssd1306_buffer[SSD1306_W * SSD1306_H / 8];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set the buffer to a color
|
||||||
|
*/
|
||||||
|
void ssd1306_setbuf(uint8_t color)
|
||||||
|
{
|
||||||
|
memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SSD1306_FULLUSE
|
||||||
|
/*
|
||||||
|
* expansion array for OLED with every other row unused
|
||||||
|
*/
|
||||||
|
const uint8_t expand[16] =
|
||||||
|
{
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x08,
|
||||||
|
0x0a,
|
||||||
|
0x20,
|
||||||
|
0x22,
|
||||||
|
0x28,
|
||||||
|
0x2a,
|
||||||
|
0x80,
|
||||||
|
0x82,
|
||||||
|
0x88,
|
||||||
|
0x8a,
|
||||||
|
0xa0,
|
||||||
|
0xa2,
|
||||||
|
0xa8,
|
||||||
|
0xaa,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send the frame buffer
|
||||||
|
*/
|
||||||
|
void ssd1306_refresh(void)
|
||||||
|
{
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
#ifdef SH1107
|
||||||
|
|
||||||
|
ssd1306_cmd(SSD1306_MEMORYMODE); // vertical addressing mode.
|
||||||
|
|
||||||
|
for (i = 0; i < SSD1306_H / 8; i++)
|
||||||
|
{
|
||||||
|
ssd1306_cmd(0xb0 | i);
|
||||||
|
ssd1306_cmd(0x00 | (0 & 0xf));
|
||||||
|
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 + 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 + 3 * SSD1306_PSZ], SSD1306_PSZ);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ssd1306_cmd(SSD1306_COLUMNADDR);
|
||||||
|
ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset)
|
||||||
|
ssd1306_cmd(SSD1306_OFFSET + SSD1306_W - 1); // Column end address (127 = reset)
|
||||||
|
|
||||||
|
ssd1306_cmd(SSD1306_PAGEADDR);
|
||||||
|
ssd1306_cmd(0); // Page start address (0 = reset)
|
||||||
|
ssd1306_cmd(7); // Page end address
|
||||||
|
|
||||||
|
#ifdef SSD1306_FULLUSE
|
||||||
|
/* for fully used rows just plow thru everything */
|
||||||
|
for (i = 0; i < sizeof(ssd1306_buffer); i += SSD1306_PSZ)
|
||||||
|
{
|
||||||
|
/* send PSZ block of data */
|
||||||
|
ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* for displays with odd rows unused expand bytes */
|
||||||
|
uint8_t tbuf[SSD1306_PSZ], j, k;
|
||||||
|
for (i = 0; i < sizeof(ssd1306_buffer); i += 128)
|
||||||
|
{
|
||||||
|
/* low nybble */
|
||||||
|
for (j = 0; j < 128; j += SSD1306_PSZ)
|
||||||
|
{
|
||||||
|
for (k = 0; k < SSD1306_PSZ; k++)
|
||||||
|
tbuf[k] = expand[ssd1306_buffer[i + j + k] & 0xf];
|
||||||
|
|
||||||
|
/* send PSZ block of data */
|
||||||
|
ssd1306_data(tbuf, SSD1306_PSZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* high nybble */
|
||||||
|
for (j = 0; j < 128; j += SSD1306_PSZ)
|
||||||
|
{
|
||||||
|
for (k = 0; k < SSD1306_PSZ; k++)
|
||||||
|
tbuf[k] = expand[(ssd1306_buffer[i + j + k] >> 4) & 0xf];
|
||||||
|
|
||||||
|
/* send PSZ block of data */
|
||||||
|
ssd1306_data(tbuf, SSD1306_PSZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* plot a pixel in the buffer
|
||||||
|
*/
|
||||||
|
void ssd1306_drawPixel(uint32_t x, uint32_t y, int color)
|
||||||
|
{
|
||||||
|
uint32_t addr;
|
||||||
|
|
||||||
|
/* clip */
|
||||||
|
if (x >= SSD1306_W)
|
||||||
|
return;
|
||||||
|
if (y >= SSD1306_H)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* compute buffer address */
|
||||||
|
addr = x + SSD1306_W * (y / 8);
|
||||||
|
|
||||||
|
/* set/clear bit in buffer */
|
||||||
|
if (color)
|
||||||
|
ssd1306_buffer[addr] |= (1 << (y & 7));
|
||||||
|
else
|
||||||
|
ssd1306_buffer[addr] &= ~(1 << (y & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* plot a pixel in the buffer
|
||||||
|
*/
|
||||||
|
void ssd1306_xorPixel(uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
uint32_t addr;
|
||||||
|
|
||||||
|
/* clip */
|
||||||
|
if (x >= SSD1306_W)
|
||||||
|
return;
|
||||||
|
if (y >= SSD1306_H)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* compute buffer address */
|
||||||
|
addr = x + SSD1306_W * (y / 8);
|
||||||
|
|
||||||
|
ssd1306_buffer[addr] ^= (1 << (y & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draw a an image from an array, directly into to the display buffer
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
uint32_t x_absolute;
|
||||||
|
uint32_t y_absolute;
|
||||||
|
uint32_t pixel;
|
||||||
|
uint32_t bytes_to_draw = width / 8;
|
||||||
|
uint32_t buffer_addr;
|
||||||
|
|
||||||
|
for (uint32_t line = 0; line < height; line++)
|
||||||
|
{
|
||||||
|
y_absolute = y + line;
|
||||||
|
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
|
||||||
|
// bitmask for current pixel in vertical (output) byte
|
||||||
|
uint32_t v_mask = 1 << (y_absolute & 7);
|
||||||
|
|
||||||
|
for (uint32_t byte = 0; byte < bytes_to_draw; byte++)
|
||||||
|
{
|
||||||
|
uint32_t input_byte = input[byte + line * bytes_to_draw];
|
||||||
|
|
||||||
|
for (pixel = 0; pixel < 8; pixel++)
|
||||||
|
{
|
||||||
|
x_absolute = x + 8 * (bytes_to_draw - byte) + pixel;
|
||||||
|
if (x_absolute >= SSD1306_W)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8
|
||||||
|
buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8);
|
||||||
|
// state of current pixel
|
||||||
|
uint8_t input_pixel = input_byte & (1 << pixel);
|
||||||
|
|
||||||
|
switch (color_mode)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// write pixels as they are
|
||||||
|
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// write pixels after inversion
|
||||||
|
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// 0 clears pixel
|
||||||
|
ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// 1 sets pixel
|
||||||
|
ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
// 0 sets pixel
|
||||||
|
ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
// 1 clears pixel
|
||||||
|
ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if SSD1306_LOG_IMAGE == 1
|
||||||
|
printf("%02x ", input_byte);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if SSD1306_LOG_IMAGE == 1
|
||||||
|
printf("\n\r");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fast vert line
|
||||||
|
*/
|
||||||
|
void ssd1306_drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color)
|
||||||
|
{
|
||||||
|
// clipping
|
||||||
|
if ((x >= SSD1306_W) || (y >= SSD1306_H)) return;
|
||||||
|
if ((y + h - 1) >= SSD1306_H) h = SSD1306_H - y;
|
||||||
|
while (h--)
|
||||||
|
{
|
||||||
|
ssd1306_drawPixel(x, y++, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fast horiz line
|
||||||
|
*/
|
||||||
|
void ssd1306_drawFastHLine(uint32_t x, uint32_t y, uint32_t w, uint32_t color)
|
||||||
|
{
|
||||||
|
// clipping
|
||||||
|
if ((x >= SSD1306_W) || (y >= SSD1306_H)) return;
|
||||||
|
if ((x + w - 1) >= SSD1306_W) w = SSD1306_W - x;
|
||||||
|
|
||||||
|
while (w--)
|
||||||
|
{
|
||||||
|
ssd1306_drawPixel(x++, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* abs() helper function for line drawing
|
||||||
|
*/
|
||||||
|
int gfx_abs(int x)
|
||||||
|
{
|
||||||
|
return (x < 0) ? -x : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* swap() helper function for line drawing
|
||||||
|
*/
|
||||||
|
void gfx_swap(int *z0, int *z1)
|
||||||
|
{
|
||||||
|
uint16_t temp = *z0;
|
||||||
|
*z0 = *z1;
|
||||||
|
*z1 = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bresenham line draw routine swiped from Wikipedia
|
||||||
|
*/
|
||||||
|
void ssd1306_drawLine(int x0, int y0, int x1, int y1, uint32_t color)
|
||||||
|
{
|
||||||
|
int32_t steep;
|
||||||
|
int32_t deltax, deltay, error, ystep, x, y;
|
||||||
|
|
||||||
|
/* flip sense 45deg to keep error calc in range */
|
||||||
|
steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0));
|
||||||
|
|
||||||
|
if (steep)
|
||||||
|
{
|
||||||
|
gfx_swap(&x0, &y0);
|
||||||
|
gfx_swap(&x1, &y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* run low->high */
|
||||||
|
if (x0 > x1)
|
||||||
|
{
|
||||||
|
gfx_swap(&x0, &x1);
|
||||||
|
gfx_swap(&y0, &y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up loop initial conditions */
|
||||||
|
deltax = x1 - x0;
|
||||||
|
deltay = gfx_abs(y1 - y0);
|
||||||
|
error = deltax / 2;
|
||||||
|
y = y0;
|
||||||
|
if (y0 < y1)
|
||||||
|
ystep = 1;
|
||||||
|
else
|
||||||
|
ystep = -1;
|
||||||
|
|
||||||
|
/* loop x */
|
||||||
|
for (x = x0; x <= x1; x++)
|
||||||
|
{
|
||||||
|
/* plot point */
|
||||||
|
if (steep)
|
||||||
|
/* flip point & plot */
|
||||||
|
ssd1306_drawPixel(y, x, color);
|
||||||
|
else
|
||||||
|
/* just plot */
|
||||||
|
ssd1306_drawPixel(x, y, color);
|
||||||
|
|
||||||
|
/* update error */
|
||||||
|
error = error - deltay;
|
||||||
|
|
||||||
|
/* update y */
|
||||||
|
if (error < 0)
|
||||||
|
{
|
||||||
|
y = y + ystep;
|
||||||
|
error = error + deltax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draws a circle
|
||||||
|
*/
|
||||||
|
void ssd1306_drawCircle(int x, int y, int radius, int color)
|
||||||
|
{
|
||||||
|
/* Bresenham algorithm */
|
||||||
|
int x_pos = -radius;
|
||||||
|
int y_pos = 0;
|
||||||
|
int err = 2 - 2 * radius;
|
||||||
|
int e2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
e2 = err;
|
||||||
|
if (e2 <= y_pos)
|
||||||
|
{
|
||||||
|
err += ++y_pos * 2 + 1;
|
||||||
|
if (-x_pos == y_pos && e2 <= x_pos)
|
||||||
|
{
|
||||||
|
e2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e2 > x_pos)
|
||||||
|
{
|
||||||
|
err += ++x_pos * 2 + 1;
|
||||||
|
}
|
||||||
|
} while (x_pos <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draws a filled circle
|
||||||
|
*/
|
||||||
|
void ssd1306_fillCircle(int x, int y, int radius, int color)
|
||||||
|
{
|
||||||
|
/* Bresenham algorithm */
|
||||||
|
int x_pos = -radius;
|
||||||
|
int y_pos = 0;
|
||||||
|
int err = 2 - 2 * radius;
|
||||||
|
int e2;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
if (e2 <= y_pos)
|
||||||
|
{
|
||||||
|
err += ++y_pos * 2 + 1;
|
||||||
|
if (-x_pos == y_pos && e2 <= x_pos)
|
||||||
|
{
|
||||||
|
e2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e2 > x_pos)
|
||||||
|
{
|
||||||
|
err += ++x_pos * 2 + 1;
|
||||||
|
}
|
||||||
|
} while (x_pos <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draw a rectangle
|
||||||
|
*/
|
||||||
|
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 + w - 1, y, h, color);
|
||||||
|
ssd1306_drawFastHLine(x, y, w, color);
|
||||||
|
ssd1306_drawFastHLine(x, y + h - 1, w, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fill a rectangle
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* scan vertical */
|
||||||
|
while (h--)
|
||||||
|
{
|
||||||
|
m = x;
|
||||||
|
w = iw;
|
||||||
|
/* scan horizontal */
|
||||||
|
while (w--)
|
||||||
|
{
|
||||||
|
/* invert pixels */
|
||||||
|
ssd1306_drawPixel(m++, n, color);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* invert a rectangle in the buffer
|
||||||
|
*/
|
||||||
|
void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
|
||||||
|
{
|
||||||
|
uint8_t m, n = y, iw = w;
|
||||||
|
|
||||||
|
/* scan vertical */
|
||||||
|
while (h--)
|
||||||
|
{
|
||||||
|
m = x;
|
||||||
|
w = iw;
|
||||||
|
/* scan horizontal */
|
||||||
|
while (w--)
|
||||||
|
{
|
||||||
|
/* invert pixels */
|
||||||
|
ssd1306_xorPixel(m++, n);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw character to the display buffer
|
||||||
|
*/
|
||||||
|
void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
|
||||||
|
{
|
||||||
|
uint16_t i, j, col;
|
||||||
|
uint8_t d;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
d = fontdata[(chr << 3) + i];
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if (d & 0x80)
|
||||||
|
col = color;
|
||||||
|
else
|
||||||
|
col = (~color) & 1;
|
||||||
|
|
||||||
|
ssd1306_drawPixel(x + j, y + i, col);
|
||||||
|
|
||||||
|
// next bit
|
||||||
|
d <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draw a string to the display
|
||||||
|
*/
|
||||||
|
void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
while ((c = *str++))
|
||||||
|
{
|
||||||
|
ssd1306_drawchar(x, y, c, color);
|
||||||
|
x += 8;
|
||||||
|
if (x > 120)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enum for font size
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
fontsize_8x8 = 1,
|
||||||
|
fontsize_16x16 = 2,
|
||||||
|
fontsize_32x32 = 4,
|
||||||
|
fontsize_64x64 = 8,
|
||||||
|
} font_size_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw character to the display buffer, scaled to 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;
|
||||||
|
uint8_t d;
|
||||||
|
|
||||||
|
// Determine the font scale factor based on the font_size parameter
|
||||||
|
uint8_t font_scale = (uint8_t)font_size;
|
||||||
|
|
||||||
|
// Loop through each row of the font data
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
// Retrieve the font data for the current row
|
||||||
|
d = fontdata[(chr << 3) + i];
|
||||||
|
|
||||||
|
// Loop through each column of the font data
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
// Determine the color to draw based on the current bit in the font data
|
||||||
|
if (d & 0x80)
|
||||||
|
col = color;
|
||||||
|
else
|
||||||
|
col = (~color) & 1;
|
||||||
|
|
||||||
|
// 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 l = 0; l < font_scale; l++)
|
||||||
|
{
|
||||||
|
ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next bit in the font data
|
||||||
|
d <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* draw a string to the display buffer, scaled to size
|
||||||
|
*/
|
||||||
|
void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
while ((c = *str++))
|
||||||
|
{
|
||||||
|
ssd1306_drawchar_sz(x, y, c, color, font_size);
|
||||||
|
x += 8 * font_size;
|
||||||
|
if (x > 128 - 8 * font_size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize I2C and OLED
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_init(void)
|
||||||
|
{
|
||||||
|
// pulse reset
|
||||||
|
ssd1306_rst();
|
||||||
|
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
|
||||||
|
// initialize OLED
|
||||||
|
#if !defined(SSD1306_CUSTOM_INIT_ARRAY) || !SSD1306_CUSTOM_INIT_ARRAY
|
||||||
|
uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
|
||||||
|
while (*cmd_list != SSD1306_TERMINATE_CMDS)
|
||||||
|
{
|
||||||
|
if (ssd1306_cmd(*cmd_list++))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear display
|
||||||
|
ssd1306_refresh();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
390
src/extralibs/ssd1306_i2c.h
Normal file
390
src/extralibs/ssd1306_i2c.h
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
/*
|
||||||
|
* Single-File-Header for SSD1306 I2C interface
|
||||||
|
* 05-07-2023 E. Brombaugh
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SSD1306_I2C_H
|
||||||
|
#define _SSD1306_I2C_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// For the CH32V203, we support remapping, if so,
|
||||||
|
// #define SSD1306_REMAP_I2C
|
||||||
|
|
||||||
|
// SSD1306 I2C address
|
||||||
|
#define SSD1306_I2C_ADDR 0x3c
|
||||||
|
|
||||||
|
// I2C Bus clock rate - must be lower the Logic clock rate
|
||||||
|
#define SSD1306_I2C_CLKRATE 1000000
|
||||||
|
|
||||||
|
// I2C Logic clock rate - must be higher than Bus clock rate
|
||||||
|
#define SSD1306_I2C_PRERATE 2000000
|
||||||
|
|
||||||
|
// uncomment this for high-speed 36% duty cycle, otherwise 33%
|
||||||
|
#define SSD1306_I2C_DUTY
|
||||||
|
|
||||||
|
// I2C Timeout count
|
||||||
|
#define TIMEOUT_MAX 100000
|
||||||
|
|
||||||
|
// uncomment this to enable IRQ-driven operation
|
||||||
|
// #define SSD1306_I2C_IRQ
|
||||||
|
|
||||||
|
#ifdef SSD1306_I2C_IRQ
|
||||||
|
// some stuff that IRQ mode needs
|
||||||
|
volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state;
|
||||||
|
|
||||||
|
// uncomment this to enable time diags in IRQ
|
||||||
|
// #define IRQ_DIAG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init just I2C
|
||||||
|
*/
|
||||||
|
void ssd1306_i2c_setup(void)
|
||||||
|
{
|
||||||
|
uint16_t tempreg;
|
||||||
|
|
||||||
|
// Reset I2C1 to init all regs
|
||||||
|
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
||||||
|
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
// set freq
|
||||||
|
tempreg = I2C1->CTLR2;
|
||||||
|
tempreg &= ~I2C_CTLR2_FREQ;
|
||||||
|
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / SSD1306_I2C_PRERATE) & I2C_CTLR2_FREQ;
|
||||||
|
I2C1->CTLR2 = tempreg;
|
||||||
|
|
||||||
|
// Set clock config
|
||||||
|
tempreg = 0;
|
||||||
|
#if (SSD1306_I2C_CLKRATE <= 100000)
|
||||||
|
// standard mode good to 100kHz
|
||||||
|
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (2 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
|
||||||
|
#else
|
||||||
|
// fast mode over 100kHz
|
||||||
|
#ifndef SSD1306_I2C_DUTY
|
||||||
|
// 33% duty cycle
|
||||||
|
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (3 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
|
||||||
|
#else
|
||||||
|
// 36% duty cycle
|
||||||
|
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (25 * SSD1306_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
|
||||||
|
tempreg |= I2C_CKCFGR_DUTY;
|
||||||
|
#endif
|
||||||
|
tempreg |= I2C_CKCFGR_FS;
|
||||||
|
#endif
|
||||||
|
I2C1->CKCFGR = tempreg;
|
||||||
|
|
||||||
|
#ifdef SSD1306_I2C_IRQ
|
||||||
|
// enable IRQ driven operation
|
||||||
|
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
||||||
|
|
||||||
|
// initialize the state
|
||||||
|
ssd1306_i2c_irq_state = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enable I2C
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_PE;
|
||||||
|
|
||||||
|
// set ACK mode
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* error descriptions
|
||||||
|
*/
|
||||||
|
char *errstr[] =
|
||||||
|
{
|
||||||
|
"not busy",
|
||||||
|
"master mode",
|
||||||
|
"transmit mode",
|
||||||
|
"tx empty",
|
||||||
|
"transmit complete",
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* error handler
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_i2c_error(uint8_t err)
|
||||||
|
{
|
||||||
|
// report error
|
||||||
|
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
|
||||||
|
|
||||||
|
// reset & initialize I2C
|
||||||
|
ssd1306_i2c_setup();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// event codes we use
|
||||||
|
#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
|
||||||
|
#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
|
||||||
|
#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check for 32-bit event codes
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask)
|
||||||
|
{
|
||||||
|
/* read order matters here! STAR1 before STAR2!! */
|
||||||
|
uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16);
|
||||||
|
return (status & event_mask) == event_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SSD1306_I2C_IRQ
|
||||||
|
/*
|
||||||
|
* packet send for IRQ-driven operation
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
|
||||||
|
{
|
||||||
|
int32_t timeout;
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
GPIOC->BSHR = (1 << (3));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// error out if buffer under/overflow
|
||||||
|
if ((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// wait for previous packet to finish
|
||||||
|
while (ssd1306_i2c_irq_state)
|
||||||
|
;
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
GPIOC->BSHR = (1 << (16 + 3));
|
||||||
|
GPIOC->BSHR = (1 << (4));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// init buffer for sending
|
||||||
|
ssd1306_i2c_send_sz = sz;
|
||||||
|
ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer;
|
||||||
|
memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz);
|
||||||
|
|
||||||
|
// wait for not busy
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(0);
|
||||||
|
|
||||||
|
// Set START condition
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_START;
|
||||||
|
|
||||||
|
// wait for master mode select
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(1);
|
||||||
|
|
||||||
|
// send 7-bit address + write flag
|
||||||
|
I2C1->DATAR = addr << 1;
|
||||||
|
|
||||||
|
// wait for transmit condition
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(2);
|
||||||
|
|
||||||
|
// Enable TXE interrupt
|
||||||
|
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
|
||||||
|
ssd1306_i2c_irq_state = 1;
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
GPIOC->BSHR = (1 << (16 + 4));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// exit
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ handler for I2C events
|
||||||
|
*/
|
||||||
|
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
|
||||||
|
void I2C1_EV_IRQHandler(void)
|
||||||
|
{
|
||||||
|
uint16_t STAR1, STAR2 __attribute__((unused));
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
GPIOC->BSHR = (1 << (4));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// read status, clear any events
|
||||||
|
STAR1 = I2C1->STAR1;
|
||||||
|
STAR2 = I2C1->STAR2;
|
||||||
|
|
||||||
|
/* check for TXE */
|
||||||
|
if (STAR1 & I2C_STAR1_TXE)
|
||||||
|
{
|
||||||
|
/* check for remaining data */
|
||||||
|
if (ssd1306_i2c_send_sz--)
|
||||||
|
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
|
||||||
|
|
||||||
|
/* was that the last byte? */
|
||||||
|
if (!ssd1306_i2c_send_sz)
|
||||||
|
{
|
||||||
|
// disable TXE interrupt
|
||||||
|
I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
|
||||||
|
|
||||||
|
// reset IRQ state
|
||||||
|
ssd1306_i2c_irq_state = 0;
|
||||||
|
|
||||||
|
// wait for tx complete
|
||||||
|
while (!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED))
|
||||||
|
;
|
||||||
|
|
||||||
|
// set STOP condition
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
GPIOC->BSHR = (1 << (16 + 4));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* low-level packet send for blocking polled operation via i2c
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_i2c_send(uint8_t addr, const uint8_t *data, int sz)
|
||||||
|
{
|
||||||
|
int32_t timeout;
|
||||||
|
|
||||||
|
// wait for not busy
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(0);
|
||||||
|
|
||||||
|
// Set START condition
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_START;
|
||||||
|
|
||||||
|
// wait for master mode select
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(1);
|
||||||
|
|
||||||
|
// send 7-bit address + write flag
|
||||||
|
I2C1->DATAR = addr << 1;
|
||||||
|
|
||||||
|
// wait for transmit condition
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(2);
|
||||||
|
|
||||||
|
// send data one byte at a time
|
||||||
|
while (sz--)
|
||||||
|
{
|
||||||
|
// wait for TX Empty
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while (!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(3);
|
||||||
|
|
||||||
|
// send command
|
||||||
|
I2C1->DATAR = *data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for tx complete
|
||||||
|
timeout = TIMEOUT_MAX;
|
||||||
|
while ((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--))
|
||||||
|
;
|
||||||
|
if (timeout == -1)
|
||||||
|
return ssd1306_i2c_error(4);
|
||||||
|
|
||||||
|
// set STOP condition
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
||||||
|
|
||||||
|
// we're happy
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* high-level packet send for I2C
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
|
||||||
|
{
|
||||||
|
uint8_t pkt[33];
|
||||||
|
|
||||||
|
/* build command or data packets */
|
||||||
|
if (cmd)
|
||||||
|
{
|
||||||
|
pkt[0] = 0;
|
||||||
|
pkt[1] = *data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pkt[0] = 0x40;
|
||||||
|
memcpy(&pkt[1], data, sz);
|
||||||
|
}
|
||||||
|
return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init I2C and GPIO
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_i2c_init(void)
|
||||||
|
{
|
||||||
|
// Enable GPIOC and I2C
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
#ifdef CH32V20x
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
|
||||||
|
|
||||||
|
#ifdef SSD1306_REMAP_I2C
|
||||||
|
AFIO->PCFR1 |= AFIO_PCFR1_I2C1_REMAP;
|
||||||
|
funPinMode(PB8, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
funPinMode(PB9, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
#else
|
||||||
|
funPinMode(PB6, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
funPinMode(PB7, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO;
|
||||||
|
// PC1 is SDA, 10MHz Output, alt func, open-drain
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 1));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 1);
|
||||||
|
|
||||||
|
// PC2 is SCL, 10MHz Output, alt func, open-drain
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 2));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IRQ_DIAG
|
||||||
|
// GPIO diags on PC3/PC4
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 3));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 3);
|
||||||
|
GPIOC->BSHR = (1 << (16 + 3));
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 4));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 4);
|
||||||
|
GPIOC->BSHR = (1 << (16 + 4));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// load I2C regs
|
||||||
|
ssd1306_i2c_setup();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// test if SSD1306 is on the bus by sending display off command
|
||||||
|
uint8_t command = 0xAF;
|
||||||
|
return ssd1306_pkt_send(&command, 1, 1);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reset is not used for SSD1306 I2C interface
|
||||||
|
*/
|
||||||
|
void ssd1306_rst(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
140
src/extralibs/ssd1306_i2c_bitbang.h
Normal file
140
src/extralibs/ssd1306_i2c_bitbang.h
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Single-File-Header for SSD1306 emulated I2C interface
|
||||||
|
* 02-12-2025 cnlohr
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SSD1306_I2C_BITBANG_H
|
||||||
|
#define _SSD1306_I2C_BITBANG_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// SSD1306 I2C address
|
||||||
|
#ifndef SSD1306_I2C_ADDR
|
||||||
|
#define SSD1306_I2C_ADDR 0x3c
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_I2C_BITBANG_SDA
|
||||||
|
#define SSD1306_I2C_BITBANG_SDA PC1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_I2C_BITBANG_SCL
|
||||||
|
#define SSD1306_I2C_BITBANG_SCL PC2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init just I2C
|
||||||
|
*/
|
||||||
|
void ssd1306_i2c_setup(void)
|
||||||
|
{
|
||||||
|
funGpioInitAll();
|
||||||
|
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP);
|
||||||
|
funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1);
|
||||||
|
funPinMode(SSD1306_I2C_BITBANG_SCL, GPIO_CFGLR_OUT_10Mhz_PP);
|
||||||
|
funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SDA_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 1);
|
||||||
|
#define SCL_HIGH funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 1);
|
||||||
|
#define SDA_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SDA, 0);
|
||||||
|
#define SCL_LOW funDigitalWrite(SSD1306_I2C_BITBANG_SCL, 0);
|
||||||
|
#define SDA_IN funDigitalRead(SSD1306_I2C_BITBANG_SDA);
|
||||||
|
#define I2CSPEEDBASE 1
|
||||||
|
#define I2CDELAY_FUNC(x) ADD_N_NOPS(x * 1)
|
||||||
|
// Delay_Us(x*1);
|
||||||
|
|
||||||
|
static void ssd1306_i2c_sendstart()
|
||||||
|
{
|
||||||
|
SCL_HIGH
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SDA_LOW
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_LOW
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssd1306_i2c_sendstop()
|
||||||
|
{
|
||||||
|
SDA_LOW
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_LOW
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_HIGH
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SDA_HIGH
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nonzero on failure.
|
||||||
|
unsigned char ssd1306_i2c_sendbyte(unsigned char data)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
if (data & 0x80)
|
||||||
|
{
|
||||||
|
SDA_HIGH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SDA_LOW;
|
||||||
|
}
|
||||||
|
data <<= 1;
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_HIGH
|
||||||
|
I2CDELAY_FUNC(2 * I2CSPEEDBASE);
|
||||||
|
SCL_LOW
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immediately after sending last bit, open up DDDR for control.
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_IN_PUPD);
|
||||||
|
SDA_HIGH
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_HIGH
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
i = SDA_IN;
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SCL_LOW
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
SDA_HIGH // Maybe?
|
||||||
|
funPinMode(SSD1306_I2C_BITBANG_SDA, GPIO_CFGLR_OUT_10Mhz_PP);
|
||||||
|
I2CDELAY_FUNC(1 * I2CSPEEDBASE);
|
||||||
|
return !!i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
|
||||||
|
{
|
||||||
|
ssd1306_i2c_sendstart();
|
||||||
|
int r = ssd1306_i2c_sendbyte(SSD1306_I2C_ADDR << 1);
|
||||||
|
if (r) return r;
|
||||||
|
// ssd1306_i2c_sendstart(); For some reason displays don't want repeated start
|
||||||
|
if (cmd)
|
||||||
|
{
|
||||||
|
if (ssd1306_i2c_sendbyte(0x00))
|
||||||
|
return 1; // Control
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ssd1306_i2c_sendbyte(0x40))
|
||||||
|
return 1; // Data
|
||||||
|
}
|
||||||
|
for (int i = 0; i < sz; i++)
|
||||||
|
{
|
||||||
|
if (ssd1306_i2c_sendbyte(data[i]))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ssd1306_i2c_sendstop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssd1306_rst(void)
|
||||||
|
{
|
||||||
|
funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_10Mhz_PP);
|
||||||
|
funDigitalWrite(SSD1306_RST_PIN, 0);
|
||||||
|
Delay_Ms(10);
|
||||||
|
funDigitalWrite(SSD1306_RST_PIN, 1);
|
||||||
|
Delay_Us(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
112
src/extralibs/ssd1306_spi.h
Normal file
112
src/extralibs/ssd1306_spi.h
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Single-File-Header for SSD1306 SPI interface
|
||||||
|
* 05-05-2023 E. Brombaugh
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SSD1306_SPI_H
|
||||||
|
#define _SSD1306_SPI_H
|
||||||
|
|
||||||
|
// control pins
|
||||||
|
#ifndef SSD1306_RST_PIN
|
||||||
|
#define SSD1306_RST_PIN PC2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_CS_PIN
|
||||||
|
#define SSD1306_CS_PIN PC3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_DC_PIN
|
||||||
|
#define SSD1306_DC_PIN PC4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_MOSI_PIN
|
||||||
|
#define SSD1306_MOSI_PIN PC6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_SCK_PIN
|
||||||
|
#define SSD1306_SCK_PIN PC5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SSD1306_BAUD_RATE_PRESCALER
|
||||||
|
#define SSD1306_BAUD_RATE_PRESCALER SPI_BaudRatePrescaler_2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init SPI and GPIO for SSD1306 OLED
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_spi_init(void)
|
||||||
|
{
|
||||||
|
// Enable GPIOC and SPI
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
|
||||||
|
|
||||||
|
funGpioInitAll();
|
||||||
|
funPinMode(SSD1306_RST_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
|
||||||
|
funPinMode(SSD1306_CS_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
|
||||||
|
funPinMode(SSD1306_DC_PIN, GPIO_CFGLR_OUT_50Mhz_PP);
|
||||||
|
funPinMode(SSD1306_MOSI_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
|
||||||
|
funPinMode(SSD1306_SCK_PIN, GPIO_CFGLR_OUT_50Mhz_AF_PP);
|
||||||
|
|
||||||
|
funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
|
||||||
|
funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
|
||||||
|
funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
|
||||||
|
|
||||||
|
// Configure SPI
|
||||||
|
SPI1->CTLR1 =
|
||||||
|
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b |
|
||||||
|
SPI_Mode_Master | SPI_Direction_1Line_Tx |
|
||||||
|
SSD1306_BAUD_RATE_PRESCALER;
|
||||||
|
|
||||||
|
// enable SPI port
|
||||||
|
SPI1->CTLR1 |= CTLR1_SPE_Set;
|
||||||
|
|
||||||
|
// always succeed
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* toggle reset line
|
||||||
|
*/
|
||||||
|
void ssd1306_rst(void)
|
||||||
|
{
|
||||||
|
funDigitalWrite(SSD1306_RST_PIN, FUN_LOW);
|
||||||
|
Delay_Ms(10);
|
||||||
|
funDigitalWrite(SSD1306_RST_PIN, FUN_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* packet send for blocking polled operation via spi
|
||||||
|
*/
|
||||||
|
uint8_t ssd1306_pkt_send(const uint8_t *data, int sz, uint8_t cmd)
|
||||||
|
{
|
||||||
|
if (cmd)
|
||||||
|
{
|
||||||
|
funDigitalWrite(SSD1306_DC_PIN, FUN_LOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
funDigitalWrite(SSD1306_DC_PIN, FUN_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
funDigitalWrite(SSD1306_CS_PIN, FUN_LOW);
|
||||||
|
|
||||||
|
// send data
|
||||||
|
while (sz--)
|
||||||
|
{
|
||||||
|
// wait for TXE
|
||||||
|
while (!(SPI1->STATR & SPI_STATR_TXE))
|
||||||
|
;
|
||||||
|
|
||||||
|
// Send byte
|
||||||
|
SPI1->DATAR = *data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for not busy before exiting
|
||||||
|
while (SPI1->STATR & SPI_STATR_BSY) {}
|
||||||
|
|
||||||
|
funDigitalWrite(SSD1306_CS_PIN, FUN_HIGH);
|
||||||
|
|
||||||
|
// we're happy
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
1792
src/extralibs/usb_defines.h
Normal file
1792
src/extralibs/usb_defines.h
Normal file
File diff suppressed because it is too large
Load diff
286
src/extralibs/ws2812b_dma_spi_led_driver.h
Normal file
286
src/extralibs/ws2812b_dma_spi_led_driver.h
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/* Single-File-Header for using asynchronous LEDs with the CH32V003 using DMA to the SPI port.
|
||||||
|
I may write another version of this to use DMA to timer ports, but, the SPI port can be used
|
||||||
|
to generate outputs very efficiently. So, for now, SPI Port. Additionally, it uses FAR less
|
||||||
|
internal bus resources than to do the same thing with timers.
|
||||||
|
|
||||||
|
**For the CH32V003 this means output will be on PORTC Pin 6**
|
||||||
|
|
||||||
|
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
|
||||||
|
|
||||||
|
If you are including this in main, simply
|
||||||
|
#define WS2812DMA_IMPLEMENTATION
|
||||||
|
|
||||||
|
Other defines inclue:
|
||||||
|
#define WSRAW
|
||||||
|
#define WSRBG
|
||||||
|
#define WSGRB
|
||||||
|
#define WS2812B_ALLOW_INTERRUPT_NESTING
|
||||||
|
|
||||||
|
You will need to implement the following two functions, as callbacks from the ISR.
|
||||||
|
uint32_t WS2812BLEDCallback( int ledno );
|
||||||
|
|
||||||
|
You willalso need to call
|
||||||
|
WS2812BDMAInit();
|
||||||
|
|
||||||
|
Then, whenyou want to update the LEDs, call:
|
||||||
|
WS2812BDMAStart( int num_leds );
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WS2812_LED_DRIVER_H
|
||||||
|
#define _WS2812_LED_DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Use DMA and SPI to stream out WS2812B LED Data via the MOSI pin.
|
||||||
|
void WS2812BDMAInit();
|
||||||
|
void WS2812BDMAStart(int leds);
|
||||||
|
|
||||||
|
// Callbacks that you must implement.
|
||||||
|
uint32_t WS2812BLEDCallback(int ledno);
|
||||||
|
|
||||||
|
#ifdef WS2812DMA_IMPLEMENTATION
|
||||||
|
|
||||||
|
// Must be divisble by 4.
|
||||||
|
#ifndef DMALEDS
|
||||||
|
#define DMALEDS 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Note first n LEDs of DMA Buffer are 0's as a "break"
|
||||||
|
// Need one extra LED at end to leave line high.
|
||||||
|
// This must be greater than WS2812B_RESET_PERIOD.
|
||||||
|
#define WS2812B_RESET_PERIOD 2
|
||||||
|
|
||||||
|
#ifdef WSRAW
|
||||||
|
#define DMA_BUFFER_LEN (((DMALEDS) / 2) * 8)
|
||||||
|
#else
|
||||||
|
#define DMA_BUFFER_LEN (((DMALEDS) / 2) * 6)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint16_t WS2812dmabuff[DMA_BUFFER_LEN];
|
||||||
|
static volatile int WS2812LEDs;
|
||||||
|
static volatile int WS2812LEDPlace;
|
||||||
|
static volatile int WS2812BLEDInUse;
|
||||||
|
// This is the code that updates a portion of the WS2812dmabuff with new data.
|
||||||
|
// This effectively creates the bitstream that outputs to the LEDs.
|
||||||
|
static void WS2812FillBuffSec(uint16_t *ptr, int numhalfwords, int tce)
|
||||||
|
{
|
||||||
|
const static uint16_t bitquartets[16] = {
|
||||||
|
0b1000100010001000,
|
||||||
|
0b1000100010001110,
|
||||||
|
0b1000100011101000,
|
||||||
|
0b1000100011101110,
|
||||||
|
0b1000111010001000,
|
||||||
|
0b1000111010001110,
|
||||||
|
0b1000111011101000,
|
||||||
|
0b1000111011101110,
|
||||||
|
0b1110100010001000,
|
||||||
|
0b1110100010001110,
|
||||||
|
0b1110100011101000,
|
||||||
|
0b1110100011101110,
|
||||||
|
0b1110111010001000,
|
||||||
|
0b1110111010001110,
|
||||||
|
0b1110111011101000,
|
||||||
|
0b1110111011101110,
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
uint16_t *end = ptr + numhalfwords;
|
||||||
|
int ledcount = WS2812LEDs;
|
||||||
|
int place = WS2812LEDPlace;
|
||||||
|
|
||||||
|
#ifdef WSRAW
|
||||||
|
while (place < 0 && ptr != end)
|
||||||
|
{
|
||||||
|
uint32_t *lptr = (uint32_t *)ptr;
|
||||||
|
lptr[0] = 0;
|
||||||
|
lptr[1] = 0;
|
||||||
|
lptr[2] = 0;
|
||||||
|
lptr[3] = 0;
|
||||||
|
ptr += 8;
|
||||||
|
place++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
while (place < 0 && ptr != end)
|
||||||
|
{
|
||||||
|
(*ptr++) = 0;
|
||||||
|
(*ptr++) = 0;
|
||||||
|
(*ptr++) = 0;
|
||||||
|
(*ptr++) = 0;
|
||||||
|
(*ptr++) = 0;
|
||||||
|
(*ptr++) = 0;
|
||||||
|
place++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (ptr != end)
|
||||||
|
{
|
||||||
|
if (place >= ledcount)
|
||||||
|
{
|
||||||
|
// Optionally, leave line high.
|
||||||
|
while (ptr != end)
|
||||||
|
(*ptr++) = 0; // 0xffff;
|
||||||
|
|
||||||
|
// Only safe to do this when we're on the second leg.
|
||||||
|
if (tce)
|
||||||
|
{
|
||||||
|
if (place == ledcount)
|
||||||
|
{
|
||||||
|
// Take the DMA out of circular mode and let it expire.
|
||||||
|
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
|
||||||
|
WS2812BLEDInUse = 0;
|
||||||
|
}
|
||||||
|
place++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WSRAW
|
||||||
|
uint32_t ledval32bit = WS2812BLEDCallback(place++);
|
||||||
|
|
||||||
|
ptr[6] = bitquartets[(ledval32bit >> 28) & 0xf];
|
||||||
|
ptr[7] = bitquartets[(ledval32bit >> 24) & 0xf];
|
||||||
|
ptr[4] = bitquartets[(ledval32bit >> 20) & 0xf];
|
||||||
|
ptr[5] = bitquartets[(ledval32bit >> 16) & 0xf];
|
||||||
|
ptr[2] = bitquartets[(ledval32bit >> 12) & 0xf];
|
||||||
|
ptr[3] = bitquartets[(ledval32bit >> 8) & 0xf];
|
||||||
|
ptr[0] = bitquartets[(ledval32bit >> 4) & 0xf];
|
||||||
|
ptr[1] = bitquartets[(ledval32bit >> 0) & 0xf];
|
||||||
|
|
||||||
|
ptr += 8;
|
||||||
|
i += 8;
|
||||||
|
|
||||||
|
#else
|
||||||
|
// Use a LUT to figure out how we should set the SPI line.
|
||||||
|
uint32_t ledval24bit = WS2812BLEDCallback(place++);
|
||||||
|
|
||||||
|
#ifdef WSRBG
|
||||||
|
ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
|
||||||
|
ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
|
||||||
|
ptr[2] = bitquartets[(ledval24bit >> 20) & 0xf];
|
||||||
|
ptr[3] = bitquartets[(ledval24bit >> 16) & 0xf];
|
||||||
|
ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
|
||||||
|
ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
|
||||||
|
#elif defined(WSGRB)
|
||||||
|
ptr[0] = bitquartets[(ledval24bit >> 12) & 0xf];
|
||||||
|
ptr[1] = bitquartets[(ledval24bit >> 8) & 0xf];
|
||||||
|
ptr[2] = bitquartets[(ledval24bit >> 4) & 0xf];
|
||||||
|
ptr[3] = bitquartets[(ledval24bit >> 0) & 0xf];
|
||||||
|
ptr[4] = bitquartets[(ledval24bit >> 20) & 0xf];
|
||||||
|
ptr[5] = bitquartets[(ledval24bit >> 16) & 0xf];
|
||||||
|
#else
|
||||||
|
ptr[0] = bitquartets[(ledval24bit >> 20) & 0xf];
|
||||||
|
ptr[1] = bitquartets[(ledval24bit >> 16) & 0xf];
|
||||||
|
ptr[2] = bitquartets[(ledval24bit >> 12) & 0xf];
|
||||||
|
ptr[3] = bitquartets[(ledval24bit >> 8) & 0xf];
|
||||||
|
ptr[4] = bitquartets[(ledval24bit >> 4) & 0xf];
|
||||||
|
ptr[5] = bitquartets[(ledval24bit >> 0) & 0xf];
|
||||||
|
#endif
|
||||||
|
ptr += 6;
|
||||||
|
i += 6;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
WS2812LEDPlace = place;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt));
|
||||||
|
void DMA1_Channel3_IRQHandler(void)
|
||||||
|
{
|
||||||
|
// GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
|
||||||
|
|
||||||
|
// Backup flags.
|
||||||
|
volatile int intfr = DMA1->INTFR;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Clear all possible flags.
|
||||||
|
DMA1->INTFCR = DMA1_IT_GL3;
|
||||||
|
|
||||||
|
// Strange note: These are backwards. DMA1_IT_HT3 should be HALF and
|
||||||
|
// DMA1_IT_TC3 should be COMPLETE. But for some reason, doing this causes
|
||||||
|
// LED jitter. I am henseforth flipping the order.
|
||||||
|
|
||||||
|
if (intfr & DMA1_IT_HT3)
|
||||||
|
{
|
||||||
|
// Halfwaay (Fill in first part)
|
||||||
|
WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN / 2, 1);
|
||||||
|
}
|
||||||
|
if (intfr & DMA1_IT_TC3)
|
||||||
|
{
|
||||||
|
// Complete (Fill in second part)
|
||||||
|
WS2812FillBuffSec(WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0);
|
||||||
|
}
|
||||||
|
intfr = DMA1->INTFR;
|
||||||
|
} while (intfr & DMA1_IT_GL3);
|
||||||
|
|
||||||
|
// GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812BDMAStart(int leds)
|
||||||
|
{
|
||||||
|
// Enter critical section.
|
||||||
|
__disable_irq();
|
||||||
|
WS2812BLEDInUse = 1;
|
||||||
|
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
|
||||||
|
DMA1_Channel3->CNTR = 0;
|
||||||
|
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
|
||||||
|
WS2812LEDs = leds;
|
||||||
|
WS2812LEDPlace = -WS2812B_RESET_PERIOD;
|
||||||
|
__enable_irq();
|
||||||
|
|
||||||
|
WS2812FillBuffSec(WS2812dmabuff, DMA_BUFFER_LEN, 0);
|
||||||
|
|
||||||
|
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
|
||||||
|
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812BDMAInit()
|
||||||
|
{
|
||||||
|
// Enable DMA + Peripherals
|
||||||
|
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
|
||||||
|
|
||||||
|
// MOSI, Configure GPIO Pin
|
||||||
|
GPIOC->CFGLR &= ~(0xf << (4 * 6));
|
||||||
|
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 6);
|
||||||
|
|
||||||
|
// Configure SPI
|
||||||
|
SPI1->CTLR1 =
|
||||||
|
SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_16b |
|
||||||
|
SPI_Mode_Master | SPI_Direction_1Line_Tx |
|
||||||
|
3 << 3; // Divisior = 16 (48/16 = 3MHz)
|
||||||
|
|
||||||
|
SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
|
||||||
|
SPI1->HSCR = 1;
|
||||||
|
|
||||||
|
SPI1->CTLR1 |= CTLR1_SPE_Set;
|
||||||
|
|
||||||
|
SPI1->DATAR = 0; // Set SPI line Low.
|
||||||
|
|
||||||
|
// DMA1_Channel3 is for SPI1TX
|
||||||
|
DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR;
|
||||||
|
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
|
||||||
|
DMA1_Channel3->CNTR = 0; // sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!)
|
||||||
|
DMA1_Channel3->CFGR =
|
||||||
|
DMA_M2M_Disable |
|
||||||
|
DMA_Priority_VeryHigh |
|
||||||
|
DMA_MemoryDataSize_HalfWord |
|
||||||
|
DMA_PeripheralDataSize_HalfWord |
|
||||||
|
DMA_MemoryInc_Enable |
|
||||||
|
DMA_Mode_Normal | // OR DMA_Mode_Circular or DMA_Mode_Normal
|
||||||
|
DMA_DIR_PeripheralDST |
|
||||||
|
DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts.
|
||||||
|
|
||||||
|
// NVIC_SetPriority( DMA1_Channel3_IRQn, 0<<4 ); //We don't need to tweak priority.
|
||||||
|
NVIC_EnableIRQ(DMA1_Channel3_IRQn);
|
||||||
|
DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
|
||||||
|
|
||||||
|
#ifdef WS2812B_ALLOW_INTERRUPT_NESTING
|
||||||
|
__set_INTSYSCR(__get_INTSYSCR() | 2); // Enable interrupt nesting.
|
||||||
|
PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
82
src/extralibs/ws2812b_simple.h
Normal file
82
src/extralibs/ws2812b_simple.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* Single-File-Header for using asynchronous LEDs with the CH32V003 using GPIO.
|
||||||
|
|
||||||
|
Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
|
||||||
|
|
||||||
|
If you are including this in main, simply
|
||||||
|
#define WS2812BSIMPLE_IMPLEMENTATION
|
||||||
|
|
||||||
|
You may also want to define
|
||||||
|
#define WS2812BSIMPLE_NO_IRQ_TWEAKING
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WS2812B_SIMPLE
|
||||||
|
#define _WS2812B_SIMPLE
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void WS2812BSimpleSend(GPIO_TypeDef *port, int pin, uint8_t *data, int len_in_bytes);
|
||||||
|
|
||||||
|
#ifdef WS2812BSIMPLE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "funconfig.h"
|
||||||
|
|
||||||
|
#if FUNCONF_SYSTICK_USE_HCLK != 1
|
||||||
|
#error WS2812B Driver Requires FUNCONF_SYSTICK_USE_HCLK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void WS2812BSimpleSend(GPIO_TypeDef *port, int pin, uint8_t *data, int len_in_bytes)
|
||||||
|
{
|
||||||
|
int port_id = (((intptr_t)port - (intptr_t)GPIOA) >> 10);
|
||||||
|
RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA << port_id); // Make sure port is enabled.
|
||||||
|
|
||||||
|
int poffset = (pin * 4);
|
||||||
|
port->CFGLR = (port->CFGLR & (~(0xf << poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP) << (poffset));
|
||||||
|
|
||||||
|
int maskon = 1 << pin;
|
||||||
|
int maskoff = 1 << (16 + pin);
|
||||||
|
|
||||||
|
port->BSHR = maskoff;
|
||||||
|
|
||||||
|
uint8_t *end = data + len_in_bytes;
|
||||||
|
while (data != end)
|
||||||
|
{
|
||||||
|
uint8_t byte = *data;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (byte & 0x80)
|
||||||
|
{
|
||||||
|
// WS2812B's need AT LEAST 625ns for a logical "1"
|
||||||
|
port->BSHR = maskon;
|
||||||
|
DelaySysTick(25);
|
||||||
|
port->BSHR = maskoff;
|
||||||
|
DelaySysTick(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0"
|
||||||
|
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
|
||||||
|
__disable_irq();
|
||||||
|
#endif
|
||||||
|
port->BSHR = maskon;
|
||||||
|
asm volatile("nop\nnop\nnop\nnop");
|
||||||
|
port->BSHR = maskoff;
|
||||||
|
#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
|
||||||
|
__enable_irq();
|
||||||
|
#endif
|
||||||
|
DelaySysTick(15);
|
||||||
|
}
|
||||||
|
byte <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->BSHR = maskoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
BIN
src/factory_bootloader.bin
Normal file
BIN
src/factory_bootloader.bin
Normal file
Binary file not shown.
122
src/generated_ch32v003.ld
Normal file
122
src/generated_ch32v003.ld
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
ENTRY( InterruptVector )
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
|
||||||
|
}
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
_sinit = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(SORT_NONE(.init)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
_einit = .;
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata*)
|
||||||
|
*(.gnu.linkonce.t.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.fini :
|
||||||
|
{
|
||||||
|
KEEP(*(SORT_NONE(.fini)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
PROVIDE( _etext = . );
|
||||||
|
PROVIDE( _eitcm = . );
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP (*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||||
|
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||||
|
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.ctors :
|
||||||
|
{
|
||||||
|
KEEP (*crtbegin.o(.ctors))
|
||||||
|
KEEP (*crtbegin?.o(.ctors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||||
|
KEEP (*(SORT(.ctors.*)))
|
||||||
|
KEEP (*(.ctors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.dtors :
|
||||||
|
{
|
||||||
|
KEEP (*crtbegin.o(.dtors))
|
||||||
|
KEEP (*crtbegin?.o(.dtors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||||
|
KEEP (*(SORT(.dtors.*)))
|
||||||
|
KEEP (*(.dtors))
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.dalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_vma = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
.dlalign :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE(_data_lma = .);
|
||||||
|
} >FLASH AT>FLASH
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__global_pointer$ = . + 0x3fc;
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
*(.gnu.linkonce.d.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.sdata2*)
|
||||||
|
*(.gnu.linkonce.s.*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.srodata.cst16)
|
||||||
|
*(.srodata.cst8)
|
||||||
|
*(.srodata.cst4)
|
||||||
|
*(.srodata.cst2)
|
||||||
|
*(.srodata .srodata.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _edata = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _sbss = .);
|
||||||
|
*(.sbss*)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.gnu.linkonce.b.*)
|
||||||
|
*(COMMON*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
PROVIDE( _ebss = .);
|
||||||
|
} >RAM AT>FLASH
|
||||||
|
PROVIDE( _end = _ebss);
|
||||||
|
PROVIDE( end = . );
|
||||||
|
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.note .note.*)
|
||||||
|
*(.eh_frame .eh_frame.*)
|
||||||
|
*(.comment .comment.*)
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/libgcc.a
Normal file
BIN
src/libgcc.a
Normal file
Binary file not shown.
15
src/startup_ch32v003.S
Normal file
15
src/startup_ch32v003.S
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Author: Jake Goodwin
|
||||||
|
# Date: 2025-02-28
|
||||||
|
# Version: v0.0.0
|
||||||
|
# Description:
|
||||||
|
# File Name: startup_ch32v003.S
|
||||||
|
|
||||||
|
|
||||||
|
# Extra Info:
|
||||||
|
# CPU: QingKeV2
|
||||||
|
# ISA: RV32EC -- 32bit RISC-V Embedded subset with compressed instructions.
|
||||||
|
# NOTE: Only has half the normal 32 registers, so 16 registers, they are
|
||||||
|
# X0-x15
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
185
src/startup_ch32v00x.S
Normal file
185
src/startup_ch32v00x.S
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
#;/********************************** (C) COPYRIGHT *******************************
|
||||||
|
#;* File Name : startup_ch32v00x.s
|
||||||
|
#;* Author : WCH
|
||||||
|
#;* Version : V1.0.0
|
||||||
|
#;* Date : 2022/08/08
|
||||||
|
#;* Description : vector table for eclipse toolchain.
|
||||||
|
#;*********************************************************************************
|
||||||
|
#;* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
|
||||||
|
#;* Attention: This software (modified or not) and binary are used for
|
||||||
|
#;* microcontroller manufactured by Nanjing Qinheng Microelectronics.
|
||||||
|
#;*******************************************************************************/
|
||||||
|
#
|
||||||
|
#; RISC-V Insturction layout:
|
||||||
|
#; <Operation> <DST>,<src1>,<src2>
|
||||||
|
#
|
||||||
|
#;Add Immediate example:
|
||||||
|
#;addi a2,x0,64
|
||||||
|
#;$a2 = $zero + 64
|
||||||
|
#;$a2 = 64
|
||||||
|
|
||||||
|
.section .init, "ax", @progbits
|
||||||
|
.globl _start
|
||||||
|
.align 2
|
||||||
|
_start:
|
||||||
|
.option norvc;
|
||||||
|
j handle_reset
|
||||||
|
.word 0
|
||||||
|
.word NMI_Handler /* NMI Handler */
|
||||||
|
.word HardFault_Handler /* Hard Fault Handler */
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word SysTick_Handler /* SysTick Handler */
|
||||||
|
.word 0
|
||||||
|
.word SW_Handler /* SW Handler */
|
||||||
|
.word 0
|
||||||
|
/* External Interrupts */
|
||||||
|
.word WWDG_IRQHandler /* Window Watchdog */
|
||||||
|
.word PVD_IRQHandler /* PVD through EXTI Line detect */
|
||||||
|
.word FLASH_IRQHandler /* Flash */
|
||||||
|
.word RCC_IRQHandler /* RCC */
|
||||||
|
.word EXTI7_0_IRQHandler /* EXTI Line 7..0 */
|
||||||
|
.word AWU_IRQHandler /* AWU */
|
||||||
|
.word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
|
||||||
|
.word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */
|
||||||
|
.word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */
|
||||||
|
.word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */
|
||||||
|
.word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */
|
||||||
|
.word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */
|
||||||
|
.word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */
|
||||||
|
.word ADC1_IRQHandler /* ADC1 */
|
||||||
|
.word I2C1_EV_IRQHandler /* I2C1 Event */
|
||||||
|
.word I2C1_ER_IRQHandler /* I2C1 Error */
|
||||||
|
.word USART1_IRQHandler /* USART1 */
|
||||||
|
.word SPI1_IRQHandler /* SPI1 */
|
||||||
|
.word TIM1_BRK_IRQHandler /* TIM1 Break */
|
||||||
|
.word TIM1_UP_IRQHandler /* TIM1 Update */
|
||||||
|
.word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation */
|
||||||
|
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||||
|
.word TIM2_IRQHandler /* TIM2 */
|
||||||
|
|
||||||
|
.option rvc;
|
||||||
|
.section .text.vector_handler, "ax", @progbits
|
||||||
|
.weak NMI_Handler
|
||||||
|
.weak HardFault_Handler
|
||||||
|
.weak SysTick_Handler
|
||||||
|
.weak SW_Handler
|
||||||
|
.weak WWDG_IRQHandler
|
||||||
|
.weak PVD_IRQHandler
|
||||||
|
.weak FLASH_IRQHandler
|
||||||
|
.weak RCC_IRQHandler
|
||||||
|
.weak EXTI7_0_IRQHandler
|
||||||
|
.weak AWU_IRQHandler
|
||||||
|
.weak DMA1_Channel1_IRQHandler
|
||||||
|
.weak DMA1_Channel2_IRQHandler
|
||||||
|
.weak DMA1_Channel3_IRQHandler
|
||||||
|
.weak DMA1_Channel4_IRQHandler
|
||||||
|
.weak DMA1_Channel5_IRQHandler
|
||||||
|
.weak DMA1_Channel6_IRQHandler
|
||||||
|
.weak DMA1_Channel7_IRQHandler
|
||||||
|
.weak ADC1_IRQHandler
|
||||||
|
.weak I2C1_EV_IRQHandler
|
||||||
|
.weak I2C1_ER_IRQHandler
|
||||||
|
.weak USART1_IRQHandler
|
||||||
|
.weak SPI1_IRQHandler
|
||||||
|
.weak TIM1_BRK_IRQHandler
|
||||||
|
.weak TIM1_UP_IRQHandler
|
||||||
|
.weak TIM1_TRG_COM_IRQHandler
|
||||||
|
.weak TIM1_CC_IRQHandler
|
||||||
|
.weak TIM2_IRQHandler
|
||||||
|
|
||||||
|
NMI_Handler: 1: j 1b
|
||||||
|
HardFault_Handler: 1: j 1b
|
||||||
|
SysTick_Handler: 1: j 1b
|
||||||
|
SW_Handler: 1: j 1b
|
||||||
|
WWDG_IRQHandler: 1: j 1b
|
||||||
|
PVD_IRQHandler: 1: j 1b
|
||||||
|
FLASH_IRQHandler: 1: j 1b
|
||||||
|
RCC_IRQHandler: 1: j 1b
|
||||||
|
EXTI7_0_IRQHandler: 1: j 1b
|
||||||
|
AWU_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel1_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel2_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel3_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel4_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel5_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel6_IRQHandler: 1: j 1b
|
||||||
|
DMA1_Channel7_IRQHandler: 1: j 1b
|
||||||
|
ADC1_IRQHandler: 1: j 1b
|
||||||
|
I2C1_EV_IRQHandler: 1: j 1b
|
||||||
|
I2C1_ER_IRQHandler: 1: j 1b
|
||||||
|
USART1_IRQHandler: 1: j 1b
|
||||||
|
SPI1_IRQHandler: 1: j 1b
|
||||||
|
TIM1_BRK_IRQHandler: 1: j 1b
|
||||||
|
TIM1_UP_IRQHandler: 1: j 1b
|
||||||
|
TIM1_TRG_COM_IRQHandler: 1: j 1b
|
||||||
|
TIM1_CC_IRQHandler: 1: j 1b
|
||||||
|
TIM2_IRQHandler: 1: j 1b
|
||||||
|
|
||||||
|
|
||||||
|
.section .text.handle_reset, "ax", @progbits
|
||||||
|
.weak handle_reset
|
||||||
|
.align 1
|
||||||
|
handle_reset:
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
la gp, __global_pointer$
|
||||||
|
.option pop
|
||||||
|
1:
|
||||||
|
la sp, _eusrstack
|
||||||
|
/* MAX: Addition needed here */
|
||||||
|
#if __GNUC__ > 10
|
||||||
|
.option arch, +zicsr
|
||||||
|
#endif
|
||||||
|
2:
|
||||||
|
/* Load data section from flash to RAM */
|
||||||
|
la a0, _data_lma
|
||||||
|
la a1, _data_vma
|
||||||
|
la a2, _edata
|
||||||
|
bgeu a1, a2, 2f
|
||||||
|
1:
|
||||||
|
lw t0, (a0)
|
||||||
|
sw t0, (a1)
|
||||||
|
addi a0, a0, 4
|
||||||
|
addi a1, a1, 4
|
||||||
|
bltu a1, a2, 1b
|
||||||
|
2:
|
||||||
|
/* clear bss section */
|
||||||
|
la a0, _sbss
|
||||||
|
la a1, _ebss
|
||||||
|
bgeu a0, a1, 2f
|
||||||
|
1:
|
||||||
|
sw zero, (a0)
|
||||||
|
addi a0, a0, 4
|
||||||
|
bltu a0, a1, 1b
|
||||||
|
2:
|
||||||
|
li t0, 0x80
|
||||||
|
csrw mstatus, t0
|
||||||
|
|
||||||
|
li t0, 0x3
|
||||||
|
csrw 0x804, t0
|
||||||
|
|
||||||
|
la t0, _start
|
||||||
|
ori t0, t0, 3
|
||||||
|
csrw mtvec, t0
|
||||||
|
|
||||||
|
#if defined(__PIO_CPP_SUPPORT__)
|
||||||
|
/* register fini (destructor array) call at exit if wanted (bloats up RAM+Flash) */
|
||||||
|
#if defined(__PIO_CPP_CALL_FINI__)
|
||||||
|
la a0,__libc_fini_array
|
||||||
|
call atexit
|
||||||
|
#endif
|
||||||
|
/* call into C++ constructors now */
|
||||||
|
call __libc_init_array
|
||||||
|
#endif
|
||||||
|
|
||||||
|
jal SystemInit
|
||||||
|
la t0, main
|
||||||
|
csrw mepc, t0
|
||||||
|
mret
|
||||||
Loading…
Add table
Reference in a new issue