2017-04-19 16:24:07 +02:00
/** STM32F1 application example
2020-02-17 18:08:17 +01:00
* @ file
2016-08-14 21:02:38 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-17 18:08:17 +01:00
* @ date 2016 - 2020
2016-08-14 21:02:38 +02:00
*/
2016-01-17 14:54:54 +01:00
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // standard utilities
2016-08-14 21:02:38 +02:00
# include <string.h> // string utilities
2019-06-18 17:43:43 +02:00
# include <time.h> // date/time utilities
# include <ctype.h> // utilities to check chars
2016-01-17 14:54:54 +01:00
/* STM32 (including CM3) libraries */
2016-01-18 16:23:35 +01:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/cm3/scb.h> // vector table definition
2016-01-29 11:25:30 +01:00
# include <libopencm3/cm3/nvic.h> // interrupt utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/rcc.h> // real-time control clock library
2016-01-29 11:25:30 +01:00
# include <libopencm3/stm32/exti.h> // external interrupt utilities
2016-08-14 21:02:38 +02:00
# include <libopencm3/stm32/rtc.h> // real time clock utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
# include <libopencm3/stm32/dbgmcu.h> // debug utilities
2018-04-06 17:37:17 +02:00
# include <libopencm3/stm32/desig.h> // design utilities
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/flash.h> // flash utilities
2016-01-17 14:54:54 +01:00
/* own libraries */
2016-01-28 21:21:50 +01:00
# include "global.h" // board definitions
2017-04-03 13:32:45 +02:00
# include "print.h" // printing utilities
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
# include "uart.h" // USART utilities
2018-10-29 15:03:29 +01:00
# endif
2016-01-18 16:23:35 +01:00
# include "usb_cdcacm.h" // USB CDC ACM utilities
2018-04-06 17:37:17 +02:00
# include "terminal.h" // handle the terminal interface
# include "menu.h" // menu utilities
2020-06-14 19:19:29 +02:00
# include "flash_internal.h" // flash utilities
2016-01-17 14:54:54 +01:00
2020-02-02 21:19:03 +01:00
/** watchdog period in ms */
# define WATCHDOG_PERIOD 10000
2019-06-18 17:43:43 +02:00
/** set to 0 if the RTC is reset when the board is powered on, only indicates the uptime
* set to 1 if VBAT can keep the RTC running when the board is unpowered , indicating the date and time
*/
2020-02-02 21:19:03 +01:00
# if defined(CORE_BOARD)
2019-06-18 17:43:43 +02:00
# define RTC_DATE_TIME 1
2020-02-02 21:19:03 +01:00
# else
# define RTC_DATE_TIME 0
# endif
/** number of RTC ticks per second
* @ note use integer divider of oscillator to keep second precision
*/
# define RTC_TICKS_SECOND 4
2019-06-18 17:43:43 +02:00
2020-03-05 14:04:51 +01:00
# if defined(RTC_DATE_TIME) && RTC_DATE_TIME
/** the start time from which to RTC ticks count
* @ note this allows the 32 - bit value to reach further in time , particularly when there are several ticks per second
*/
const time_t rtc_offset = 1577833200 ; // We 1. Jan 00:00:00 CET 2020
# endif
2019-06-18 17:43:43 +02:00
/** RTC time when device is started */
static time_t time_start = 0 ;
2016-10-23 17:42:27 +02:00
2016-08-14 21:02:38 +02:00
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @ {
*/
2020-06-10 17:40:54 +02:00
static volatile bool rtc_internal_tick_flag = false ; /**< flag set when internal RTC ticked */
2016-08-14 21:02:38 +02:00
/** @} */
2017-04-03 13:32:45 +02:00
size_t putc ( char c )
2016-01-17 14:54:54 +01:00
{
2017-04-03 13:32:45 +02:00
size_t length = 0 ; // number of characters printed
2018-04-06 17:37:17 +02:00
static char last_c = 0 ; // to remember on which character we last sent
if ( ' \n ' = = c ) { // send carriage return (CR) + line feed (LF) newline for each LF
if ( ' \r ' ! = last_c ) { // CR has not already been sent
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
uart_putchar_nonblocking ( ' \r ' ) ; // send CR over USART
2018-10-29 15:03:29 +01:00
# endif
2017-04-19 16:24:07 +02:00
usb_cdcacm_putchar ( ' \r ' ) ; // send CR over USB
2018-04-06 17:37:17 +02:00
length + + ; // remember we printed 1 character
2016-01-17 14:54:54 +01:00
}
}
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-04-06 17:37:17 +02:00
uart_putchar_nonblocking ( c ) ; // send byte over USART
2018-10-29 15:03:29 +01:00
# endif
2018-04-06 17:37:17 +02:00
usb_cdcacm_putchar ( c ) ; // send byte over USB
length + + ; // remember we printed 1 character
last_c = c ; // remember last character
2019-03-26 19:27:40 +01:00
return length ; // return number of characters printed
2016-01-17 14:54:54 +01:00
}
2018-04-06 17:37:17 +02:00
/** display available commands
* @ param [ in ] argument no argument required
*/
static void command_help ( void * argument ) ;
/** show software and hardware version
* @ param [ in ] argument no argument required
*/
static void command_version ( void * argument ) ;
/** show uptime
* @ param [ in ] argument no argument required
*/
static void command_uptime ( void * argument ) ;
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
/** show date and time
* @ param [ in ] argument date and time to set
*/
static void command_datetime ( void * argument ) ;
# endif
2018-04-06 17:37:17 +02:00
/** reset board
* @ param [ in ] argument no argument required
*/
static void command_reset ( void * argument ) ;
/** switch to DFU bootloader
* @ param [ in ] argument no argument required
*/
static void command_bootloader ( void * argument ) ;
/** list of all supported commands */
static const struct menu_command_t menu_commands [ ] = {
{
. shortcut = ' h ' ,
. name = " help " ,
. command_description = " display help " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_help ,
} ,
{
. shortcut = ' v ' ,
. name = " version " ,
. command_description = " show software and hardware version " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_version ,
} ,
{
. shortcut = ' u ' ,
. name = " uptime " ,
. command_description = " show uptime " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_uptime ,
} ,
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
{
. shortcut = ' d ' ,
. name = " date " ,
2019-07-29 18:33:05 +02:00
. command_description = " show/set date and time " ,
2019-06-18 17:43:43 +02:00
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [YYYY-MM-DD HH:MM:SS] " ,
. command_handler = & command_datetime ,
} ,
# endif
2018-04-06 17:37:17 +02:00
{
. shortcut = ' r ' ,
. name = " reset " ,
. command_description = " reset board " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_reset ,
} ,
{
. shortcut = ' b ' ,
. name = " bootloader " ,
. command_description = " reboot into DFU bootloader " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & command_bootloader ,
} ,
} ;
static void command_help ( void * argument )
{
( void ) argument ; // we won't use the argument
printf ( " available commands: \n " ) ;
menu_print_commands ( menu_commands , LENGTH ( menu_commands ) ) ; // print global commands
}
static void command_version ( void * argument )
{
( void ) argument ; // we won't use the argument
printf ( " firmware date: %04u-%02u-%02u \n " , BUILD_YEAR , BUILD_MONTH , BUILD_DAY ) ; // show firmware build date
2020-06-10 18:02:53 +02:00
// show flash size
2020-06-14 19:19:29 +02:00
puts ( " MCU advertised flash size: " ) ;
2020-06-10 18:02:53 +02:00
if ( 0xffff = = DESIG_FLASH_SIZE ) {
2020-06-14 19:19:29 +02:00
puts ( " unknown (probably a counterfeit/defective micro-controller \n " ) ;
2020-06-10 18:02:53 +02:00
} else {
printf ( " %u KB \n " , DESIG_FLASH_SIZE ) ;
}
2020-06-14 19:19:29 +02:00
if ( ( uint32_t ) & __flash_end > = FLASH_BASE ) {
printf ( " linker advertised flash size: %u KB \n " , ( ( uint32_t ) & __flash_end - FLASH_BASE ) / 1024 ) ;
}
uint32_t flash_size = flash_internal_probe_read_size ( ) ;
printf ( " readable flash size: %u bytes (%u KB) \n " , flash_size , flash_size / 1024 ) ;
flash_size = flash_internal_probe_write_size ( ) ;
printf ( " writable flash size: %u bytes (%u KB) \n " , flash_size , flash_size / 1024 ) ;
2020-03-16 20:55:50 +01:00
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK ;
2020-06-10 18:02:53 +02:00
const uint16_t rev_id = DBGMCU_IDCODE > > 16 ;
2020-06-14 19:19:29 +02:00
char * unreadable ;
if ( 0 = = DBGMCU_IDCODE & & 0 = = ( SCS_DHCSR & SCS_DHCSR_C_DEBUGEN ) ) {
unreadable = " (not readable, retry with debug attached) " ;
} else if ( 0x307 = = DBGMCU_IDCODE & & ( SCS_DHCSR & SCS_DHCSR_C_DEBUGEN ) ) {
unreadable = " (erroneous reading, read over debug first) " ;
} else {
unreadable = " " ;
}
printf ( " MCU id code: %+08x (DEV_ID=0x%03x REV_ID=0x%04x)%s \n " , DBGMCU_IDCODE , dev_id , rev_id , unreadable ) ; // see below for details
printf ( " device id: %08x%08x%04x%04x \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
//printf("SCS_DHCSR: %+08x, DEBUGEN: %u\n", SCS_DHCSR, SCS_DHCSR & SCS_DHCSR_C_DEBUGEN);
// SCS_DHCSR: 0x03010000, DEBUGEN: 0 debug not attached
// SCS_DHCSR: 0x01010001, DEBUGEN: 1 debug attached
2020-06-10 18:02:53 +02:00
# if DEBUG
bool fake = false ; // if details indicate it's not an STM32
puts ( " chip family: " ) ;
2020-03-16 20:55:50 +01:00
switch ( dev_id ) {
2020-06-14 19:19:29 +02:00
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)
2020-03-16 20:55:50 +01:00
puts ( " not readable, retry with debug attached " ) ;
2018-04-06 17:37:17 +02:00
break ;
2020-03-16 20:55:50 +01:00
// from RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx
2020-06-14 19:19:29 +02:00
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)
puts ( " erroneous reading, read over debug first " ) ;
break ;
2018-04-06 17:37:17 +02:00
case 0x412 :
2020-03-16 20:55:50 +01:00
puts ( " STM32F10x low-density " ) ;
2018-04-06 17:37:17 +02:00
break ;
case 0x410 :
2020-03-16 20:55:50 +01:00
puts ( " STM32F10x medium-density " ) ;
2018-04-06 17:37:17 +02:00
break ;
case 0x414 :
2020-03-16 20:55:50 +01:00
puts ( " STM32F10x high-density " ) ;
2018-04-06 17:37:17 +02:00
break ;
case 0x430 :
2020-03-16 20:55:50 +01:00
puts ( " STM32F10x XL-density " ) ;
2018-04-06 17:37:17 +02:00
break ;
case 0x418 :
2020-06-14 19:19:29 +02:00
puts ( " STM32F10x connectivity " ) ;
2020-03-15 14:11:55 +01:00
break ;
2020-03-16 20:55:50 +01:00
// from RM0091 STM32F0x8
2020-03-15 14:11:55 +01:00
case 0x444 :
puts ( " STM32F03x " ) ;
break ;
case 0x445 :
puts ( " STM32F04x " ) ;
break ;
case 0x440 :
puts ( " STM32F05x " ) ;
break ;
case 0x448 :
puts ( " STM32F07x " ) ;
break ;
case 0x442 :
puts ( " STM32F09x " ) ;
break ;
2020-03-17 09:32:29 +01:00
// from RM0444 STM32G0x1
2020-03-16 20:55:50 +01:00
case 0x460 :
puts ( " STM32G071xx/STM32G081xx " ) ;
break ;
case 0x466 :
puts ( " STM32G031xx/STM32G041xx " ) ;
break ;
2020-03-17 09:32:29 +01:00
// from RM0090 STM32F4x5/STM32F4x7
case 0x413 :
puts ( " STM32F405/STM32F407/STM32F415/STM32F417 " ) ;
break ;
case 0x419 :
puts ( " STM32F42x/STM32F43x " ) ;
break ;
2020-03-17 09:43:18 +01:00
// from RM0368
case 0x423 :
puts ( " STM32F401xB/C " ) ;
break ;
case 0x433 :
puts ( " STM32F401xD/E " ) ;
break ;
2020-03-17 09:47:28 +01:00
// from RM0383
case 0x431 :
puts ( " STM32F411xC/E " ) ;
break ;
2020-03-15 14:11:55 +01:00
default :
puts ( " unknown " ) ;
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 14:11:55 +01:00
break ;
}
2020-06-10 18:02:53 +02:00
putc ( ' \n ' ) ;
2020-03-15 14:11:55 +01:00
puts ( " chip revision: " ) ;
switch ( DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK ) {
case 0x412 :
if ( 0x1000 = = rev_id ) {
putc ( ' A ' ) ;
} else {
puts ( " unknown " ) ;
}
break ;
case 0x410 :
if ( 0x0000 = = rev_id ) {
putc ( ' A ' ) ;
} else if ( 0x2000 = = rev_id ) {
putc ( ' B ' ) ;
} else if ( 0x2001 = = rev_id ) {
putc ( ' Z ' ) ;
} else if ( 0x2003 = = rev_id ) {
puts ( " 1/2/3/X/Y " ) ;
} else {
puts ( " unknown " ) ;
}
break ;
case 0x414 :
if ( 0x1000 = = rev_id ) {
puts ( " A/1 " ) ;
} else if ( 0x1001 = = rev_id ) {
putc ( ' Z ' ) ;
} else if ( 0x1003 = = rev_id ) {
puts ( " 1/2/3/X/Y " ) ;
} else {
puts ( " unknown " ) ;
}
break ;
case 0x430 :
if ( 0x1003 = = rev_id ) {
puts ( " A/1 " ) ;
} else {
puts ( " unknown " ) ;
}
break ;
case 0x418 :
if ( 0x1000 = = rev_id ) {
putc ( ' A ' ) ;
} else if ( 0x1001 = = rev_id ) {
putc ( ' Z ' ) ;
} else {
puts ( " unknown " ) ;
}
2018-04-06 17:37:17 +02:00
break ;
default :
2020-03-15 14:11:55 +01:00
printf ( " unknown " ) ;
2018-04-06 17:37:17 +02:00
break ;
}
2020-06-10 18:02:53 +02:00
putc ( ' \n ' ) ;
2020-03-15 18:25:51 +01:00
// from RM0091 STM32F0x8 reference manual (not sure if it applies to F1)
2020-06-14 19:19:29 +02:00
puts ( " manufacturing information (STM32F0x8 schema): \n " ) ;
2020-03-15 14:11:55 +01:00
printf ( " - X,Y wafer coordinate: %08x \n " , DESIG_UNIQUE_ID0 ) ;
printf ( " - lot number: %c%c%c%c%c%c%c \n " , DESIG_UNIQUE_ID2 > > 24 , DESIG_UNIQUE_ID2 > > 16 , DESIG_UNIQUE_ID2 > > 8 , DESIG_UNIQUE_ID2 > > 0 , DESIG_UNIQUE_ID1 > > 24 , DESIG_UNIQUE_ID1 > > 16 , DESIG_UNIQUE_ID1 > > 8 ) ;
printf ( " - wafer number: %u \n " , DESIG_UNIQUE_ID1 & 0xff ) ;
2020-03-15 18:25:51 +01:00
// from ARMv7-M and Cortex-M3 TRM
// ARMv7-M B3.2.3
printf ( " CPUID: 0x%08x \n " , SCB_CPUID ) ;
const uint8_t cpuid_implementer = ( SCB_CPUID & SCB_CPUID_IMPLEMENTER ) > > SCB_CPUID_IMPLEMENTER_LSB ;
printf ( " - implementer: %s (0x%02x) \n " , 0x41 = = cpuid_implementer ? " ARM " : " unknown " , cpuid_implementer ) ;
const uint8_t cpuid_architecture = ( SCB_CPUID & SCB_CPUID_CONSTANT ) > > SCB_CPUID_CONSTANT_LSB ;
puts ( " - architecture: " ) ;
switch ( cpuid_architecture ) {
case 0xc :
puts ( " ARMv6-M " ) ;
break ;
case 0xf :
puts ( " ARMv7-M " ) ;
break ;
default :
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 18:25:51 +01:00
puts ( " unknown " ) ;
}
printf ( " (0x%x) \n " , cpuid_architecture ) ;
const uint16_t cpuid_partno = ( SCB_CPUID & SCB_CPUID_PARTNO ) > > SCB_CPUID_PARTNO_LSB ;
puts ( " - part number: " ) ;
switch ( cpuid_partno ) {
case 0xC60 :
puts ( " Cortex-M0+ " ) ;
break ;
case 0xC20 :
2020-06-14 19:19:29 +02:00
puts ( " Cortex-M0 " ) ;
2020-03-15 18:25:51 +01:00
break ;
case 0xC23 : // the ARM spec actually mentions 0xC24
2020-06-14 19:19:29 +02:00
puts ( " Cortex-M3 " ) ;
2020-03-15 18:25:51 +01:00
break ;
case 0xC24 :
2020-06-14 19:19:29 +02:00
puts ( " Cortex-M4 " ) ;
2020-03-15 18:25:51 +01:00
break ;
case 0xC27 :
2020-06-14 19:19:29 +02:00
puts ( " Cortex-M7 " ) ;
2020-03-15 18:25:51 +01:00
break ;
default :
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 18:25:51 +01:00
puts ( " unknown " ) ;
}
printf ( " (0x%03x) \n " , cpuid_partno ) ;
const uint8_t cpuid_variant = ( SCB_CPUID & SCB_CPUID_VARIANT ) > > SCB_CPUID_VARIANT_LSB ;
printf ( " - variant: %u \n " , cpuid_variant ) ;
const uint8_t cpuid_revision = ( SCB_CPUID & SCB_CPUID_REVISION ) > > SCB_CPUID_REVISION_LSB ;
printf ( " - revision: %u \n " , cpuid_revision ) ;
// 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]
puts ( " JEP106 ID: " ) ;
if ( 0 = = jep106_continuation & & 0x20 = = jep106_identification ) {
puts ( " STM " ) ;
} else if ( 7 = = jep106_continuation & & 0x51 = = jep106_identification ) {
puts ( " GigaDevice " ) ;
} else if ( 4 = = jep106_continuation & & 0x3b = = jep106_identification ) {
puts ( " ARM " ) ;
} else {
puts ( " unknown " ) ;
}
printf ( " (cont.=%u, ID=0x%02x), part=0x%03x \n " , jep106_continuation , jep106_identification , pidr_partno ) ;
// guess the micro-controller
2020-06-14 19:19:29 +02:00
char * mcu = " STM32 " ; // which MCU is identified
2020-03-15 18:25:51 +01:00
puts ( " MCU: " ) ;
if ( 1 = = cpuid_variant & & 1 = = cpuid_revision & & 0 = = jep106_continuation & & 0x20 = = jep106_identification ) { // STM32 uses Cortex-M3 r1p1 and the right JEP106 ID
2020-06-14 19:19:29 +02:00
mcu = " STM32 " ;
2020-03-15 18:25:51 +01:00
} else if ( 2 = = cpuid_variant & & 1 = = cpuid_revision & & 7 = = jep106_continuation & & 0x51 = = jep106_identification ) { // GD32 uses Cortex-M3 r2p1 and the right JEP106 ID
2020-06-14 19:19:29 +02:00
mcu = " GD32 " ;
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 18:25:51 +01:00
} else if ( 2 = = cpuid_variant & & 1 = = cpuid_revision & & 4 = = jep106_continuation & & 0x3b = = jep106_identification ) { // GD32 uses Cortex-M3 r2p1 and ARM JEP106 ID
2020-06-14 19:19:29 +02:00
mcu = " CKS32 " ;
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 18:25:51 +01:00
} else {
2020-06-14 19:19:29 +02:00
mcu = " unknown " ;
2020-03-16 20:55:50 +01:00
fake = true ;
2020-03-15 18:25:51 +01:00
}
2020-06-14 19:19:29 +02:00
puts ( mcu ) ;
2020-03-15 18:25:51 +01:00
putc ( ' \n ' ) ;
2020-03-16 20:55:50 +01:00
// detect fake STM32
if ( 0x412 = = dev_id | | 0x410 = = dev_id | | 0x414 = = dev_id | | 0x430 = = dev_id | | 0x418 = = dev_id ) { // STM32F10x
2020-06-14 19:19:29 +02:00
// STM32F10x uses a Cortex-M3 r1p1
2020-03-16 20:55:50 +01:00
if ( 0xC23 ! = cpuid_partno ) { // Cortex-M3
fake = true ;
}
if ( 1 ! = cpuid_variant ) { // r1
fake = true ;
}
if ( 1 ! = cpuid_revision ) { // p1
fake = true ;
}
}
2020-06-14 19:19:29 +02:00
if ( 0 ! = DBGMCU_IDCODE & & 0 = = ( SCS_DHCSR & SCS_DHCSR_C_DEBUGEN ) ) { // STM32 can't read the MCU ID without debug attached (see errata). CKS32 is not affected by this issue
fake = true ;
}
if ( 0 = = strcmp ( mcu , " STM32 " ) ) {
printf ( " this %s to be a genuine STM32 \n " , fake ? " does not seem " : " seems " ) ;
}
2020-06-10 18:02:53 +02:00
# endif
2018-04-06 17:37:17 +02:00
}
static void command_uptime ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-06-10 17:43:05 +02:00
const uint32_t uptime = ( rtc_get_counter_val ( ) - time_start ) / RTC_TICKS_SECOND ; // get time from internal RTC
2019-06-18 17:43:43 +02:00
printf ( " uptime: %u.%02u:%02u:%02u \n " , uptime / ( 24 * 60 * 60 ) , ( uptime / ( 60 * 60 ) ) % 24 , ( uptime / 60 ) % 60 , uptime % 60 ) ;
2018-04-06 17:37:17 +02:00
}
2019-06-18 17:43:43 +02:00
# if RTC_DATE_TIME
static void command_datetime ( void * argument )
{
char * datetime = ( char * ) argument ; // argument is optional date time
if ( NULL = = argument ) { // no date and time provided, just show the current day and time
2020-03-05 14:04:51 +01:00
const time_t time_rtc = rtc_get_counter_val ( ) / RTC_TICKS_SECOND + rtc_offset ; // get time from internal RTC
const struct tm * time_tm = localtime ( & time_rtc ) ; // convert time
const char * days [ ] = { " Su " , " Mo " , " Tu " , " We " , " Th " , " Fr " , " Sa " } ; // the days of the week
printf ( " date: %s %d-%02d-%02d %02d:%02d:%02d \n " , days [ time_tm - > tm_wday ] , 1900 + time_tm - > tm_year , 1 + time_tm - > tm_mon , time_tm - > tm_mday , time_tm - > tm_hour , time_tm - > tm_min , time_tm - > tm_sec ) ;
2019-06-18 17:43:43 +02:00
} else { // date and time provided, set it
const char * malformed = " date and time malformed, expecting YYYY-MM-DD HH:MM:SS \n " ;
struct tm time_tm ; // to store the parsed date time
if ( strlen ( datetime ) ! = ( 4 + 1 + 2 + 1 + 2 ) + 1 + ( 2 + 1 + 2 + 1 + 2 ) ) { // verify date/time is long enough
printf ( malformed ) ;
return ;
}
if ( ! ( isdigit ( ( int8_t ) datetime [ 0 ] ) & & isdigit ( ( int8_t ) datetime [ 1 ] ) & & isdigit ( ( int8_t ) datetime [ 2 ] ) & & isdigit ( ( int8_t ) datetime [ 3 ] ) & & ' - ' = = datetime [ 4 ] & & isdigit ( ( int8_t ) datetime [ 5 ] ) & & isdigit ( ( int8_t ) datetime [ 6 ] ) & & ' - ' = = datetime [ 7 ] & & isdigit ( ( int8_t ) datetime [ 8 ] ) & & isdigit ( ( int8_t ) datetime [ 9 ] ) & & ' ' = = datetime [ 10 ] & & isdigit ( ( int8_t ) datetime [ 11 ] ) & & isdigit ( ( int8_t ) datetime [ 12 ] ) & & ' : ' = = datetime [ 13 ] & & isdigit ( ( int8_t ) datetime [ 14 ] ) & & isdigit ( ( int8_t ) datetime [ 15 ] ) & & ' : ' = = datetime [ 16 ] & & isdigit ( ( int8_t ) datetime [ 17 ] ) & & isdigit ( ( int8_t ) datetime [ 18 ] ) ) ) { // verify format (good enough to not fail parsing)
printf ( malformed ) ;
return ;
}
time_tm . tm_year = strtol ( & datetime [ 0 ] , NULL , 10 ) - 1900 ; // parse year
2020-03-05 14:04:51 +01:00
time_tm . tm_mon = strtol ( & datetime [ 5 ] , NULL , 10 ) - 1 ; // parse month
2019-06-18 17:43:43 +02:00
time_tm . tm_mday = strtol ( & datetime [ 8 ] , NULL , 10 ) ; // parse day
time_tm . tm_hour = strtol ( & datetime [ 11 ] , NULL , 10 ) ; // parse hour
time_tm . tm_min = strtol ( & datetime [ 14 ] , NULL , 10 ) ; // parse minutes
time_tm . tm_sec = strtol ( & datetime [ 17 ] , NULL , 10 ) ; // parse seconds
time_t time_rtc = mktime ( & time_tm ) ; // get back seconds
2020-03-05 14:04:51 +01:00
time_rtc - = rtc_offset ; // remove start offset
2020-02-17 18:08:17 +01:00
time_start = time_rtc * RTC_TICKS_SECOND + ( rtc_get_counter_val ( ) - time_start ) ; // update uptime with current date
rtc_set_counter_val ( time_rtc * RTC_TICKS_SECOND ) ; // save date/time to internal RTC
2020-03-05 14:04:51 +01:00
printf ( " date and time saved: %d-%02d-%02d %02d:%02d:%02d \n " , 1900 + time_tm . tm_year , 1 + time_tm . tm_mon , time_tm . tm_mday , time_tm . tm_hour , time_tm . tm_min , time_tm . tm_sec ) ;
2019-06-18 17:43:43 +02:00
}
}
# endif
2018-04-06 17:37:17 +02:00
static void command_reset ( void * argument )
{
( void ) argument ; // we won't use the argument
scb_reset_system ( ) ; // reset device
while ( true ) ; // wait for the reset to happen
}
static void command_bootloader ( void * argument )
{
( void ) argument ; // we won't use the argument
2020-01-02 13:01:40 +01:00
// set DFU magic to specific RAM location
__dfu_magic [ 0 ] = ' D ' ;
__dfu_magic [ 1 ] = ' F ' ;
__dfu_magic [ 2 ] = ' U ' ;
__dfu_magic [ 3 ] = ' ! ' ;
scb_reset_system ( ) ; // reset system (core and peripherals)
2018-04-06 17:37:17 +02:00
while ( true ) ; // wait for the reset to happen
}
2016-01-17 14:54:54 +01:00
2016-08-14 21:02:38 +02:00
/** process user command
* @ param [ in ] str user command string ( \ 0 ended )
*/
static void process_command ( char * str )
2016-01-17 14:54:54 +01:00
{
2018-04-06 17:37:17 +02:00
// ensure actions are available
2019-06-18 17:44:50 +02:00
if ( NULL = = menu_commands | | 0 = = LENGTH ( menu_commands ) ) {
2018-04-06 17:37:17 +02:00
return ;
2016-08-14 21:02:38 +02:00
}
2018-04-06 17:37:17 +02:00
// don't handle empty lines
2019-06-18 17:44:50 +02:00
if ( ! str | | 0 = = strlen ( str ) ) {
2018-04-06 17:37:17 +02:00
return ;
}
bool command_handled = false ;
if ( ! command_handled ) {
command_handled = menu_handle_command ( str , menu_commands , LENGTH ( menu_commands ) ) ; // try if this is not a global command
}
if ( ! command_handled ) {
printf ( " command not recognized. enter help to list commands \n " ) ;
2016-08-14 21:02:38 +02:00
}
2016-01-17 14:54:54 +01:00
}
2016-08-14 21:02:38 +02:00
/** program entry point
* this is the firmware function started by the micro - controller
*/
2016-10-23 17:42:27 +02:00
void main ( void ) ;
void main ( void )
2018-04-06 17:37:17 +02:00
{
2016-01-29 00:24:49 +01:00
rcc_clock_setup_in_hse_8mhz_out_72mhz ( ) ; // use 8 MHz high speed external clock to generate 72 MHz internal clock
2016-08-14 21:02:38 +02:00
2016-10-23 17:42:27 +02:00
# if DEBUG
2017-01-30 09:44:51 +01:00
// enable functionalities for easier debug
2016-10-23 17:42:27 +02:00
DBGMCU_CR | = DBGMCU_CR_IWDG_STOP ; // stop independent watchdog counter when code is halted
DBGMCU_CR | = DBGMCU_CR_WWDG_STOP ; // stop window watchdog counter when code is halted
DBGMCU_CR | = DBGMCU_CR_STANDBY ; // allow debug also in standby mode (keep digital part and clock powered)
DBGMCU_CR | = DBGMCU_CR_STOP ; // allow debug also in stop mode (keep clock powered)
DBGMCU_CR | = DBGMCU_CR_SLEEP ; // allow debug also in sleep mode (keep clock powered)
2017-02-06 17:40:28 +01:00
# else
2017-01-30 09:44:51 +01:00
// setup watchdog to reset in case we get stuck (i.e. when an error occurred)
iwdg_set_period_ms ( WATCHDOG_PERIOD ) ; // set independent watchdog period
iwdg_start ( ) ; // start independent watchdog
2016-10-23 17:42:27 +02:00
# endif
2017-04-03 13:32:45 +02:00
board_setup ( ) ; // setup board
2019-09-19 11:59:02 +02:00
# if !defined(STLINKV2)
2018-02-18 15:18:42 +01:00
uart_setup ( ) ; // setup USART (for printing)
2018-10-29 15:03:29 +01:00
# endif
2017-04-19 16:26:40 +02:00
usb_cdcacm_setup ( ) ; // setup USB CDC ACM (for printing)
2020-01-02 13:11:08 +01:00
puts ( " \n welcome to the CuVoodoo STM32F1 example application \n " ) ; // print welcome message
2016-01-29 00:24:49 +01:00
2019-12-21 19:26:27 +01:00
# if DEBUG
// show reset cause
if ( RCC_CSR & ( RCC_CSR_LPWRRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_IWDGRSTF | RCC_CSR_SFTRSTF | RCC_CSR_PORRSTF | RCC_CSR_PINRSTF ) ) {
puts ( " reset cause(s): " ) ;
if ( RCC_CSR & RCC_CSR_LPWRRSTF ) {
puts ( " low-power " ) ;
}
if ( RCC_CSR & RCC_CSR_WWDGRSTF ) {
puts ( " window-watchdog " ) ;
}
if ( RCC_CSR & RCC_CSR_IWDGRSTF ) {
puts ( " independent-watchdog " ) ;
}
if ( RCC_CSR & RCC_CSR_SFTRSTF ) {
puts ( " software " ) ;
}
if ( RCC_CSR & RCC_CSR_PORRSTF ) {
puts ( " POR/PDR " ) ;
}
if ( RCC_CSR & RCC_CSR_PINRSTF ) {
puts ( " pin " ) ;
}
putc ( ' \n ' ) ;
RCC_CSR | = RCC_CSR_RMVF ; // clear reset flags
}
# endif
2017-01-30 09:44:51 +01:00
# if !(DEBUG)
// show watchdog information
2019-12-21 19:26:58 +01:00
printf ( " setup watchdog: %.2fs " , WATCHDOG_PERIOD / 1000.0 ) ;
if ( FLASH_OBR & FLASH_OBR_OPTERR ) {
2020-02-19 21:16:07 +01:00
puts ( " (option bytes not set in flash: software wachtdog used, not automatically started at reset) \n " ) ;
2019-12-21 19:26:58 +01:00
} else if ( FLASH_OBR & FLASH_OBR_WDG_SW ) {
2020-02-19 21:16:07 +01:00
puts ( " (software watchdog used, not automatically started at reset) \n " ) ;
2017-01-30 09:44:51 +01:00
} else {
2020-02-19 21:16:07 +01:00
puts ( " (hardware watchdog used, automatically started at reset) \n " ) ;
2017-01-30 09:44:51 +01:00
}
# endif
2016-08-14 21:02:38 +02:00
// setup RTC
2020-02-19 21:16:07 +01:00
puts ( " setup internal RTC: " ) ;
2020-01-03 19:41:39 +01:00
# if defined(BLUE_PILL) || defined(STLINKV2) || defined(BLASTER) // for boards without a Low Speed External oscillator
2018-10-29 14:14:32 +01:00
// note: the blue pill LSE oscillator is affected when toggling the onboard LED, thus prefer the HSE
2020-02-02 21:19:03 +01:00
rtc_auto_awake ( RCC_HSE , 8000000 / 128 / RTC_TICKS_SECOND - 1 ) ; // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running)
2018-10-28 22:44:53 +01:00
# else // for boards with an precise Low Speed External oscillator
2020-02-02 21:19:03 +01:00
rtc_auto_awake ( RCC_LSE , 32768 / RTC_TICKS_SECOND - 1 ) ; // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
2018-10-28 22:44:53 +01:00
# endif
2016-08-14 21:02:38 +02:00
rtc_interrupt_enable ( RTC_SEC ) ; // enable RTC interrupt on "seconds"
nvic_enable_irq ( NVIC_RTC_IRQ ) ; // allow the RTC to interrupt
2019-06-18 17:43:43 +02:00
time_start = rtc_get_counter_val ( ) ; // get start time from internal RTC
2020-02-19 21:16:07 +01:00
puts ( " OK \n " ) ;
2016-08-14 21:02:38 +02:00
2018-04-06 17:37:17 +02:00
// setup terminal
terminal_prefix = " " ; // set default prefix
terminal_process = & process_command ; // set central function to process commands
terminal_setup ( ) ; // start terminal
2016-10-23 17:42:27 +02:00
2018-04-06 17:37:17 +02:00
// start main loop
2016-02-18 10:39:08 +01:00
bool action = false ; // if an action has been performed don't go to sleep
button_flag = false ; // reset button flag
2016-01-29 00:24:49 +01:00
while ( true ) { // infinite loop
2016-10-23 17:42:27 +02:00
iwdg_reset ( ) ; // kick the dog
2019-06-18 17:45:53 +02:00
if ( user_input_available ) { // user input is available
2016-02-18 10:39:08 +01:00
action = true ; // action has been performed
2016-01-29 00:24:49 +01:00
led_toggle ( ) ; // toggle LED
2018-04-06 17:37:17 +02:00
char c = user_input_get ( ) ; // store receive character
terminal_send ( c ) ; // send received character to terminal
2016-08-14 21:02:38 +02:00
}
2019-06-18 17:45:53 +02:00
if ( button_flag ) { // user pressed button
2016-08-14 21:02:38 +02:00
action = true ; // action has been performed
2020-02-19 21:16:07 +01:00
puts ( " button pressed \n " ) ;
2016-02-18 10:39:08 +01:00
led_toggle ( ) ; // toggle LED
2020-02-19 21:16:37 +01:00
sleep_ms ( 100 ) ; // wait a bit to remove noise and double trigger
2016-08-14 21:02:38 +02:00
button_flag = false ; // reset flag
2016-02-18 10:39:08 +01:00
}
2019-06-18 17:45:53 +02:00
if ( rtc_internal_tick_flag ) { // the internal RTC ticked
2016-08-14 21:02:38 +02:00
rtc_internal_tick_flag = false ; // reset flag
2017-06-27 15:44:03 +02:00
action = true ; // action has been performed
2020-02-02 21:19:03 +01:00
if ( 0 = = ( rtc_get_counter_val ( ) % RTC_TICKS_SECOND ) ) { // one seond has passed
led_toggle ( ) ; // toggle LED (good to indicate if main function is stuck)
}
2016-08-14 21:02:38 +02:00
}
if ( action ) { // go to sleep if nothing had to be done, else recheck for activity
2016-02-18 10:39:08 +01:00
action = false ;
} else {
__WFI ( ) ; // go to sleep
}
2016-10-23 17:42:27 +02:00
} // main loop
2016-01-17 14:54:54 +01:00
}
2016-01-29 11:25:30 +01:00
2016-08-14 21:02:38 +02:00
/** @brief interrupt service routine called when tick passed on RTC */
void rtc_isr ( void )
{
rtc_clear_flag ( RTC_SEC ) ; // clear flag
rtc_internal_tick_flag = true ; // notify to show new time
}