/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
/** STM32F1 BusVoodoo application
* @file application.c
* @author King Kévin
* @date 2016-2017
*/
/* standard libraries */
#include // standard integer types
#include // standard utilities
#include // string utilities
#include // math utilities
/* STM32 (including CM3) libraries */
#include // Cortex M3 utilities
#include // vector table definition
#include // interrupt utilities
#include // general purpose input output library
#include // real-time control clock library
#include // external interrupt utilities
#include // independent watchdog utilities
#include // debug utilities
#include // flash utilities
#include // design utilities
#include // ADC utilities
#include // DAC utilities
/* own libraries */
#include "global.h" // board definitions
#include "print.h" // printing utilities
#include "usart.h" // USART utilities
#include "usb_cdcacm.h" // USB CDC ACM utilities
//#include "rs.h" // RS-232/485 utilities
#include "i2c_master.h"
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
/** @defgroup busvoodoo_peripherals peripheral pin definitions
* @{
*/
#define BUSVOODOO_5VPULLUP_PORT A /**< 5V pull-up enable pin (active low) */
#define BUSVOODOO_5VPULLUP_PIN 15 /**< 5V pull-up enable pin (active low) */
#define BUSVOODOO_XVPULLUP_PORT B /**< xV pull-up enable pin (active low) */
#define BUSVOODOO_XVPULLUP_PIN 3 /**< xV pull-up enable pin (active low) */
#define BUSVOODOO_XVEN_PORT A /**< xV enable pin (active high) */
#define BUSVOODOO_XVEN_PIN 6 /**< xV enable pin (active high) */
#define BUSVOODOO_12VEN_PORT A /**< 12V enable pin (active high) */
#define BUSVOODOO_12VEN_PIN 7 /**< 12V enable pin (active high) */
#define BUSVOODOO_RS232EN_PORT C /**< RS-232 enable pin (active low) */
#define BUSVOODOO_RS232EN_PIN 13 /**< RS-232 enable pin (active low) */
#define BUSVOODOO_VOUTEN_PORT B /**< voltage output (5V and 3.3V) enable pin (active low) */
#define BUSVOODOO_VOUTEN_PIN 4 /**< voltage output (5V and 3.3V) enable pin (active low) */
/** @} */
/** @defgroup busvoodoo_adc inputs to measure voltages
* @{
*/
#define BUSVOODOO_3V3_CHANNEL 12 /**< ADC channel to measure 5V rail */
#define BUSVOODOO_5V_CHANNEL 13 /**< ADC channel to measure 3.3V rail */
#define BUSVOODOO_XV_CHANNEL 11 /**< ADC channel to measure xV rail */
#define BUSVOODOO_12V_CHANNEL 10 /**< ADC channel to measure 12V rail */
/** @} */
#define BUSVOOFOO_XVCTL_CHANNEL CHANNEL_1 /**< DAC channel to control xV output voltage */
#define BUSVOODOO_XV_DEFAULT (0.8*(1+33.0/10.0)) /**< default (when not driven) xV voltage regulator output voltage based on R1 and R2 */
#define BUSVOODOO_XV_TEST 2.5 /**< target xV output voltage to test if we can set control the xV voltage regulator */
#define BUSVOODOO_XV_SET(x) ((0.8*(1+33.0/10.0)-x)*(10.0/33.0)+0.8) /**< voltage to output for the DAC to set the desired xV output voltage (based on resistor values on the xV adjust pins and xV voltage reference) */
#define BUSVOOFOO_12VCTL_CHANNEL CHANNEL_2 /**< DAC channel to control 12V output voltage */
#define BUSVOODOO_12V_DEFAULT (1.25*(1+100.0/10.0)) /**< default (when not driven) 12V voltage regulator output voltage based on R1 and R2 */
#define BUSVOODOO_12V_TEST 12.0 /**< target 12V output voltage to test if we can set control the 12V voltage regulator */
#define BUSVOODOO_12V_SET(x) ((1.25*(1+100.0/10.0)-x)*(10.0/100.0)+1.25) /**< voltage to output for the DAC to set the desired
12V output voltage (based on resistor values on the 12V adjust pins and 12V voltage reference) */
/** @defgroup busvoodoo_io I/O pin definitions
* @{
*/
static const char* busvoodoo_io_names[13] = {"I2C_SMBA/SPI_NSS/I2S_WS", "SDIO_CMD", "USART_CTS/SPI_SCK/I2S_CK", "SDIO_D3/UART_RX", "I2C_SDA/USART_RX", "SDIO_D0", "SPI_MOSI/I2S_SD", "SDIO_CK/USART_CK", "I2C_SCL/USART_TX", "SDIO_D1", "I2S_MCK", "USART_RTS/SPI_MISO", "SDIO_D2/UART_TX"}; /**< I/O individual signal names */
static const uint32_t busvoodoo_io_ports[13] = {GPIOB, GPIOD, GPIOB, GPIOC, GPIOB, GPIOC, GPIOB, GPIOC, GPIOB, GPIOC, GPIOC, GPIOB, GPIOC}; /**< port of individual signals */
static const uint32_t busvoodoo_io_pins[13] = {GPIO12, GPIO2, GPIO13, GPIO11, GPIO11, GPIO8, GPIO15, GPIO12, GPIO10, GPIO9, GPIO6, GPIO14, GPIO10}; /**< pin of individual signals */
// ideal pinout
//static const uint8_t busvoodoo_io_groups[13] = {1, 1, 3, 3, 5, 5, 2, 2, 4, 4, 6, 6, 6}; /**< which I/O (group) does the signal belong to */
// schematic/layout v0.001 pinout (SDIO_D2/UART_TX landed in group 6 instead of 3)
static const uint8_t busvoodoo_io_groups[13] = {1, 1, 3, 3, 5, 5, 2, 2, 4, 4, 6, 6, 3}; /**< which I/O (group) does the signal belong to */
/** @} */
size_t putc(char c)
{
size_t length = 0; // number of characters printed
static char newline = 0; // to remember on which character we sent the newline
if (0==c) {
length = 0; // don't print string termination character
} else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination
if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r)
usart_putchar_nonblocking('\r'); // send CR over USART
usb_cdcacm_putchar('\r'); // send CR over USB
usart_putchar_nonblocking('\n'); // send LF over USART
usb_cdcacm_putchar('\n'); // send LF over USB
length += 2; // remember we printed 2 characters
newline = c; // remember on which character we sent the newline
} else {
length = 0; // the \r or \n of \n\r or \r\n has already been printed
}
} else {
usart_putchar_nonblocking(c); // send byte over USART
usb_cdcacm_putchar(c); // send byte over USB
newline = 0; // clear new line
length++; // remember we printed 1 character
}
return length; // return number of characters printed
}
static bool wait_space(void)
{
// disable watchdog when waiting for user input
printf("press space to continue, or any other key to abort\n");
while (!usart_received && !usb_cdcacm_received) { // wait for user input
__WFI(); // go to sleep
}
char c = 0;
if (usart_received) {
c = usart_getchar(); // read user input from UART
} else if (usb_cdcacm_received) {
c = usb_cdcacm_getchar(); // read user input from USB
} else {
return false; // this should not happen
}
if (' '==c) { // space entered
return true;
} else { // something else entered
return false;
}
}
/** set safe state by disabling all outputs */
static void safe_state(void)
{
// disable voltage outputs
gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable 5V and 3.3V output on connector
gpio_set_mode(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_VOUTEN_PIN)); // set pin as output (open-drain pulled high to disable the pMOS)
gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator
gpio_set_mode(GPIO(BUSVOODOO_XVEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_XVEN_PIN)); // set pin as output (push-pull, pulled low for safety)
gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // disable 12V voltage regulator
gpio_set_mode(GPIO(BUSVOODOO_12VEN_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(BUSVOODOO_12VEN_PIN)); // set pin as output (push-pull, pulled low for safety)
// disable embedded pull-ups
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); // disable JTAG (but keep SWD) so to use the underlying GPIOs (PA15, PB3, PB4)
gpio_set(GPIO(BUSVOODOO_5VPULLUP_PORT), GPIO(BUSVOODOO_5VPULLUP_PIN)); // set pin high to disable 5V embedded pull-up
gpio_set_mode(GPIO(BUSVOODOO_5VPULLUP_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_5VPULLUP_PIN)); // set pin as output (open-drain pulled high to disable the pMOS)
gpio_set(GPIO(BUSVOODOO_XVPULLUP_PORT), GPIO(BUSVOODOO_XVPULLUP_PIN)); // set pin high to disable xV embedded pull-up
gpio_set_mode(GPIO(BUSVOODOO_XVPULLUP_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(BUSVOODOO_XVPULLUP_PIN)); // set pin as output (open-drain pulled high to disable the pMOS)
// disable all signal I/O outputs
for (uint8_t pin=0; pin5.5) {
printf("5V power rail voltage is too high: %d.%02uV, check USB port\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
// check 3.3V power rail
voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage
if (voltage<3.0) {
printf("3.3V power rail voltage is too low: %d.%02uV, check OLED connector and voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
} else if (voltage>3.6) {
printf("3.3V power rail voltage is too high: %d.%02uV, check OLED connector and voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
// check xV voltage regulator
gpio_set(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // enable xV voltage regulator
sleep_ms(1); // let the voltage regulator start and voltage settle
voltage = rail_voltage(BUSVOODOO_XV_CHANNEL); // get xV voltage
// without being driven it should be around the default voltage
if (voltageBUSVOODOO_XV_DEFAULT+0.2) {
printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
// check if we can control xV
voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get reference voltage
if (isnan(voltage)) {
printf("can get 3V3 rail voltage");
goto error;
}
uint16_t dac_set = BUSVOODOO_XV_SET(BUSVOODOO_XV_TEST)/voltage*4095; // DAC value corresponding to the voltage
dac_load_data_buffer_single(dac_set, RIGHT12, BUSVOOFOO_XVCTL_CHANNEL); // set output so the voltage regulator is set to 2.5V
dac_software_trigger(BUSVOOFOO_XVCTL_CHANNEL); // transfer the value to the DAC
dac_enable(BUSVOOFOO_XVCTL_CHANNEL); // enable DAC
sleep_ms(5); // let voltage settle
voltage = rail_voltage(BUSVOODOO_XV_CHANNEL); // get xV voltage
// check if it matched desired voltage
if (voltage<-BUSVOODOO_XV_TEST-0.2) {
printf("xV voltage is too low: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
} else if (voltage>BUSVOODOO_XV_TEST+0.2) {
printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
dac_disable(BUSVOOFOO_XVCTL_CHANNEL); // disable xV control
gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator
sleep_ms(1); // let voltage settle
// check 12V voltage regulator
gpio_set(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // enable 12V voltage regulator
sleep_ms(1); // let the voltage regulator start and voltage settle
voltage = rail_voltage(BUSVOODOO_12V_CHANNEL); // get 12V voltage
// without being driven it should be around the default voltage
if (voltageBUSVOODOO_12V_DEFAULT+0.3) {
printf("12V voltage is too high: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
// check if we can control 12V voltage regulator
voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get reference voltage
if (isnan(voltage)) {
printf("can get 3V3 rail voltage");
goto error;
}
dac_set = BUSVOODOO_12V_SET(BUSVOODOO_12V_TEST)/voltage*4095; // DAC value corresponding to the voltage
dac_load_data_buffer_single(dac_set, RIGHT12, BUSVOOFOO_12VCTL_CHANNEL); // set output so the voltage regulator is set to desired output voltage
dac_software_trigger(BUSVOOFOO_12VCTL_CHANNEL); // transfer the value to the DAC
dac_enable(BUSVOOFOO_12VCTL_CHANNEL); // enable DAC
sleep_ms(5); // let voltage settle
voltage = rail_voltage(BUSVOODOO_12V_CHANNEL); // get 12V voltage
if (voltage<-BUSVOODOO_12V_TEST-0.3) {
printf("12V voltage is too low: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
} else if (voltage>BUSVOODOO_12V_TEST+0.3) {
printf("12V voltage is too high: %d.%02uV, check 12V voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
dac_disable(BUSVOOFOO_12VCTL_CHANNEL); // disable 12V control
gpio_clear(GPIO(BUSVOODOO_12VEN_PORT), GPIO(BUSVOODOO_12VEN_PIN)); // disable 12V voltage regulator
sleep_ms(1); // let voltage settle
// pull all pins down and ensure they are low
for (uint8_t pin=0; pin5.5) {
printf("5V power rail voltage is too high: %d.%02uV, check USB port\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
for (uint8_t pin=0; pinBUSVOODOO_XV_DEFAULT+0.2) {
printf("xV voltage is too high: %d.%02uV, check xV voltage regulator\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
for (uint8_t pin=0; pin5.5) {
printf("5V power rail voltage is too high: %d.%02uV, check pin 2 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
voltage = rail_voltage(BUSVOODOO_3V3_CHANNEL); // get 3.3V power rail voltage
if (voltage<3.0) {
printf("3.3V power rail voltage is too low: %d.%02uV, check pin 3 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
} else if (voltage>3.6) {
printf("3.3V power rail voltage is too high: %d.%02uV, check pin 3 on I/O connector\n", (int32_t)voltage, (uint32_t)((voltage-(int32_t)voltage)*100));
#if DEBUG
while (true);
#else
goto error;
#endif
}
gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout
to_return = true; // all tests are successful
error:
safe_state(); // set back to safe state
if (!to_return) {
printf("the test procedure has been interrupted for safety reasons\n");
}
return to_return;
}
/** perform tests using external user */
static void test_external(void)
{
safe_state(); // start from safe state with all outputs switched off
// test 5V output on pin 2
printf("check pin 2 on I/O connector to verify 5V output, it should be switched off\n");
if (!wait_space()) {
goto end;
}
gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout
printf("check pin 2 on I/O connector to verify 5V output, it should be switched on\n");
if (!wait_space()) {
goto end;
}
gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout
// test 3.3V output on pin 3
printf("check pin 3 on I/O connector to verify 3.3V output, it should be switched off\n");
if (!wait_space()) {
goto end;
}
gpio_clear(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // enable Vout
printf("check pin 3 on I/O connector to verify 3.3V output, it should be switched on\n");
if (!wait_space()) {
goto end;
}
gpio_set(GPIO(BUSVOODOO_VOUTEN_PORT), GPIO(BUSVOODOO_VOUTEN_PIN)); // disable Vout
// test xV output on pin 4
printf("check pin 4 on I/O connector to verify xV output, it should be switched off\n");
if (!wait_space()) {
goto end;
}
gpio_set(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // enable xV voltage regulator
printf("check pin 4 on I/O connector to verify xV output, it should be switched at 3.4V\n");
if (!wait_space()) {
goto end;
}
gpio_clear(GPIO(BUSVOODOO_XVEN_PORT), GPIO(BUSVOODOO_XVEN_PIN)); // disable xV voltage regulator
// test I/O pins
for (uint8_t io=1; io<=6; io++) { // test each I/O pin
for (uint8_t pin=0; pin0) { // there is a command to process
command[command_i] = 0; // end string
command_i = 0; // prepare for next command
process_command(command); // process user command
}
} else { // user command input
command[command_i] = c; // save command input
if (command_i