mark03
"Yes, it’s true, but I have a very good reason for doing it. I'm designing a very small PCB for a Chinese product with few SMD components, so a low price is a very important option. I've been using Atmel products for 30 years—their datasheets are perfect, with 300 or more pages and everything well-documented. But do Atmel (Microchip) MCUs cost $0.10 in quantities of a thousand? The price of the ATTiny85 was too high for my project. So, I tried to find the best, most affordable Chinese MCU, and the CH32V003J4M6 is my favorite. I used Deepseek and Gemini to help me with the programming, and step by step, everything is working now.
Now, back to my problem: I want to use the internal Flash memory (an external EEPROM costs around $0.01 in quantities of 10,000) to store no more than 64 bytes, and possibly more in the future. I've been searching for a code to do this. All my tests of changing the Option Bytes have resulted in the MCU not working properly. Also, changing a special bit and using pin PD1 as SWIO sets only PD1, which bricks the chip—a very cheap brick, but a brick nonetheless.
Then, with help from
Bruce Hoult
and
microvia
, I found and analyzed some code, and now I have a working solution. But how do I test if a Flash erase operation erases only 64 bytes or a full 1024-byte page?
I did the following:
Created a file flash_test.h with 2048 bytes of test data.
Modified the Link.ld file to store this test data at the end of the Flash section (Deepseek helped me with this).
Compiled and flashed the MCU.
Used the WCH Link Utility to read the complete Flash memory and save it as a binary file.
Then, I used my code to erase a part of the Flash memory. I used the following addresses:
#define USER_MEMORY_BASE 0x08003FC0
#define USER_MEMORY_SIZE 64
I also tried another address (my testing data is 2048 bytes, so I used an address within this range). I then read the entire Flash again and compared the files. The result was that only 64 bytes were erased after the erase page command. I set the last 64 bytes at 0x08003FC0 and will continue to use this section, hoping the MCU doesn't get bricked.
Here is my eeprom.h file
// Adresa user memory podla dokumentивcie
#define USER_MEMORY_BASE 0x08003FC0
#define USER_MEMORY_SIZE 64
#define FLASH_KEY1 ((uint32_t)0x45670123)
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)
#define CR_STRT_Set ((uint32_t)0x00000040)
#define CR_LOCK_Set ((uint32_t)0x00000080)
#define CR_PAGE_PG ((uint32_t)0x00010000)
#define CR_PAGE_ER ((uint32_t)0x00020000)
#define CR_BUF_LOAD ((uint32_t)0x00040000)
#define CR_BUF_RST ((uint32_t)0x00080000)
uint8_t read_bytes(uint8_t start_addr, uint8_t *buffer, uint8_t length);
uint8_t SaveFlash(const uint8_t EraseOnly);
void print_full_memory(void);
and eeprom.c
#include <ch32v00x_it.h>
#include "eeprom.h"
#include <ch32v00x_flash.h>
uint8_t read_bytes(uint8_t start_addr, uint8_t *buffer, uint8_t length)
{
if (start_addr >= USER_MEMORY_SIZE || buffer == NULL) return 0;
if (start_addr + length > USER_MEMORY_SIZE) length = USER_MEMORY_SIZE - start_addr;
__disable_irq();
for (uint8_t i = 0; i < length; i++) {
buffer[i] = *((volatile uint8_t*)(USER_MEMORY_BASE + start_addr + i));
}
__enable_irq();
return length;
}
uint8_t SaveFlash(const uint8_t EraseOnly)
{
__disable_irq();
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
FLASH->MODEKEYR = FLASH_KEY1;
FLASH->MODEKEYR = FLASH_KEY2;
FLASH->ACTLR = (FLASH->ACTLR & ~FLASH_ACTLR_LATENCY) | FLASH_ACTLR_LATENCY_1;
if( FLASH->CTLR & 0x8080 ) // Testuje ci je zamknuta pamet alebo nieco ine, ma byt 0
{
__enable_irq();
return 1;
}
uint32_t * ptr = (uint32_t*)USER_MEMORY_BASE;
//Erase Page
FLASH->CTLR = CR_PAGE_ER;
FLASH->ADDR = (intptr_t)ptr;
FLASH->CTLR = CR_STRT_Set | CR_PAGE_ER;
while( FLASH->STATR & FLASH_STATR_BSY ); // Takes about 3ms.
if( ptr[0] != 0xffffffff )
{
__enable_irq();
return 2;
}
// Clear buffer and prep for flashing.
FLASH->CTLR = CR_PAGE_PG; // synonym of FTPG.
FLASH->CTLR = CR_BUF_RST | CR_PAGE_PG;
FLASH->ADDR = (intptr_t)ptr; // This can actually happen about anywhere toward the end here.
// Note: It takes about 6 clock cycles for this to finish.
int i;
for( i = 0; i < 16; i++ )
{
if (EraseOnly) ptr[i] = 0xFFFFFFFF;
else ptr[i] = 0xabcd1234 + i; //Write to the memory
FLASH->CTLR = CR_PAGE_PG | FLASH_CTLR_BUF_LOAD; // Load the buffer.
while( FLASH->STATR & FLASH_STATR_BSY ); // Only needed if running from RAM.
}
// Actually write the flash out. (Takes about 3ms)
FLASH->CTLR = CR_PAGE_PG|CR_STRT_Set;
while( FLASH->STATR & FLASH_STATR_BSY );
if( ptr[0] != 0xabcd1234 )
{
__enable_irq();
return 3;
}
__enable_irq();
return 0;
}
void print_full_memory(void)
{
uint8_t buffer[64];
// Na
read_bytes(0, buffer, USER_MEMORY_SIZE);
printf("\r\n=== User memory (64B) ===\r\n");
printf("---------------------------------------------------------\r\n");
for (uint8_t i = 0; i < 64; i += 16) {
printf("%02X: ", i);
for (uint8_t j = 0; j < 16; j++) {
printf("%02X ", buffer[i + j]);
}
printf("\r\n");
}
printf("\r\n=========================================\r\n");
}
I use MounRiver Studio ..
This is my teting code .. not finall code .. this is only if any interesting.. and yes , of course .. many bugs ..
Any ideas ??
Best reagrds.