f103id/identifier.c

2638 lines
64 KiB
C

/* F103 MCU identifier
* used to fingerprint and identify STM32F103 or variants, and their clones
* Copyright: 2020 King Kévin <kingkevin@cuvoodoo.info>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdio.h> // print utilities
#include <errno.h> // error definitions (used for _write)
#include <string.h> // string comparison utilities
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/scb.h> // vector table definition
#include <libopencm3/cm3/scs.h> // System Control Space definitions
#include <libopencm3/stm32/rcc.h> // domain clock utilities
#include <libopencm3/stm32/gpio.h> // GPIO utilities to configure UART
#include <libopencm3/stm32/usart.h> // UART utilities to communicate with user
#include <libopencm3/stm32/desig.h> // design utilities
#include <libopencm3/stm32/flash.h> // flash utilities
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
#include <libopencm3/stm32/crc.h> // CRC utilities
// peripherals to test
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/spi.h>
#include <libopencm3/stm32/i2c.h>
#include <libopencm3/stm32/can.h>
#include <libopencm3/stm32/st_usbfs.h>
#include <libopencm3/stm32/f0/cec.h>
// if print should really output data (used to silence when doing measurements)
static bool enable_output = true;
// get the length of an array
#define LENGTH(x) (sizeof(x) / sizeof((x)[0]))
// device information collected over time
static char* manufacturer_str = "???";
static char* family_str = "???";
static char pin_count_variant = '?';
static uint8_t pin_count_nb = 0;
static char flash_size_variant = '?';
static uint8_t sram_size = 0;
int _write(int file, char *ptr, int len);
int _write(int file, char *ptr, int len)
{
if (!enable_output) { // don't print anything
return len;
} else if (file == 1) {
for (int i = 0; i < len; i++) {
usart_send_blocking(USART1, ptr[i]);
}
return len;
}
errno = EIO;
return -1;
}
// start embedded (UART) bootloader (e.g. the application in the system memory)
static void bootloader(void)
{
const uint32_t address = 0x1FFFF000; // system memory address
SCB_VTOR = (volatile uint32_t)(address); // set vector table to application vector table (store at the beginning of the application)
__asm__ volatile ("MSR msp,%0" : :"r"(*(uint32_t*)address)); // set stack pointer to address provided in the beginning of the application (loaded into a register first)
(*(void(**)(void))((uint32_t)address + 4))(); // start system memory (by jumping to the reset function which address is stored as second entry of the vector table)
while (true); // this should not be reached
}
// reset MCU
static void reset(void)
{
scb_reset_system(); // reset device
while (true); // wait for the reset to happen
}
// use the same UART output as the bootloader (serial configuration: 115200 8N1)
static void uart_setup(void)
{
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_AFIO);
rcc_periph_clock_enable(RCC_USART1);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
gpio_set(GPIOA, GPIO_USART1_RX);
gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_USART1_RX);
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
usart_set_stopbits(USART1, USART_STOPBITS_1);
usart_set_mode(USART1, USART_MODE_TX_RX);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
usart_enable(USART1);
}
// show CPU information
static void cpu(void)
{
printf("CPU information:\r\n");
// from ARMv7-M and Cortex-M3 TRM
// ARMv7-M B3.2.3
const uint8_t cpuid_implementer = (SCB_CPUID & SCB_CPUID_IMPLEMENTER) >> SCB_CPUID_IMPLEMENTER_LSB;
const uint8_t cpuid_architecture = (SCB_CPUID & SCB_CPUID_CONSTANT) >> SCB_CPUID_CONSTANT_LSB;
const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB;
const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB;
const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB;
printf("- CPUID: 0x%08lx\r\n", SCB_CPUID);
printf("- implementer: %s (0x%02x)\r\n", 0x41 == cpuid_implementer ? "ARM" : "unknown", cpuid_implementer);
printf("- architecture: ");
switch (cpuid_architecture) {
case 0xc:
printf("ARMv6-M");
break;
case 0xf:
printf("ARMv7-M");
break;
default:
printf("unknown");
}
printf(" (0x%x)\r\n", cpuid_architecture);
printf("- part number: ");
switch (cpuid_partno) {
case 0xC60:
printf("Cortex-M0+");
break;
case 0xC20:
printf("Cortex-M0");
break;
case 0xC23: // the ARM spec actually mentions 0xC24
printf("Cortex-M3");
break;
case 0xC24:
printf("Cortex-M4");
break;
case 0xC27:
printf("Cortex-M7");
break;
default:
printf("unknown");
}
printf(" (0x%03x)\r\n", cpuid_partno);
printf("- variant: %u\r\n", cpuid_variant);
printf("- revision: %u\r\n", cpuid_revision);
}
/** get flash page size
* @return flash page size (in bytes)
*/
static uint16_t flash_internal_page_size(void)
{
if (desig_get_flash_size() < 256) {
if ((*(uint32_t*)0x1FFFF000 & 0xFFFE0000) == 0x20000000) { // non-connectivity system memory start detected (MSP address pointing to SRAM
return 1024;
} else { // connectivity system memory start is at 0x1FFFB000
return 2048;
}
} else {
return 2048;
}
return 0;
}
/** probe the readable size of the internal flash
* @return tested size (in bytes)
*/
static uint32_t flash_internal_probe_read_size(void)
{
// we will check is a flash address is readable until a bus fault occurs
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
uint32_t address = FLASH_BASE; // start with the start of flash
while (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs
(void)*(volatile uint8_t*)address; // access address
address += flash_internal_page_size(); // got to next address
}
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
return address - 1 - FLASH_BASE;
}
/** probe the additional writable size of the internal flash, after the advertised size (and linker provided)
* @return tested size (in bytes)
*/
static uint32_t flash_internal_probe_write_size(void)
{
if (0 == desig_get_flash_size()) { // no flash size advertised
return 0;
}
// prepare for reading the flash
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
// prepare for writing the flash
flash_unlock(); // unlock flash to be able to write it
// try reading and writing the flash, page per page
const uint32_t start = FLASH_BASE + desig_get_flash_size() * 1024; // start with the end of the advertised flash
uint32_t address = start; // address to test
const uint16_t test_data = 0x2342; // the data we will write and read to test page
while (address < 0x1FFFEFFF) { // this is where the system memory starts
// try reading the flash
(void)*(volatile uint32_t*)address; // access address
if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs
break; // page not readable
}
// try writing the flash
flash_erase_page(address); // erase current page
if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong
break;
}
flash_program_half_word(address, test_data); // writes test data
if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong
break;
}
if (test_data != *((uint16_t*)address)) { // verify data is written correctly
break;
}
flash_erase_page(address); // erase test data
if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong
break;
}
address += flash_internal_page_size(); // go to next page
}
flash_clear_status_flags(); // clear all flag
flash_lock(); // protect again from writing
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
return address - start;
}
// show flash and SRAM sizes
static void sizes(void)
{
printf("flash & SRAM size information (can take 2 minutes):\r\n");
// test flash size
printf("- advertised flash size: ");
if (0xffff == desig_get_flash_size()) {
printf("unknown (probably a counterfeit/defective micro-controller\r\n");
} else {
printf("%u KB\r\n", desig_get_flash_size());
}
const uint32_t flash_read_size = flash_internal_probe_read_size();
printf("- readable flash size: %lu bytes (%lu KB)\r\n", flash_read_size, flash_read_size / 1024);
const uint32_t flash_write_size = flash_internal_probe_write_size();
printf("- over advertised writable flash size: %lu bytes (%lu KB)\r\n", flash_write_size, flash_write_size / 1024);
// test and show SRAM size
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
uint32_t address = 0x20000000; // the start of the SRAM section
uint32_t last_read = address; // to track the last readable SRAM address
uint32_t last_write = address; // to track the last writable SRAM address
while (address < 0x22000000) { // this is where the bit banding region starts
// try reading the SRAM
uint8_t sram_backup = *(volatile uint8_t*)address; // access address
if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs
break; // page not readable
} else {
last_read = address;
}
// try writing the RAM
*(volatile uint8_t*)address = 0x42;
if (0 == (SCB_CFSR & SCB_CFSR_BFARVALID) && 0x42 == *(volatile uint8_t*)address) { // no bus fault occurs
last_write = address;
*(volatile uint8_t*)address = sram_backup;
}
address++; // go to next byte
}
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
const uint32_t sram_read_size = last_read + 1 - 0x20000000; // calculate readable size
printf("- readable SRAM: %lu bytes (%lu KB)\r\n", sram_read_size, sram_read_size / 1024);
const uint32_t sram_write_size = last_write + 1 - 0x20000000; // calculate writable size
printf("- writable SRAM: %lu bytes (%lu KB)\r\n", sram_write_size, sram_write_size / 1024);
sram_size = sram_write_size / 1024; // remember SRAM size for later identification
// set size variant
switch (desig_get_flash_size()) {
case 16:
flash_size_variant = '4';
break;
case 32:
flash_size_variant = '6';
break;
case 64:
flash_size_variant = '8';
break;
case 128:
flash_size_variant = 'B';
break;
case 256:
flash_size_variant = 'C';
break;
case 384:
flash_size_variant = 'D';
break;
case 512:
flash_size_variant = 'E';
break;
case 768:
flash_size_variant = 'F';
break;
case 1024:
flash_size_variant = 'G';
break;
}
}
static void unique_id(void)
{
printf("device unique ID: %08lx%08lx%04lx%04lx\r\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16); // not that the half-works are reversed in the first word
}
static void errata(void)
{
printf("checking errata: access to debug register\r\n");
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK;
const uint16_t rev_id = DBGMCU_IDCODE >> 16;
printf("- MCU ID code: 0x%08lx (DEV_ID=0x%03x REV_ID=0x%04x)\r\n", DBGMCU_IDCODE, dev_id, rev_id);
const bool debug_attached = (0 != (SCS_DHCSR & SCS_DHCSR_C_DEBUGEN));
printf("- debug attached: %s\r\n", debug_attached ? "yes" : "no");
if (debug_attached) {
if (0x307 == DBGMCU_IDCODE) {
printf("- debug has been attached, but MCU ID code is not readable, as documented in the errata\r\n");
} else if (0 == DBGMCU_IDCODE) {
printf("- debug is/has been attached, but MCU ID code is still not readable, this is an unknown behaviour\r\n");
} else {
printf("- debug is attached, and MCU ID code is now readable. Detach debug (JTAG/SWD) to be able to verify errata issue\r\n");
}
} else {
if (0 == DBGMCU_IDCODE) {
printf("- debug is not attached, and MCU ID code is not readable, as documented in the errata\r\n");
} else {
printf("- debug is not attached, but MCU ID code is readable -> this is not an STM32\r\n");
}
}
}
static void jep(void)
{
// read JTAG ID (see ARM CoreSight B2.2.2)
const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0]
const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4]
const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // PART_0, PIDR0 bits[7:0] Part number bits[7:0], PART_1, PIDR1 bits[3:0] Part number bits[11:8]
printf("JEP106 ID: ");
if (0 == jep106_continuation && 0x20 == jep106_identification) {
printf("STM");
} else if (7 == jep106_continuation && 0x51 == jep106_identification) {
printf("GigaDevice");
} else if (4 == jep106_continuation && 0x3b == jep106_identification) {
printf("ARM"); // used by CKS and APM
} else if (5 == jep106_continuation && 0x55 == jep106_identification) {
printf("iVivity"); // JEP106 says it should be "iVivity Inc.", but this has been seen in Hangshun HK32F103C8T6
} else {
printf("unknown");
}
printf(" (cont.=%u, ID=0x%02x), part=0x%03x\r\n", jep106_continuation, jep106_identification, pidr_partno);
}
static void family(void)
{
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK;
const uint16_t rev_id = DBGMCU_IDCODE >> 16;
const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // JEP106 part number actually corresponds to the dev_id
uint16_t family_id = 0;
printf("chip family ");
if (0 == dev_id || 0x307 == dev_id) { // on STM32 the dev_id is not readable unless debug is attached, but the part_no corresponds to the dev_id
printf("(PIDR_PARTNO=0x%03x): ", pidr_partno);
family_id = pidr_partno;
} else { // on not STM32 the dev_id is readable (and the PIDR part number does not correspond)
printf("(DEV_ID=0x%03x): ", dev_id);
family_id = dev_id;
}
switch (family_id) {
case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xx8/B and STM32F10xxC/D/E Errata sheet, without workaround) (CKS32F103 is not affected by this issue)
printf("not readable, retry with debug attached");
break;
case 0x307: // this is completely undocumented (even in the errata), but even with debug attached, DBGMCU_IDCODE = 0x00000307, until the DBGMCU_IDCODE is read over SWJ, only then it's correct. but the JEP106 ID part number is always right (with same meaning). this has been seen on a genuine STM32F103C8T6 (AFAICS)
printf("erroneous reading, read over debug first");
break;
// from RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx
case 0x412:
family_str = "F10x";
printf("STM32F10x low-density");
break;
case 0x410:
family_str = "F10x";
printf("STM32F10x medium-density");
break;
case 0x414:
family_str = "F10x";
printf("STM32F10x high-density");
break;
case 0x430:
family_str = "F10x";
printf("STM32F10x XL-density");
break;
case 0x418:
family_str = "F10x";
printf("STM32F10x connectivity");
break;
// from RM0091 STM32F0x8
case 0x444:
family_str = "F03x";
printf("STM32F03x");
break;
case 0x445:
family_str = "F04x";
printf("STM32F04x");
break;
case 0x440:
family_str = "F05x";
printf("STM32F05x");
break;
case 0x448:
family_str = "F07x";
printf("STM32F07x");
break;
case 0x442:
family_str = "F09x";
printf("STM32F09x");
break;
// from RM0444 STM32G0x1
case 0x460:
family_str = "G071/081";
printf("STM32G071xx/STM32G081xx");
break;
case 0x466:
family_str = "G031/041";
printf("STM32G031xx/STM32G041xx");
break;
// from RM0090 STM32F405/415, STM32F407/417, STM32F427/437, STM32F429/439
case 0x413:
family_str = "F405/407/415/417";
printf("STM32F405/STM32F407/STM32F415/STM32F417");
break;
case 0x419:
family_str = "F42x/43x";
printf("STM32F42x/STM32F43x");
break;
// from RM0368 STM32F401xB/C, STM32F401xD/E
case 0x423:
family_str = "F401";
printf("STM32F401xB/C");
break;
case 0x433:
family_str = "F401";
printf("STM32F401xD/E");
break;
// from RM0383
case 0x431:
family_str = "F411";
printf("STM32F411xC/E");
break;
// from RM0041 STM32F100xx
case 0x420:
family_str = "F100";
printf("STM32F100 low/medium-density");
break;
case 0x428:
family_str = "F100";
printf("STM32F100 high-density");
break;
// empirical
case 0x4c3: // CKS uses this as part number, but the dev_id is readable. APM also uses this part number, but the dev_id is not readable
family_str = "F10x";
printf("APM32F10x");
break;
default:
printf("unknown");
break;
}
printf("\r\n");
if (0 != dev_id && 0x307 != dev_id) { // rev_id is only valid when dev_id is valid
printf("chip revision (REV_ID=0x%04x): ", rev_id);
switch (dev_id) {
case 0x412:
if (0x1000 == rev_id) {
printf("A");
} else {
printf("unknown");
}
break;
case 0x410:
if (0x0000 == rev_id) {
printf("A");
} else if (0x2000 == rev_id) {
printf("B");
} else if (0x2001 == rev_id) {
printf("Z");
} else if (0x2003 == rev_id) {
printf("1/2/3/X/Y");
} else {
printf("unknown");
}
break;
case 0x414:
if (0x1000 == rev_id) {
printf("A/1");
} else if (0x1001 == rev_id) {
printf("Z");
} else if (0x1003 == rev_id) {
printf("1/2/3/X/Y");
} else {
printf("unknown");
}
break;
case 0x430:
if (0x1003 == rev_id) {
printf("A/1");
} else {
printf("unknown");
}
break;
case 0x418:
if (0x1000 == rev_id) {
printf("A");
} else if (0x1001 == rev_id) {
printf("Z");
} else {
printf("unknown");
}
break;
default:
printf("unknown");
break;
}
printf("\r\n");
} else {
printf("to get chip revision information, attach debug\r\n");
}
}
static void manufacturer(void)
{
// read JTAG ID (see ARM CoreSight B2.2.2)
const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0]
const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4]
// ARMv7-M B3.2.3
const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB;
const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB;
const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB;
printf("manufacturer: ");
if (0xC23 == cpuid_partno && 1 == cpuid_variant && 1 == cpuid_revision && 0 == jep106_continuation && 0x20 == jep106_identification) { // STM32 uses Cortex-M3 r1p1 and the right JEP106 ID
manufacturer_str = "STM";
printf("STMicroelectronics");
} else if (2 == cpuid_variant && 1 == cpuid_revision && 7 == jep106_continuation && 0x51 == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and the right JEP106 ID
manufacturer_str = "GD";
printf("GigaDevice");
} else if (2 == cpuid_variant && 1 == cpuid_revision && 4 == jep106_continuation && 0x3b == jep106_identification) { // CKS32 and APM32 use Cortex-M3 r2p1 and ARM JEP106 ID
if (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) { // on CKS32 the dev_id is readable
manufacturer_str = "CKS";
printf("China Key System");
} else { // on APM32 the dev_id is not readable
manufacturer_str = "APM";
printf("Apemix");
}
} else if (2 == cpuid_variant && 1 == cpuid_revision && 5 == jep106_continuation && 0x55 == jep106_identification) { // HK32 uses Cortex-M3 r2p1 and the JEP106 ID from iVivity
manufacturer_str = "HK";
printf("Hangshun");
} else {
manufacturer_str = "???";
printf("unknown");
}
printf("\r\n");
}
static void system_memory(void)
{
// verify if embedded (USART) bootloader is present
printf("system memory: ");
if ((*(volatile uint32_t*)(INFO_BASE + 0) & 0xFFFE0000) == 0x20000000 && // check is MSP points to SRAM (0x200001fc on genuine STM32)
(*(volatile uint32_t*)(INFO_BASE + 4) & 0xFFFFF000) == INFO_BASE) { // check is reset vector is in system memory (0x1ffff021 on genuine STM32)
// calculate CRC
rcc_periph_clock_enable(RCC_CRC); // enable cock for CRC domain to calculate in hardware the CRC
crc_reset();
uint32_t crc = 0;
for (uint32_t addr = INFO_BASE; addr < DESIG_FLASH_SIZE_BASE; addr += 4) { // go through system memory
crc = crc_calculate(*(volatile uint32_t*)addr);
}
if (0x8f992132 == crc) {
printf("STM32 bootloader v2.2");
} else if (0x0338b6af == crc) {
printf("GD32 bootloader v1.0");
} else if (0xac54f62a == crc) {
printf("APM32 bootloader v2.2");
} else if (0x9196c048 == crc) {
printf("HK32 bootloader v2.2");
} else {
printf("unknown bootloader");
}
printf(" (CRC=0x%08lx)", crc);
} else {
printf("absent");
}
printf("\r\n");
}
struct register_name_t {
const uint32_t address;
const char* name;
};
static const struct register_name_t rom_registers[] = {
{
.address = 0xE00FF000,
.name = "ROMSCS",
},
{
.address = 0xE00FF004,
.name = "ROMDWT",
},
{
.address = 0xE00FF008,
.name = "ROMFPB",
},
{
.address = 0xE00FF00C,
.name = "ROMITM",
},
{
.address = 0xE00FF010,
.name = "ROMTPIU",
},
{
.address = 0xE00FF014,
.name = "ROMETM",
},
{
.address = 0xE00FFFCC,
.name = "MEMTYPE",
},
{
.address = 0xE00FFFD0,
.name = "PID4",
},
{
.address = 0xE00FFFD4,
.name = "PID5",
},
{
.address = 0xE00FFFD8,
.name = "PID6",
},
{
.address = 0xE00FFFDC,
.name = "PID7",
},
{
.address = 0xE00FFFE0,
.name = "PID0",
},
{
.address = 0xE00FFFE4,
.name = "PID1",
},
{
.address = 0xE00FFFE8,
.name = "PID2",
},
{
.address = 0xE00FFFEC,
.name = "PID3",
},
{
.address = 0xE00FFFF0,
.name = "CID0",
},
{
.address = 0xE00FFFF4,
.name = "CID1",
},
{
.address = 0xE00FFFF8,
.name = "CID2",
},
{
.address = 0xE00FFFFC,
.name = "CID3",
},
};
static void rom_table(void)
{
// ROM table is at 0xE00FF000-0xE00FFFFF
// see ARMv7-M ARM C1.2.2 The ARMv7-M ROM Table
printf("ROM table entries:\r\n");
for (uint8_t i = 0; i < LENGTH(rom_registers); i++) {
const uint32_t value = *(uint32_t*)rom_registers[i].address;
printf("- 0x%08lx (%s): 0x%08lx\r\n", rom_registers[i].address, rom_registers[i].name, value);
}
}
struct range_accessibility_t {
uint32_t start;
uint32_t end;
bool accessible;
};
struct mcu_map_t {
const char* name;
const struct range_accessibility_t* ranges;
const uint8_t ranges_nb;
};
static const struct range_accessibility_t ranges_stm32f103[] = {
{
.start = 0x00000000,
.end = 0x0001ffff,
.accessible = true,
},
{
.start = 0x00020000,
.end = 0x07ffffff,
.accessible = false,
},
{
.start = 0x08000000,
.end = 0x0801ffff,
.accessible = true,
},
{
.start = 0x08020000,
.end = 0x1fffefff,
.accessible = false,
},
{
.start = 0x1ffff000,
.end = 0x1ffff9ff,
.accessible = true,
},
{
.start = 0x1ffffa00,
.end = 0x1fffffff,
.accessible = false,
},
{
.start = 0x20000000,
.end = 0x20004fff,
.accessible = true,
},
{
.start = 0x20005000,
.end = 0x21ffffff,
.accessible = false,
},
{
.start = 0x22000000,
.end = 0x2209ffff,
.accessible = true,
},
{
.start = 0x220a0000,
.end = 0x3fffffff,
.accessible = false,
},
{
.start = 0x40000000,
.end = 0x40007fff,
.accessible = true,
},
{
.start = 0x40008000,
.end = 0x4000ffff,
.accessible = false,
},
{
.start = 0x40010000,
.end = 0x40013fff,
.accessible = true,
},
{
.start = 0x40014000,
.end = 0x4001ffff,
.accessible = false,
},
{
.start = 0x40020000,
.end = 0x400203ff,
.accessible = true,
},
{
.start = 0x40020400,
.end = 0x40020fff,
.accessible = false,
},
{
.start = 0x40021000,
.end = 0x400213ff,
.accessible = true,
},
{
.start = 0x40021400,
.end = 0x40022fff,
.accessible = false,
},
{
.start = 0x40023000,
.end = 0x400233ff,
.accessible = true,
},
{
.start = 0x40023400,
.end = 0x41ffffff,
.accessible = false,
},
{
.start = 0x42000000,
.end = 0x420fffff,
.accessible = true,
},
{
.start = 0x42100000,
.end = 0x421fffff,
.accessible = false,
},
{
.start = 0x42200000,
.end = 0x4227ffff,
.accessible = true,
},
{
.start = 0x42280000,
.end = 0x423fffff,
.accessible = false,
},
{
.start = 0x42400000,
.end = 0x42407fff,
.accessible = true,
},
{
.start = 0x42408000,
.end = 0x4241ffff,
.accessible = false,
},
{
.start = 0x42420000,
.end = 0x42427fff,
.accessible = true,
},
{
.start = 0x42428000,
.end = 0x4245ffff,
.accessible = false,
},
{
.start = 0x42460000,
.end = 0x42467fff,
.accessible = true,
},
{
.start = 0x42468000,
.end = 0xdfffffff,
.accessible = false,
},
{
.start = 0xe0000000,
.end = 0xe00fffff,
.accessible = true,
},
{
.start = 0xe0100000,
.end = 0xffffffff,
.accessible = false,
},
};
static const struct range_accessibility_t ranges_cks32f103c8[] = {
{
.start = 0x00000000,
.end = 0x0801ffff,
.accessible = true,
},
{
.start = 0x08020000,
.end = 0x1fffefff,
.accessible = false,
},
{
.start = 0x1ffff000,
.end = 0x1ffff9ff,
.accessible = true,
},
{
.start = 0x1ffffa00,
.end = 0x1fffffff,
.accessible = false,
},
{
.start = 0x20000000,
.end = 0x40000bff,
.accessible = true,
},
{
.start = 0x40000c00,
.end = 0x400027ff,
.accessible = false,
},
{
.start = 0x40002800,
.end = 0x400033ff,
.accessible = true,
},
{
.start = 0x40003400,
.end = 0x400037ff,
.accessible = false,
},
{
.start = 0x40003800,
.end = 0x40003bff,
.accessible = true,
},
{
.start = 0x40003c00,
.end = 0x400043ff,
.accessible = false,
},
{
.start = 0x40004400,
.end = 0x40004bff,
.accessible = true,
},
{
.start = 0x40004c00,
.end = 0x400053ff,
.accessible = false,
},
{
.start = 0x40005400,
.end = 0x400067ff,
.accessible = true,
},
{
.start = 0x40006800,
.end = 0x40006bff,
.accessible = false,
},
{
.start = 0x40006c00,
.end = 0x400073ff,
.accessible = true,
},
{
.start = 0x40007400,
.end = 0x4000ffff,
.accessible = false,
},
{
.start = 0x40010000,
.end = 0x40011bff,
.accessible = true,
},
{
.start = 0x40011c00,
.end = 0x400123ff,
.accessible = false,
},
{
.start = 0x40012400,
.end = 0x400133ff,
.accessible = true,
},
{
.start = 0x40013400,
.end = 0x400137ff,
.accessible = false,
},
{
.start = 0x40013800,
.end = 0x40013bff,
.accessible = true,
},
{
.start = 0x40013c00,
.end = 0x4001ffff,
.accessible = false,
},
{
.start = 0x40020000,
.end = 0x400203ff,
.accessible = true,
},
{
.start = 0x40020400,
.end = 0x40020fff,
.accessible = false,
},
{
.start = 0x40021000,
.end = 0x400213ff,
.accessible = true,
},
{
.start = 0x40021400,
.end = 0x40021fff,
.accessible = false,
},
{
.start = 0x40022000,
.end = 0x400223ff,
.accessible = true,
},
{
.start = 0x40022400,
.end = 0x40022fff,
.accessible = false,
},
{
.start = 0x40023000,
.end = 0x400233ff,
.accessible = true,
},
{
.start = 0x40023400,
.end = 0x41ffffff,
.accessible = false,
},
{
.start = 0x42000000,
.end = 0x42017fff,
.accessible = true,
},
{
.start = 0x42018000,
.end = 0x4204ffff,
.accessible = false,
},
{
.start = 0x42050000,
.end = 0x42067fff,
.accessible = true,
},
{
.start = 0x42068000,
.end = 0x4206ffff,
.accessible = false,
},
{
.start = 0x42070000,
.end = 0x42077fff,
.accessible = true,
},
{
.start = 0x42078000,
.end = 0x42087fff,
.accessible = false,
},
{
.start = 0x42088000,
.end = 0x42097fff,
.accessible = true,
},
{
.start = 0x42098000,
.end = 0x420a7fff,
.accessible = false,
},
{
.start = 0x420a8000,
.end = 0x420cffff,
.accessible = true,
},
{
.start = 0x420d0000,
.end = 0x420d7fff,
.accessible = false,
},
{
.start = 0x420d8000,
.end = 0x420e7fff,
.accessible = true,
},
{
.start = 0x420e8000,
.end = 0x421fffff,
.accessible = false,
},
{
.start = 0x42200000,
.end = 0x42237fff,
.accessible = true,
},
{
.start = 0x42238000,
.end = 0x42247fff,
.accessible = false,
},
{
.start = 0x42248000,
.end = 0x42267fff,
.accessible = true,
},
{
.start = 0x42268000,
.end = 0x4226ffff,
.accessible = false,
},
{
.start = 0x42270000,
.end = 0x42277fff,
.accessible = true,
},
{
.start = 0x42278000,
.end = 0x423fffff,
.accessible = false,
},
{
.start = 0x42400000,
.end = 0x42407fff,
.accessible = true,
},
{
.start = 0x42408000,
.end = 0x4241ffff,
.accessible = false,
},
{
.start = 0x42420000,
.end = 0x42427fff,
.accessible = true,
},
{
.start = 0x42428000,
.end = 0x4243ffff,
.accessible = false,
},
{
.start = 0x42440000,
.end = 0x42447fff,
.accessible = true,
},
{
.start = 0x42448000,
.end = 0x4245ffff,
.accessible = false,
},
{
.start = 0x42460000,
.end = 0x42467fff,
.accessible = true,
},
{
.start = 0x42468000,
.end = 0xa0000fff,
.accessible = false,
},
{
.start = 0xa0001000,
.end = 0xffffffff,
.accessible = true,
},
};
static const struct range_accessibility_t ranges_gd32f103c8[] = {
{
.start = 0x00000000,
.end = 0x20004fff,
.accessible = true,
},
{
.start = 0x20005000,
.end = 0x21ffffff,
.accessible = false,
},
{
.start = 0x22000000,
.end = 0x2209ffff,
.accessible = true,
},
{
.start = 0x220a0000,
.end = 0x3fffffff,
.accessible = false,
},
{
.start = 0x40000000,
.end = 0x40017fff,
.accessible = true,
},
{
.start = 0x40018000,
.end = 0x4001ffff,
.accessible = false,
},
{
.start = 0x40020000,
.end = 0x400203ff,
.accessible = true,
},
{
.start = 0x40020400,
.end = 0x40020fff,
.accessible = false,
},
{
.start = 0x40021000,
.end = 0x400213ff,
.accessible = true,
},
{
.start = 0x40021400,
.end = 0x40021fff,
.accessible = false,
},
{
.start = 0x40022000,
.end = 0x400223ff,
.accessible = true,
},
{
.start = 0x40022400,
.end = 0x40022fff,
.accessible = false,
},
{
.start = 0x40023000,
.end = 0x400233ff,
.accessible = true,
},
{
.start = 0x40023400,
.end = 0x41ffffff,
.accessible = false,
},
{
.start = 0x42000000,
.end = 0x422fffff,
.accessible = true,
},
{
.start = 0x42300000,
.end = 0x423fffff,
.accessible = false,
},
{
.start = 0x42400000,
.end = 0x42407fff,
.accessible = true,
},
{
.start = 0x42408000,
.end = 0x4241ffff,
.accessible = false,
},
{
.start = 0x42420000,
.end = 0x42427fff,
.accessible = true,
},
{
.start = 0x42428000,
.end = 0x4243ffff,
.accessible = false,
},
{
.start = 0x42440000,
.end = 0x42447fff,
.accessible = true,
},
{
.start = 0x42448000,
.end = 0x4245ffff,
.accessible = false,
},
{
.start = 0x42460000,
.end = 0x42467fff,
.accessible = true,
},
{
.start = 0x42468000,
.end = 0x5fffffff,
.accessible = false,
},
{
.start = 0x60000000,
.end = 0xa0000fff,
.accessible = true,
},
{
.start = 0xa0001000,
.end = 0xdfffffff,
.accessible = false,
},
{
.start = 0xe0000000,
.end = 0xe00fffff,
.accessible = true,
},
{
.start = 0xe0100000,
.end = 0xffffffff,
.accessible = false,
},
};
static const struct range_accessibility_t ranges_apm32f103c8[] = {
{
.start = 0x00000000,
.end = 0x0001ffff,
.accessible = true,
},
{
.start = 0x00020000,
.end = 0x07ffffff,
.accessible = false,
},
{
.start = 0x08000000,
.end = 0x0801ffff,
.accessible = true,
},
{
.start = 0x08020000,
.end = 0x1fffefff,
.accessible = false,
},
{
.start = 0x1ffff000,
.end = 0x1ffffbff,
.accessible = true,
},
{
.start = 0x1ffffc00,
.end = 0x1fffffff,
.accessible = false,
},
{
.start = 0x20000000,
.end = 0x20004fff,
.accessible = true,
},
{
.start = 0x20005000,
.end = 0x21ffffff,
.accessible = false,
},
{
.start = 0x22000000,
.end = 0x2209ffff,
.accessible = true,
},
{
.start = 0x220a0000,
.end = 0x3fffffff,
.accessible = false,
},
{
.start = 0x40000000,
.end = 0x40017fff,
.accessible = true,
},
{
.start = 0x40018000,
.end = 0x4001ffff,
.accessible = false,
},
{
.start = 0x40020000,
.end = 0x400203ff,
.accessible = true,
},
{
.start = 0x40020400,
.end = 0x40020fff,
.accessible = false,
},
{
.start = 0x40021000,
.end = 0x400213ff,
.accessible = true,
},
{
.start = 0x40021400,
.end = 0x40022fff,
.accessible = false,
},
{
.start = 0x40023000,
.end = 0x400233ff,
.accessible = true,
},
{
.start = 0x40023400,
.end = 0x40023fff,
.accessible = false,
},
{
.start = 0x40024000,
.end = 0x400243ff,
.accessible = true,
},
{
.start = 0x40024400,
.end = 0x41ffffff,
.accessible = false,
},
{
.start = 0x42000000,
.end = 0x422fffff,
.accessible = true,
},
{
.start = 0x42300000,
.end = 0x423fffff,
.accessible = false,
},
{
.start = 0x42400000,
.end = 0x42407fff,
.accessible = true,
},
{
.start = 0x42408000,
.end = 0x4241ffff,
.accessible = false,
},
{
.start = 0x42420000,
.end = 0x42427fff,
.accessible = true,
},
{
.start = 0x42428000,
.end = 0x4245ffff,
.accessible = false,
},
{
.start = 0x42460000,
.end = 0x42467fff,
.accessible = true,
},
{
.start = 0x42468000,
.end = 0x4247ffff,
.accessible = false,
},
{
.start = 0x42480000,
.end = 0x42487fff,
.accessible = true,
},
{
.start = 0x42488000,
.end = 0x5fffffff,
.accessible = false,
},
{
.start = 0x60000000,
.end = 0xafffffff,
.accessible = true,
},
{
.start = 0xb0000000,
.end = 0xdfffffff,
.accessible = false,
},
{
.start = 0xe0000000,
.end = 0xe00fffff,
.accessible = true,
},
{
.start = 0xe0100000,
.end = 0xffffffff,
.accessible = false,
},
};
static const struct range_accessibility_t ranges_hk32f103c8[] = {
{
.start = 0x00000000,
.end = 0x20004fff,
.accessible = true,
},
{
.start = 0x20005000,
.end = 0x21ffffff,
.accessible = false,
},
{
.start = 0x22000000,
.end = 0x2209ffff,
.accessible = true,
},
{
.start = 0x220a0000,
.end = 0x3fffffff,
.accessible = false,
},
{
.start = 0x40000000,
.end = 0x40017fff,
.accessible = true,
},
{
.start = 0x40018000,
.end = 0x4001ffff,
.accessible = false,
},
{
.start = 0x40020000,
.end = 0x400203ff,
.accessible = true,
},
{
.start = 0x40020400,
.end = 0x40020fff,
.accessible = false,
},
{
.start = 0x40021000,
.end = 0x400213ff,
.accessible = true,
},
{
.start = 0x40021400,
.end = 0x40021fff,
.accessible = false,
},
{
.start = 0x40022000,
.end = 0x400223ff,
.accessible = true,
},
{
.start = 0x40022400,
.end = 0x40022fff,
.accessible = false,
},
{
.start = 0x40023000,
.end = 0x400233ff,
.accessible = true,
},
{
.start = 0x40023400,
.end = 0x4002ffff,
.accessible = false,
},
{
.start = 0x40030000,
.end = 0x400303ff,
.accessible = true,
},
{
.start = 0x40030400,
.end = 0x41ffffff,
.accessible = false,
},
{
.start = 0x42000000,
.end = 0x422fffff,
.accessible = true,
},
{
.start = 0x42300000,
.end = 0x423fffff,
.accessible = false,
},
{
.start = 0x42400000,
.end = 0x42407fff,
.accessible = true,
},
{
.start = 0x42408000,
.end = 0x4241ffff,
.accessible = false,
},
{
.start = 0x42420000,
.end = 0x42427fff,
.accessible = true,
},
{
.start = 0x42428000,
.end = 0x4243ffff,
.accessible = false,
},
{
.start = 0x42440000,
.end = 0x42447fff,
.accessible = true,
},
{
.start = 0x42448000,
.end = 0x4245ffff,
.accessible = false,
},
{
.start = 0x42460000,
.end = 0x42467fff,
.accessible = true,
},
{
.start = 0x42468000,
.end = 0x425fffff,
.accessible = false,
},
{
.start = 0x42600000,
.end = 0x42607fff,
.accessible = true,
},
{
.start = 0x42608000,
.end = 0xdfffffff,
.accessible = false,
},
{
.start = 0xe0000000,
.end = 0xe00fffff,
.accessible = true,
},
{
.start = 0xe0100000,
.end = 0xffffffff,
.accessible = false,
},
};
static const struct mcu_map_t mcu_maps[] = {
{
.name = "STM32F103Cx",
.ranges = ranges_stm32f103,
.ranges_nb = LENGTH(ranges_stm32f103),
},
{
.name = "CKS32F103C8",
.ranges = ranges_cks32f103c8,
.ranges_nb = LENGTH(ranges_cks32f103c8),
},
{
.name = "GD32F103C8",
.ranges = ranges_gd32f103c8,
.ranges_nb = LENGTH(ranges_gd32f103c8),
},
{
.name = "APM32F103C8",
.ranges = ranges_apm32f103c8,
.ranges_nb = LENGTH(ranges_apm32f103c8),
},
{
.name = "HK32F103C8",
.ranges = ranges_hk32f103c8,
.ranges_nb = LENGTH(ranges_hk32f103c8),
},
};
static void memory_map(void)
{
#define ADDRESS_START 0UL // the start address of the range to test for accessibility
#define ADDRESS_END 0xffffffffUL // the end address of the range to test for accessibility
#if ADDRESS_END < ADDRESS_START || ADDRESS_START > UINT32_MAX || ADDRESS_END > UINT32_MAX
#error "error in range to test"
#else
printf("testing accessibility of address space (takes 2 minutes):\r\n");
struct range_accessibility_t ranges[57];
uint8_t ranges_nb = 0;
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear BusFault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
uint32_t start = ADDRESS_START; // start address of (in)accessible range
(void)*(uint8_t*)start; // access the first address for a correct start
bool accessible = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID));
for (uint64_t addr = ADDRESS_START; addr <= ADDRESS_END; addr += 256) { // go through address space (but don't check every byte)
const uint32_t address = (uint32_t)addr;
(void)*(volatile uint8_t*)address; // try accessing address
const bool valid = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)); // check is a bus fault occurred
if (!valid) {
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear BusFault flag
}
if (valid != accessible) { // range is now [not] accessible
// re-enable interrupts to be able to transmit
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
printf("- 0x%08lx-0x%08lx: %s\r\n", start, address - 1, accessible ? "accessible" : "inaccessible"); // show range
if (ranges_nb < LENGTH(ranges)) {
ranges[ranges_nb].start = start;
ranges[ranges_nb].end = address - 1;
ranges[ranges_nb].accessible = accessible;
ranges_nb++;
}
start = addr; // start with new range
accessible = valid; // remember range accessibility
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
}
}
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
printf("- 0x%08lx-0x%08lx: %s\r\n", start, ADDRESS_END, accessible ? "accessible" : "inaccessible"); // show range
if (ranges_nb < LENGTH(ranges)) {
ranges[ranges_nb].start = start;
ranges[ranges_nb].end = ADDRESS_END;
ranges[ranges_nb].accessible = accessible;
ranges_nb++;
}
bool found = false;
for (uint8_t i = 0; i < LENGTH(mcu_maps); i++) {
if (mcu_maps[i].ranges_nb != ranges_nb) {
continue;
}
bool match = true;
for (uint8_t j = 0; j < mcu_maps[i].ranges_nb; j++) {
if (mcu_maps[i].ranges[j].start != ranges[j].start || mcu_maps[i].ranges[j].end != ranges[j].end || mcu_maps[i].ranges[j].accessible != ranges[j].accessible) {
match = false;
break;
}
}
if (match) {
found = true;
printf("- memory map matches %s\r\n", mcu_maps[i].name);
}
}
if (!found) {
printf("- no matching memory map found. please send chip details (press a) to f103id@cuvoodoo.info\r\n");
}
#endif
}
struct gpio_def_t {
const enum rcc_periph_clken rcc; /**< GPIO RCC */
const uint32_t port; /**< GPIO port */
const uint16_t* pins; /**< GPIO pin */
const uint8_t pins_nb; /**< GPIO pin */
};
struct pin_count_t {
const char name; /**< letter corresponding to pin count ordering schema */
const uint8_t count; /**< number of pins */
const struct gpio_def_t* gpio; /**< list of pins exclusive to this model */
const uint8_t gpio_nb; /**< number of pins exclusive to this model */
};
static const uint16_t gpio_z_f[] = {GPIO11, GPIO12, GPIO13, GPIO14, GPIO15};
static const uint16_t gpio_z_g[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15};
static const struct gpio_def_t gpio_z[] = {
{
.rcc = RCC_GPIOF,
.port = GPIOF,
.pins = gpio_z_f,
.pins_nb = LENGTH(gpio_z_f),
},
{
.rcc = RCC_GPIOG,
.port = GPIOG,
.pins = gpio_z_g,
.pins_nb = LENGTH(gpio_z_g),
},
};
static const struct pin_count_t pins_z = {
.name = 'Z',
.count = 144,
.gpio = gpio_z,
.gpio_nb = LENGTH(gpio_z),
};
static const uint16_t gpio_v_d[] = {GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15};
static const uint16_t gpio_v_e[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15};
static const struct gpio_def_t gpio_v[] = {
{
.rcc = RCC_GPIOD,
.port = GPIOD,
.pins = gpio_v_d,
.pins_nb = LENGTH(gpio_v_d),
},
{
.rcc = RCC_GPIOE,
.port = GPIOE,
.pins = gpio_v_e,
.pins_nb = LENGTH(gpio_v_e),
},
};
static const struct pin_count_t pins_v = {
.name = 'V',
.count = 100,
.gpio = gpio_v,
.gpio_nb = LENGTH(gpio_v),
};
static const uint16_t gpio_r_c[] = {GPIO0, GPIO1, GPIO2, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12}; // PC3 is not on TFBGA64 but LQFP64
static const uint16_t gpio_r_d[] = {GPIO2}; // PD0 and PD1 are on TFBGA64 but on LQFP64
static const struct gpio_def_t gpio_r[] = {
{
.rcc = RCC_GPIOC,
.port = GPIOC,
.pins = gpio_r_c,
.pins_nb = LENGTH(gpio_r_c),
},
{
.rcc = RCC_GPIOD,
.port = GPIOD,
.pins = gpio_r_d,
.pins_nb = LENGTH(gpio_r_d),
},
};
static const struct pin_count_t pins_r = {
.name = 'R',
.count = 64,
.gpio = gpio_r,
.gpio_nb = LENGTH(gpio_r),
};
static const uint16_t gpio_c_b[] = {GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14};
static const struct gpio_def_t gpio_c[] = {
{
.rcc = RCC_GPIOB,
.port = GPIOB,
.pins = gpio_c_b,
.pins_nb = LENGTH(gpio_c_b),
},
};
static const struct pin_count_t pins_c = {
.name = 'C',
.count = 48,
.gpio = gpio_c,
.gpio_nb = LENGTH(gpio_c),
};
// any one pin is enough since all variants should have it
static const uint16_t gpio_t_b[] = {GPIO0};
static const struct gpio_def_t gpio_t[] = {
{
.rcc = RCC_GPIOB,
.port = GPIOB,
.pins = gpio_t_b,
.pins_nb = LENGTH(gpio_t_b),
},
};
static const struct pin_count_t pins_t = {
.name = 'T',
.count = 36,
.gpio = gpio_t,
.gpio_nb = LENGTH(gpio_t),
};
// pin count variants, from most to least pins
static const struct pin_count_t pins_counts[] = {pins_z, pins_v, pins_r, pins_c, pins_t};
// try to figure out how many pins are present
// works well for STM32 and CKS32, but not GD32 (100 found for 48-pin chip)
// on STM32GC102 pull down does not set the input low
static void pin_count(void) {
printf("testing pins to figure out variant (pins tied to ground can lead to false positives):\r\n");
// figure out which pin count variant this MCU is
// just checking the default values of the ports is not enough (103C has GPIOE and GPIOF peripherals although it should have not pins from these ports)
// this we test if setting the pins actually work
for (uint8_t i = 0; i < LENGTH(pins_counts); i++) {
bool port_accessible = true; // if the port/pin exisits
for (uint8_t j = 0; j < pins_counts[i].gpio_nb; j++) {
// check if port exists
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
*(volatile uint32_t*)pins_counts[i].gpio[j].port; // read register
if (SCB_CFSR & SCB_CFSR_BFARVALID) { // bus fault occurs
port_accessible = false;
}
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
if (!port_accessible) {
break;
}
// port A which is the only one used, for serial, should not be tested
rcc_periph_clock_enable(pins_counts[i].gpio[j].rcc); // enable clock to get correct values from registers
for (uint8_t k = 0; k < pins_counts[i].gpio[j].pins_nb; k++) { // test each pin
for (volatile uint32_t wait = 0; wait < 10; wait++); // somehow not waiting a bit can lead to false negatives
gpio_set_mode(pins_counts[i].gpio[j].port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, pins_counts[i].gpio[j].pins[k]); // set a input to not affect to much anything connected to the pin
gpio_clear(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // be sure it is pulled low
if (gpio_get(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k])) { // pin seems to be tied high externally
continue; // this pin can't be tested
}
gpio_set(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // pull up (using set/reset register)
//GPIO_ODR(pins_counts[i].gpio[j].port) |= pins_counts[i].gpio[j].pins[k]; // pull up (using output register)
if (0 == (GPIO_ODR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // pull up is not registered
port_accessible = false;
}
if (0 == (GPIO_IDR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // when the pin is missing the pin remains low although it is pulled up
port_accessible = false;
}
gpio_clear(pins_counts[i].gpio[j].port, pins_counts[i].gpio[j].pins[k]); // set back to default state (using set/reset register)
//GPIO_ODR(pins_counts[i].gpio[j].port) &= ~pins_counts[i].gpio[j].pins[k]; // set back to default state (using output register)
if (0 != (GPIO_ODR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // pull down is not registered
port_accessible = false;
}
if (0 != (GPIO_IDR(pins_counts[i].gpio[j].port) & pins_counts[i].gpio[j].pins[k])) { // be sure is it low
port_accessible = false;
}
gpio_set_mode(pins_counts[i].gpio[j].port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, pins_counts[i].gpio[j].pins[k]); // puts back to default state
if (!port_accessible) {
break;
}
}
rcc_periph_clock_enable(pins_counts[i].gpio[j].rcc); // put back to disabled mode
}
if (port_accessible) {
printf("- this is pin count variant %c, with %u pins\r\n", pins_counts[i].name, pins_counts[i].count);
pin_count_variant = pins_counts[i].name;
pin_count_nb = pins_counts[i].count;
break;
} else {
printf("- this is not pin count variant %c, with %u pins\r\n", pins_counts[i].name, pins_counts[i].count);
}
}
if (0 == pin_count_nb) {
printf("- could not identify number of pins: the pinout is unknown\r\n");
}
}
/** how to test a peripheral */
struct periph_test_t {
const char* name; /**< name of the peripheral */
const enum rcc_periph_clken rcc; /**< peripheral RCC */
const enum rcc_periph_rst rst; /**< peripheral reset signal */
volatile uint32_t* reg; /**< register of this peripheral to test */
const uint32_t value; /**< value to write to the register */
};
enum periph_e {
// don't list GPIOA since we use it for USART1
PERIPH_GPIOB,
PERIPH_GPIOC,
PERIPH_GPIOD,
PERIPH_GPIOE,
PERIPH_GPIOF,
PERIPH_GPIOG,
PERIPH_TIM1, // advanced
PERIPH_TIM2, // general purpose
PERIPH_TIM3, // general purpose
PERIPH_TIM4, // general purpose
PERIPH_TIM5, // general
PERIPH_TIM6, // basic
PERIPH_TIM7, // basic
PERIPH_TIM8, // advanced
PERIPH_TIM15, // general purpose
PERIPH_TIM16, // general purpose
PERIPH_TIM17, // general purpose
// don't list USART1 since we use is for communication
PERIPH_USART2,
PERIPH_USART3,
PERIPH_SPI1,
PERIPH_SPI2,
PERIPH_I2C1,
PERIPH_I2C2,
PERIPH_CEC,
PERIPH_USB,
PERIPH_CAN1,
PERIPH_CAN2,
PERIPH_MAX,
};
static bool mcu_peripherals[PERIPH_MAX];
static const struct periph_test_t periph_tests[PERIPH_MAX] = {
[PERIPH_GPIOB] = {
.name = "GPIOB",
.rcc = RCC_GPIOB,
.rst = RST_GPIOB,
.reg = &GPIOB_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_GPIOC] = {
.name = "GPIOC",
.rcc = RCC_GPIOC,
.rst = RST_GPIOC,
.reg = &GPIOC_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_GPIOD] = {
.name = "GPIOD",
.rcc = RCC_GPIOD,
.rst = RST_GPIOD,
.reg = &GPIOD_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_GPIOE] = {
.name = "GPIOE",
.rcc = RCC_GPIOE,
.rst = RST_GPIOE,
.reg = &GPIOE_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_GPIOF] = {
.name = "GPIOF",
.rcc = RCC_GPIOF,
.rst = RST_GPIOF,
.reg = &GPIOF_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_GPIOG] = {
.name = "GPIOG",
.rcc = RCC_GPIOG,
.rst = RST_GPIOG,
.reg = &GPIOG_ODR, // write to output register, which should no cause any trouble when the pin in an input
.value = (1 << 0),
},
[PERIPH_TIM1] = {
.name = "TIM1",
.rcc = RCC_TIM1,
.rst = RST_TIM1,
.reg = &TIM1_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM2] = {
.name = "TIM2",
.rcc = RCC_TIM2,
.rst = RST_TIM2,
.reg = &TIM2_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM3] = {
.name = "TIM3",
.rcc = RCC_TIM3,
.rst = RST_TIM3,
.reg = &TIM3_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM4] = {
.name = "TIM4",
.rcc = RCC_TIM4,
.rst = RST_TIM4,
.reg = &TIM4_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM5] = {
.name = "TIM5",
.rcc = RCC_TIM5,
.rst = RST_TIM5,
.reg = &TIM5_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM6] = {
.name = "TIM6",
.rcc = RCC_TIM6,
.rst = RST_TIM6,
.reg = &TIM6_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM7] = {
.name = "TIM7",
.rcc = RCC_TIM7,
.rst = RST_TIM7,
.reg = &TIM7_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM8] = {
.name = "TIM8",
.rcc = RCC_TIM8,
.rst = RST_TIM8,
.reg = &TIM8_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM15] = {
.name = "TIM15",
.rcc = RCC_TIM15,
.rst = RST_TIM15,
.reg = &TIM15_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM16] = {
.name = "TIM16",
.rcc = RCC_TIM16,
.rst = RST_TIM16,
.reg = &TIM16_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_TIM17] = {
.name = "TIM17",
.rcc = RCC_TIM17,
.rst = RST_TIM17,
.reg = &TIM17_CNT, // writing in the counter register has no side effect
.value = (1 << 0),
},
[PERIPH_USART2] = {
.name = "USART2",
.rcc = RCC_USART2,
.rst = RST_USART2,
.reg = &USART2_BRR, // write a fake baud rate
.value = (1 << 0),
},
[PERIPH_USART3] = {
.name = "USART3",
.rcc = RCC_USART3,
.rst = RST_USART3,
.reg = &USART3_BRR, // write a fake baud rate
.value = (1 << 0),
},
[PERIPH_SPI1] = {
.name = "SPI1",
.rcc = RCC_SPI1,
.rst = RST_SPI1,
.reg = &SPI1_CRCPR, // write to the polynomial (should already be at 7)
.value = (1 << 3),
},
[PERIPH_SPI2] = {
.name = "SPI2",
.rcc = RCC_SPI2,
.rst = RST_SPI2,
.reg = &SPI2_CRCPR, // write to the polynomial (should already be at 7)
.value = (1 << 3),
},
[PERIPH_I2C1] = {
.name = "I2C1",
.rcc = RCC_I2C1,
.rst = RST_I2C1,
.reg = &I2C1_OAR1, // write own address
.value = (1 << 0),
},
[PERIPH_I2C2] = {
.name = "I2C2",
.rcc = RCC_I2C2,
.rst = RST_I2C2,
.reg = &I2C2_OAR1, // write own address
.value = (1 << 0),
},
[PERIPH_CEC] = {
.name = "CEC",
.rcc = RCC_CEC,
.rst = RST_CEC,
.reg = &CEC_CFGR, // write own address
.value = (1 << 0),
},
[PERIPH_USB] = {
.name = "USB",
.rcc = RCC_USB,
.rst = RST_USB,
.reg = USB_DADDR_REG, // device address
.value = (1 << 0),
},
[PERIPH_CAN1] = {
.name = "CAN1",
.rcc = RCC_CAN1,
.rst = RST_CAN1,
.reg = &CAN_MCR(CAN1),
.value = (1 << 4), // write NART
},
[PERIPH_CAN2] = {
.name = "CAN2",
.rcc = RCC_CAN2,
.rst = RST_CAN2,
.reg = &CAN_MCR(CAN2),
.value = (1 << 4), // write NART
},
/*
[PERIPH_] = {
.name = "",
.rcc = RCC_,
.rst = RST_,
.reg = &,
.mask = 0x,
.value = (1 << ),
},
*/
};
// any bus fault error
#define SCB_CFSR_BFERR (SCB_CFSR_IBUSERR | SCB_CFSR_PRECISERR | SCB_CFSR_IMPRECISERR | SCB_CFSR_UNSTKERR | SCB_CFSR_STKERR | SCB_CFSR_BFARVALID)
static void peripherals(void)
{
printf("testing peripheral presence:\r\n");
for (uint8_t i = 0; i < LENGTH(periph_tests); i++) {
const struct periph_test_t peripheral = periph_tests[i];
mcu_peripherals[i] = false; // initialise
if (NULL == peripheral.name || 0 == peripheral.reg) { // if the name is missing, the rest probably is too
continue;
}
rcc_periph_clock_enable(peripheral.rcc); // enable clock for domain to read the registers
rcc_periph_reset_pulse(peripheral.rst); // be sure the values have their reset value
// test if the register exists
cm_disable_faults(); // disable all faults, particularly BusFault
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
const uint32_t value = *peripheral.reg; // read register
bool periph_accessible = (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)); // check for fault occurs
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
// test if register is writable
bool periph_writable = false;
if (periph_accessible) {
*peripheral.reg |= peripheral.value;
periph_writable = (peripheral.value == (*peripheral.reg & peripheral.value));
*peripheral.reg = value; // write back previous value
if (SCB_CFSR & SCB_CFSR_BFERR) { // bus fault error occurred (should be SCB_CFSR_IMPRECISERR but we catch all)
periph_accessible = false; // accessing failed
periph_writable = false; // writing failed
SCB_CFSR |= SCB_CFSR_BFERR; // clear all error flags
}
}
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
cm_enable_faults(); // re-enable faults
// clean up
rcc_periph_reset_pulse(peripheral.rst); // be sure the values have their reset value
rcc_periph_clock_disable(peripheral.rcc); // disable domain again
mcu_peripherals[i] = periph_accessible && periph_writable;
printf("- %s: %s\r\n", peripheral.name, periph_accessible ? (periph_writable ? "present" : "absent") : "missing");
}
}
struct mcu_model_t {
char* name;
uint8_t flash; // in KB
uint8_t sram; // in KB
uint8_t pins; // package pin count
bool* peripherals; // which of the peripherals are present
};
// according to datasheet (untested)
static const bool peripheralas_f100x6[PERIPH_MAX] = {
[PERIPH_GPIOB] = true,
[PERIPH_GPIOC] = true,
[PERIPH_TIM1] = true,
[PERIPH_TIM2] = true,
[PERIPH_TIM3] = true,
[PERIPH_TIM5] = true,
[PERIPH_TIM15] = true,
[PERIPH_TIM16] = true,
[PERIPH_TIM17] = true,
[PERIPH_USART2] = true,
[PERIPH_SPI1] = true,
[PERIPH_I2C1] = true,
[PERIPH_CEC] = true,
};
// according to datasheet (untested)
static const bool peripheralas_f100x8[PERIPH_MAX] = {
[PERIPH_GPIOB] = true,
[PERIPH_GPIOC] = true,
[PERIPH_TIM1] = true,
[PERIPH_TIM2] = true,
[PERIPH_TIM3] = true,
[PERIPH_TIM4] = true,
[PERIPH_TIM5] = true,
[PERIPH_TIM15] = true,
[PERIPH_TIM16] = true,
[PERIPH_TIM17] = true,
[PERIPH_USART2] = true,
[PERIPH_USART3] = true,
[PERIPH_SPI1] = true,
[PERIPH_SPI2] = true,
[PERIPH_I2C1] = true,
[PERIPH_I2C2] = true,
[PERIPH_CEC] = true,
};
// according to datasheet (untested)
static const bool peripheralas_f103t8[PERIPH_MAX] = {
[PERIPH_GPIOB] = true,
[PERIPH_GPIOC] = true,
[PERIPH_TIM1] = true,
[PERIPH_TIM2] = true,
[PERIPH_TIM3] = true,
[PERIPH_TIM4] = true,
[PERIPH_USART2] = true,
[PERIPH_SPI1] = true,
[PERIPH_I2C1] = true,
[PERIPH_USB] = true,
[PERIPH_CAN1] = true,
};
// according to datasheet (tested)
static const bool peripheralas_f103c8[PERIPH_MAX] = {
[PERIPH_GPIOB] = true,
[PERIPH_GPIOC] = true,
[PERIPH_GPIOD] = true, // this is not defined in datasheet
[PERIPH_GPIOE] = true, // this is not defined in datasheet
[PERIPH_TIM1] = true,
[PERIPH_TIM2] = true,
[PERIPH_TIM3] = true,
[PERIPH_TIM4] = true,
[PERIPH_USART2] = true,
[PERIPH_USART3] = true,
[PERIPH_SPI1] = true,
[PERIPH_SPI2] = true,
[PERIPH_I2C1] = true,
[PERIPH_I2C2] = true,
[PERIPH_USB] = true,
[PERIPH_CAN1] = true,
};
// empirically found (tested)
static const bool peripheralas_hk32f103c8[PERIPH_MAX] = {
[PERIPH_GPIOB] = true,
[PERIPH_GPIOC] = true,
[PERIPH_GPIOD] = true, // this is not defined in STM32 datasheet
//[PERIPH_GPIOE] = true, // contrary to STM32, this is not present
[PERIPH_TIM1] = true,
[PERIPH_TIM2] = true,
[PERIPH_TIM3] = true,
[PERIPH_TIM4] = true,
[PERIPH_USART2] = true,
[PERIPH_USART3] = true,
[PERIPH_SPI1] = true,
[PERIPH_SPI2] = true,
[PERIPH_I2C1] = true,
[PERIPH_I2C2] = true,
//[PERIPH_USB] = true, // contrary to STM32, this is not present
[PERIPH_CAN1] = true,
};
static const struct mcu_model_t mcu_models[] = {
{
.name = "F100C4",
.flash = 16,
.sram = 4,
.pins = 48,
.peripherals = (bool*)peripheralas_f100x6,
},
{
.name = "F100C6",
.flash = 32,
.sram = 4,
.pins = 48,
.peripherals = (bool*)peripheralas_f100x6,
},
{
.name = "F100C8",
.flash = 64,
.sram = 8,
.pins = 48,
.peripherals = (bool*)peripheralas_f100x8,
},
{
.name = "F100CB",
.flash = 128,
.sram = 8,
.pins = 48,
.peripherals = (bool*)peripheralas_f100x8,
},
{
.name = "F100R4",
.flash = 16,
.sram = 4,
.pins = 64,
.peripherals = (bool*)peripheralas_f100x6,
},
{
.name = "F100R6",
.flash = 32,
.sram = 4,
.pins = 64,
.peripherals = (bool*)peripheralas_f100x6,
},
{
.name = "F100R8",
.flash = 64,
.sram = 8,
.pins = 64,
.peripherals = (bool*)peripheralas_f100x8,
},
{
.name = "F100RB",
.flash = 128,
.sram = 8,
.pins = 64,
.peripherals = (bool*)peripheralas_f100x8,
},
// F103
{
.name = "F103T8",
.flash = 64,
.sram = 20,
.pins = 36,
.peripherals = (bool*)peripheralas_f103t8,
},
{
.name = "F103TB",
.flash = 128,
.sram = 20,
.pins = 36,
.peripherals = (bool*)peripheralas_f103t8,
},
{
.name = "F103C8",
.flash = 64,
.sram = 20,
.pins = 48,
.peripherals = (bool*)peripheralas_f103c8,
},
{
.name = "F103CB",
.flash = 128,
.sram = 20,
.pins = 48,
.peripherals = (bool*)peripheralas_f103c8,
},
{
.name = "F103R8",
.flash = 64,
.sram = 20,
.pins = 64,
.peripherals = (bool*)peripheralas_f103c8,
},
{
.name = "F103RB",
.flash = 128,
.sram = 20,
.pins = 64,
.peripherals = (bool*)peripheralas_f103c8,
},
{
.name = "F103V8",
.flash = 64,
.sram = 20,
.pins = 100,
.peripherals = (bool*)peripheralas_f103c8,
},
{
.name = "F103VB",
.flash = 128,
.sram = 20,
.pins = 100,
.peripherals = (bool*)peripheralas_f103c8,
},
// HK32F103
{
.name = "F103C8",
.flash = 64,
.sram = 20,
.pins = 48,
.peripherals = (bool*)peripheralas_hk32f103c8,
},
{
.name = "F103CB",
.flash = 128,
.sram = 20,
.pins = 48,
.peripherals = (bool*)peripheralas_hk32f103c8,
},
};
static void identify(void)
{
printf("identifying MCU (can take 2 minutes): ");
if (0 == strcmp(manufacturer_str, "???")) {
enable_output = false;
manufacturer(); // sets manufacturer_str
enable_output = true;
}
if (0 == strcmp(family_str, "???")) {
enable_output = false;
family(); // set family_str (cpu could also figure it out, but is less precise)
enable_output = true;
}
if (0 == strcmp(family_str, "F10x") || 0 == strcmp(family_str, "F100")) { // further identify F1 MCU
enable_output = false;
peripherals(); // set mcu_peripherals
enable_output = true;
if (0 == sram_size) {
enable_output = false;
sizes(); // sets flash_size_variant and sram_size
enable_output = true;
}
if ('?' == pin_count_variant || 0 == pin_count_nb) {
enable_output = false;
pin_count(); // sets pin_count_variant
enable_output = true;
}
char* model = "???";
for (uint8_t i = 0; i < LENGTH(mcu_models); i++) {
bool peripherals_match = true;
for (uint8_t j = 0; j < LENGTH(mcu_peripherals) && peripherals_match; j++) {
peripherals_match = peripherals_match && (mcu_models[i].peripherals[j] == mcu_peripherals[j]);
}
if (peripherals_match && mcu_models[i].flash == desig_get_flash_size() && mcu_models[i].sram == sram_size && mcu_models[i].pins == pin_count_nb) {
model = mcu_models[i].name;
break;
}
}
if (0 != strcmp(model, "???")) { // found the model
printf("%s32%s\r\n", manufacturer_str, model);
} else {
printf("%s32%s%c%c\r\n", manufacturer_str, family_str, pin_count_variant, flash_size_variant);
}
} else { // MCU not from the F1 family are not supported
printf("%s32%s\r\n", manufacturer_str, family_str);
}
}
static void version(void)
{
printf("identifier version: %u\r\n", 1);
}
int main(void)
{
// use default internal clock (use the least amount of resources to be compatible with most clones, and interfere the least with the measurements
uart_setup(); // only use UART to interact with the user
setvbuf(stdout, NULL, _IONBF, 0); // disable print buffer
printf("\r\nCuVoodoo F103 MCU identifier\r\n");
while (true) {
char c = usart_recv_blocking(USART1);
printf("%c\r\n", c); // echo back
switch (c) {
case 'b':
printf("starting embedded UART bootloader\r\n");
while(!usart_get_flag(USART1, USART_SR_TC)); // wait for transmission to be complete
bootloader();
break;
case 'a':
version();
unique_id();
cpu();
jep();
manufacturer();
family();
sizes();
peripherals();
pin_count();
identify();
system_memory();
rom_table();
errata();
memory_map();
printf("all done\r\n");
break;
case 'c': // show CPU information
cpu();
break;
case 'e': // test errata
errata();
break;
case 'f': // show family
family();
break;
case 'i':
identify();
break;
case 'j': // show JEP106 ID
jep();
break;
case 'h': // print help
printf("available commands:\r\n");
printf("h show this help\r\n");
printf("b start embedded UART bootloader\r\n");
printf("r reset MCU (e.g. restart application)\r\n");
printf("v show identifier version\r\n");
printf("a show all information\r\n");
printf("m show manufacturer\r\n");
printf("f show MCU family\r\n");
printf("s show flash and SRAM size\r\n");
printf("c show CPU\r\n");
printf("u show MCU unique ID\r\n");
printf("j show JEP106 ID\r\n");
printf("t show ROM table\r\n");
printf("y show system memory\r\n");
printf("i identify MCU model\r\n");
printf("p test peripherals\r\n");
printf("n test pin count\r\n");
printf("l test accessibility of address space\r\n");
printf("e test errata\r\n");
printf("\r\nto submit issues or unknown device details, send all information (press a) to f103id@cuvoodoo.info\r\n");
break;
case 'l':
memory_map();
break;
case 'm': // show manufacturer
manufacturer();
break;
case 'n':
pin_count();
break;
case 'p':
peripherals();
break;
case 'r':
printf("rebooting\r\n");
while(!usart_get_flag(USART1, USART_SR_TC)); // wait for transmission to be complete
reset();
break;
case 's':
sizes();
break;
case 't':
rom_table();
break;
case 'u':
unique_id();
break;
case 'v':
version();
break;
case 'y':
system_memory();
break;
case '\r': // do nothing on new line
case '\n': // do nothing on new line
break;
default:
printf("unknown command. type h for help\r\n");
break;
}
}
return 0;
}