From fdf800af4fdc90364577b5acc111edda7a3d0797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:41:13 +0100 Subject: [PATCH 01/22] Rakefile: sepcify board for project --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index ec51220..2afb466 100644 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,7 @@ FIRMWARES = [BOOTLOADER, APPLICATION] # which development board is used # supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, CORE_BOARD, STLINKV2, BLASTER, BUSVOODOO -BOARD = ENV["BOARD"] || "CORE_BOARD" +BOARD = ENV["BOARD"] || "STLINKV2" # libopencm3 definitions LIBOPENCM3_DIR = "libopencm3" From 781dd8f44d07a37cf769e597b7b82faddf71c719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:43:25 +0100 Subject: [PATCH 02/22] ld: limit to 64kB flash to be compatible with most MCU --- bootloader.ld | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootloader.ld b/bootloader.ld index 7aef15e..d76dd59 100644 --- a/bootloader.ld +++ b/bootloader.ld @@ -19,8 +19,8 @@ PROVIDE(__flash_end = __application_beginning + 128K - 8K); PROVIDE(__application_end = 0); PROVIDE(__flash_end = 0); */ -PROVIDE(__application_end = __application_beginning + 128K - 8K); -PROVIDE(__flash_end = __application_beginning + 128K - 8K); +PROVIDE(__application_end = __application_beginning + 64K - 8K); +PROVIDE(__flash_end = __application_beginning + 64K - 8K); PROVIDE(__dfu_magic = ORIGIN(ram) - 4); From 140f1126e6f17a6b9f6fda02780e09c7e6e4a05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:44:07 +0100 Subject: [PATCH 03/22] global: define RST as DFU force input --- global.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/global.h b/global.h index 4db388b..37c51fc 100644 --- a/global.h +++ b/global.h @@ -665,6 +665,9 @@ #elif defined (BLASTER) #define DFU_FORCE_PIN PA8 /**< GPIO pin (pin PA8, not SWD and UART pin on debug port) */ #define DFU_FORCE_VALUE 0 /**< short to nearby ground connection to force DFU */ +#elif defined(STLINKV2) + #define DFU_FORCE_PIN PB6 /**< RST pin pin */ + #define DFU_FORCE_VALUE 0 /**< short to nearby GND pin to force DFU */ #elif defined(BUSVOODOO) #if BUSVOODOO_HARDWARE_VERSION==0 /* on BusVoodoo v0 DFU input is on PC7 */ From 5810e7209689750037f20f9fc720691777f3a7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:45:06 +0100 Subject: [PATCH 04/22] USB: set project name as product string --- lib/usb_cdcacm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usb_cdcacm.c b/lib/usb_cdcacm.c index 95c1874..339f735 100644 --- a/lib/usb_cdcacm.c +++ b/lib/usb_cdcacm.c @@ -242,7 +242,7 @@ static char usb_serial[] = "00112233445566778899aabb"; */ static const char* usb_strings[] = { "CuVoodoo", /**< manufacturer string */ - "CuVoodoo STM32F1xx firmware", /**< product string */ + "CuVoodoo dachboden clock jockey", /**< product string */ (const char*)usb_serial, /**< device ID used as serial number */ "DFU bootloader (runtime mode)", /**< DFU interface string */ }; From cdc6eb68966ea8dd93b5f0fadf0027d4ed96143e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:46:00 +0100 Subject: [PATCH 05/22] application: new complete clock jockey application, untested --- application.c | 122 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/application.c b/application.c index ff1a1c6..97c0f99 100644 --- a/application.c +++ b/application.c @@ -37,6 +37,7 @@ #include // debug utilities #include // design utilities #include // flash utilities +#include // timer utilities /* own libraries */ #include "global.h" // board definitions @@ -47,12 +48,16 @@ #include "usb_cdcacm.h" // USB CDC ACM utilities #include "terminal.h" // handle the terminal interface #include "menu.h" // menu utilities +#include "flash_internal.h" // menu utilities -#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ +#define WATCHDOG_PERIOD 3000 /**< watchdog period in ms */ /** 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 */ -#define RTC_DATE_TIME 1 +#define RTC_DATE_TIME 0 + +/** number of RTC ticks per second */ +#define RTC_TICKS 10 /** RTC time when device is started */ static time_t time_start = 0; @@ -63,6 +68,13 @@ static time_t time_start = 0; volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */ /** @} */ +volatile uint32_t tacho_count = 0; /**< tachometer edge count */ +bool tacho_display = false; /**< if the current tachometer count should be displayed */ +#define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ +#define TACHO_TARGET_DEFAULT 40 /**< the default target tachometer count */ +uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ +#define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ + size_t putc(char c) { size_t length = 0; // number of characters printed @@ -117,6 +129,30 @@ static void command_reset(void* argument); */ static void command_bootloader(void* argument); +static void command_tacho(void* argument) +{ + (void)argument; // we won't use the argument + + if (argument) { // tachometer value has been provided + uint32_t target = *(uint32_t*)argument; // get target tachometer value + printf("setting target tachometer value to %u\n", target); + tacho_target = target; + // save to EEPROM + const uint32_t eeprom_tacho[2] = {tacho_target, tacho_target ^ 0xffffffff}; + const int32_t rc = flash_internal_eeprom_write((uint8_t*)eeprom_tacho, sizeof(eeprom_tacho)); + if (rc != sizeof(eeprom_tacho)) { + printf("could not save value to EEPROM: %d\n", rc); + } + } else { + if (tacho_display) { + tacho_display = false; + } else { + printf("target tachometer value set to %u\n", tacho_target); + tacho_display = true; + } + } +} + /** list of all supported commands */ static const struct menu_command_t menu_commands[] = { { @@ -169,6 +205,14 @@ static const struct menu_command_t menu_commands[] = { .argument_description = NULL, .command_handler = &command_bootloader, }, + { + .shortcut = 't', + .name = "tacho", + .command_description = "set/show/hide tachometer target and current value", + .argument = MENU_ARGUMENT_UNSIGNED, + .argument_description = "[target]", + .command_handler = &command_tacho, + }, }; static void command_help(void* argument) @@ -330,7 +374,7 @@ void main(void) uart_setup(); // setup USART (for printing) #endif usb_cdcacm_setup(); // setup USB CDC ACM (for printing) - puts("\nwelcome to the CuVoodoo STM32F1 example application\n"); // print welcome message + puts("\nwelcome to the CuVoodoo dachboden clock turner\n"); // print welcome message #if DEBUG // show reset cause @@ -374,7 +418,7 @@ void main(void) printf("setup internal RTC: "); #if defined(BLUE_PILL) || defined(STLINKV2) || defined(BLASTER) // for boards without a Low Speed External oscillator // note: the blue pill LSE oscillator is affected when toggling the onboard LED, thus prefer the HSE - rtc_auto_awake(RCC_HSE, 8000000 / 128 - 1); // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running) + rtc_auto_awake(RCC_HSE, 8000000 / 128 / RTC_TICKS - 1); // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running) #else // for boards with an precise Low Speed External oscillator rtc_auto_awake(RCC_LSE, 32768 - 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) #endif @@ -383,6 +427,39 @@ void main(void) time_start = rtc_get_counter_val(); // get start time from internal RTC printf("OK\n"); + printf("setup SSR: "); + rcc_periph_clock_enable(GPIO_RCC(SSR_PIN)); // enable clock for GPIO peripheral + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // set high to switch off + gpio_set_mode(GPIO_PORT(SSR_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(SSR_PIN)); // set SSR - control pin to open drain. active low, connected to 5V on the + pin + printf("OK\n"); + + // setup timer to measure motor tachometer + printf("setup tachometer measurer: "); + rcc_periph_clock_enable(GPIO_RCC(TACHO_PIN)); // enable clock for GPIO peripheral + gpio_set_mode(GPIO_PORT(TACHO_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(TACHO_PIN)); // set tachometer pin to input + rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt + exti_select_source(GPIO_EXTI(TACHO_PIN), GPIO_PORT(TACHO_PIN)); // mask external interrupt of this pin only for this port + gpio_set(GPIO_PORT(TACHO_PIN), GPIO_PIN(TACHO_PIN)); // pull up to eliminate noise + exti_set_trigger(GPIO_EXTI(TACHO_PIN), EXTI_TRIGGER_FALLING); // trigger when opto-coupler triggers + exti_enable_request(GPIO_EXTI(TACHO_PIN)); // enable external interrupt + nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(TACHO_PIN)); // enable interrupt + printf("OK\n"); + + // load target tachometer value from EEPROM + uint32_t eeprom_tacho[2]; + flash_internal_eeprom_setup(1); // use 1 page for EEPROM emulation + bool eeprom_read = flash_internal_eeprom_read((uint8_t*)eeprom_tacho, sizeof(eeprom_tacho)); + printf("target tachometer count ("); + if (eeprom_read && eeprom_tacho[0] == (eeprom_tacho[1] ^ 0xffffffff)) { + tacho_target = eeprom_tacho[0]; + printf("set"); + } else { + tacho_target = TACHO_TARGET_DEFAULT; + printf("default"); + } + bool tacho_safety = false; // if the motor is switched of for safety reasons + printf("): %u\n", tacho_target); + // setup terminal terminal_prefix = ""; // set default prefix terminal_process = &process_command; // set central function to process commands @@ -390,7 +467,6 @@ void main(void) // start main loop bool action = false; // if an action has been performed don't go to sleep - button_flag = false; // reset button flag while (true) { // infinite loop iwdg_reset(); // kick the dog if (user_input_available) { // user input is available @@ -399,19 +475,30 @@ void main(void) char c = user_input_get(); // store receive character terminal_send(c); // send received character to terminal } - if (button_flag) { // user pressed button - action = true; // action has been performed - printf("button pressed\n"); - led_toggle(); // toggle LED - for (uint32_t i = 0; i < 1000000; i++) { // wait a bit to remove noise and double trigger - __asm__("nop"); - } - button_flag = false; // reset flag - } if (rtc_internal_tick_flag) { // the internal RTC ticked rtc_internal_tick_flag = false; // reset flag action = true; // action has been performed + uint32_t tacho = tacho_count; // backup before clearing + tacho_count = 0; // restart count led_toggle(); // toggle LED (good to indicate if main function is stuck) + if (!tacho_safety) { + if (tacho < tacho_target) { + gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR on to provide power + } else { + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + } + if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s + tacho_safety = true; // turn safety on + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + printf("tachometer does not indicate the motor is turning\n"); + printf("either the tachometer is defective, or the motor is stuck\n"); + printf("switching the motor off for safety\n"); + printf("reboot to retry\n"); + } + } + if (tacho_display) { + printf("%u\n", tacho); // display tachometer frequency + } } if (action) { // go to sleep if nothing had to be done, else recheck for activity action = false; @@ -427,3 +514,10 @@ void rtc_isr(void) rtc_clear_flag(RTC_SEC); // clear flag rtc_internal_tick_flag = true; // notify to show new time } + +/** interrupt service routine called when tachometer edge is detected is pressed */ +void GPIO_EXTI_ISR(TACHO_PIN)(void) +{ + exti_reset_request(GPIO_EXTI(TACHO_PIN)); // reset interrupt + tacho_count++; // increment edge count +} From d74fd3aa09b925f517f4fba464340e34b9a99024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:46:22 +0100 Subject: [PATCH 06/22] README: document project --- README.md | 100 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index c0e06f0..88de999 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,65 @@ -This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031). - project ======= summary ------- -*describe project purpose* +this clock jockey monitors the tachometer of a motor turning a clock, and regulates its speed by switching the power to it. technology ---------- -*described electronic details* +the goal was to make a round clock quadrant turn. +the quadrant is made out of wood, around 1 m heigh, and weights around 5 kg. +it is mounted on a disco ball base. +the bearing is strong enough to hold it and allow the clock to turn around its center. + +originally the disco ball base included a motor, but it was too fragile to withhold the forces. +two plastic gears in the gear box broke. +this was the state of the clock when I joined the team. +I decided to fix the turning clock. +the small DC or stepper motor I tried were too weak to turn the clock. +I decided to use a universal motor salvaged from a washing machine. + +the pinout of the motor was undocumented, but not hard to figure out. +two wires went to a small module at the end of the rotor shaft. +this is the tachometer to measure the rotation speed. +it is also easy to trace the two wires going to the brushes feeding the power to the rotor coils. +next, measure the resistance of the remaining pin pairs. +one will have 0 Ohms. +this is the fuse to protect against over-heating. +there will be two pairs of 0.6 Ohms, and one of 1.2 Ohms. +these are the two coils for the stator, with a center tap. + +wire the motor with 220V AC the following way: +AC L - fuse - rotor - stator (2 coils, not center tap) - AC N +to limit the speed I used a 4000 W capable SCR. +the SCR was not enough to regulate the motor speed. +setting the potentiometer to a fixed point would results in the motor to stop turning after some time, of to speed up to fast. +the user would have to adjust the potentiometer using then knob periodically to have a somewhat constant speed. + +to overcome this limitation I developed the clock jockey. +this device will monitor the speed of the motor using the tachometer, and switch the power to the motor using a Solid State Relay (SSR), so to reach and stay at the predefined speed. + +the motor tachometer provides an AC signal. +basically it's a generating motor linked to the shaft of the actual motor. +the AC frequency (and voltage) indicates the speed of the motor. +the AC signal is rectified using a full bridge rectifier. +the rectified signal is input to an PC817 optocoupler (with ~ 200 Ohm inline resistor). +the full bridge rectifier protects to optocoupler, which has a reverse breakdown voltage of 6 V, lower than the seen -10 V peaks of the AC signal. +it also allows to get two pulses per AC period, for a more accurate speed measurement. +the optocoupler is directly connected to the clock jockey. +an omron G3MB-202P (5V) is also connected to the clock jockey to switch to power going to the motor. +the SCR is still in line to limit the maximum delivered power. board ===== -The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board). +the device reuses an [ST-LINK/V2 clone](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2). +this board uses a STM32F103C8T6 micro-controller. -The underlying template also supports following board: - -- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6 -- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6 -- [blue pill](ihttps://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6 -- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6 -- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6 -- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6 - -**Which board is used is defined in the Makefile**. -This is required to map the user LED and button provided on the board +since the device is firmware protected (against read-out), you will first need to remove this protection using an SWD adapter and running `rake remove_protection`. +you can then flash the bootloader using SWD and application using DFU a documented in the section below. The ST-LINK V2 mini clone has SWD test points on the board. Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware. @@ -44,11 +75,34 @@ Now you can remove the read protection (and erase flash), run `rake remove_prote connections =========== -Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment): +connect the peripherals the following way: -- *list board to preipheral pin connections* +- PC817X emitter, SWIM pin (PB11, pulled up to 3V3 on board) +- PC817X collector, GND +- G3MB-202P SSR 3+, 5V (5V required by SSR, with embedded resistor) +- G3MB-202P SSR 4-, SWDIO (SWCLK pin in not 5V tolerant) -All pins are configured using `define`s in the corresponding source code. +all pins are configured using `define`s in the corresponding source code. + +usage +===== + +the firmware will simply count the number of optocoupler falling edges (2 per revolution). +it will periodically compare this count to the target value. +if this is below, the SSR will be switched on. +if it is above, the SSR will be switched off. + +to set this target, connect the clock jokey to a computer. +it will appear as a serial device (using the CDC ACM USB profile). +use a serial terminal program and connect to it (the baud rate does not matter). +enter `tacho xx`, with xx being the target tachometer count. +to view the set and current tachometer values, enter `tacho`. + +the target tachometer count is somewhat relative. +it is proportional to the motor speed and tied to the periodic check (currently set to 0.1 s). + +if the tacho count it at 0 for too many seconds after boot, the clock jockey will with off the power. +this is a safety feature since the tachometer is either not reading the speed, or the motor is stuck. code ==== @@ -86,7 +140,7 @@ The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along Ope To flash the `booltoader` using SWD run `rake flash_booloader`. Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`. -To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board. +To force the bootloader to start the DFU mode, short the RST pin to the nearby ground pin. It is also possible to flash the `application` image using SWD by running `rake flash_application`. debug @@ -98,8 +152,4 @@ To start the debugging session run `rake debug`. USB --- -The firmware offers serial communication over USART1 and USB (using the CDC ACM device class). - -You can also reset the board by setting the serial width to 5 bits over USB. -To reset the board run `rake reset`. -This only works if provided USB CDC ACM is running correctly and the micro-controller isn't stuck. +The firmware offers serial communication over USB (using the CDC ACM device class). From 3bf0871f395c704569a1bebb257a2376761637d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:01:53 +0100 Subject: [PATCH 07/22] ld: don't provide flash size --- application.ld | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application.ld b/application.ld index b6a4961..6fcd7bf 100644 --- a/application.ld +++ b/application.ld @@ -4,7 +4,7 @@ * the first 4 bytes of the RAM is reserved for the DFU magic word (DFU! to start DFU bootloader) */ -/* Define memory regions. */ +/* define memory regions. */ MEMORY { rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 64K - 8K @@ -19,9 +19,9 @@ PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom)); PROVIDE(__application_end = 0); PROVIDE(__flash_end = 0); */ -PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom)); -PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom)); - +PROVIDE(__application_end = 0); +PROVIDE(__flash_end = 0); +/* RAM location reserved so application can talk to bootloader and tell to start DFU */ PROVIDE(__dfu_magic = ORIGIN(ram) - 4); /* include rest of the definitions for the STM32F1 family */ From 666d1ed9838d485c9fb228dbcbce609975ecdb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:02:22 +0100 Subject: [PATCH 08/22] ld: don't provide flash size --- bootloader.ld | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bootloader.ld b/bootloader.ld index d76dd59..a70d0a9 100644 --- a/bootloader.ld +++ b/bootloader.ld @@ -4,7 +4,7 @@ * the first 4 bytes of the RAM is reserved for the DFU magic word (DFU! to start DFU bootloader) */ -/* Define memory regions. */ +/* define memory regions. */ MEMORY { rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K @@ -19,9 +19,9 @@ PROVIDE(__flash_end = __application_beginning + 128K - 8K); PROVIDE(__application_end = 0); PROVIDE(__flash_end = 0); */ -PROVIDE(__application_end = __application_beginning + 64K - 8K); -PROVIDE(__flash_end = __application_beginning + 64K - 8K); - +PROVIDE(__application_end = 0); +PROVIDE(__flash_end = 0); +/* RAM location reserved so application can talk to bootloader and tell to start DFU */ PROVIDE(__dfu_magic = ORIGIN(ram) - 4); /* include rest of the definitions for the STM32F1 family */ From ecac618d67c1a85d38ff5761ed9d3dbdf35854d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:02:49 +0100 Subject: [PATCH 09/22] README: remove other boord details --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 88de999..37c4e88 100644 --- a/README.md +++ b/README.md @@ -61,17 +61,6 @@ this board uses a STM32F103C8T6 micro-controller. since the device is firmware protected (against read-out), you will first need to remove this protection using an SWD adapter and running `rake remove_protection`. you can then flash the bootloader using SWD and application using DFU a documented in the section below. -The ST-LINK V2 mini clone has SWD test points on the board. -Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware. -To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected. - -The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board. -SWD is disabled in the main firmware, and it has read protection. -To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory. -To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device. -The red LED should stay off while the green LED is on. -Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected. - connections =========== From 363179e8373c9a05d810a15a7896385ecd3d7432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:04:20 +0100 Subject: [PATCH 10/22] flash_internal: fix provided flash size detection --- lib/flash_internal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/flash_internal.c b/lib/flash_internal.c index 167dc4e..b7c12fb 100644 --- a/lib/flash_internal.c +++ b/lib/flash_internal.c @@ -51,7 +51,7 @@ static bool flash_internal_range(uint32_t address, size_t size) if (address < FLASH_BASE) { // start address is before the start of the internal flash return false; } - if ((uint32_t)&__flash_end >= FLASH_BASE) { // check if the end for the internal flash is enforce by the linker script + if ((uint32_t)&__flash_end >= FLASH_BASE) { // check if the end for the internal flash is enforced by the linker script if ((address + size) > (uint32_t)&__flash_end) { // end address is after the end of the enforced internal flash return false; } @@ -231,7 +231,7 @@ void flash_internal_eeprom_setup(uint16_t pages) flash_internal_eeprom_pages = pages; // just need to remember the number of pages // get allocated memory address - if ((uint32_t)&__flash_end > (FLASH_BASE + DESIG_FLASH_SIZE * 1024)) { // user specified larger flash than advertised by chip + if ((uint32_t)&__flash_end >= FLASH_BASE) { // check if the end for the internal flash is enforced by the linker script flash_internal_eeprom_start = (uint32_t)&__flash_end - flash_internal_eeprom_pages * flash_internal_page_size(); } else { flash_internal_eeprom_start = (FLASH_BASE + DESIG_FLASH_SIZE * 1024) - flash_internal_eeprom_pages * flash_internal_page_size(); From 882ea71c4e56680a6044d411cf6027bba7a0f88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:05:27 +0100 Subject: [PATCH 11/22] application: reduce default motor speed for safety --- application.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.c b/application.c index 97c0f99..9b6fc3c 100644 --- a/application.c +++ b/application.c @@ -71,7 +71,7 @@ volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ti volatile uint32_t tacho_count = 0; /**< tachometer edge count */ bool tacho_display = false; /**< if the current tachometer count should be displayed */ #define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ -#define TACHO_TARGET_DEFAULT 40 /**< the default target tachometer count */ +#define TACHO_TARGET_DEFAULT 20 /**< the default target tachometer count (slow for safety) */ uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ #define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ From c9e2f9ea93e7cdc37c47e20a5108e22033a4fd87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:05:47 +0100 Subject: [PATCH 12/22] application: fix relay control --- application.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application.c b/application.c index 9b6fc3c..81ce97b 100644 --- a/application.c +++ b/application.c @@ -73,7 +73,7 @@ bool tacho_display = false; /**< if the current tachometer count should be displ #define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ #define TACHO_TARGET_DEFAULT 20 /**< the default target tachometer count (slow for safety) */ uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ -#define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ +#define SSR_PIN PB14 /**< pin to control the SSR (SWDIO, active low, open drain to 5V) */ size_t putc(char c) { @@ -429,7 +429,7 @@ void main(void) printf("setup SSR: "); rcc_periph_clock_enable(GPIO_RCC(SSR_PIN)); // enable clock for GPIO peripheral - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // set high to switch off + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // set high to switch off gpio_set_mode(GPIO_PORT(SSR_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(SSR_PIN)); // set SSR - control pin to open drain. active low, connected to 5V on the + pin printf("OK\n"); @@ -483,13 +483,13 @@ void main(void) led_toggle(); // toggle LED (good to indicate if main function is stuck) if (!tacho_safety) { if (tacho < tacho_target) { - gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR on to provide power + gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR on to provide power } else { - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power } if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s tacho_safety = true; // turn safety on - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power printf("tachometer does not indicate the motor is turning\n"); printf("either the tachometer is defective, or the motor is stuck\n"); printf("switching the motor off for safety\n"); From 8096ae426b84fac311f93d984d2cf4f8f9d23fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:10:58 +0100 Subject: [PATCH 13/22] application: improve motor not spinning protection --- application.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/application.c b/application.c index 81ce97b..cc97a6c 100644 --- a/application.c +++ b/application.c @@ -15,7 +15,7 @@ /** STM32F1 application example * @file application.c * @author King Kévin - * @date 2016-2019 + * @date 2016-2020 */ /* standard libraries */ @@ -458,6 +458,7 @@ void main(void) printf("default"); } bool tacho_safety = false; // if the motor is switched of for safety reasons + uint32_t tacho_activity = rtc_get_counter_val(); // when was the last time we saw the motor spinning printf("): %u\n", tacho_target); // setup terminal @@ -487,10 +488,12 @@ void main(void) } else { gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power } - if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s + if (tacho) { // we tachometer indicates the motor is spinning + tacho_activity = rtc_get_counter_val(); // updated the last time we saw it spinning + } else if (rtc_get_counter_val() > tacho_activity + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s tacho_safety = true; // turn safety on gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power - printf("tachometer does not indicate the motor is turning\n"); + printf("\ntachometer does not indicate the motor is turning\n"); printf("either the tachometer is defective, or the motor is stuck\n"); printf("switching the motor off for safety\n"); printf("reboot to retry\n"); From 87db9a239ee832c8097810f3b6303b89df1ce696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:41:13 +0100 Subject: [PATCH 14/22] Rakefile: sepcify board for project --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index ec51220..2afb466 100644 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,7 @@ FIRMWARES = [BOOTLOADER, APPLICATION] # which development board is used # supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, CORE_BOARD, STLINKV2, BLASTER, BUSVOODOO -BOARD = ENV["BOARD"] || "CORE_BOARD" +BOARD = ENV["BOARD"] || "STLINKV2" # libopencm3 definitions LIBOPENCM3_DIR = "libopencm3" From e457f308e5bfb2e3f13ef2cdcaabd75ee32876cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:44:07 +0100 Subject: [PATCH 15/22] global: define RST as DFU force input --- global.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/global.h b/global.h index 4db388b..37c51fc 100644 --- a/global.h +++ b/global.h @@ -665,6 +665,9 @@ #elif defined (BLASTER) #define DFU_FORCE_PIN PA8 /**< GPIO pin (pin PA8, not SWD and UART pin on debug port) */ #define DFU_FORCE_VALUE 0 /**< short to nearby ground connection to force DFU */ +#elif defined(STLINKV2) + #define DFU_FORCE_PIN PB6 /**< RST pin pin */ + #define DFU_FORCE_VALUE 0 /**< short to nearby GND pin to force DFU */ #elif defined(BUSVOODOO) #if BUSVOODOO_HARDWARE_VERSION==0 /* on BusVoodoo v0 DFU input is on PC7 */ From 079cba9e1ae8f18242592cb79fbbaf8de2aa6f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:45:06 +0100 Subject: [PATCH 16/22] USB: set project name as product string --- lib/usb_cdcacm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/usb_cdcacm.c b/lib/usb_cdcacm.c index 95c1874..339f735 100644 --- a/lib/usb_cdcacm.c +++ b/lib/usb_cdcacm.c @@ -242,7 +242,7 @@ static char usb_serial[] = "00112233445566778899aabb"; */ static const char* usb_strings[] = { "CuVoodoo", /**< manufacturer string */ - "CuVoodoo STM32F1xx firmware", /**< product string */ + "CuVoodoo dachboden clock jockey", /**< product string */ (const char*)usb_serial, /**< device ID used as serial number */ "DFU bootloader (runtime mode)", /**< DFU interface string */ }; From 925c020c1617cc56148f86350ea88f68c2cfe7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:46:00 +0100 Subject: [PATCH 17/22] application: new complete clock jockey application, untested --- application.c | 122 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/application.c b/application.c index ff1a1c6..97c0f99 100644 --- a/application.c +++ b/application.c @@ -37,6 +37,7 @@ #include // debug utilities #include // design utilities #include // flash utilities +#include // timer utilities /* own libraries */ #include "global.h" // board definitions @@ -47,12 +48,16 @@ #include "usb_cdcacm.h" // USB CDC ACM utilities #include "terminal.h" // handle the terminal interface #include "menu.h" // menu utilities +#include "flash_internal.h" // menu utilities -#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ +#define WATCHDOG_PERIOD 3000 /**< watchdog period in ms */ /** 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 */ -#define RTC_DATE_TIME 1 +#define RTC_DATE_TIME 0 + +/** number of RTC ticks per second */ +#define RTC_TICKS 10 /** RTC time when device is started */ static time_t time_start = 0; @@ -63,6 +68,13 @@ static time_t time_start = 0; volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */ /** @} */ +volatile uint32_t tacho_count = 0; /**< tachometer edge count */ +bool tacho_display = false; /**< if the current tachometer count should be displayed */ +#define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ +#define TACHO_TARGET_DEFAULT 40 /**< the default target tachometer count */ +uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ +#define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ + size_t putc(char c) { size_t length = 0; // number of characters printed @@ -117,6 +129,30 @@ static void command_reset(void* argument); */ static void command_bootloader(void* argument); +static void command_tacho(void* argument) +{ + (void)argument; // we won't use the argument + + if (argument) { // tachometer value has been provided + uint32_t target = *(uint32_t*)argument; // get target tachometer value + printf("setting target tachometer value to %u\n", target); + tacho_target = target; + // save to EEPROM + const uint32_t eeprom_tacho[2] = {tacho_target, tacho_target ^ 0xffffffff}; + const int32_t rc = flash_internal_eeprom_write((uint8_t*)eeprom_tacho, sizeof(eeprom_tacho)); + if (rc != sizeof(eeprom_tacho)) { + printf("could not save value to EEPROM: %d\n", rc); + } + } else { + if (tacho_display) { + tacho_display = false; + } else { + printf("target tachometer value set to %u\n", tacho_target); + tacho_display = true; + } + } +} + /** list of all supported commands */ static const struct menu_command_t menu_commands[] = { { @@ -169,6 +205,14 @@ static const struct menu_command_t menu_commands[] = { .argument_description = NULL, .command_handler = &command_bootloader, }, + { + .shortcut = 't', + .name = "tacho", + .command_description = "set/show/hide tachometer target and current value", + .argument = MENU_ARGUMENT_UNSIGNED, + .argument_description = "[target]", + .command_handler = &command_tacho, + }, }; static void command_help(void* argument) @@ -330,7 +374,7 @@ void main(void) uart_setup(); // setup USART (for printing) #endif usb_cdcacm_setup(); // setup USB CDC ACM (for printing) - puts("\nwelcome to the CuVoodoo STM32F1 example application\n"); // print welcome message + puts("\nwelcome to the CuVoodoo dachboden clock turner\n"); // print welcome message #if DEBUG // show reset cause @@ -374,7 +418,7 @@ void main(void) printf("setup internal RTC: "); #if defined(BLUE_PILL) || defined(STLINKV2) || defined(BLASTER) // for boards without a Low Speed External oscillator // note: the blue pill LSE oscillator is affected when toggling the onboard LED, thus prefer the HSE - rtc_auto_awake(RCC_HSE, 8000000 / 128 - 1); // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running) + rtc_auto_awake(RCC_HSE, 8000000 / 128 / RTC_TICKS - 1); // use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running) #else // for boards with an precise Low Speed External oscillator rtc_auto_awake(RCC_LSE, 32768 - 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) #endif @@ -383,6 +427,39 @@ void main(void) time_start = rtc_get_counter_val(); // get start time from internal RTC printf("OK\n"); + printf("setup SSR: "); + rcc_periph_clock_enable(GPIO_RCC(SSR_PIN)); // enable clock for GPIO peripheral + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // set high to switch off + gpio_set_mode(GPIO_PORT(SSR_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(SSR_PIN)); // set SSR - control pin to open drain. active low, connected to 5V on the + pin + printf("OK\n"); + + // setup timer to measure motor tachometer + printf("setup tachometer measurer: "); + rcc_periph_clock_enable(GPIO_RCC(TACHO_PIN)); // enable clock for GPIO peripheral + gpio_set_mode(GPIO_PORT(TACHO_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(TACHO_PIN)); // set tachometer pin to input + rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt + exti_select_source(GPIO_EXTI(TACHO_PIN), GPIO_PORT(TACHO_PIN)); // mask external interrupt of this pin only for this port + gpio_set(GPIO_PORT(TACHO_PIN), GPIO_PIN(TACHO_PIN)); // pull up to eliminate noise + exti_set_trigger(GPIO_EXTI(TACHO_PIN), EXTI_TRIGGER_FALLING); // trigger when opto-coupler triggers + exti_enable_request(GPIO_EXTI(TACHO_PIN)); // enable external interrupt + nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(TACHO_PIN)); // enable interrupt + printf("OK\n"); + + // load target tachometer value from EEPROM + uint32_t eeprom_tacho[2]; + flash_internal_eeprom_setup(1); // use 1 page for EEPROM emulation + bool eeprom_read = flash_internal_eeprom_read((uint8_t*)eeprom_tacho, sizeof(eeprom_tacho)); + printf("target tachometer count ("); + if (eeprom_read && eeprom_tacho[0] == (eeprom_tacho[1] ^ 0xffffffff)) { + tacho_target = eeprom_tacho[0]; + printf("set"); + } else { + tacho_target = TACHO_TARGET_DEFAULT; + printf("default"); + } + bool tacho_safety = false; // if the motor is switched of for safety reasons + printf("): %u\n", tacho_target); + // setup terminal terminal_prefix = ""; // set default prefix terminal_process = &process_command; // set central function to process commands @@ -390,7 +467,6 @@ void main(void) // start main loop bool action = false; // if an action has been performed don't go to sleep - button_flag = false; // reset button flag while (true) { // infinite loop iwdg_reset(); // kick the dog if (user_input_available) { // user input is available @@ -399,19 +475,30 @@ void main(void) char c = user_input_get(); // store receive character terminal_send(c); // send received character to terminal } - if (button_flag) { // user pressed button - action = true; // action has been performed - printf("button pressed\n"); - led_toggle(); // toggle LED - for (uint32_t i = 0; i < 1000000; i++) { // wait a bit to remove noise and double trigger - __asm__("nop"); - } - button_flag = false; // reset flag - } if (rtc_internal_tick_flag) { // the internal RTC ticked rtc_internal_tick_flag = false; // reset flag action = true; // action has been performed + uint32_t tacho = tacho_count; // backup before clearing + tacho_count = 0; // restart count led_toggle(); // toggle LED (good to indicate if main function is stuck) + if (!tacho_safety) { + if (tacho < tacho_target) { + gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR on to provide power + } else { + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + } + if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s + tacho_safety = true; // turn safety on + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + printf("tachometer does not indicate the motor is turning\n"); + printf("either the tachometer is defective, or the motor is stuck\n"); + printf("switching the motor off for safety\n"); + printf("reboot to retry\n"); + } + } + if (tacho_display) { + printf("%u\n", tacho); // display tachometer frequency + } } if (action) { // go to sleep if nothing had to be done, else recheck for activity action = false; @@ -427,3 +514,10 @@ void rtc_isr(void) rtc_clear_flag(RTC_SEC); // clear flag rtc_internal_tick_flag = true; // notify to show new time } + +/** interrupt service routine called when tachometer edge is detected is pressed */ +void GPIO_EXTI_ISR(TACHO_PIN)(void) +{ + exti_reset_request(GPIO_EXTI(TACHO_PIN)); // reset interrupt + tacho_count++; // increment edge count +} From f858253f78c7c7ec5922ec4f5f003206c7ebe23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Thu, 9 Jan 2020 20:46:22 +0100 Subject: [PATCH 18/22] README: document project --- README.md | 100 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index c0e06f0..88de999 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,65 @@ -This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031). - project ======= summary ------- -*describe project purpose* +this clock jockey monitors the tachometer of a motor turning a clock, and regulates its speed by switching the power to it. technology ---------- -*described electronic details* +the goal was to make a round clock quadrant turn. +the quadrant is made out of wood, around 1 m heigh, and weights around 5 kg. +it is mounted on a disco ball base. +the bearing is strong enough to hold it and allow the clock to turn around its center. + +originally the disco ball base included a motor, but it was too fragile to withhold the forces. +two plastic gears in the gear box broke. +this was the state of the clock when I joined the team. +I decided to fix the turning clock. +the small DC or stepper motor I tried were too weak to turn the clock. +I decided to use a universal motor salvaged from a washing machine. + +the pinout of the motor was undocumented, but not hard to figure out. +two wires went to a small module at the end of the rotor shaft. +this is the tachometer to measure the rotation speed. +it is also easy to trace the two wires going to the brushes feeding the power to the rotor coils. +next, measure the resistance of the remaining pin pairs. +one will have 0 Ohms. +this is the fuse to protect against over-heating. +there will be two pairs of 0.6 Ohms, and one of 1.2 Ohms. +these are the two coils for the stator, with a center tap. + +wire the motor with 220V AC the following way: +AC L - fuse - rotor - stator (2 coils, not center tap) - AC N +to limit the speed I used a 4000 W capable SCR. +the SCR was not enough to regulate the motor speed. +setting the potentiometer to a fixed point would results in the motor to stop turning after some time, of to speed up to fast. +the user would have to adjust the potentiometer using then knob periodically to have a somewhat constant speed. + +to overcome this limitation I developed the clock jockey. +this device will monitor the speed of the motor using the tachometer, and switch the power to the motor using a Solid State Relay (SSR), so to reach and stay at the predefined speed. + +the motor tachometer provides an AC signal. +basically it's a generating motor linked to the shaft of the actual motor. +the AC frequency (and voltage) indicates the speed of the motor. +the AC signal is rectified using a full bridge rectifier. +the rectified signal is input to an PC817 optocoupler (with ~ 200 Ohm inline resistor). +the full bridge rectifier protects to optocoupler, which has a reverse breakdown voltage of 6 V, lower than the seen -10 V peaks of the AC signal. +it also allows to get two pulses per AC period, for a more accurate speed measurement. +the optocoupler is directly connected to the clock jockey. +an omron G3MB-202P (5V) is also connected to the clock jockey to switch to power going to the motor. +the SCR is still in line to limit the maximum delivered power. board ===== -The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board). +the device reuses an [ST-LINK/V2 clone](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2). +this board uses a STM32F103C8T6 micro-controller. -The underlying template also supports following board: - -- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6 -- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6 -- [blue pill](ihttps://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6 -- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6 -- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6 -- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6 - -**Which board is used is defined in the Makefile**. -This is required to map the user LED and button provided on the board +since the device is firmware protected (against read-out), you will first need to remove this protection using an SWD adapter and running `rake remove_protection`. +you can then flash the bootloader using SWD and application using DFU a documented in the section below. The ST-LINK V2 mini clone has SWD test points on the board. Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware. @@ -44,11 +75,34 @@ Now you can remove the read protection (and erase flash), run `rake remove_prote connections =========== -Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment): +connect the peripherals the following way: -- *list board to preipheral pin connections* +- PC817X emitter, SWIM pin (PB11, pulled up to 3V3 on board) +- PC817X collector, GND +- G3MB-202P SSR 3+, 5V (5V required by SSR, with embedded resistor) +- G3MB-202P SSR 4-, SWDIO (SWCLK pin in not 5V tolerant) -All pins are configured using `define`s in the corresponding source code. +all pins are configured using `define`s in the corresponding source code. + +usage +===== + +the firmware will simply count the number of optocoupler falling edges (2 per revolution). +it will periodically compare this count to the target value. +if this is below, the SSR will be switched on. +if it is above, the SSR will be switched off. + +to set this target, connect the clock jokey to a computer. +it will appear as a serial device (using the CDC ACM USB profile). +use a serial terminal program and connect to it (the baud rate does not matter). +enter `tacho xx`, with xx being the target tachometer count. +to view the set and current tachometer values, enter `tacho`. + +the target tachometer count is somewhat relative. +it is proportional to the motor speed and tied to the periodic check (currently set to 0.1 s). + +if the tacho count it at 0 for too many seconds after boot, the clock jockey will with off the power. +this is a safety feature since the tachometer is either not reading the speed, or the motor is stuck. code ==== @@ -86,7 +140,7 @@ The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along Ope To flash the `booltoader` using SWD run `rake flash_booloader`. Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`. -To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board. +To force the bootloader to start the DFU mode, short the RST pin to the nearby ground pin. It is also possible to flash the `application` image using SWD by running `rake flash_application`. debug @@ -98,8 +152,4 @@ To start the debugging session run `rake debug`. USB --- -The firmware offers serial communication over USART1 and USB (using the CDC ACM device class). - -You can also reset the board by setting the serial width to 5 bits over USB. -To reset the board run `rake reset`. -This only works if provided USB CDC ACM is running correctly and the micro-controller isn't stuck. +The firmware offers serial communication over USB (using the CDC ACM device class). From 7078e37bc5e32d5d397fb03973d2eec07d53f529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:02:49 +0100 Subject: [PATCH 19/22] README: remove other boord details --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 88de999..37c4e88 100644 --- a/README.md +++ b/README.md @@ -61,17 +61,6 @@ this board uses a STM32F103C8T6 micro-controller. since the device is firmware protected (against read-out), you will first need to remove this protection using an SWD adapter and running `rake remove_protection`. you can then flash the bootloader using SWD and application using DFU a documented in the section below. -The ST-LINK V2 mini clone has SWD test points on the board. -Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware. -To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected. - -The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board. -SWD is disabled in the main firmware, and it has read protection. -To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory. -To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device. -The red LED should stay off while the green LED is on. -Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected. - connections =========== From f10b43e7328f4917368bcd1f47048690cf6eec01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:05:27 +0100 Subject: [PATCH 20/22] application: reduce default motor speed for safety --- application.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.c b/application.c index 97c0f99..9b6fc3c 100644 --- a/application.c +++ b/application.c @@ -71,7 +71,7 @@ volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ti volatile uint32_t tacho_count = 0; /**< tachometer edge count */ bool tacho_display = false; /**< if the current tachometer count should be displayed */ #define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ -#define TACHO_TARGET_DEFAULT 40 /**< the default target tachometer count */ +#define TACHO_TARGET_DEFAULT 20 /**< the default target tachometer count (slow for safety) */ uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ #define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ From 97c5faf5e508f9b6e4799f2521645156b6531702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:05:47 +0100 Subject: [PATCH 21/22] application: fix relay control --- application.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application.c b/application.c index 9b6fc3c..81ce97b 100644 --- a/application.c +++ b/application.c @@ -73,7 +73,7 @@ bool tacho_display = false; /**< if the current tachometer count should be displ #define TACHO_PIN PB11 /**< tachometer input on SWIM pin, pulled up to 3V3 by 680R */ #define TACHO_TARGET_DEFAULT 20 /**< the default target tachometer count (slow for safety) */ uint32_t tacho_target = TACHO_TARGET_DEFAULT; /**< the target tachometer count (switch SSR on when below, off when above) */ -#define SSR_PIN PB14 /**< pin to control the SSR (active low, open drain to 5V) */ +#define SSR_PIN PB14 /**< pin to control the SSR (SWDIO, active low, open drain to 5V) */ size_t putc(char c) { @@ -429,7 +429,7 @@ void main(void) printf("setup SSR: "); rcc_periph_clock_enable(GPIO_RCC(SSR_PIN)); // enable clock for GPIO peripheral - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // set high to switch off + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // set high to switch off gpio_set_mode(GPIO_PORT(SSR_PIN), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN(SSR_PIN)); // set SSR - control pin to open drain. active low, connected to 5V on the + pin printf("OK\n"); @@ -483,13 +483,13 @@ void main(void) led_toggle(); // toggle LED (good to indicate if main function is stuck) if (!tacho_safety) { if (tacho < tacho_target) { - gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR on to provide power + gpio_clear(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR on to provide power } else { - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power } if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s tacho_safety = true; // turn safety on - gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(TACHO_PIN)); // switch SSR off to cut power + gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power printf("tachometer does not indicate the motor is turning\n"); printf("either the tachometer is defective, or the motor is stuck\n"); printf("switching the motor off for safety\n"); From 4519bfa2d372f9767ec2bacf38a166774193725e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Fri, 10 Jan 2020 11:10:58 +0100 Subject: [PATCH 22/22] application: improve motor not spinning protection --- application.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/application.c b/application.c index 81ce97b..cc97a6c 100644 --- a/application.c +++ b/application.c @@ -15,7 +15,7 @@ /** STM32F1 application example * @file application.c * @author King Kévin - * @date 2016-2019 + * @date 2016-2020 */ /* standard libraries */ @@ -458,6 +458,7 @@ void main(void) printf("default"); } bool tacho_safety = false; // if the motor is switched of for safety reasons + uint32_t tacho_activity = rtc_get_counter_val(); // when was the last time we saw the motor spinning printf("): %u\n", tacho_target); // setup terminal @@ -487,10 +488,12 @@ void main(void) } else { gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power } - if (0 == tacho && rtc_get_counter_val() > time_start + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s + if (tacho) { // we tachometer indicates the motor is spinning + tacho_activity = rtc_get_counter_val(); // updated the last time we saw it spinning + } else if (rtc_get_counter_val() > tacho_activity + 5 * RTC_TICKS) { // the motor does not seem to turn, after 5 s tacho_safety = true; // turn safety on gpio_set(GPIO_PORT(SSR_PIN), GPIO_PIN(SSR_PIN)); // switch SSR off to cut power - printf("tachometer does not indicate the motor is turning\n"); + printf("\ntachometer does not indicate the motor is turning\n"); printf("either the tachometer is defective, or the motor is stuck\n"); printf("switching the motor off for safety\n"); printf("reboot to retry\n");