first commit

This commit is contained in:
2024-11-08 00:11:06 +06:00
commit 1e0915ac08
9 changed files with 532 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
.pio
.vscode
*.elf
*.hex
*.lst
*.bin
*.map
.clangd
.cache

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "ch32v003fun"]
path = ch32v003fun
url = https://github.com/cnlohr/ch32v003fun.git

13
Makefile Normal file
View File

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

157
ch32ext.c Normal file
View File

@@ -0,0 +1,157 @@
#include <stdio.h>
#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;
}
}
}

151
ch32ext.ld Normal file
View File

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

1
ch32v003fun Submodule

Submodule ch32v003fun added at e4b70191d4

24
funconfig.h Normal file
View File

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

150
simple_eeprom.h Normal file
View File

@@ -0,0 +1,150 @@
#ifndef __SIMPLE_EEPROM_H
#define __SIMPLE_EEPROM_H
#include <stdbool.h>
#include <stdint.h>
#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

24
state_manager.h Normal file
View File

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