From 1e0915ac08cea0a69d450c356fea22f86cd0bf66 Mon Sep 17 00:00:00 2001 From: kuwoyuki Date: Fri, 8 Nov 2024 00:11:06 +0600 Subject: [PATCH] first commit --- .gitignore | 9 +++ .gitmodules | 3 + Makefile | 13 ++++ ch32ext.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ ch32ext.ld | 151 ++++++++++++++++++++++++++++++++++++++++++++++ ch32v003fun | 1 + funconfig.h | 24 ++++++++ simple_eeprom.h | 150 +++++++++++++++++++++++++++++++++++++++++++++ state_manager.h | 24 ++++++++ 9 files changed, 532 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 ch32ext.c create mode 100644 ch32ext.ld create mode 160000 ch32v003fun create mode 100644 funconfig.h create mode 100644 simple_eeprom.h create mode 100644 state_manager.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e98125 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.pio +.vscode +*.elf +*.hex +*.lst +*.bin +*.map +.clangd +.cache diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6176b1e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ch32v003fun"] + path = ch32v003fun + url = https://github.com/cnlohr/ch32v003fun.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0d1d60a --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +all : flash + +TARGET:=ch32ext +CH32V003FUN=./ch32v003fun/ch32v003fun +MINICHLINK=./ch32v003fun/minichlink +PREFIX=riscv64-elf +LINKER_SCRIPT=./ch32ext.ld +# CFLAGS += -march=rv32ec_zicsr + +include $(CH32V003FUN)/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean diff --git a/ch32ext.c b/ch32ext.c new file mode 100644 index 0000000..3214ad1 --- /dev/null +++ b/ch32ext.c @@ -0,0 +1,157 @@ +#include + +#include "ch32v003fun.h" +#include "simple_eeprom.h" +#include "state_manager.h" + +#define RS485_DIR (1 << 0) // RS485 direction control +#define SRCLR (1 << 1) // Shift register clear (active low) +#define SRCLK (1 << 2) // Shift register clock +#define RCLK (1 << 3) // Storage register clock +#define SER (1 << 4) // Serial data input + +// "protocol" defines +#define BOARD_ADDRESS 0x01 +#define CMD_SET_OUTPUTS 0x01 +#define BROADCAST_ADDR 0xFF + +// "protocol" states +enum rx_states { + STATE_ADDR, + STATE_IGNORE, + STATE_CMD, + STATE_DATA_HIGH, + STATE_DATA_LOW +}; + +void setup_uart(int uartBRR) { + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | + RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO; + + // RS485_DIR as output, set recv mode + GPIOC->CFGLR &= ~(0xf << (4 * 0)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 0); + GPIOC->BCR = RS485_DIR; + + // UART pins (PD5=TX, PD6=RX) + GPIOD->CFGLR &= ~(0xf << (4 * 5) | 0xf << (4 * 6)); + GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5); + GPIOD->CFGLR |= GPIO_CNF_IN_FLOATING << (4 * 6); + + // 115200 8N1 + USART1->CTLR1 = + USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx | USART_Mode_Rx; + USART1->CTLR2 = USART_StopBits_1; + USART1->CTLR3 = USART_HardwareFlowControl_None; + USART1->BRR = uartBRR; + USART1->CTLR1 |= CTLR1_UE_Set; +} + +void setup_gpio(void) { + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + + // PC1-PC4 as out + for (int pin = 1; pin <= 4; pin++) { + GPIOC->CFGLR &= ~(0xf << (4 * pin)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * pin); + } + + GPIOC->BCR = SRCLR | SRCLK | RCLK | SER; +} + +void shift_out(uint16_t data) { + GPIOC->BCR = RCLK; + + // reorder + uint16_t ordered_data = ((data & 0xFF00) >> 8) | // 1st reg (Q0-Q7) + ((data & 0x00FF) << 8); // 2nd reg (Q8-Q15) + + // shift out 16 bits, MSB 1st + for (int8_t i = 15; i >= 0; i--) { + GPIOC->BCR = SRCLK; + if (ordered_data & (1 << i)) { + GPIOC->BSHR = SER; + } else { + GPIOC->BCR = SER; + } + GPIOC->BSHR = SRCLK; + } + + // latch + GPIOC->BSHR = RCLK; + GPIOC->BCR = RCLK; +} + +uint8_t uart_receive_byte(void) { + while (!(USART1->STATR & USART_FLAG_RXNE)); + return USART1->DATAR & 0xFF; +} + +int main(void) { + SystemInit(); + setup_gpio(); + setup_uart(UART_BRR); + + // reset shift reg + GPIOC->BSHR = SRCLR; + GPIOC->BSHR = RCLK; // rising edge on RCLK + GPIOC->BCR = RCLK; + + // load last state + uint16_t current_state = load_state(); + printf("Loaded state: %04x\n", current_state); + shift_out(current_state); + + uint8_t rx_state = STATE_ADDR; + uint8_t addr, cmd; + uint16_t data = 0; + uint8_t bytes_to_ignore = 0; + + printf("SystemClk:%d\r\n", FUNCONF_SYSTEM_CORE_CLOCK); + printf("ChipID:%08lx\r\n", *(uint32_t *)0x1FFFF7C4); + + while (1) { + uint8_t byte = uart_receive_byte(); + + switch (rx_state) { + case STATE_ADDR: + addr = byte; + if (addr == BOARD_ADDRESS || addr == BROADCAST_ADDR) { + rx_state = STATE_CMD; + } else { + // crc??)) + bytes_to_ignore = 3; // skip cmd + data_high + data_low + rx_state = STATE_IGNORE; + } + break; + + case STATE_IGNORE: + bytes_to_ignore--; + if (bytes_to_ignore == 0) { + rx_state = STATE_ADDR; + } + break; + + case STATE_CMD: + cmd = byte; + rx_state = STATE_DATA_HIGH; + break; + + case STATE_DATA_HIGH: + data = (uint16_t)byte << 8; + rx_state = STATE_DATA_LOW; + break; + + case STATE_DATA_LOW: + data |= byte; + if (cmd == CMD_SET_OUTPUTS) { + printf("Set outputs: 0x%04X\n", data); + shift_out(data); + save_state(data); + dump_eeprom(); + } + rx_state = STATE_ADDR; + break; + } + } +} \ No newline at end of file diff --git a/ch32ext.ld b/ch32ext.ld new file mode 100644 index 0000000..1c82465 --- /dev/null +++ b/ch32ext.ld @@ -0,0 +1,151 @@ +ENTRY(InterruptVector) + +MEMORY { + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K /* 16KB FLASH */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K /* 2KB RAM */ +} + +SECTIONS { + /* Initialization code section */ + .init : { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + /* Main code section */ + .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)); + + /DISCARD/ : { + *(.note .note.*) + *(.eh_frame .eh_frame.*) + *(.comment .comment.*) + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.ARM.exidx*) + } + + /* EEPROM section - reserves exactly one 64-byte page */ + .eeprom : { + . = ALIGN(64); + _reserved_nv_start = .; + KEEP(*(.eeprom)); + . = _reserved_nv_start + 64; + _reserved_nv_end = .; + } >FLASH AT>FLASH + + PROVIDE(_reserved_nv_start = _reserved_nv_start); + PROVIDE(_reserved_nv_end = _reserved_nv_end); +} diff --git a/ch32v003fun b/ch32v003fun new file mode 160000 index 0000000..e4b7019 --- /dev/null +++ b/ch32v003fun @@ -0,0 +1 @@ +Subproject commit e4b70191d4c51f28e1517fde7b558d6d45480195 diff --git a/funconfig.h b/funconfig.h new file mode 100644 index 0000000..794e5f9 --- /dev/null +++ b/funconfig.h @@ -0,0 +1,24 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +// #define FUNCONF_USE_PLL 1 // Use built-in 2x PLL +// #define FUNCONF_USE_HSI 1 // Use HSI Internal Oscillator +// #define FUNCONF_USE_HSE 0 // Use External Oscillator +// #define FUNCONF_HSITRIM 0x10 // Use factory calibration on HSI Trim. +// #define FUNCONF_SYSTEM_CORE_CLOCK 48000000 // Computed Clock in Hz (Default only for 003, other chips have other defaults) +// #define FUNCONF_HSE_BYPASS 0 // Use HSE Bypass feature (for oscillator input) +// #define FUNCONF_USE_CLK_SEC 1 // Use clock security system, enabled by default +#define FUNCONF_USE_DEBUGPRINTF 1 +// #define FUNCONF_USE_UARTPRINTF 0 +// #define FUNCONF_NULL_PRINTF 0 // Have printf but direct it "nowhere" +#define FUNCONF_SYSTICK_USE_HCLK 1 // Should systick be at 48 MHz or 6MHz? +// #define FUNCONF_TINYVECTOR 0 // If enabled, Does not allow normal interrupts. +// #define FUNCONF_UART_PRINTF_BAUD 115200 // Only used if FUNCONF_USE_UARTPRINTF is set. +// #define FUNCONF_DEBUGPRINTF_TIMEOUT 160000 // Arbitrary time units +// #define FUNCONF_ENABLE_HPE 1 // Enable hardware interrupt stack. Very good on QingKeV4, i.e. x035, v10x, v20x, v30x, but questionable on 003. +// #define FUNCONF_USE_5V_VDD 0 // Enable this if you plan to use your part at 5V - affects USB and PD configration on the x035. +// #define FUNCONF_DEBUG 0 // Log fatal errors with "printf" + +#define CH32V003 1 + +#endif \ No newline at end of file diff --git a/simple_eeprom.h b/simple_eeprom.h new file mode 100644 index 0000000..a09c6cf --- /dev/null +++ b/simple_eeprom.h @@ -0,0 +1,150 @@ +#ifndef __SIMPLE_EEPROM_H +#define __SIMPLE_EEPROM_H + +#include +#include + +#include "ch32v003fun.h" + +// config +#define EEPROM_PAGE_SIZE 64 // size in bytes +#define EEPROM_HALFWORD_SIZE 2 // size of halfword in bytes +#define EEPROM_MAX_ENTRIES (EEPROM_PAGE_SIZE / EEPROM_HALFWORD_SIZE) + +typedef enum { + EEPROM_OK = 0, + EEPROM_ERROR_WRITE, + EEPROM_ERROR_ERASE, + EEPROM_ERROR_VERIFY +} eeprom_status_t; + +// ######## internal variables +// import from .ld, halal by +// https://sourceware.org/binutils/docs/ld/Source-Code-Reference.html +extern char _reserved_nv_start[]; +extern char _reserved_nv_end[]; + +#define EEPROM_BASE (FLASH_BASE + (uint32_t)(uintptr_t)_reserved_nv_start) + +static void flash_unlock(void) { + if (FLASH->CTLR & 0x80) { + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } +} + +static void flash_lock(void) { FLASH->CTLR = 0x80; } + +static void flash_unlock_fast_mode(void) { + if (FLASH->CTLR & 0x8000) { + FLASH->MODEKEYR = FLASH_KEY1; + FLASH->MODEKEYR = FLASH_KEY2; + } +} + +static void flash_wait_busy(void) { while (FLASH->STATR & FLASH_STATR_BSY); } + +static eeprom_status_t flash_erase_page(void) { + uint16_t *page_addr = (uint16_t *)EEPROM_BASE; + + flash_unlock_fast_mode(); + flash_wait_busy(); + + // erase page + FLASH->CTLR = CR_PAGE_ER; + FLASH->ADDR = (uint32_t)page_addr; + FLASH->CTLR = CR_STRT_Set | CR_PAGE_ER; + flash_wait_busy(); + + // verify erase + for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { + if (page_addr[i] != 0xFFFF) { + return EEPROM_ERROR_ERASE; + } + } + + // disable fast mode + FLASH->CTLR = 0x8000; + return EEPROM_OK; +} + +static int find_next_free_slot(void) { + uint16_t *word = (uint16_t *)EEPROM_BASE; + for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { + if (word[i] == 0xFFFF) { // look for erased entries + return i; + } + } + return EEPROM_MAX_ENTRIES; +} + +eeprom_status_t eeprom_write(uint16_t data) { + flash_unlock(); + + // find next available slot + int slot = find_next_free_slot(); + uint16_t *word = (uint16_t *)EEPROM_BASE + slot; + + // page is full, erase it + if (slot >= EEPROM_MAX_ENTRIES) { + eeprom_status_t status = flash_erase_page(); + if (status != EEPROM_OK) { + flash_lock(); + return status; + } + word = (uint16_t *)EEPROM_BASE; // reset to start of page + } + + // write data + FLASH->CTLR = CR_PG_Set; + *word = data; // write full 16-bit value + flash_wait_busy(); + + // verify write + if (*word != data) { + flash_lock(); + return EEPROM_ERROR_VERIFY; + } + + flash_lock(); + return EEPROM_OK; +} + +uint16_t eeprom_read(void) { + uint16_t *word = + (uint16_t *)(EEPROM_BASE + EEPROM_PAGE_SIZE - EEPROM_HALFWORD_SIZE); + + // scan back from the end until we find a non-erased value + for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { + uint16_t val = *word; + if (val != 0xFFFF) { + return val; + } + word--; + } + + return 0xFFFF; // nothing found +} + +eeprom_status_t eeprom_init(void) { + uint16_t *page = (uint16_t *)EEPROM_BASE; + + // check empty/partial page + bool needs_erase = true; + for (int i = 0; i < EEPROM_MAX_ENTRIES; i++) { + if (page[i] != 0xFFFF) { + needs_erase = false; + break; + } + } + + if (needs_erase) { + return flash_erase_page(); + } + + return EEPROM_OK; +} + +eeprom_status_t eeprom_wipe(void) { return flash_erase_page(); } + +#endif \ No newline at end of file diff --git a/state_manager.h b/state_manager.h new file mode 100644 index 0000000..cea2dbe --- /dev/null +++ b/state_manager.h @@ -0,0 +1,24 @@ +#ifndef __STATE_MANAGER_H +#define __STATE_MANAGER_H + +#include "simple_eeprom.h" + +#define DEFAULT_STATE 0x0000 + +void save_state(uint16_t state) { eeprom_write(state); } + +uint16_t load_state(void) { return eeprom_read(); } + +void dump_eeprom(void) { + uint16_t *word = (uint16_t *)(EEPROM_BASE); + printf("EEPROM contents:"); + for (int i = 0; i < 32; i++) { + uint16_t val = word[i]; + // if (!(val & 0x8000)) { + printf(" %04X", val); + // } + } + printf("\n"); +} + +#endif \ No newline at end of file