Added stuff from ch32fun dirs

This commit is contained in:
Jake Goodwin 2025-03-06 17:49:49 -08:00
parent 0e9243a7e0
commit 7ef358200b
33 changed files with 31847 additions and 0 deletions

7
src/attic/CMakeLists.txt Normal file
View file

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

File diff suppressed because it is too large Load diff

View file

@ -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 );
}
}
}

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

File diff suppressed because it is too large Load diff

1052
src/ch32fun/ch32fun.h Normal file

File diff suppressed because it is too large Load diff

222
src/ch32fun/ch32fun.ld Normal file
View 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
View 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

View 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

File diff suppressed because it is too large Load diff

192
src/ch32v003.ld Normal file
View 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*)
}
}

View file

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

View 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

View 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

View 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.
*/

View 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

File diff suppressed because it is too large Load diff

651
src/extralibs/hsusb_v30x.c Normal file
View 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;
}

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

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

File diff suppressed because it is too large Load diff

View 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

View 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

Binary file not shown.

122
src/generated_ch32v003.ld Normal file
View 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

Binary file not shown.

15
src/startup_ch32v003.S Normal file
View 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
View 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