From dba5a9fe68ebb996eeee69fa9b573fe5a1561ce2 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Thu, 16 Oct 2014 20:20:20 -0700 Subject: refactor: move test-style examples to ./tests --- examples/test-bkp.cpp | 80 --- examples/test-dac.cpp | 51 -- examples/test-fsmc.cpp | 122 ----- examples/test-print.cpp | 184 ------- examples/test-ring-buffer-insertion.cpp | 114 ---- examples/test-serial-flush.cpp | 38 -- examples/test-serialusb.cpp | 126 ----- examples/test-servo.cpp | 152 ------ examples/test-session.cpp | 938 -------------------------------- examples/test-spi-roundtrip.cpp | 192 ------- examples/test-systick.cpp | 49 -- examples/test-timers.cpp | 537 ------------------ examples/test-usart-dma.cpp | 211 ------- tests/test-bkp.cpp | 80 +++ tests/test-dac.cpp | 51 ++ tests/test-fsmc.cpp | 122 +++++ tests/test-print.cpp | 184 +++++++ tests/test-ring-buffer-insertion.cpp | 114 ++++ tests/test-serial-flush.cpp | 38 ++ tests/test-serialusb.cpp | 126 +++++ tests/test-servo.cpp | 152 ++++++ tests/test-session.cpp | 938 ++++++++++++++++++++++++++++++++ tests/test-spi-roundtrip.cpp | 192 +++++++ tests/test-systick.cpp | 49 ++ tests/test-timers.cpp | 537 ++++++++++++++++++ tests/test-usart-dma.cpp | 211 +++++++ 26 files changed, 2794 insertions(+), 2794 deletions(-) delete mode 100644 examples/test-bkp.cpp delete mode 100644 examples/test-dac.cpp delete mode 100644 examples/test-fsmc.cpp delete mode 100644 examples/test-print.cpp delete mode 100644 examples/test-ring-buffer-insertion.cpp delete mode 100644 examples/test-serial-flush.cpp delete mode 100644 examples/test-serialusb.cpp delete mode 100644 examples/test-servo.cpp delete mode 100644 examples/test-session.cpp delete mode 100644 examples/test-spi-roundtrip.cpp delete mode 100644 examples/test-systick.cpp delete mode 100644 examples/test-timers.cpp delete mode 100644 examples/test-usart-dma.cpp create mode 100644 tests/test-bkp.cpp create mode 100644 tests/test-dac.cpp create mode 100644 tests/test-fsmc.cpp create mode 100644 tests/test-print.cpp create mode 100644 tests/test-ring-buffer-insertion.cpp create mode 100644 tests/test-serial-flush.cpp create mode 100644 tests/test-serialusb.cpp create mode 100644 tests/test-servo.cpp create mode 100644 tests/test-session.cpp create mode 100644 tests/test-spi-roundtrip.cpp create mode 100644 tests/test-systick.cpp create mode 100644 tests/test-timers.cpp create mode 100644 tests/test-usart-dma.cpp diff --git a/examples/test-bkp.cpp b/examples/test-bkp.cpp deleted file mode 100644 index 719cac7..0000000 --- a/examples/test-bkp.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include // for snprintf() - -#include -#include -#include - -void print_bkp_contents(); -void write_to_bkp(uint16 val); - -#define comm Serial2 - -void setup() { - pinMode(BOARD_BUTTON_PIN, INPUT); - - comm.begin(9600); - comm.println("*** Beginning BKP test"); - - comm.println("Init..."); - bkp_init(); - comm.println("Done."); - - print_bkp_contents(); - write_to_bkp(10); - print_bkp_contents(); - - comm.println("Enabling backup writes."); - bkp_enable_writes(); - write_to_bkp(20); - print_bkp_contents(); - - comm.println("Disabling backup writes."); - bkp_disable_writes(); - write_to_bkp(30); - print_bkp_contents(); - - comm.println("Done testing backup registers; press button to enable " - "independent watchdog (in order to cause a reset)."); - waitForButtonPress(0); - iwdg_init(IWDG_PRE_4, 1); - comm.println(); -} - -void loop() { -} - -void print_bkp_contents() { - comm.println("Backup data register contents:"); - char buf[100]; - for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { - snprintf(buf, sizeof buf, "DR%d: %d ", i, bkp_read(i)); - comm.print(buf); - if (i % 5 == 0) comm.println(); - } - comm.println(); -} - -void write_to_bkp(uint16 val) { - comm.print("Attempting to write "); - comm.print(val); - comm.println(" to backup registers..."); - for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { - bkp_write(i, val); - } - comm.println("Done."); -} - -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - init(); - setup(); - - while (1) { - loop(); - } - return 0; -} - diff --git a/examples/test-dac.cpp b/examples/test-dac.cpp deleted file mode 100644 index af188cc..0000000 --- a/examples/test-dac.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Simple DAC test. - * - * Author: Marti Bolivar - * - * This file is released into the public domain. - */ - -#include -#include - -uint16 count = 0; - -void setup() { - pinMode(BOARD_LED_PIN, OUTPUT); - digitalWrite(BOARD_LED_PIN, HIGH); - - Serial1.begin(9600); - Serial1.println("**** Beginning DAC test"); - - Serial1.print("Init... "); - dac_init(DAC, DAC_CH1 | DAC_CH2); - Serial1.println("Done."); -} - -void loop() { - toggleLED(); - delay(100); - - count += 100; - if (count > 4095) { - count = 0; - } - - dac_write_channel(DAC, 1, 4095 - count); - dac_write_channel(DAC, 2, count); -} - -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} - diff --git a/examples/test-fsmc.cpp b/examples/test-fsmc.cpp deleted file mode 100644 index 1621317..0000000 --- a/examples/test-fsmc.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include // for ptrdiff_t - -#include -#include - -#ifndef BOARD_maple_native -#error "Sorry, this example only works on Maple Native." -#endif - -// Start of FSMC SRAM bank 1 -static uint16 *const sram_start = (uint16*)0x60000000; -// End of Maple Native SRAM chip address space (512K 16-bit words) -static uint16 *const sram_end = (uint16*)0x60100000; - -void test_single_write(void); -void test_all_addresses(void); - -void setup() { - pinMode(BOARD_LED_PIN, OUTPUT); - digitalWrite(BOARD_LED_PIN, HIGH); - - SerialUSB.read(); - SerialUSB.println("*** Beginning RAM chip test"); - - test_single_write(); - test_all_addresses(); - - SerialUSB.println("Tests pass, finished."); - SerialUSB.println("***\n"); -} - -void loop() { -} - -void test_single_write() { - uint16 *ptr = sram_start; - uint16 tmp; - - SerialUSB.print("Writing 0x1234... "); - *ptr = 0x1234; - SerialUSB.println("Done."); - - SerialUSB.print("Reading... "); - tmp = *ptr; - SerialUSB.print("Done: 0x"); - SerialUSB.println(tmp, HEX); - - if (tmp != 0x1234) { - SerialUSB.println("Mismatch; abort."); - ASSERT(0); - } -} - -void test_all_addresses() { - uint32 start, end; - uint16 count = 0; - uint16 *ptr; - - SerialUSB.println("Now writing all memory addresses (unrolled loop)"); - start = micros(); - for (ptr = sram_start; ptr < sram_end;) { - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - *ptr++ = count++; - } - end = micros(); - SerialUSB.print("Done. Elapsed time (us): "); - SerialUSB.println(end - start); - - SerialUSB.println("Validating writes."); - for (ptr = sram_start, count = 0; ptr < sram_end; ptr++, count++) { - uint16 value = *ptr; - if (value != count) { - SerialUSB.print("mismatch: 0x"); - SerialUSB.print((uint32)ptr); - SerialUSB.print(" = 0x"); - SerialUSB.print(value, HEX); - SerialUSB.print(", should be 0x"); - SerialUSB.print(count, HEX); - SerialUSB.println("."); - ASSERT(0); - } - } - SerialUSB.println("Done; all writes seem valid."); - - ptrdiff_t nwrites = sram_end - sram_start; - double us_per_write = double(end-start) / double(nwrites); - SerialUSB.print("Number of writes = "); - SerialUSB.print(nwrites); - SerialUSB.print("; avg. time per write = "); - SerialUSB.print(us_per_write); - SerialUSB.print(" us ("); - SerialUSB.print(1 / us_per_write); - SerialUSB.println(" MHz)"); -} - -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - - return 0; -} diff --git a/examples/test-print.cpp b/examples/test-print.cpp deleted file mode 100644 index bdc1894..0000000 --- a/examples/test-print.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * print-test.cpp - * - * Tests the various Print methods. (For USBSerial; assuming that - * writing a single character works, this should generalize to - * HardwareSerial). - * - * This file is released into the public domain. - */ - -#include -#undef min -#undef max - -// For snprintf() -#include -// The that comes with newlib is missing LLONG_MAX, etc. -#include - -using namespace std; - -#define BUF_SIZE 100 -char buf[BUF_SIZE]; - -void test_numbers(void); -void test_base_arithmetic(void); -void test_floating_point(void); - -void print_separator(void); - -void setup() { - while (!SerialUSB.available()) - continue; - SerialUSB.read(); -} - -void loop() { - SerialUSB.println("Testing Print methods."); - print_separator(); - - test_numbers(); - print_separator(); - - test_base_arithmetic(); - print_separator(); - - test_floating_point(); - print_separator(); - - SerialUSB.println("Test finished."); - while (true) { - continue; - } -} - -void test_numbers(void) { - SerialUSB.println("Numeric types:"); - - SerialUSB.print("unsigned char: "); - // prevent Print from treating it as an (extended) ASCII character: - SerialUSB.println((uint32)numeric_limits::max()); - - SerialUSB.print("int: "); - SerialUSB.print(numeric_limits::min()); - SerialUSB.print(" -- "); - SerialUSB.println(numeric_limits::max()); - - SerialUSB.print("unsigned int: "); - SerialUSB.print(numeric_limits::max()); - SerialUSB.println(); - - SerialUSB.print("long: "); - SerialUSB.print(numeric_limits::min()); - SerialUSB.print(" -- "); - SerialUSB.println(numeric_limits::max()); - - SerialUSB.print("long long: "); - SerialUSB.print(numeric_limits::min()); - SerialUSB.print(" -- "); - SerialUSB.println(numeric_limits::max()); - - SerialUSB.print("unsigned long long: "); - SerialUSB.println(numeric_limits::max()); -} - -void base_test(int base) { - SerialUSB.print("\tuint8: "); - SerialUSB.println(numeric_limits::max(), base); - SerialUSB.print("\tint: "); - SerialUSB.print(numeric_limits::max(), base); - SerialUSB.print(", unsigned int: "); - SerialUSB.println(numeric_limits::max(), base); - SerialUSB.print("\tlong: "); - SerialUSB.print(numeric_limits::max(), base); - SerialUSB.print(", unsigned long: "); - SerialUSB.println(numeric_limits::max(), base); - SerialUSB.print("\tlong long: "); - SerialUSB.print(numeric_limits::max(), base); - SerialUSB.print(", unsigned long long: "); - SerialUSB.println(numeric_limits::max(), base); -} - -void test_base_arithmetic(void) { - SerialUSB.println("Base arithmetic:"); - - SerialUSB.println("Binary:"); - base_test(BIN); - - SerialUSB.println("Octal:"); - base_test(OCT); - - SerialUSB.println("Decimal:"); - base_test(DEC); - - SerialUSB.println("Hexadecimal:"); - base_test(HEX); -} - -void test_floating_point(void) { - double dmax = numeric_limits::max(); - - SerialUSB.println("Floating point:"); - - SerialUSB.print("println(-5.67): "); - SerialUSB.print(-5.67); - SerialUSB.print(". println(5.67, 5): "); - SerialUSB.println(5.67, 5); - SerialUSB.print("println((double)(LLONG_MAX - 10)): "); - SerialUSB.print((double)(numeric_limits::max() - 10)); - SerialUSB.print("; from snprintf(): "); - snprintf(buf, BUF_SIZE, "%.2f", - (double)(numeric_limits::max() - 10)); - SerialUSB.println(buf); - SerialUSB.print("println((double)LLONG_MAX / 2): "); - SerialUSB.print((double)(numeric_limits::max()) / 2); - SerialUSB.print("; from snprintf(): "); - snprintf(buf, BUF_SIZE, "%.2f", - (double)(numeric_limits::max()) / 2); - SerialUSB.println(buf); - SerialUSB.print("DBL_MAX: "); - SerialUSB.print(dmax); - SerialUSB.print("; from snprintf(): "); - snprintf(buf, BUF_SIZE, "%g", dmax); - SerialUSB.println(buf); - SerialUSB.print("-DBL_MAX / 2: "); - SerialUSB.print(-dmax / 2.0); - SerialUSB.print("; from snprintf(): "); - snprintf(buf, BUF_SIZE, "%g", -dmax / 2.0); - SerialUSB.println(buf); - SerialUSB.print("Double epsilon, round error: "); - SerialUSB.print(numeric_limits::epsilon()); - SerialUSB.print(", "); - SerialUSB.println(numeric_limits::round_error()); - - SerialUSB.println(); - - float fmax = numeric_limits::max(); - - SerialUSB.print("println(-5.67f): "); - SerialUSB.println(-5.67f); - SerialUSB.print("Float max: "); - SerialUSB.println(fmax); -} - -void print_separator(void) { - SerialUSB.println(); - SerialUSB.println(" ** "); - SerialUSB.println(); -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (1) { - loop(); - } - return 0; -} diff --git a/examples/test-ring-buffer-insertion.cpp b/examples/test-ring-buffer-insertion.cpp deleted file mode 100644 index 2188b03..0000000 --- a/examples/test-ring-buffer-insertion.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Simple ring_buffer test. - * - * Does a basic test of functionality on rb_full_count(), rb_reset(), - * rb_push_insert(), and rb_safe_insert(). - * - * To test: - * - * - Connect a serial monitor to SerialUSB - * - Press any key - * - * This file is released into the public domain. - */ - -#include - -#include - -#define BUF_SIZE 64 -ring_buffer ring_buf; -ring_buffer *rb; -uint8 rb_buffer[BUF_SIZE]; - -void test_rb_push_insert(int num_bytes_to_insert); -void test_rb_safe_insert(int num_bytes_to_insert); -void test_rb_insertion_function(int num_bytes_to_insert, - int (*insertion_fn)(ring_buffer*, uint8), - const char insertion_fn_name[]); -void print_rb_contents(void); - -void setup() { - rb = &ring_buf; - rb_init(rb, BUF_SIZE, rb_buffer); - - while (!SerialUSB.available()) - ; - - SerialUSB.println("Beginning test."); - SerialUSB.println(); -} - -void loop() { - test_rb_push_insert(63); - SerialUSB.println("------------------------------"); - test_rb_push_insert(64); - SerialUSB.println("------------------------------"); - test_rb_safe_insert(63); - SerialUSB.println("------------------------------"); - test_rb_safe_insert(64); - SerialUSB.println("------------------------------"); - - SerialUSB.println(); - SerialUSB.println("Test finished."); - while (true) - ; -} - -void test_rb_push_insert(int num_bytes_to_insert) { - test_rb_insertion_function(num_bytes_to_insert, - rb_push_insert, - "rb_push_insert()"); -} - -void test_rb_safe_insert(int num_bytes_to_insert) { - test_rb_insertion_function(num_bytes_to_insert, - rb_safe_insert, - "rb_safe_insert()"); -} - -void test_rb_insertion_function(int num_bytes_to_insert, - int (*insertion_fn)(ring_buffer *, uint8), - const char insertion_fn_name[]) { - SerialUSB.println("resetting ring buffer."); - rb_reset(rb); - print_rb_contents(); - - SerialUSB.print(insertion_fn_name); - SerialUSB.print("-ing "); - SerialUSB.print(num_bytes_to_insert); - SerialUSB.println(" bytes."); - for (uint8 i = 1; i <= num_bytes_to_insert; i++) - insertion_fn(rb, i); - - uint16 count = rb_full_count(rb); - SerialUSB.print("rb_full_count(rb) = "); - SerialUSB.println(count); - - print_rb_contents(); -} - -void print_rb_contents() { - uint16 count = rb_full_count(rb); - SerialUSB.print("ring buffer contents: "); - for (uint16 i = 0; i < count; i++) { - SerialUSB.print((int)rb_remove(rb)); - if (i < count - 1) SerialUSB.print(", "); - } - SerialUSB.println(); -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-serial-flush.cpp b/examples/test-serial-flush.cpp deleted file mode 100644 index 409d1f9..0000000 --- a/examples/test-serial-flush.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Tests the "flush" Serial function. - */ - -#include - -void setup() { - Serial1.begin(9600); - Serial1.println("Hello world!"); -} - -void loop() { - Serial1.println("Waiting for multiple input..."); - while (Serial1.available() < 5) - ; - Serial1.println(Serial1.read()); - Serial1.println(Serial1.read()); - Serial1.flush(); - - if (Serial1.available()) { - Serial1.println("FAIL! Still had junk in the buffer..."); - } -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-serialusb.cpp b/examples/test-serialusb.cpp deleted file mode 100644 index 098e445..0000000 --- a/examples/test-serialusb.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Tests SerialUSB functionality. - -#include -#include "usb_cdcacm.h" - -#define QUICKPRINT 0 -#define BIGSTUFF 1 -#define NUMBERS 2 -#define SIMPLE 3 -#define ONOFF 4 - -uint32 state = 0; - -void setup() { - /* Set up the LED to blink */ - pinMode(BOARD_LED_PIN, OUTPUT); - - /* Set up Serial2 for use as a debug channel */ - Serial2.begin(9600); - Serial2.println("This is the debug channel. Press any key."); - while (!Serial2.available()) - ; - Serial2.read(); -} - -uint8 c1 = '-'; - -void loop() { - toggleLED(); - delay(1000); - - if (Serial2.available()) { - Serial2.read(); - state++; - } - - switch (state) { - case QUICKPRINT: - for (int i = 0; i < 30; i++) { - usb_cdcacm_putc((char)c1, 1); - SerialUSB.print('.'); - SerialUSB.print('|'); - } - Serial2.println(SerialUSB.pending(), DEC); - SerialUSB.println(); - break; - case BIGSTUFF: - SerialUSB.println("0123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789" - "012345678901234567890"); - SerialUSB.println((int64)123456789, DEC); - SerialUSB.println(3.1415926535); - Serial2.println(SerialUSB.pending(), DEC); - break; - case NUMBERS: - SerialUSB.println("Numbers! -----------------------------"); - Serial2.println("Numbers! -----------------------------"); - SerialUSB.println('1'); - Serial2.println('1'); - SerialUSB.println(1, DEC); - Serial2.println(1, DEC); - SerialUSB.println(-1, DEC); - Serial2.println(-1, DEC); - SerialUSB.println(3.14159265); - Serial2.println(3.14159265); - SerialUSB.println(123456789, DEC); - Serial2.println(123456789, DEC); - SerialUSB.println(-123456789, DEC); - Serial2.println(-123456789, DEC); - SerialUSB.println(65535, HEX); - Serial2.println(65535, HEX); - break; - case SIMPLE: - Serial2.println("Trying write('a')"); - SerialUSB.write('a'); - Serial2.println("Trying write(\"b\")"); - SerialUSB.write("b"); - Serial2.println("Trying print('c')"); - SerialUSB.print('c'); - Serial2.println("Trying print(\"d\")"); - SerialUSB.print("d"); - Serial2.println("Trying print(\"efg\")"); - SerialUSB.print("efg"); - Serial2.println("Trying println(\"hij\\n\\r\")"); - SerialUSB.print("hij\n\r"); - SerialUSB.write(' '); - SerialUSB.println(); - Serial2.println("Trying println(123456789, DEC)"); - SerialUSB.println(123456789, DEC); - Serial2.println("Trying println(3.141592)"); - SerialUSB.println(3.141592); - Serial2.println("Trying println(\"DONE\")"); - SerialUSB.println("DONE"); - break; - case ONOFF: - Serial2.println("Shutting down..."); - SerialUSB.println("Shutting down..."); - SerialUSB.end(); - Serial2.println("Waiting 4 seconds..."); - delay(4000); - Serial2.println("Starting up..."); - SerialUSB.begin(); - SerialUSB.println("Hello World!"); - Serial2.println("Waiting 4 seconds..."); - delay(4000); - state++; - break; - default: - state = 0; - } -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-servo.cpp b/examples/test-servo.cpp deleted file mode 100644 index 6f6e3ba..0000000 --- a/examples/test-servo.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Basic Servo library test program. - * - * Setup: - * - * - Connect a potentiometer to POT_PIN (default pin 15) - * - Connect an oscilloscope to SERVO_PIN1 (default pin 5) and - * SERVO_PIN2 (default pin 6). - * - Connect a serial monitor to SerialUSB - * - * The potentiometer controls the target angle for each of two Servo - * objects, one with angles in [-90, 90], and another in [0, 180]. - * Servo pulse width range is [1000, 2000]. - * - * Serial2 will tell you what inputs it's giving to each servo object, - * and some information it gets back. Pressing the button - * detaches/reattaches the Servo objects. - * - * Tests you should perform: - * - * - Check calculated pulse widths for each servo's target angle - * - Check that calculated pulse widths match actual pulse widths - * - Check that the period of the pulse train is roughly 20 ms - * - Check that the pulses stop when detached, and resume when reattached - * - Check that Servo::write() and Servo::read() round-trip properly - * - * This file is released into the public domain. - */ - -#include - -#include - -#include "libraries/Servo/Servo.h" - -#define POT_PIN 15 - -#define MIN_PW 1000 -#define MAX_PW 2000 - -#define SERVO_PIN1 5 -#define MIN_ANGLE1 0 -#define MAX_ANGLE1 180 - -#define SERVO_PIN2 6 -#define MIN_ANGLE2 (-90) -#define MAX_ANGLE2 90 - -Servo servo1; -Servo servo2; - -#define BUF_SIZE 100 -char buf[BUF_SIZE]; - -#define print_buf(fmt, ...) do { \ - snprintf(buf, BUF_SIZE, fmt, __VA_ARGS__); \ - Serial2.println(buf); } while (0) - -int averageAnalogReads(int); -void attach(); -void detach(); - -void setup() { - pinMode(POT_PIN, INPUT_ANALOG); - pinMode(BOARD_BUTTON_PIN, INPUT); - pinMode(BOARD_LED_PIN, OUTPUT); - - Serial2.begin(9600); - - servo1.attach(SERVO_PIN1, MIN_PW, MAX_PW, MIN_ANGLE1, MAX_ANGLE1); - servo2.attach(SERVO_PIN2, MIN_PW, MAX_PW, MIN_ANGLE2, MAX_ANGLE2); - - ASSERT(servo1.attachedPin() == SERVO_PIN1); - ASSERT(servo2.attachedPin() == SERVO_PIN2); -} - -void loop() { - delay(250); - toggleLED(); - - if (isButtonPressed()) { - if (servo1.attached()) detach(); - else attach(); - } - - if (!servo1.attached()) return; - - int32 average = averageAnalogReads(250); - int16 angle1 = (int16)map(average, 0, 4095, MIN_ANGLE1, MAX_ANGLE1); - int16 angle2 = (int16)map(average, 0, 4095, MIN_ANGLE2, MAX_ANGLE2); - - print_buf("pot reading = %d, angle 1 = %d, angle 2 = %d.", - average, angle1, angle2); - - servo1.write(angle1); - servo2.write(angle2); - - int16 read1 = servo1.read(); - int16 read2 = servo2.read(); - - print_buf("write/read angle 1: %d/%d, angle 2: %d/%d", - angle1, read1, angle2, read2); - - ASSERT(abs(angle1 - read1) <= 1); - ASSERT(abs(angle2 - read2) <= 1); - - print_buf("pulse width 1: %d, pulse width 2: %d", - servo1.readMicroseconds(), servo2.readMicroseconds()); - - Serial2.println("\n--------------------------\n"); -} - -int32 averageAnalogReads(int n) { - uint64 total = 0; - - for (int i = 0; i < n; i++) { - total += analogRead(POT_PIN); - } - - return (int32)(total / n); -} - -void attach() { - Serial2.println("attaching"); - servo1.attach(SERVO_PIN1); - servo2.attach(SERVO_PIN2); - ASSERT(servo1.attachedPin() == SERVO_PIN1); - ASSERT(servo2.attachedPin() == SERVO_PIN2); -} - -void detach() { - Serial2.println("detaching"); - servo1.detach(); - servo2.detach(); - ASSERT(!servo1.attached()); - ASSERT(!servo2.attached()); -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-session.cpp b/examples/test-session.cpp deleted file mode 100644 index 284b4b0..0000000 --- a/examples/test-session.cpp +++ /dev/null @@ -1,938 +0,0 @@ -// Interactive Test Session for LeafLabs Maple -// Copyright (c) 2010 LeafLabs LLC. -// -// Useful for testing Maple features and troubleshooting. -// Communicates over SerialUSB. - -#include - -#include - -// ASCII escape character -#define ESC ((uint8)27) - -// Default USART baud rate -#define BAUD 9600 - -// Number of times to sample a pin per ADC noise measurement -#define N_ADC_NOISE_MEASUREMENTS 40 - -uint8 gpio_state[BOARD_NR_GPIO_PINS]; - -const char* dummy_data = ("123456789012345678901234567890\r\n" - "123456789012345678901234567890\r\n"); - -// Commands -void cmd_print_help(void); -void cmd_adc_stats(void); -void cmd_stressful_adc_stats(void); -void cmd_everything(void); -void cmd_serial1_serial3(void); -void cmd_serial1_echo(void); -void cmd_gpio_monitoring(void); -void cmd_sequential_adc_reads(void); -void cmd_gpio_qa(void); -void cmd_sequential_gpio_toggling(void); -void cmd_gpio_toggling(void); -void cmd_sequential_debug_gpio_toggling(void); -void cmd_debug_gpio_toggling(void); -void cmd_but_test(void); -void cmd_sequential_pwm_test(void); -void cmd_servo_sweep(void); -void cmd_board_info(void); - -// Helper functions -void measure_adc_noise(uint8 pin); -void fast_gpio(int pin); -void serial_baud_test(HardwareSerial **serials, int n, unsigned baud); -void serial_echo_test(HardwareSerial *serial, unsigned baud); -void init_all_timers(uint16 prescale); -void enable_usarts(void); -void disable_usarts(void); -void print_board_array(const char* msg, const uint8 arr[], int len); - -// -- setup() and loop() ------------------------------------------------------ - -void setup() { - // Set up the LED to blink - pinMode(BOARD_LED_PIN, OUTPUT); - - // Start up the serial ports - Serial1.begin(BAUD); - Serial2.begin(BAUD); - Serial3.begin(BAUD); - - // Send a message out over SerialUSB interface - SerialUSB.println(" "); - SerialUSB.println(" __ __ _ _"); - SerialUSB.println(" | \\/ | __ _ _ __ | | ___| |"); - SerialUSB.println(" | |\\/| |/ _` | '_ \\| |/ _ \\ |"); - SerialUSB.println(" | | | | (_| | |_) | | __/_|"); - SerialUSB.println(" |_| |_|\\__,_| .__/|_|\\___(_)"); - SerialUSB.println(" |_|"); - SerialUSB.println(" by leaflabs"); - SerialUSB.println(""); - SerialUSB.println(""); - SerialUSB.println("Maple interactive test program (type '?' for help)"); - SerialUSB.println("-------------------------------------------------------" - "---"); - SerialUSB.print("> "); - -} - -void loop () { - toggleLED(); - delay(250); - - while (SerialUSB.available()) { - uint8 input = SerialUSB.read(); - SerialUSB.println((char)input); - - switch(input) { - case '\r': - break; - - case ' ': - SerialUSB.println("spacebar, nice!"); - break; - - case '?': - case 'h': - cmd_print_help(); - break; - - case 'u': - SerialUSB.println("Hello World!"); - break; - - case 'w': - Serial1.println("Hello World!"); - Serial2.println("Hello World!"); - Serial3.println("Hello World!"); - break; - - case 'm': - cmd_serial1_serial3(); - break; - - case 'E': - cmd_serial1_echo(); - break; - - case '.': - while (!SerialUSB.available()) { - Serial1.print("."); - Serial2.print("."); - Serial3.print("."); - SerialUSB.print("."); - } - break; - - case 'd': - SerialUSB.println("Disabling USB. Press BUT to re-enable."); - SerialUSB.end(); - pinMode(BOARD_BUTTON_PIN, INPUT); - while (!isButtonPressed()) - ; - SerialUSB.begin(); - break; - - case 'n': - cmd_adc_stats(); - break; - - case 'N': - cmd_stressful_adc_stats(); - break; - - case 'e': - cmd_everything(); - break; - - case 'W': - while (!SerialUSB.available()) { - Serial1.print(dummy_data); - Serial2.print(dummy_data); - Serial3.print(dummy_data); - } - break; - - case 'U': { - SerialUSB.println("Dumping data to USB. Press any key."); - int nprints = 0; - int start = millis(); - while (!SerialUSB.available()) { - SerialUSB.print(dummy_data); - nprints++; - } - int elapsed = millis() - start; - SerialUSB.read(); // consume available character - size_t nbytes = nprints * strlen(dummy_data); - SerialUSB.println(); - SerialUSB.print("Sent "); - SerialUSB.print(nbytes); - SerialUSB.print(" bytes ("); - SerialUSB.print((nbytes / (double)elapsed) * (1000.0 / 1024.0)); - SerialUSB.println(" kB/sec)"); - } - break; - - case 'g': - cmd_sequential_gpio_toggling(); - break; - - case 'G': - cmd_gpio_toggling(); - break; - - case 'j': - cmd_sequential_debug_gpio_toggling(); - break; - - case 'J': - cmd_debug_gpio_toggling(); - break; - - case 'B': - cmd_but_test(); - break; - - case 'f': - SerialUSB.println("Wiggling D4 as fast as possible in bursts. " - "Press any key."); - pinMode(4, OUTPUT); - while (!SerialUSB.available()) { - fast_gpio(4); - delay(1); - } - break; - - case 'p': - cmd_sequential_pwm_test(); - break; - - case '_': - SerialUSB.println("Delaying for 5 seconds..."); - delay(5000); - break; - - // Be sure to update cmd_print_help() if you implement these: - - case 't': // TODO - SerialUSB.println("Unimplemented."); - break; - - case 'T': // TODO - SerialUSB.println("Unimplemented."); - break; - - case 's': - cmd_servo_sweep(); - break; - - // Be sure to update cmd_print_help() if you implement these: - - case 'i': // TODO - SerialUSB.println("Unimplemented."); - break; - - case 'I': // TODO - SerialUSB.println("Unimplemented."); - break; - - case 'r': - cmd_gpio_monitoring(); - break; - - case 'a': - cmd_sequential_adc_reads(); - break; - - case 'b': - cmd_board_info(); - break; - - case '+': - cmd_gpio_qa(); - break; - - default: // ------------------------------- - SerialUSB.print("Unexpected byte: 0x"); - SerialUSB.print((int)input, HEX); - SerialUSB.println(", press h for help."); - } - - SerialUSB.print("> "); - } -} - -// -- Commands ---------------------------------------------------------------- - -void cmd_print_help(void) { - SerialUSB.println(""); - SerialUSB.println("Command Listing"); - SerialUSB.println("\t?: print this menu"); - SerialUSB.println("\td: Disable SerialUSB (press button to re-enable)"); - SerialUSB.println("\th: print this menu"); - SerialUSB.println("\tw: print Hello World on all 3 USARTS"); - SerialUSB.println("\tn: measure noise and do statistics"); - SerialUSB.println("\tN: measure noise and do statistics with background " - "stuff"); - SerialUSB.println("\ta: show realtime ADC info"); - SerialUSB.println("\t.: echo '.' until new input"); - SerialUSB.println("\tu: print Hello World on USB"); - SerialUSB.println("\t_: do as little as possible for a couple seconds " - "(delay)"); - SerialUSB.println("\tp: test all PWM channels sequentially"); - SerialUSB.println("\tW: dump data as fast as possible on all 3 USARTS"); - SerialUSB.println("\tU: dump data as fast as possible over USB" - " and measure data rate"); - SerialUSB.println("\tg: toggle GPIOs sequentially"); - SerialUSB.println("\tG: toggle GPIOs at the same time"); - SerialUSB.println("\tj: toggle debug port GPIOs sequentially"); - SerialUSB.println("\tJ: toggle debug port GPIOs simultaneously"); - SerialUSB.println("\tB: test the built-in button"); - SerialUSB.println("\tf: toggle pin 4 as fast as possible in bursts"); - SerialUSB.println("\tr: monitor and print GPIO status changes"); - SerialUSB.println("\ts: output a sweeping servo PWM on all PWM channels"); - SerialUSB.println("\tm: output data on USART1 and USART3 at various " - "baud rates"); - SerialUSB.println("\tE: echo data on USART1 at various baud rates"); - SerialUSB.println("\tb: print information about the board."); - SerialUSB.println("\t+: test shield mode (for quality assurance testing)"); - - SerialUSB.println("Unimplemented:"); - SerialUSB.println("\te: do everything all at once until new input"); - SerialUSB.println("\tt: output a 1khz squarewave on all GPIOs"); - SerialUSB.println("\tT: output a 1hz squarewave on all GPIOs"); - SerialUSB.println("\ti: print out a bunch of info about system state"); - SerialUSB.println("\tI: print out status of all headers"); -} - -void cmd_adc_stats(void) { - SerialUSB.println("Taking ADC noise stats. Press ESC to stop, " - "'R' to repeat same pin, anything else for next pin."); - - uint32 i = 0; - while (i < BOARD_NR_ADC_PINS) { - measure_adc_noise(boardADCPins[i]); - - SerialUSB.println("----------"); - uint8 c = SerialUSB.read(); - if (c == ESC) { - break; - } else if (c != 'r' && c != 'R') { - i++; - } - } -} - -void cmd_stressful_adc_stats(void) { - SerialUSB.println("Taking ADC noise stats under duress. Press ESC to " - "stop, 'R' to repeat same pin, anything else for next " - "pin."); - - uint32 i = 0; - while (i < BOARD_NR_ADC_PINS) { - // use PWM to create digital noise - for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { - if (boardADCPins[i] != boardPWMPins[j]) { - pinMode(boardPWMPins[j], PWM); - pwmWrite(boardPWMPins[j], 1000 + i); - } - } - - measure_adc_noise(boardADCPins[i]); - - // turn off the noise - for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { - if (boardADCPins[i] != boardPWMPins[j]) { - pinMode(boardPWMPins[j], OUTPUT); - digitalWrite(boardPWMPins[j], LOW); - } - } - - SerialUSB.println("----------"); - uint8 c = SerialUSB.read(); - if (c == ESC) { - break; - } else if (c != 'r' && c != 'R') { - i++; - } - } -} - -void cmd_everything(void) { // TODO - // Be sure to update cmd_print_help() if you implement this. - - // print to usart - // print to usb - // toggle gpios - // enable pwm - SerialUSB.println("Unimplemented."); -} - -void cmd_serial1_serial3(void) { - HardwareSerial *serial_1_and_3[] = {&Serial1, &Serial3}; - - SerialUSB.println("Testing 57600 baud on USART1 and USART3. " - "Press any key to stop."); - serial_baud_test(serial_1_and_3, 2, 57600); - SerialUSB.read(); - - SerialUSB.println("Testing 115200 baud on USART1 and USART3. " - "Press any key to stop."); - serial_baud_test(serial_1_and_3, 2, 115200); - SerialUSB.read(); - - SerialUSB.println("Testing 9600 baud on USART1 and USART3. " - "Press any key to stop."); - serial_baud_test(serial_1_and_3, 2, 9600); - SerialUSB.read(); - - SerialUSB.println("Resetting USART1 and USART3..."); - Serial1.begin(BAUD); - Serial3.begin(BAUD); -} - -void cmd_serial1_echo(void) { - SerialUSB.println("Testing serial echo at various baud rates. " - "Press any key for next baud rate, or ESC to quit " - "early."); - while (!SerialUSB.available()) - ; - - if (SerialUSB.read() == ESC) return; - SerialUSB.println("Testing 115200 baud on USART1."); - serial_echo_test(&Serial1, 115200); - - if (SerialUSB.read() == ESC) return; - SerialUSB.println("Testing 57600 baud on USART1."); - serial_echo_test(&Serial1, 57600); - - if (SerialUSB.read() == ESC) return; - SerialUSB.println("Testing 9600 baud on USART1."); - serial_echo_test(&Serial1, 9600); -} - -void cmd_gpio_monitoring(void) { - SerialUSB.println("Monitoring pin state changes. Press any key to stop."); - - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - pinMode(i, INPUT_PULLDOWN); - gpio_state[i] = (uint8)digitalRead(i); - } - - while (!SerialUSB.available()) { - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - - uint8 current_state = (uint8)digitalRead(i); - if (current_state != gpio_state[i]) { - SerialUSB.print("State change on pin "); - SerialUSB.print(i, DEC); - if (current_state) { - SerialUSB.println(":\tHIGH"); - } else { - SerialUSB.println(":\tLOW"); - } - gpio_state[i] = current_state; - } - } - } - - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - pinMode(i, OUTPUT); - } -} - -void cmd_sequential_adc_reads(void) { - SerialUSB.print("Sequentially reading most ADC ports."); - SerialUSB.println("Press any key for next port, or ESC to stop."); - - for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { - if (boardUsesPin(boardADCPins[i])) - continue; - - SerialUSB.print("Reading pin "); - SerialUSB.print(boardADCPins[i], DEC); - SerialUSB.println("..."); - pinMode(boardADCPins[i], INPUT_ANALOG); - while (!SerialUSB.available()) { - int sample = analogRead(boardADCPins[i]); - SerialUSB.print(boardADCPins[i], DEC); - SerialUSB.print("\t"); - SerialUSB.print(sample, DEC); - SerialUSB.print("\t"); - SerialUSB.print("|"); - for (int j = 0; j < 4096; j += 100) { - if (sample >= j) { - SerialUSB.print("#"); - } else { - SerialUSB.print(" "); - } - } - SerialUSB.print("| "); - for (int j = 0; j < 12; j++) { - if (sample & (1 << (11 - j))) { - SerialUSB.print("1"); - } else { - SerialUSB.print("0"); - } - } - SerialUSB.println(); - } - pinMode(boardADCPins[i], OUTPUT); - digitalWrite(boardADCPins[i], 0); - if (SerialUSB.read() == ESC) - break; - } -} - -bool test_single_pin_is_high(int high_pin, const char* err_msg) { - bool ok = true; - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) continue; - - if (digitalRead(i) == HIGH && i != high_pin) { - SerialUSB.println(); - SerialUSB.print("\t*** FAILURE! pin "); - SerialUSB.print(i, DEC); - SerialUSB.print(' '); - SerialUSB.println(err_msg); - ok = false; - } - } - return ok; -} - -bool wait_for_low_transition(uint8 pin) { - uint32 start = millis(); - while (millis() - start < 2000) { - if (digitalRead(pin) == LOW) { - return true; - } - } - return false; -} - -void cmd_gpio_qa(void) { - bool all_pins_ok = true; - const int not_a_pin = -1; - SerialUSB.println("Doing QA testing for unused GPIO pins."); - - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) continue; - - pinMode(i, INPUT); - } - - SerialUSB.println("Waiting to start."); - ASSERT(!boardUsesPin(0)); - while (digitalRead(0) == LOW) continue; - - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) { - SerialUSB.print("Skipping pin "); - SerialUSB.println(i, DEC); - continue; - } - bool pin_ok = true; - SerialUSB.print("Checking pin "); - SerialUSB.print(i, DEC); - while (digitalRead(i) == LOW) continue; - - pin_ok = pin_ok && test_single_pin_is_high(i, "is also HIGH"); - - if (!wait_for_low_transition(i)) { - SerialUSB.println("Transition to low timed out; something is " - "very wrong. Aborting test."); - return; - } - - pin_ok = pin_ok && test_single_pin_is_high(not_a_pin, "is still HIGH"); - - if (pin_ok) { - SerialUSB.println(": ok"); - } - - all_pins_ok = all_pins_ok && pin_ok; - } - - if (all_pins_ok) { - SerialUSB.println("Finished; test passes."); - } else { - SerialUSB.println("**** TEST FAILS *****"); - } - - for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) continue; - - pinMode(i, OUTPUT); - digitalWrite(i, LOW); - gpio_state[i] = 0; - } -} - -void cmd_sequential_gpio_toggling(void) { - SerialUSB.println("Sequentially toggling all unused pins. " - "Press any key for next pin, ESC to stop."); - - for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - - SerialUSB.print("Toggling pin "); - SerialUSB.print((int)i, DEC); - SerialUSB.println("..."); - - pinMode(i, OUTPUT); - do { - togglePin(i); - } while (!SerialUSB.available()); - - digitalWrite(i, LOW); - if (SerialUSB.read() == ESC) - break; - } -} - -void cmd_gpio_toggling(void) { - SerialUSB.println("Toggling all unused pins simultaneously. " - "Press any key to stop."); - - for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - pinMode(i, OUTPUT); - } - - while (!SerialUSB.available()) { - for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - togglePin(i); - } - } - - for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { - if (boardUsesPin(i)) - continue; - digitalWrite(i, LOW); - } -} - -uint8 debugGPIOPins[] = {BOARD_JTMS_SWDIO_PIN, - BOARD_JTCK_SWCLK_PIN, - BOARD_JTDI_PIN, - BOARD_JTDO_PIN, - BOARD_NJTRST_PIN}; - -#define N_DEBUG_PINS 5 - -void cmd_sequential_debug_gpio_toggling(void) { - SerialUSB.println("Toggling all debug (JTAG/SWD) pins sequentially. " - "This will permanently disable debug port " - "functionality."); - disableDebugPorts(); - - for (int i = 0; i < N_DEBUG_PINS; i++) { - pinMode(debugGPIOPins[i], OUTPUT); - } - - for (int i = 0; i < N_DEBUG_PINS; i++) { - int pin = debugGPIOPins[i]; - SerialUSB.print("Toggling pin "); - SerialUSB.print(pin, DEC); - SerialUSB.println("..."); - - pinMode(pin, OUTPUT); - do { - togglePin(pin); - } while (!SerialUSB.available()); - - digitalWrite(pin, LOW); - if (SerialUSB.read() == ESC) - break; - } - - for (int i = 0; i < N_DEBUG_PINS; i++) { - digitalWrite(debugGPIOPins[i], 0); - } -} - -void cmd_debug_gpio_toggling(void) { - SerialUSB.println("Toggling debug GPIO simultaneously. " - "This will permanently disable JTAG and Serial Wire " - "debug port functionality. " - "Press any key to stop."); - disableDebugPorts(); - - for (uint32 i = 0; i < N_DEBUG_PINS; i++) { - pinMode(debugGPIOPins[i], OUTPUT); - } - - while (!SerialUSB.available()) { - for (uint32 i = 0; i < N_DEBUG_PINS; i++) { - togglePin(debugGPIOPins[i]); - } - } - - for (uint32 i = 0; i < N_DEBUG_PINS; i++) { - digitalWrite(debugGPIOPins[i], LOW); - } -} - -void cmd_but_test(void) { - SerialUSB.println("Press the button to test. Press any key to stop."); - pinMode(BOARD_BUTTON_PIN, INPUT); - - while (!SerialUSB.available()) { - if (isButtonPressed()) { - uint32 tstamp = millis(); - SerialUSB.print("Button press detected, timestamp: "); - SerialUSB.println(tstamp); - } - } - SerialUSB.read(); -} - -void cmd_sequential_pwm_test(void) { - SerialUSB.println("Sequentially testing PWM on all unused pins. " - "Press any key for next pin, ESC to stop."); - - for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { - if (boardUsesPin(i)) - continue; - - SerialUSB.print("PWM out on header D"); - SerialUSB.print(boardPWMPins[i], DEC); - SerialUSB.println("..."); - pinMode(boardPWMPins[i], PWM); - pwmWrite(boardPWMPins[i], 16000); - - while (!SerialUSB.available()) { - delay(10); - } - - pinMode(boardPWMPins[i], OUTPUT); - digitalWrite(boardPWMPins[i], 0); - if (SerialUSB.read() == ESC) - break; - } -} - -void cmd_servo_sweep(void) { - SerialUSB.println("Testing all PWM headers with a servo sweep. " - "Press any key to stop."); - SerialUSB.println(); - - disable_usarts(); - init_all_timers(21); - - for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { - if (boardUsesPin(i)) - continue; - pinMode(boardPWMPins[i], PWM); - pwmWrite(boardPWMPins[i], 4000); - } - - // 1.25ms = 4096counts = 0deg - // 1.50ms = 4915counts = 90deg - // 1.75ms = 5734counts = 180deg - int rate = 4096; - while (!SerialUSB.available()) { - rate += 20; - if (rate > 5734) - rate = 4096; - for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { - if (boardUsesPin(i)) - continue; - pwmWrite(boardPWMPins[i], rate); - } - delay(20); - } - - for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { - if (boardUsesPin(i)) - continue; - pinMode(boardPWMPins[i], OUTPUT); - } - init_all_timers(1); - enable_usarts(); -} - -void cmd_board_info(void) { // TODO print more information - SerialUSB.println("Board information"); - SerialUSB.println("================="); - - SerialUSB.print("* Clock speed (MHz): "); - SerialUSB.println(CYCLES_PER_MICROSECOND); - - SerialUSB.print("* BOARD_LED_PIN: "); - SerialUSB.println(BOARD_LED_PIN); - - SerialUSB.print("* BOARD_BUTTON_PIN: "); - SerialUSB.println(BOARD_BUTTON_PIN); - - SerialUSB.print("* GPIO information (BOARD_NR_GPIO_PINS = "); - SerialUSB.print(BOARD_NR_GPIO_PINS); - SerialUSB.println("):"); - print_board_array("ADC pins", boardADCPins, BOARD_NR_ADC_PINS); - print_board_array("PWM pins", boardPWMPins, BOARD_NR_PWM_PINS); - print_board_array("Used pins", boardUsedPins, BOARD_NR_USED_PINS); -} - -// -- Helper functions -------------------------------------------------------- - -void measure_adc_noise(uint8 pin) { - const int N = 1000; - uint16 x; - float mean = 0; - float delta = 0; - float M2 = 0; - pinMode(pin, INPUT_ANALOG); - - // Variance algorithm from Welford, via Knuth, by way of Wikipedia: - // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm - for (int sample = 0; sample < N_ADC_NOISE_MEASUREMENTS; sample++) { - for (int i = 1; i <= N; i++) { - x = analogRead(pin); - delta = x - mean; - mean += delta / i; - M2 = M2 + delta * (x - mean); - } - SerialUSB.print("header: D"); - SerialUSB.print(pin, DEC); - SerialUSB.print("\tn: "); - SerialUSB.print(N, DEC); - SerialUSB.print("\tmean: "); - SerialUSB.print(mean); - SerialUSB.print("\tvariance: "); - SerialUSB.println(M2 / (float)(N-1)); - } - - pinMode(pin, OUTPUT); -} - -void fast_gpio(int maple_pin) { - gpio_dev *dev = PIN_MAP[maple_pin].gpio_device; - uint32 bit = PIN_MAP[maple_pin].gpio_bit; - - gpio_write_bit(dev, bit, 1); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); - gpio_toggle_bit(dev, bit); -} - -void serial_baud_test(HardwareSerial **serials, int n, unsigned baud) { - for (int i = 0; i < n; i++) { - serials[i]->begin(baud); - } - while (!SerialUSB.available()) { - for (int i = 0; i < n; i++) { - serials[i]->println(dummy_data); - if (serials[i]->available()) { - serials[i]->println(serials[i]->read()); - delay(1000); - } - } - } -} - -void serial_echo_test(HardwareSerial *serial, unsigned baud) { - serial->begin(baud); - while (!SerialUSB.available()) { - if (!serial->available()) - continue; - serial->print(serial->read()); - } -} - -static uint16 init_all_timers_prescale = 0; - -static void set_prescale(timer_dev *dev) { - timer_set_prescaler(dev, init_all_timers_prescale); -} - -void init_all_timers(uint16 prescale) { - init_all_timers_prescale = prescale; - timer_foreach(set_prescale); -} - -void enable_usarts(void) { - Serial1.begin(BAUD); - Serial2.begin(BAUD); - Serial3.begin(BAUD); -#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) - Serial4.begin(BAUD); - Serial5.begin(BAUD); -#endif -} - -void disable_usarts(void) { - Serial1.end(); - Serial2.end(); - Serial3.end(); -#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) - Serial4.end(); - Serial5.end(); -#endif -} - -void print_board_array(const char* msg, const uint8 arr[], int len) { - SerialUSB.print("\t"); - SerialUSB.print(msg); - SerialUSB.print(" ("); - SerialUSB.print(len); - SerialUSB.print("): "); - for (int i = 0; i < len; i++) { - SerialUSB.print(arr[i], DEC); - if (i < len - 1) SerialUSB.print(", "); - } - SerialUSB.println(); -} - -// -- premain() and main() ---------------------------------------------------- - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (1) { - loop(); - } - return 0; -} diff --git a/examples/test-spi-roundtrip.cpp b/examples/test-spi-roundtrip.cpp deleted file mode 100644 index ddc9875..0000000 --- a/examples/test-spi-roundtrip.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Polling SPI loopback test. - * - * Bob is nowhere to be found, so Alice decides to talk to herself. - * - * Instructions: Connect SPI2 (Alice) to herself (i.e., MISO to MOSI). - * Connect to Alice via SerialUSB. Press any key to start. - * - * Alice will talk to herself for a little while. The sketch will - * report if Alice can't hear anything she says. She'll then start - * talking forever at various frequencies, bit orders, and modes. Use - * an oscilloscope to make sure she's not trying to lie about any of - * those things. - * - * This file is released into the public domain. - * - * Author: Marti Bolivar - */ - -#include - -HardwareSPI alice(2); - -#define NFREQS 8 -const SPIFrequency spi_freqs[] = { - SPI_140_625KHZ, - SPI_281_250KHZ, - SPI_562_500KHZ, - SPI_1_125MHZ, - SPI_2_25MHZ, - SPI_4_5MHZ, - SPI_9MHZ, - SPI_18MHZ, -}; - -#define TEST_BUF_SIZE 10 -uint8 test_buf[TEST_BUF_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - -void bad_assert(const char* file, int line, const char* exp) { - SerialUSB.println(); - SerialUSB.print("ERROR: FAILED ASSERT("); - SerialUSB.print(exp); - SerialUSB.print("): "); - SerialUSB.print(file); - SerialUSB.print(": "); - SerialUSB.println(line); - throb(); -} - -#undef ASSERT -#define ASSERT(exp) \ - if (exp) { \ - } else { \ - bad_assert(__FILE__, __LINE__, #exp); \ - } - -void haveConversation(uint32 bitOrder); -void soliloquies(uint32 bitOrder); - -void setup() { - pinMode(BOARD_LED_PIN, OUTPUT); - SerialUSB.read(); -} - -void loop() { - SerialUSB.println("** Having a conversation, MSB first"); - haveConversation(MSBFIRST); - - SerialUSB.println("** Having a conversation, LSB first"); - haveConversation(LSBFIRST); - - SerialUSB.println(); - SerialUSB.println("*** All done! It looks like everything worked."); - SerialUSB.println(); - - SerialUSB.println("** Alice will now wax eloquent in various styles. " - "Press any key for the next configuration."); - soliloquies(MSBFIRST); - soliloquies(LSBFIRST); - - while (true) - ; -} - -void printFrequencyString(SPIFrequency frequency); -void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode); - -void haveConversation(uint32 bitOrder) { - for (int f = 0; f < NFREQS; f++) { - for (int mode = 0; mode < 4; mode++) { - chat(spi_freqs[f], bitOrder, mode); - delay(10); - } - } -} - -void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { - SerialUSB.print("Having a chat.\tFrequency: "); - printFrequencyString(frequency); - SerialUSB.print(",\tbitOrder: "); - SerialUSB.print(bitOrder == MSBFIRST ? "MSB" : "LSB"); - SerialUSB.print(",\tmode: "); - SerialUSB.print(mode); - SerialUSB.print("."); - - SerialUSB.print(" [1] "); - alice.begin(frequency, bitOrder, mode); - - SerialUSB.print(" [2] "); - uint32 txed = 0; - while (txed < TEST_BUF_SIZE) { - ASSERT(alice.transfer(test_buf[txed]) == test_buf[txed]); - txed++; - } - - SerialUSB.print(" [3] "); - alice.end(); - - SerialUSB.println(" ok."); -} - -void soliloquy(SPIFrequency freq, uint32 bitOrder, uint32 mode); - -void soliloquies(uint32 bitOrder) { - for (int f = 0; f < NFREQS; f++) { - for (int mode = 0; mode < 4; mode++) { - soliloquy(spi_freqs[f], bitOrder, mode); - } - } -} - -void soliloquy(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { - const uint8 repeat = 0xAE; - SerialUSB.print("Alice is giving a soliloquy (repeating 0x"); - SerialUSB.print(repeat, HEX); - SerialUSB.print("). Frequency: "); - printFrequencyString(frequency); - SerialUSB.print(", bitOrder: "); - SerialUSB.print(bitOrder == MSBFIRST ? "big-endian" : "little-endian"); - SerialUSB.print(", SPI mode: "); - SerialUSB.println(mode); - - alice.begin(frequency, bitOrder, mode); - while (!SerialUSB.available()) { - alice.write(repeat); - delayMicroseconds(200); - } - SerialUSB.read(); -} - -void printFrequencyString(SPIFrequency frequency) { - switch (frequency) { - case SPI_18MHZ: - SerialUSB.print("18 MHz"); - break; - case SPI_9MHZ: - SerialUSB.print("9 MHz"); - break; - case SPI_4_5MHZ: - SerialUSB.print("4.5 MHz"); - break; - case SPI_2_25MHZ: - SerialUSB.print("2.25 MHZ"); - break; - case SPI_1_125MHZ: - SerialUSB.print("1.125 MHz"); - break; - case SPI_562_500KHZ: - SerialUSB.print("562.500 KHz"); - break; - case SPI_281_250KHZ: - SerialUSB.print("281.250 KHz"); - break; - case SPI_140_625KHZ: - SerialUSB.print("140.625 KHz"); - break; - } -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-systick.cpp b/examples/test-systick.cpp deleted file mode 100644 index 356f302..0000000 --- a/examples/test-systick.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Tests the SysTick enable/disable functions - -#include -#include - -void setup() { - pinMode(BOARD_LED_PIN, OUTPUT); - pinMode(BOARD_BUTTON_PIN, INPUT); -} - -bool disable = true; -long time = 0; - -void loop() { - volatile int i = 0; - toggleLED(); - - // An artificial delay - for(i = 0; i < 150000; i++) - ; - - if (isButtonPressed()) { - if (disable) { - systick_disable(); - SerialUSB.println("Disabling SysTick"); - } else { - SerialUSB.println("Re-enabling SysTick"); - systick_enable(); - } - disable = !disable; - } - - SerialUSB.println(millis()); -} - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp deleted file mode 100644 index e646916..0000000 --- a/examples/test-timers.cpp +++ /dev/null @@ -1,537 +0,0 @@ -// -// This is a mostly Wirish-free timer test. Wirish usage is minimized -// because this is a test of the C timer interface in -// , so it's good if it can be made to work even -// when most or all of Wirish is missing. Because of that, you may -// need to customize the following output configuration: -// -// Output is printed: -// - on COMM_USART, -// - via TX pin on port COMM_USART_PORT, bit COMM_USART_TX_BIT -// - via RX pin on port COMM_USART_PORT, bit COMM_USART_RX_BIT -// - at COMM_USART_BAUD baud. -#define COMM_USART USART6 -#define COMM_USART_BAUD 115200 -#define COMM_USART_PORT GPIOG -#define COMM_USART_TX_BIT 14 -#define COMM_USART_RX_BIT 9 -// Other optional configuration below. - -#include -#include -#include -#include -#include -#include - -// -// Configuration -// - -// More output if true -static bool verbose = true; - -// Timers to test -// FIXME use feature test macros for smaller MCUs -static timer_dev *timers[] = { - // Available on all currently supported MCUs - TIMER1, TIMER2, TIMER3, TIMER4, - // Available on F1 (HD and up), F2 - TIMER5, TIMER6, TIMER7, TIMER8, - // Available on F1 (XL), F2 - TIMER9, TIMER10, TIMER11, TIMER12, TIMER13, TIMER14, -}; - -// -// Test routines -// - -typedef void (*timer_test_t)(timer_dev *); - -static void runTest(const char description[], timer_test_t test); -static void runTests(void); - -static void testGetAndSetCount(timer_dev*); -static void testPauseAndResume(timer_dev*); -static void testTimerChannels(timer_dev*); - -// -// Helpers -// - -static void initTimer(timer_dev *dev); -static int timerNumber(timer_dev *dev); -// Hack: a systick-based delay, useful until delay_us() is fixed -static void _delay(uint32 msec); -// Wirish-less USART initialization routine -static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx); -// Return whether or not the timer has capture/compare channel `ch'. -// TODO: does something like this belong in the standard timer library? -static bool timer_has_cc_ch(timer_dev *dev, int ch); - -// Printing routines and variants for verbose mode -static void putstr(const char str[]); -static void println(void); -static void putstrln(const char str[]); -static void putudec(uint32 val); -static void puttimn(timer_dev *dev); -static void v_putstr(const char str[]); -static void v_println(); -static void v_putstrln(const char str[]); -static void v_putudec(uint32 val); -static void v_puttimn(timer_dev *dev); -// Used to visually separate output from different tests -static void printBanner(void); - -// -// Handler state -// - -static int count1 = 0; -static int count2 = 0; -static int count3 = 0; -static int count4 = 0; -static int timer_num; // Current timer we're considering - -// -// Timer capture/compare interrupt handlers -// -// These are shared between timers. The global variable timer_num -// controls which timer they affect. -// - -static void handler1(void); -static void handler2(void); -static void handler3(void); -static void handler4(void); -static voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; - -// -// setup() and loop() -// - -void setup() { - init_usart(COMM_USART, COMM_USART_PORT, - COMM_USART_TX_BIT, COMM_USART_RX_BIT); - _delay(5); - println(); - printBanner(); - putstr("Initializing timers...\r\n"); - timer_foreach(initTimer); - putstr("Done. Running tests.\r\n"); - runTests(); - printBanner(); - putstr("Done testing timers.\r\n"); -} - -void loop() { -} - -// -// Test routine implementations -// - -static void runTests(void) { - runTest("timer_get_count()/timer_set_count()", testGetAndSetCount); - runTest("timer_pause()/timer_resume()", testPauseAndResume); - runTest("capture/compare channels and interrupts", - testTimerChannels); -} - -static void runTest(const char description[], timer_test_t test) { - printBanner(); - putstr("Testing "); - putstr(description); - putstrln("."); - timer_foreach(test); -} - -static void testGetAndSetCount(timer_dev *dev) { - unsigned before, after; - unsigned val_to_set = 1234; - - timer_pause(dev); - before = timer_get_count(dev); - timer_set_count(dev, val_to_set); - after = timer_get_count(dev); - timer_resume(dev); - - if (after != val_to_set) { - puttimn(dev); - putstr(": "); - putstr("*** FAIL: get/set count for "); - puttimn(dev); - putstr("."); - putstr("Start count = "); - putudec(before); - putstr(". Count set to "); - putudec(val_to_set); - putstr(", and now count is = "); - putudec(after); - println(); - } else if (verbose) { - puttimn(dev); - putstr(": "); - putstrln("[ok]"); - } -} - -// This hack works on all currently supported STM32 series, but you -// may need to do something smarter in the future. The assertions -// ensure that our assumptions hold for your target. -static timer_dev *getDifferentTimerOnSameBusAs(timer_dev *dev) { - rcc_clk_domain dev_domain = rcc_dev_clk(dev->clk_id); - ASSERT(RCC_APB1 == dev_domain || RCC_APB2 == dev_domain); - ASSERT(rcc_dev_clk(TIMER1->clk_id) == RCC_APB2); - ASSERT(rcc_dev_clk(TIMER2->clk_id) == RCC_APB1); - ASSERT(rcc_dev_clk(TIMER8->clk_id) == RCC_APB2); - ASSERT(rcc_dev_clk(TIMER3->clk_id) == RCC_APB1); - - if (dev->clk_id == RCC_TIMER1) { - return TIMER8; - } - if (dev->clk_id == RCC_TIMER2) { - return TIMER3; - } - return dev_domain == RCC_APB2 ? TIMER1 : TIMER2; -} - -// Rough test of pause and resume. -// -// Approximately half the time, dev is in the "pause" state and the -// timer doesn't increment, while another timer (`base_dev') on the -// same bus continues. dev and base_dev have identical start counts -// and prescalers. -// -// Since dev and base_dev share a bus (and thus a base clock), and we -// configure them to have the same prescaler and start count, the -// ratio of their end counts should be approximately 1 : 2. We check -// to make sure this is true, up to tolerance `epsilon'. -static void testPauseAndResume(timer_dev *dev) { - timer_dev *base_dev = getDifferentTimerOnSameBusAs(dev); - unsigned start_count = 0, reload = 65535; - // This prescaler should be enough to ensure that we don't - // overflow, while still giving us a reasonably large number of - // timer ticks. - uint16 prescaler = CYCLES_PER_MICROSECOND * 50; - double epsilon = .02; - - if (rcc_dev_clk(base_dev->clk_id) != rcc_dev_clk(dev->clk_id)) { - putstrln("*** ERROR: cannot run test. Bus info is messed up."); - return; - } - - // Pause and set up timers - timer_pause(base_dev); - timer_pause(dev); - timer_set_count(base_dev, start_count); - timer_set_count(dev, start_count); - timer_set_reload(base_dev, reload); - timer_set_reload(dev, reload); - timer_set_prescaler(base_dev, prescaler); - timer_set_prescaler(dev, prescaler); - timer_generate_update(base_dev); - timer_generate_update(dev); - - // Resume the timers and run the test - ASSERT(timer_get_count(base_dev) == start_count); - ASSERT(timer_get_count(dev) == start_count); - timer_resume(base_dev); - timer_resume(dev); - _delay(1000); - timer_pause(dev); - _delay(1000); - timer_pause(base_dev); - - // Check the results - unsigned dev_count = timer_get_count(dev); - unsigned base_count = timer_get_count(base_dev); - double count_ratio = ((double)dev_count / base_count); - bool fail = false; - if (count_ratio > 0.5 + epsilon || count_ratio < 0.5 - epsilon) { - fail = true; - } - if (fail || verbose) { - puttimn(dev); - putstr(" vs. "); - puttimn(base_dev); - putstr(": "); - if (fail) putstr("*** FAIL: "); - else putstr("[ok] "); - putstr("(dev = "); - putudec(dev_count); - putstr(") / (base = "); - putudec(base_count); - putstr(") = "); - // hack hack hack - putudec((int)count_ratio); - count_ratio -= (int)count_ratio; - putstr("."); - int cr_x_100 = (int)(count_ratio * 100); - int hundredths = cr_x_100 % 10; - cr_x_100 /= 10; - int tenths = cr_x_100 % 10; - putudec(tenths); - putudec(hundredths); - println(); - } -} - -// This function touches every capture/compare channel of a given -// timer. The channel counts should be equal within a timer -// regardless of other interrupts on the system (note that this -// doesn't really test timers with only a single capture/compare -// channel; for that, you'll want to do visual inspection of timers -// that share a bus, in verbose mode). -static void testTimerChannels(timer_dev *dev) { - switch (dev->type) { - case TIMER_BASIC: - v_putstr("Skipping basic timer "); - v_puttimn(dev); - v_println(); - return; - case TIMER_ADVANCED: - case TIMER_GENERAL: - // Set up - v_puttimn(dev); - v_println(); - v_putstr("\tchannels: "); - - timer_num = timerNumber(dev); - timer_pause(dev); - count1 = 0; - count2 = 0; - count3 = 0; - count4 = 0; - timer_set_reload(dev, 0xFFFF); - timer_set_prescaler(dev, 1); - for (int c = 1; c <= 4; c++) { - if (timer_has_cc_ch(dev, c)) { - v_putudec(c); - v_putstr("\t"); - timer_set_compare(dev, c, 0xFFFF); - timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); - timer_attach_interrupt(dev, c, handlers[c - 1]); - } - } - v_println(); - - // Run test - timer_generate_update(dev); - timer_resume(dev); - _delay(250); - timer_pause(dev); - - // Print results - v_putstr("\tcounts: "); - bool fail = false; - bool mismatched[4] = {false, false, false, false}; - int counts[4]; - counts[0] = count1; - counts[1] = count2; - counts[2] = count3; - counts[3] = count4; - bool first = true; - int first_count = -1; - for (int c = 1; c <= 4; c++) { - if (timer_has_cc_ch(dev, c)) { - if (first) { - first_count = counts[c - 1]; - first = false; - } - if (!first && (counts[c - 1] != first_count)) { - mismatched[c - 1] = true; - fail = true; - } - v_putudec(counts[c - 1]); - v_putstr("\t"); - } - } - v_println(); - if (fail) { - for (int i = 0; i < 4; i++) { - if (mismatched[i]) { - putstr("*** FAIL: mismatch on "); - puttimn(dev); - putstr(", channel "); - putudec(i + 1); - putstr(": expected "); - putudec(first_count); - putstr(", got "); - putudec(counts[i]); - println(); - } - } - } else { - puttimn(dev); - putstrln(" [ok]"); - } - v_println(); - - // Clean up - for (int c = 1; c <= 4; c++) { - if (timer_has_cc_ch(dev, c)) { - timer_set_mode(dev, c, TIMER_DISABLED); - } - } - break; - } -} - -// -// Helper implementations -// - -static void _delay(uint32 msec) { - uint32 end = systick_uptime() + msec; - while (systick_uptime() < end) - ; -} - -static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx) { - usart_config_gpios_async(dev, gdev, rx, gdev, tx, 0); - usart_init(dev); - usart_set_baud_rate(dev, USART_USE_PCLK, COMM_USART_BAUD); - usart_enable(dev); -} - -static bool timer_has_cc_ch(timer_dev *dev, int ch) { - ASSERT(1 <= ch && ch <= 4); - if (dev->type == TIMER_BASIC) - return false; - int tn = timerNumber(dev); - return (// TIM1-5 and 8 have all four channels - (tn <= 5 || tn == 8) || - // TIM9 and 12 only have channels 1 and 2 - ((tn == 9 || tn == 12) && ch <= 2) || - // All other general purpose timers only have channel 1 - (ch == 1)); -} - -static void putstr(const char str[]) { - usart_putstr(COMM_USART, str); -} - -static void println(void) { - putstr("\r\n"); -} - -static void putstrln(const char str[]) { - putstr(str); - println(); -} - -static void putudec(uint32 val) { - usart_putudec(COMM_USART, val); -} - -static void puttimn(timer_dev *dev) { - putstr("TIM"); - putudec(timerNumber(dev)); -} - -static void v_putstr(const char str[]) { - if (verbose) putstr(str); -} - -static void v_println() { - if (verbose) println(); -} - -__attribute__((unused)) /* (shut up, gcc) */ -static void v_putstrln(const char str[]) { - if (verbose) putstrln(str); -} - -static void v_putudec(uint32 val) { - if (verbose) putudec(val); -} - -static void v_puttimn(timer_dev *dev) { - if (verbose) puttimn(dev); -} - -// Used to visually separate output from different tests -static void printBanner(void) { - putstrln("-----------------------------------------------------"); -} - -static void initTimer(timer_dev *dev) { - v_puttimn(dev); - timer_init(dev); - switch (dev->type) { - case TIMER_ADVANCED: - case TIMER_GENERAL: - v_putstr(" channels "); - for (int c = 1; c <= 4; c++) { - if (timer_has_cc_ch(dev, c)) { - v_putudec(c); - v_putstr(" "); - timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); - } - } - break; - case TIMER_BASIC: - break; - } - v_println(); -} - -static int timerNumber(timer_dev *dev) { - switch (dev->clk_id) { - case RCC_TIMER1: return 1; - case RCC_TIMER2: return 2; - case RCC_TIMER3: return 3; - case RCC_TIMER4: return 4; - case RCC_TIMER5: return 5; - case RCC_TIMER6: return 6; - case RCC_TIMER7: return 7; - case RCC_TIMER8: return 8; - case RCC_TIMER9: return 9; - case RCC_TIMER10: return 10; - case RCC_TIMER11: return 11; - case RCC_TIMER12: return 12; - case RCC_TIMER13: return 13; - case RCC_TIMER14: return 14; - default: - ASSERT(0); - return 0; - } -} - -// -// IRQ Handlers -// - -static void handler1(void) { - count1++; -} - -static void handler2(void) { - count2++; -} - -static void handler3(void) { - count3++; -} - -static void handler4(void) { - count4++; -} - -// -// init() and main() -// - -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/examples/test-usart-dma.cpp b/examples/test-usart-dma.cpp deleted file mode 100644 index d10dc68..0000000 --- a/examples/test-usart-dma.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @file examples/test-usart-dma.cpp - * @author Marti Bolivar - * - * Simple test of DMA used with a USART receiver. - * - * Configures a USART receiver for use with DMA. Received bytes are - * placed into a buffer, with an interrupt firing when the buffer is - * full. At that point, the USART transmitter will print the contents - * of the byte buffer. The buffer is continually filled and refilled - * in this manner. - * - * This example isn't very robust; don't use it in production. In - * particular, since the buffer keeps filling (DMA_CIRC_MODE is set), - * if you keep sending characters after filling the buffer, you'll - * overwrite earlier bytes; this may happen before those earlier bytes - * are done printing. (Typing quickly and seeing how it affects the - * output is a fun way to make sense of how the interrupts and the - * main thread of execution interleave.) - * - * This code is released into the public domain. - */ - -#include -#include -#include - -#include - -/* - * Configuration and state - */ - -// Serial port and DMA configuration. You can change these to suit -// your purposes. -HardwareSerial *serial = &Serial2; -#define USART_DMA_DEV DMA1 -#if STM32_MCU_SERIES == STM32_SERIES_F1 -// On STM32F1 microcontrollers (like what's on Maple and Maple Mini), -// dma tubes are channels. -#define USART_RX_DMA_TUBE DMA_CH6 -#elif (STM32_MCU_SERIES == STM32_SERIES_F2 || \ - STM32_MCU_SERIES == STM32_SERIES_F4) -// On STM32F2 and STM32F4 microcontrollers (Maple 2 will have an F4), -// dma tubes are streams. -#define USART_RX_DMA_TUBE DMA_S5 -#else -#error "unsupported stm32 series" -#endif -// The serial port will make a DMA request each time it receives data. -// This is the dma_request_src we use to tell the DMA tube to handle -// that DMA request. -#define USART_DMA_REQ_SRC DMA_REQ_SRC_USART2_RX -#define BAUD 9600 - -// This will store the DMA configuration for USART RX. -dma_tube_config tube_config; - -// This will store received USART characters. -#define BUF_SIZE 20 -char rx_buf[BUF_SIZE]; - -// The interrupt handler, rx_dma_irq(), sets this to 1. -volatile uint32 irq_fired = 0; -// Used to store DMA interrupt status register (ISR) bits inside -// rx_dma_irq(). This helps explain what's going on inside loop(); see -// comments below. -volatile uint32 isr = 0; - -/* - * Helper functions - */ - -// This is our DMA interrupt handler. -void rx_dma_irq(void) { - irq_fired = 1; - isr = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); -} - -// Configure the USART receiver for use with DMA: -// 1. Turn it on. -// 2. Set the "DMA request on RX" bit in USART_CR3 (USART_CR3_DMAR). -void setup_usart(void) { - serial->begin(BAUD); - usart_dev *serial_dev = serial->c_dev(); - serial_dev->regs->CR3 = USART_CR3_DMAR; -} - -// Set up our dma_tube_config structure. (We could have done this -// above, when we declared tube_config, but having this function makes -// it easier to explain what's going on). -void setup_tube_config(void) { - // We're receiving from the USART data register. serial->c_dev() - // returns a pointer to the libmaple usart_dev for that serial - // port, so this is a pointer to its data register. - tube_config.tube_src = &serial->c_dev()->regs->DR; - // We're only interested in the bottom 8 bits of that data register. - tube_config.tube_src_size = DMA_SIZE_8BITS; - // We're storing to rx_buf. - tube_config.tube_dst = rx_buf; - // rx_buf is a char array, and a "char" takes up 8 bits on STM32. - tube_config.tube_dst_size = DMA_SIZE_8BITS; - // Only fill BUF_SIZE - 1 characters, to leave a null byte at the end. - tube_config.tube_nr_xfers = BUF_SIZE - 1; - // Flags: - // - DMA_CFG_DST_INC so we start at the beginning of rx_buf and - // fill towards the end. - // - DMA_CFG_CIRC so we go back to the beginning and start over when - // rx_buf fills up. - // - DMA_CFG_CMPLT_IE to turn on interrupts on transfer completion. - tube_config.tube_flags = DMA_CFG_DST_INC | DMA_CFG_CIRC | DMA_CFG_CMPLT_IE; - // Target data: none. It's important to set this to NULL if you - // don't have any special (microcontroller-specific) configuration - // in mind, which we don't. - tube_config.target_data = NULL; - // DMA request source. - tube_config.tube_req_src = USART_DMA_REQ_SRC; -} - -// Configure the DMA controller to serve DMA requests from the USART. -void setup_dma_xfer(void) { - // First, turn it on. - dma_init(USART_DMA_DEV); - // Next, configure it by calling dma_tube_cfg(), and check to make - // sure it succeeded. DMA tubes have many restrictions on their - // configuration, and there are configurations which work on some - // types of STM32 but not others. libmaple tries hard to make - // things just work, but checking the return status is important! - int status = dma_tube_cfg(USART_DMA_DEV, USART_RX_DMA_TUBE, &tube_config); - ASSERT(status == DMA_TUBE_CFG_SUCCESS); - // Now we'll perform any other configuration we want. For this - // example, we attach an interrupt handler. - dma_attach_interrupt(USART_DMA_DEV, USART_RX_DMA_TUBE, rx_dma_irq); - // Turn on the DMA tube. It will now begin serving requests. - dma_enable(USART_DMA_DEV, USART_RX_DMA_TUBE); -} - -/* - * setup() and loop() - */ - -void setup(void) { - pinMode(BOARD_LED_PIN, OUTPUT); - setup_tube_config(); - setup_dma_xfer(); - setup_usart(); -} - -void loop(void) { - toggleLED(); - delay(100); - - // See if the interrupt handler got called since the last time we - // checked. - if (irq_fired) { - serial->println("** IRQ **"); - // Notice how the interrupt status register (ISR) bits show - // transfer complete _and_ half-complete here, but the ISR - // bits we print next will be zero. That's because the - // variable "isr" gets set _inside_ rx_dma_irq(). After it - // exits, libmaple cleans up by clearing the tube's ISR - // bits. (If it didn't, and we forgot to, the interrupt would - // repeatedly fire forever.) - serial->print("ISR bits: 0x"); - serial->println(isr, HEX); - irq_fired = 0; - } - - // Print the ISR bits. - // - // Notice that the "transfer half-complete" ISR flag gets set when - // we reach the rx_buf half-way point. This is true even though we - // don't tell the DMA controller to interrupt us on a - // half-complete transfer. That is, the ISR bits get set at the - // right times no matter what; we just don't get interrupted - // unless we asked. (If an error or other problem occurs, the - // relevant ISR bits will get set in the same way). - serial->print("["); - serial->print(millis()); - serial->print("]\tISR bits: 0x"); - uint8 isr_bits = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); - serial->print(isr_bits, HEX); - - // Print the contents of rx_buf. If you keep typing after it fills - // up, the new characters will overwrite the old ones, thanks to - // DMA_CIRC_MODE. - serial->print("\tCharacter buffer contents: '"); - serial->print(rx_buf); - serial->println("'"); - if (isr_bits == 0x7) { - serial->println("** Clearing ISR bits."); - dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); - } -} - -// ------- init() and main() -------------------------------------------------- - -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need libmaple may fail. -__attribute__((constructor)) void premain() { - init(); -} - -int main(void) { - setup(); - - while (true) { - loop(); - } - return 0; -} diff --git a/tests/test-bkp.cpp b/tests/test-bkp.cpp new file mode 100644 index 0000000..719cac7 --- /dev/null +++ b/tests/test-bkp.cpp @@ -0,0 +1,80 @@ +#include // for snprintf() + +#include +#include +#include + +void print_bkp_contents(); +void write_to_bkp(uint16 val); + +#define comm Serial2 + +void setup() { + pinMode(BOARD_BUTTON_PIN, INPUT); + + comm.begin(9600); + comm.println("*** Beginning BKP test"); + + comm.println("Init..."); + bkp_init(); + comm.println("Done."); + + print_bkp_contents(); + write_to_bkp(10); + print_bkp_contents(); + + comm.println("Enabling backup writes."); + bkp_enable_writes(); + write_to_bkp(20); + print_bkp_contents(); + + comm.println("Disabling backup writes."); + bkp_disable_writes(); + write_to_bkp(30); + print_bkp_contents(); + + comm.println("Done testing backup registers; press button to enable " + "independent watchdog (in order to cause a reset)."); + waitForButtonPress(0); + iwdg_init(IWDG_PRE_4, 1); + comm.println(); +} + +void loop() { +} + +void print_bkp_contents() { + comm.println("Backup data register contents:"); + char buf[100]; + for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { + snprintf(buf, sizeof buf, "DR%d: %d ", i, bkp_read(i)); + comm.print(buf); + if (i % 5 == 0) comm.println(); + } + comm.println(); +} + +void write_to_bkp(uint16 val) { + comm.print("Attempting to write "); + comm.print(val); + comm.println(" to backup registers..."); + for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { + bkp_write(i, val); + } + comm.println("Done."); +} + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + init(); + setup(); + + while (1) { + loop(); + } + return 0; +} + diff --git a/tests/test-dac.cpp b/tests/test-dac.cpp new file mode 100644 index 0000000..af188cc --- /dev/null +++ b/tests/test-dac.cpp @@ -0,0 +1,51 @@ +/* + * Simple DAC test. + * + * Author: Marti Bolivar + * + * This file is released into the public domain. + */ + +#include +#include + +uint16 count = 0; + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + digitalWrite(BOARD_LED_PIN, HIGH); + + Serial1.begin(9600); + Serial1.println("**** Beginning DAC test"); + + Serial1.print("Init... "); + dac_init(DAC, DAC_CH1 | DAC_CH2); + Serial1.println("Done."); +} + +void loop() { + toggleLED(); + delay(100); + + count += 100; + if (count > 4095) { + count = 0; + } + + dac_write_channel(DAC, 1, 4095 - count); + dac_write_channel(DAC, 2, count); +} + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} + diff --git a/tests/test-fsmc.cpp b/tests/test-fsmc.cpp new file mode 100644 index 0000000..1621317 --- /dev/null +++ b/tests/test-fsmc.cpp @@ -0,0 +1,122 @@ +#include // for ptrdiff_t + +#include +#include + +#ifndef BOARD_maple_native +#error "Sorry, this example only works on Maple Native." +#endif + +// Start of FSMC SRAM bank 1 +static uint16 *const sram_start = (uint16*)0x60000000; +// End of Maple Native SRAM chip address space (512K 16-bit words) +static uint16 *const sram_end = (uint16*)0x60100000; + +void test_single_write(void); +void test_all_addresses(void); + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + digitalWrite(BOARD_LED_PIN, HIGH); + + SerialUSB.read(); + SerialUSB.println("*** Beginning RAM chip test"); + + test_single_write(); + test_all_addresses(); + + SerialUSB.println("Tests pass, finished."); + SerialUSB.println("***\n"); +} + +void loop() { +} + +void test_single_write() { + uint16 *ptr = sram_start; + uint16 tmp; + + SerialUSB.print("Writing 0x1234... "); + *ptr = 0x1234; + SerialUSB.println("Done."); + + SerialUSB.print("Reading... "); + tmp = *ptr; + SerialUSB.print("Done: 0x"); + SerialUSB.println(tmp, HEX); + + if (tmp != 0x1234) { + SerialUSB.println("Mismatch; abort."); + ASSERT(0); + } +} + +void test_all_addresses() { + uint32 start, end; + uint16 count = 0; + uint16 *ptr; + + SerialUSB.println("Now writing all memory addresses (unrolled loop)"); + start = micros(); + for (ptr = sram_start; ptr < sram_end;) { + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + *ptr++ = count++; + } + end = micros(); + SerialUSB.print("Done. Elapsed time (us): "); + SerialUSB.println(end - start); + + SerialUSB.println("Validating writes."); + for (ptr = sram_start, count = 0; ptr < sram_end; ptr++, count++) { + uint16 value = *ptr; + if (value != count) { + SerialUSB.print("mismatch: 0x"); + SerialUSB.print((uint32)ptr); + SerialUSB.print(" = 0x"); + SerialUSB.print(value, HEX); + SerialUSB.print(", should be 0x"); + SerialUSB.print(count, HEX); + SerialUSB.println("."); + ASSERT(0); + } + } + SerialUSB.println("Done; all writes seem valid."); + + ptrdiff_t nwrites = sram_end - sram_start; + double us_per_write = double(end-start) / double(nwrites); + SerialUSB.print("Number of writes = "); + SerialUSB.print(nwrites); + SerialUSB.print("; avg. time per write = "); + SerialUSB.print(us_per_write); + SerialUSB.print(" us ("); + SerialUSB.print(1 / us_per_write); + SerialUSB.println(" MHz)"); +} + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + + return 0; +} diff --git a/tests/test-print.cpp b/tests/test-print.cpp new file mode 100644 index 0000000..bdc1894 --- /dev/null +++ b/tests/test-print.cpp @@ -0,0 +1,184 @@ +/* + * print-test.cpp + * + * Tests the various Print methods. (For USBSerial; assuming that + * writing a single character works, this should generalize to + * HardwareSerial). + * + * This file is released into the public domain. + */ + +#include +#undef min +#undef max + +// For snprintf() +#include +// The that comes with newlib is missing LLONG_MAX, etc. +#include + +using namespace std; + +#define BUF_SIZE 100 +char buf[BUF_SIZE]; + +void test_numbers(void); +void test_base_arithmetic(void); +void test_floating_point(void); + +void print_separator(void); + +void setup() { + while (!SerialUSB.available()) + continue; + SerialUSB.read(); +} + +void loop() { + SerialUSB.println("Testing Print methods."); + print_separator(); + + test_numbers(); + print_separator(); + + test_base_arithmetic(); + print_separator(); + + test_floating_point(); + print_separator(); + + SerialUSB.println("Test finished."); + while (true) { + continue; + } +} + +void test_numbers(void) { + SerialUSB.println("Numeric types:"); + + SerialUSB.print("unsigned char: "); + // prevent Print from treating it as an (extended) ASCII character: + SerialUSB.println((uint32)numeric_limits::max()); + + SerialUSB.print("int: "); + SerialUSB.print(numeric_limits::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits::max()); + + SerialUSB.print("unsigned int: "); + SerialUSB.print(numeric_limits::max()); + SerialUSB.println(); + + SerialUSB.print("long: "); + SerialUSB.print(numeric_limits::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits::max()); + + SerialUSB.print("long long: "); + SerialUSB.print(numeric_limits::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits::max()); + + SerialUSB.print("unsigned long long: "); + SerialUSB.println(numeric_limits::max()); +} + +void base_test(int base) { + SerialUSB.print("\tuint8: "); + SerialUSB.println(numeric_limits::max(), base); + SerialUSB.print("\tint: "); + SerialUSB.print(numeric_limits::max(), base); + SerialUSB.print(", unsigned int: "); + SerialUSB.println(numeric_limits::max(), base); + SerialUSB.print("\tlong: "); + SerialUSB.print(numeric_limits::max(), base); + SerialUSB.print(", unsigned long: "); + SerialUSB.println(numeric_limits::max(), base); + SerialUSB.print("\tlong long: "); + SerialUSB.print(numeric_limits::max(), base); + SerialUSB.print(", unsigned long long: "); + SerialUSB.println(numeric_limits::max(), base); +} + +void test_base_arithmetic(void) { + SerialUSB.println("Base arithmetic:"); + + SerialUSB.println("Binary:"); + base_test(BIN); + + SerialUSB.println("Octal:"); + base_test(OCT); + + SerialUSB.println("Decimal:"); + base_test(DEC); + + SerialUSB.println("Hexadecimal:"); + base_test(HEX); +} + +void test_floating_point(void) { + double dmax = numeric_limits::max(); + + SerialUSB.println("Floating point:"); + + SerialUSB.print("println(-5.67): "); + SerialUSB.print(-5.67); + SerialUSB.print(". println(5.67, 5): "); + SerialUSB.println(5.67, 5); + SerialUSB.print("println((double)(LLONG_MAX - 10)): "); + SerialUSB.print((double)(numeric_limits::max() - 10)); + SerialUSB.print("; from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits::max() - 10)); + SerialUSB.println(buf); + SerialUSB.print("println((double)LLONG_MAX / 2): "); + SerialUSB.print((double)(numeric_limits::max()) / 2); + SerialUSB.print("; from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits::max()) / 2); + SerialUSB.println(buf); + SerialUSB.print("DBL_MAX: "); + SerialUSB.print(dmax); + SerialUSB.print("; from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", dmax); + SerialUSB.println(buf); + SerialUSB.print("-DBL_MAX / 2: "); + SerialUSB.print(-dmax / 2.0); + SerialUSB.print("; from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", -dmax / 2.0); + SerialUSB.println(buf); + SerialUSB.print("Double epsilon, round error: "); + SerialUSB.print(numeric_limits::epsilon()); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits::round_error()); + + SerialUSB.println(); + + float fmax = numeric_limits::max(); + + SerialUSB.print("println(-5.67f): "); + SerialUSB.println(-5.67f); + SerialUSB.print("Float max: "); + SerialUSB.println(fmax); +} + +void print_separator(void) { + SerialUSB.println(); + SerialUSB.println(" ** "); + SerialUSB.println(); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (1) { + loop(); + } + return 0; +} diff --git a/tests/test-ring-buffer-insertion.cpp b/tests/test-ring-buffer-insertion.cpp new file mode 100644 index 0000000..2188b03 --- /dev/null +++ b/tests/test-ring-buffer-insertion.cpp @@ -0,0 +1,114 @@ +/* + * Simple ring_buffer test. + * + * Does a basic test of functionality on rb_full_count(), rb_reset(), + * rb_push_insert(), and rb_safe_insert(). + * + * To test: + * + * - Connect a serial monitor to SerialUSB + * - Press any key + * + * This file is released into the public domain. + */ + +#include + +#include + +#define BUF_SIZE 64 +ring_buffer ring_buf; +ring_buffer *rb; +uint8 rb_buffer[BUF_SIZE]; + +void test_rb_push_insert(int num_bytes_to_insert); +void test_rb_safe_insert(int num_bytes_to_insert); +void test_rb_insertion_function(int num_bytes_to_insert, + int (*insertion_fn)(ring_buffer*, uint8), + const char insertion_fn_name[]); +void print_rb_contents(void); + +void setup() { + rb = &ring_buf; + rb_init(rb, BUF_SIZE, rb_buffer); + + while (!SerialUSB.available()) + ; + + SerialUSB.println("Beginning test."); + SerialUSB.println(); +} + +void loop() { + test_rb_push_insert(63); + SerialUSB.println("------------------------------"); + test_rb_push_insert(64); + SerialUSB.println("------------------------------"); + test_rb_safe_insert(63); + SerialUSB.println("------------------------------"); + test_rb_safe_insert(64); + SerialUSB.println("------------------------------"); + + SerialUSB.println(); + SerialUSB.println("Test finished."); + while (true) + ; +} + +void test_rb_push_insert(int num_bytes_to_insert) { + test_rb_insertion_function(num_bytes_to_insert, + rb_push_insert, + "rb_push_insert()"); +} + +void test_rb_safe_insert(int num_bytes_to_insert) { + test_rb_insertion_function(num_bytes_to_insert, + rb_safe_insert, + "rb_safe_insert()"); +} + +void test_rb_insertion_function(int num_bytes_to_insert, + int (*insertion_fn)(ring_buffer *, uint8), + const char insertion_fn_name[]) { + SerialUSB.println("resetting ring buffer."); + rb_reset(rb); + print_rb_contents(); + + SerialUSB.print(insertion_fn_name); + SerialUSB.print("-ing "); + SerialUSB.print(num_bytes_to_insert); + SerialUSB.println(" bytes."); + for (uint8 i = 1; i <= num_bytes_to_insert; i++) + insertion_fn(rb, i); + + uint16 count = rb_full_count(rb); + SerialUSB.print("rb_full_count(rb) = "); + SerialUSB.println(count); + + print_rb_contents(); +} + +void print_rb_contents() { + uint16 count = rb_full_count(rb); + SerialUSB.print("ring buffer contents: "); + for (uint16 i = 0; i < count; i++) { + SerialUSB.print((int)rb_remove(rb)); + if (i < count - 1) SerialUSB.print(", "); + } + SerialUSB.println(); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-serial-flush.cpp b/tests/test-serial-flush.cpp new file mode 100644 index 0000000..409d1f9 --- /dev/null +++ b/tests/test-serial-flush.cpp @@ -0,0 +1,38 @@ +/* + * Tests the "flush" Serial function. + */ + +#include + +void setup() { + Serial1.begin(9600); + Serial1.println("Hello world!"); +} + +void loop() { + Serial1.println("Waiting for multiple input..."); + while (Serial1.available() < 5) + ; + Serial1.println(Serial1.read()); + Serial1.println(Serial1.read()); + Serial1.flush(); + + if (Serial1.available()) { + Serial1.println("FAIL! Still had junk in the buffer..."); + } +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-serialusb.cpp b/tests/test-serialusb.cpp new file mode 100644 index 0000000..098e445 --- /dev/null +++ b/tests/test-serialusb.cpp @@ -0,0 +1,126 @@ +// Tests SerialUSB functionality. + +#include +#include "usb_cdcacm.h" + +#define QUICKPRINT 0 +#define BIGSTUFF 1 +#define NUMBERS 2 +#define SIMPLE 3 +#define ONOFF 4 + +uint32 state = 0; + +void setup() { + /* Set up the LED to blink */ + pinMode(BOARD_LED_PIN, OUTPUT); + + /* Set up Serial2 for use as a debug channel */ + Serial2.begin(9600); + Serial2.println("This is the debug channel. Press any key."); + while (!Serial2.available()) + ; + Serial2.read(); +} + +uint8 c1 = '-'; + +void loop() { + toggleLED(); + delay(1000); + + if (Serial2.available()) { + Serial2.read(); + state++; + } + + switch (state) { + case QUICKPRINT: + for (int i = 0; i < 30; i++) { + usb_cdcacm_putc((char)c1, 1); + SerialUSB.print('.'); + SerialUSB.print('|'); + } + Serial2.println(SerialUSB.pending(), DEC); + SerialUSB.println(); + break; + case BIGSTUFF: + SerialUSB.println("0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "012345678901234567890"); + SerialUSB.println((int64)123456789, DEC); + SerialUSB.println(3.1415926535); + Serial2.println(SerialUSB.pending(), DEC); + break; + case NUMBERS: + SerialUSB.println("Numbers! -----------------------------"); + Serial2.println("Numbers! -----------------------------"); + SerialUSB.println('1'); + Serial2.println('1'); + SerialUSB.println(1, DEC); + Serial2.println(1, DEC); + SerialUSB.println(-1, DEC); + Serial2.println(-1, DEC); + SerialUSB.println(3.14159265); + Serial2.println(3.14159265); + SerialUSB.println(123456789, DEC); + Serial2.println(123456789, DEC); + SerialUSB.println(-123456789, DEC); + Serial2.println(-123456789, DEC); + SerialUSB.println(65535, HEX); + Serial2.println(65535, HEX); + break; + case SIMPLE: + Serial2.println("Trying write('a')"); + SerialUSB.write('a'); + Serial2.println("Trying write(\"b\")"); + SerialUSB.write("b"); + Serial2.println("Trying print('c')"); + SerialUSB.print('c'); + Serial2.println("Trying print(\"d\")"); + SerialUSB.print("d"); + Serial2.println("Trying print(\"efg\")"); + SerialUSB.print("efg"); + Serial2.println("Trying println(\"hij\\n\\r\")"); + SerialUSB.print("hij\n\r"); + SerialUSB.write(' '); + SerialUSB.println(); + Serial2.println("Trying println(123456789, DEC)"); + SerialUSB.println(123456789, DEC); + Serial2.println("Trying println(3.141592)"); + SerialUSB.println(3.141592); + Serial2.println("Trying println(\"DONE\")"); + SerialUSB.println("DONE"); + break; + case ONOFF: + Serial2.println("Shutting down..."); + SerialUSB.println("Shutting down..."); + SerialUSB.end(); + Serial2.println("Waiting 4 seconds..."); + delay(4000); + Serial2.println("Starting up..."); + SerialUSB.begin(); + SerialUSB.println("Hello World!"); + Serial2.println("Waiting 4 seconds..."); + delay(4000); + state++; + break; + default: + state = 0; + } +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-servo.cpp b/tests/test-servo.cpp new file mode 100644 index 0000000..6f6e3ba --- /dev/null +++ b/tests/test-servo.cpp @@ -0,0 +1,152 @@ +/* + * Basic Servo library test program. + * + * Setup: + * + * - Connect a potentiometer to POT_PIN (default pin 15) + * - Connect an oscilloscope to SERVO_PIN1 (default pin 5) and + * SERVO_PIN2 (default pin 6). + * - Connect a serial monitor to SerialUSB + * + * The potentiometer controls the target angle for each of two Servo + * objects, one with angles in [-90, 90], and another in [0, 180]. + * Servo pulse width range is [1000, 2000]. + * + * Serial2 will tell you what inputs it's giving to each servo object, + * and some information it gets back. Pressing the button + * detaches/reattaches the Servo objects. + * + * Tests you should perform: + * + * - Check calculated pulse widths for each servo's target angle + * - Check that calculated pulse widths match actual pulse widths + * - Check that the period of the pulse train is roughly 20 ms + * - Check that the pulses stop when detached, and resume when reattached + * - Check that Servo::write() and Servo::read() round-trip properly + * + * This file is released into the public domain. + */ + +#include + +#include + +#include "libraries/Servo/Servo.h" + +#define POT_PIN 15 + +#define MIN_PW 1000 +#define MAX_PW 2000 + +#define SERVO_PIN1 5 +#define MIN_ANGLE1 0 +#define MAX_ANGLE1 180 + +#define SERVO_PIN2 6 +#define MIN_ANGLE2 (-90) +#define MAX_ANGLE2 90 + +Servo servo1; +Servo servo2; + +#define BUF_SIZE 100 +char buf[BUF_SIZE]; + +#define print_buf(fmt, ...) do { \ + snprintf(buf, BUF_SIZE, fmt, __VA_ARGS__); \ + Serial2.println(buf); } while (0) + +int averageAnalogReads(int); +void attach(); +void detach(); + +void setup() { + pinMode(POT_PIN, INPUT_ANALOG); + pinMode(BOARD_BUTTON_PIN, INPUT); + pinMode(BOARD_LED_PIN, OUTPUT); + + Serial2.begin(9600); + + servo1.attach(SERVO_PIN1, MIN_PW, MAX_PW, MIN_ANGLE1, MAX_ANGLE1); + servo2.attach(SERVO_PIN2, MIN_PW, MAX_PW, MIN_ANGLE2, MAX_ANGLE2); + + ASSERT(servo1.attachedPin() == SERVO_PIN1); + ASSERT(servo2.attachedPin() == SERVO_PIN2); +} + +void loop() { + delay(250); + toggleLED(); + + if (isButtonPressed()) { + if (servo1.attached()) detach(); + else attach(); + } + + if (!servo1.attached()) return; + + int32 average = averageAnalogReads(250); + int16 angle1 = (int16)map(average, 0, 4095, MIN_ANGLE1, MAX_ANGLE1); + int16 angle2 = (int16)map(average, 0, 4095, MIN_ANGLE2, MAX_ANGLE2); + + print_buf("pot reading = %d, angle 1 = %d, angle 2 = %d.", + average, angle1, angle2); + + servo1.write(angle1); + servo2.write(angle2); + + int16 read1 = servo1.read(); + int16 read2 = servo2.read(); + + print_buf("write/read angle 1: %d/%d, angle 2: %d/%d", + angle1, read1, angle2, read2); + + ASSERT(abs(angle1 - read1) <= 1); + ASSERT(abs(angle2 - read2) <= 1); + + print_buf("pulse width 1: %d, pulse width 2: %d", + servo1.readMicroseconds(), servo2.readMicroseconds()); + + Serial2.println("\n--------------------------\n"); +} + +int32 averageAnalogReads(int n) { + uint64 total = 0; + + for (int i = 0; i < n; i++) { + total += analogRead(POT_PIN); + } + + return (int32)(total / n); +} + +void attach() { + Serial2.println("attaching"); + servo1.attach(SERVO_PIN1); + servo2.attach(SERVO_PIN2); + ASSERT(servo1.attachedPin() == SERVO_PIN1); + ASSERT(servo2.attachedPin() == SERVO_PIN2); +} + +void detach() { + Serial2.println("detaching"); + servo1.detach(); + servo2.detach(); + ASSERT(!servo1.attached()); + ASSERT(!servo2.attached()); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-session.cpp b/tests/test-session.cpp new file mode 100644 index 0000000..284b4b0 --- /dev/null +++ b/tests/test-session.cpp @@ -0,0 +1,938 @@ +// Interactive Test Session for LeafLabs Maple +// Copyright (c) 2010 LeafLabs LLC. +// +// Useful for testing Maple features and troubleshooting. +// Communicates over SerialUSB. + +#include + +#include + +// ASCII escape character +#define ESC ((uint8)27) + +// Default USART baud rate +#define BAUD 9600 + +// Number of times to sample a pin per ADC noise measurement +#define N_ADC_NOISE_MEASUREMENTS 40 + +uint8 gpio_state[BOARD_NR_GPIO_PINS]; + +const char* dummy_data = ("123456789012345678901234567890\r\n" + "123456789012345678901234567890\r\n"); + +// Commands +void cmd_print_help(void); +void cmd_adc_stats(void); +void cmd_stressful_adc_stats(void); +void cmd_everything(void); +void cmd_serial1_serial3(void); +void cmd_serial1_echo(void); +void cmd_gpio_monitoring(void); +void cmd_sequential_adc_reads(void); +void cmd_gpio_qa(void); +void cmd_sequential_gpio_toggling(void); +void cmd_gpio_toggling(void); +void cmd_sequential_debug_gpio_toggling(void); +void cmd_debug_gpio_toggling(void); +void cmd_but_test(void); +void cmd_sequential_pwm_test(void); +void cmd_servo_sweep(void); +void cmd_board_info(void); + +// Helper functions +void measure_adc_noise(uint8 pin); +void fast_gpio(int pin); +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud); +void serial_echo_test(HardwareSerial *serial, unsigned baud); +void init_all_timers(uint16 prescale); +void enable_usarts(void); +void disable_usarts(void); +void print_board_array(const char* msg, const uint8 arr[], int len); + +// -- setup() and loop() ------------------------------------------------------ + +void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); + + // Start up the serial ports + Serial1.begin(BAUD); + Serial2.begin(BAUD); + Serial3.begin(BAUD); + + // Send a message out over SerialUSB interface + SerialUSB.println(" "); + SerialUSB.println(" __ __ _ _"); + SerialUSB.println(" | \\/ | __ _ _ __ | | ___| |"); + SerialUSB.println(" | |\\/| |/ _` | '_ \\| |/ _ \\ |"); + SerialUSB.println(" | | | | (_| | |_) | | __/_|"); + SerialUSB.println(" |_| |_|\\__,_| .__/|_|\\___(_)"); + SerialUSB.println(" |_|"); + SerialUSB.println(" by leaflabs"); + SerialUSB.println(""); + SerialUSB.println(""); + SerialUSB.println("Maple interactive test program (type '?' for help)"); + SerialUSB.println("-------------------------------------------------------" + "---"); + SerialUSB.print("> "); + +} + +void loop () { + toggleLED(); + delay(250); + + while (SerialUSB.available()) { + uint8 input = SerialUSB.read(); + SerialUSB.println((char)input); + + switch(input) { + case '\r': + break; + + case ' ': + SerialUSB.println("spacebar, nice!"); + break; + + case '?': + case 'h': + cmd_print_help(); + break; + + case 'u': + SerialUSB.println("Hello World!"); + break; + + case 'w': + Serial1.println("Hello World!"); + Serial2.println("Hello World!"); + Serial3.println("Hello World!"); + break; + + case 'm': + cmd_serial1_serial3(); + break; + + case 'E': + cmd_serial1_echo(); + break; + + case '.': + while (!SerialUSB.available()) { + Serial1.print("."); + Serial2.print("."); + Serial3.print("."); + SerialUSB.print("."); + } + break; + + case 'd': + SerialUSB.println("Disabling USB. Press BUT to re-enable."); + SerialUSB.end(); + pinMode(BOARD_BUTTON_PIN, INPUT); + while (!isButtonPressed()) + ; + SerialUSB.begin(); + break; + + case 'n': + cmd_adc_stats(); + break; + + case 'N': + cmd_stressful_adc_stats(); + break; + + case 'e': + cmd_everything(); + break; + + case 'W': + while (!SerialUSB.available()) { + Serial1.print(dummy_data); + Serial2.print(dummy_data); + Serial3.print(dummy_data); + } + break; + + case 'U': { + SerialUSB.println("Dumping data to USB. Press any key."); + int nprints = 0; + int start = millis(); + while (!SerialUSB.available()) { + SerialUSB.print(dummy_data); + nprints++; + } + int elapsed = millis() - start; + SerialUSB.read(); // consume available character + size_t nbytes = nprints * strlen(dummy_data); + SerialUSB.println(); + SerialUSB.print("Sent "); + SerialUSB.print(nbytes); + SerialUSB.print(" bytes ("); + SerialUSB.print((nbytes / (double)elapsed) * (1000.0 / 1024.0)); + SerialUSB.println(" kB/sec)"); + } + break; + + case 'g': + cmd_sequential_gpio_toggling(); + break; + + case 'G': + cmd_gpio_toggling(); + break; + + case 'j': + cmd_sequential_debug_gpio_toggling(); + break; + + case 'J': + cmd_debug_gpio_toggling(); + break; + + case 'B': + cmd_but_test(); + break; + + case 'f': + SerialUSB.println("Wiggling D4 as fast as possible in bursts. " + "Press any key."); + pinMode(4, OUTPUT); + while (!SerialUSB.available()) { + fast_gpio(4); + delay(1); + } + break; + + case 'p': + cmd_sequential_pwm_test(); + break; + + case '_': + SerialUSB.println("Delaying for 5 seconds..."); + delay(5000); + break; + + // Be sure to update cmd_print_help() if you implement these: + + case 't': // TODO + SerialUSB.println("Unimplemented."); + break; + + case 'T': // TODO + SerialUSB.println("Unimplemented."); + break; + + case 's': + cmd_servo_sweep(); + break; + + // Be sure to update cmd_print_help() if you implement these: + + case 'i': // TODO + SerialUSB.println("Unimplemented."); + break; + + case 'I': // TODO + SerialUSB.println("Unimplemented."); + break; + + case 'r': + cmd_gpio_monitoring(); + break; + + case 'a': + cmd_sequential_adc_reads(); + break; + + case 'b': + cmd_board_info(); + break; + + case '+': + cmd_gpio_qa(); + break; + + default: // ------------------------------- + SerialUSB.print("Unexpected byte: 0x"); + SerialUSB.print((int)input, HEX); + SerialUSB.println(", press h for help."); + } + + SerialUSB.print("> "); + } +} + +// -- Commands ---------------------------------------------------------------- + +void cmd_print_help(void) { + SerialUSB.println(""); + SerialUSB.println("Command Listing"); + SerialUSB.println("\t?: print this menu"); + SerialUSB.println("\td: Disable SerialUSB (press button to re-enable)"); + SerialUSB.println("\th: print this menu"); + SerialUSB.println("\tw: print Hello World on all 3 USARTS"); + SerialUSB.println("\tn: measure noise and do statistics"); + SerialUSB.println("\tN: measure noise and do statistics with background " + "stuff"); + SerialUSB.println("\ta: show realtime ADC info"); + SerialUSB.println("\t.: echo '.' until new input"); + SerialUSB.println("\tu: print Hello World on USB"); + SerialUSB.println("\t_: do as little as possible for a couple seconds " + "(delay)"); + SerialUSB.println("\tp: test all PWM channels sequentially"); + SerialUSB.println("\tW: dump data as fast as possible on all 3 USARTS"); + SerialUSB.println("\tU: dump data as fast as possible over USB" + " and measure data rate"); + SerialUSB.println("\tg: toggle GPIOs sequentially"); + SerialUSB.println("\tG: toggle GPIOs at the same time"); + SerialUSB.println("\tj: toggle debug port GPIOs sequentially"); + SerialUSB.println("\tJ: toggle debug port GPIOs simultaneously"); + SerialUSB.println("\tB: test the built-in button"); + SerialUSB.println("\tf: toggle pin 4 as fast as possible in bursts"); + SerialUSB.println("\tr: monitor and print GPIO status changes"); + SerialUSB.println("\ts: output a sweeping servo PWM on all PWM channels"); + SerialUSB.println("\tm: output data on USART1 and USART3 at various " + "baud rates"); + SerialUSB.println("\tE: echo data on USART1 at various baud rates"); + SerialUSB.println("\tb: print information about the board."); + SerialUSB.println("\t+: test shield mode (for quality assurance testing)"); + + SerialUSB.println("Unimplemented:"); + SerialUSB.println("\te: do everything all at once until new input"); + SerialUSB.println("\tt: output a 1khz squarewave on all GPIOs"); + SerialUSB.println("\tT: output a 1hz squarewave on all GPIOs"); + SerialUSB.println("\ti: print out a bunch of info about system state"); + SerialUSB.println("\tI: print out status of all headers"); +} + +void cmd_adc_stats(void) { + SerialUSB.println("Taking ADC noise stats. Press ESC to stop, " + "'R' to repeat same pin, anything else for next pin."); + + uint32 i = 0; + while (i < BOARD_NR_ADC_PINS) { + measure_adc_noise(boardADCPins[i]); + + SerialUSB.println("----------"); + uint8 c = SerialUSB.read(); + if (c == ESC) { + break; + } else if (c != 'r' && c != 'R') { + i++; + } + } +} + +void cmd_stressful_adc_stats(void) { + SerialUSB.println("Taking ADC noise stats under duress. Press ESC to " + "stop, 'R' to repeat same pin, anything else for next " + "pin."); + + uint32 i = 0; + while (i < BOARD_NR_ADC_PINS) { + // use PWM to create digital noise + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], PWM); + pwmWrite(boardPWMPins[j], 1000 + i); + } + } + + measure_adc_noise(boardADCPins[i]); + + // turn off the noise + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], OUTPUT); + digitalWrite(boardPWMPins[j], LOW); + } + } + + SerialUSB.println("----------"); + uint8 c = SerialUSB.read(); + if (c == ESC) { + break; + } else if (c != 'r' && c != 'R') { + i++; + } + } +} + +void cmd_everything(void) { // TODO + // Be sure to update cmd_print_help() if you implement this. + + // print to usart + // print to usb + // toggle gpios + // enable pwm + SerialUSB.println("Unimplemented."); +} + +void cmd_serial1_serial3(void) { + HardwareSerial *serial_1_and_3[] = {&Serial1, &Serial3}; + + SerialUSB.println("Testing 57600 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 57600); + SerialUSB.read(); + + SerialUSB.println("Testing 115200 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 115200); + SerialUSB.read(); + + SerialUSB.println("Testing 9600 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 9600); + SerialUSB.read(); + + SerialUSB.println("Resetting USART1 and USART3..."); + Serial1.begin(BAUD); + Serial3.begin(BAUD); +} + +void cmd_serial1_echo(void) { + SerialUSB.println("Testing serial echo at various baud rates. " + "Press any key for next baud rate, or ESC to quit " + "early."); + while (!SerialUSB.available()) + ; + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 115200 baud on USART1."); + serial_echo_test(&Serial1, 115200); + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 57600 baud on USART1."); + serial_echo_test(&Serial1, 57600); + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 9600 baud on USART1."); + serial_echo_test(&Serial1, 9600); +} + +void cmd_gpio_monitoring(void) { + SerialUSB.println("Monitoring pin state changes. Press any key to stop."); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(i, INPUT_PULLDOWN); + gpio_state[i] = (uint8)digitalRead(i); + } + + while (!SerialUSB.available()) { + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + + uint8 current_state = (uint8)digitalRead(i); + if (current_state != gpio_state[i]) { + SerialUSB.print("State change on pin "); + SerialUSB.print(i, DEC); + if (current_state) { + SerialUSB.println(":\tHIGH"); + } else { + SerialUSB.println(":\tLOW"); + } + gpio_state[i] = current_state; + } + } + } + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(i, OUTPUT); + } +} + +void cmd_sequential_adc_reads(void) { + SerialUSB.print("Sequentially reading most ADC ports."); + SerialUSB.println("Press any key for next port, or ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { + if (boardUsesPin(boardADCPins[i])) + continue; + + SerialUSB.print("Reading pin "); + SerialUSB.print(boardADCPins[i], DEC); + SerialUSB.println("..."); + pinMode(boardADCPins[i], INPUT_ANALOG); + while (!SerialUSB.available()) { + int sample = analogRead(boardADCPins[i]); + SerialUSB.print(boardADCPins[i], DEC); + SerialUSB.print("\t"); + SerialUSB.print(sample, DEC); + SerialUSB.print("\t"); + SerialUSB.print("|"); + for (int j = 0; j < 4096; j += 100) { + if (sample >= j) { + SerialUSB.print("#"); + } else { + SerialUSB.print(" "); + } + } + SerialUSB.print("| "); + for (int j = 0; j < 12; j++) { + if (sample & (1 << (11 - j))) { + SerialUSB.print("1"); + } else { + SerialUSB.print("0"); + } + } + SerialUSB.println(); + } + pinMode(boardADCPins[i], OUTPUT); + digitalWrite(boardADCPins[i], 0); + if (SerialUSB.read() == ESC) + break; + } +} + +bool test_single_pin_is_high(int high_pin, const char* err_msg) { + bool ok = true; + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + + if (digitalRead(i) == HIGH && i != high_pin) { + SerialUSB.println(); + SerialUSB.print("\t*** FAILURE! pin "); + SerialUSB.print(i, DEC); + SerialUSB.print(' '); + SerialUSB.println(err_msg); + ok = false; + } + } + return ok; +} + +bool wait_for_low_transition(uint8 pin) { + uint32 start = millis(); + while (millis() - start < 2000) { + if (digitalRead(pin) == LOW) { + return true; + } + } + return false; +} + +void cmd_gpio_qa(void) { + bool all_pins_ok = true; + const int not_a_pin = -1; + SerialUSB.println("Doing QA testing for unused GPIO pins."); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + + pinMode(i, INPUT); + } + + SerialUSB.println("Waiting to start."); + ASSERT(!boardUsesPin(0)); + while (digitalRead(0) == LOW) continue; + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) { + SerialUSB.print("Skipping pin "); + SerialUSB.println(i, DEC); + continue; + } + bool pin_ok = true; + SerialUSB.print("Checking pin "); + SerialUSB.print(i, DEC); + while (digitalRead(i) == LOW) continue; + + pin_ok = pin_ok && test_single_pin_is_high(i, "is also HIGH"); + + if (!wait_for_low_transition(i)) { + SerialUSB.println("Transition to low timed out; something is " + "very wrong. Aborting test."); + return; + } + + pin_ok = pin_ok && test_single_pin_is_high(not_a_pin, "is still HIGH"); + + if (pin_ok) { + SerialUSB.println(": ok"); + } + + all_pins_ok = all_pins_ok && pin_ok; + } + + if (all_pins_ok) { + SerialUSB.println("Finished; test passes."); + } else { + SerialUSB.println("**** TEST FAILS *****"); + } + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + gpio_state[i] = 0; + } +} + +void cmd_sequential_gpio_toggling(void) { + SerialUSB.println("Sequentially toggling all unused pins. " + "Press any key for next pin, ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + + SerialUSB.print("Toggling pin "); + SerialUSB.print((int)i, DEC); + SerialUSB.println("..."); + + pinMode(i, OUTPUT); + do { + togglePin(i); + } while (!SerialUSB.available()); + + digitalWrite(i, LOW); + if (SerialUSB.read() == ESC) + break; + } +} + +void cmd_gpio_toggling(void) { + SerialUSB.println("Toggling all unused pins simultaneously. " + "Press any key to stop."); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(i, OUTPUT); + } + + while (!SerialUSB.available()) { + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + togglePin(i); + } + } + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + digitalWrite(i, LOW); + } +} + +uint8 debugGPIOPins[] = {BOARD_JTMS_SWDIO_PIN, + BOARD_JTCK_SWCLK_PIN, + BOARD_JTDI_PIN, + BOARD_JTDO_PIN, + BOARD_NJTRST_PIN}; + +#define N_DEBUG_PINS 5 + +void cmd_sequential_debug_gpio_toggling(void) { + SerialUSB.println("Toggling all debug (JTAG/SWD) pins sequentially. " + "This will permanently disable debug port " + "functionality."); + disableDebugPorts(); + + for (int i = 0; i < N_DEBUG_PINS; i++) { + pinMode(debugGPIOPins[i], OUTPUT); + } + + for (int i = 0; i < N_DEBUG_PINS; i++) { + int pin = debugGPIOPins[i]; + SerialUSB.print("Toggling pin "); + SerialUSB.print(pin, DEC); + SerialUSB.println("..."); + + pinMode(pin, OUTPUT); + do { + togglePin(pin); + } while (!SerialUSB.available()); + + digitalWrite(pin, LOW); + if (SerialUSB.read() == ESC) + break; + } + + for (int i = 0; i < N_DEBUG_PINS; i++) { + digitalWrite(debugGPIOPins[i], 0); + } +} + +void cmd_debug_gpio_toggling(void) { + SerialUSB.println("Toggling debug GPIO simultaneously. " + "This will permanently disable JTAG and Serial Wire " + "debug port functionality. " + "Press any key to stop."); + disableDebugPorts(); + + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + pinMode(debugGPIOPins[i], OUTPUT); + } + + while (!SerialUSB.available()) { + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + togglePin(debugGPIOPins[i]); + } + } + + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + digitalWrite(debugGPIOPins[i], LOW); + } +} + +void cmd_but_test(void) { + SerialUSB.println("Press the button to test. Press any key to stop."); + pinMode(BOARD_BUTTON_PIN, INPUT); + + while (!SerialUSB.available()) { + if (isButtonPressed()) { + uint32 tstamp = millis(); + SerialUSB.print("Button press detected, timestamp: "); + SerialUSB.println(tstamp); + } + } + SerialUSB.read(); +} + +void cmd_sequential_pwm_test(void) { + SerialUSB.println("Sequentially testing PWM on all unused pins. " + "Press any key for next pin, ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + + SerialUSB.print("PWM out on header D"); + SerialUSB.print(boardPWMPins[i], DEC); + SerialUSB.println("..."); + pinMode(boardPWMPins[i], PWM); + pwmWrite(boardPWMPins[i], 16000); + + while (!SerialUSB.available()) { + delay(10); + } + + pinMode(boardPWMPins[i], OUTPUT); + digitalWrite(boardPWMPins[i], 0); + if (SerialUSB.read() == ESC) + break; + } +} + +void cmd_servo_sweep(void) { + SerialUSB.println("Testing all PWM headers with a servo sweep. " + "Press any key to stop."); + SerialUSB.println(); + + disable_usarts(); + init_all_timers(21); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(boardPWMPins[i], PWM); + pwmWrite(boardPWMPins[i], 4000); + } + + // 1.25ms = 4096counts = 0deg + // 1.50ms = 4915counts = 90deg + // 1.75ms = 5734counts = 180deg + int rate = 4096; + while (!SerialUSB.available()) { + rate += 20; + if (rate > 5734) + rate = 4096; + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pwmWrite(boardPWMPins[i], rate); + } + delay(20); + } + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(boardPWMPins[i], OUTPUT); + } + init_all_timers(1); + enable_usarts(); +} + +void cmd_board_info(void) { // TODO print more information + SerialUSB.println("Board information"); + SerialUSB.println("================="); + + SerialUSB.print("* Clock speed (MHz): "); + SerialUSB.println(CYCLES_PER_MICROSECOND); + + SerialUSB.print("* BOARD_LED_PIN: "); + SerialUSB.println(BOARD_LED_PIN); + + SerialUSB.print("* BOARD_BUTTON_PIN: "); + SerialUSB.println(BOARD_BUTTON_PIN); + + SerialUSB.print("* GPIO information (BOARD_NR_GPIO_PINS = "); + SerialUSB.print(BOARD_NR_GPIO_PINS); + SerialUSB.println("):"); + print_board_array("ADC pins", boardADCPins, BOARD_NR_ADC_PINS); + print_board_array("PWM pins", boardPWMPins, BOARD_NR_PWM_PINS); + print_board_array("Used pins", boardUsedPins, BOARD_NR_USED_PINS); +} + +// -- Helper functions -------------------------------------------------------- + +void measure_adc_noise(uint8 pin) { + const int N = 1000; + uint16 x; + float mean = 0; + float delta = 0; + float M2 = 0; + pinMode(pin, INPUT_ANALOG); + + // Variance algorithm from Welford, via Knuth, by way of Wikipedia: + // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm + for (int sample = 0; sample < N_ADC_NOISE_MEASUREMENTS; sample++) { + for (int i = 1; i <= N; i++) { + x = analogRead(pin); + delta = x - mean; + mean += delta / i; + M2 = M2 + delta * (x - mean); + } + SerialUSB.print("header: D"); + SerialUSB.print(pin, DEC); + SerialUSB.print("\tn: "); + SerialUSB.print(N, DEC); + SerialUSB.print("\tmean: "); + SerialUSB.print(mean); + SerialUSB.print("\tvariance: "); + SerialUSB.println(M2 / (float)(N-1)); + } + + pinMode(pin, OUTPUT); +} + +void fast_gpio(int maple_pin) { + gpio_dev *dev = PIN_MAP[maple_pin].gpio_device; + uint32 bit = PIN_MAP[maple_pin].gpio_bit; + + gpio_write_bit(dev, bit, 1); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); +} + +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud) { + for (int i = 0; i < n; i++) { + serials[i]->begin(baud); + } + while (!SerialUSB.available()) { + for (int i = 0; i < n; i++) { + serials[i]->println(dummy_data); + if (serials[i]->available()) { + serials[i]->println(serials[i]->read()); + delay(1000); + } + } + } +} + +void serial_echo_test(HardwareSerial *serial, unsigned baud) { + serial->begin(baud); + while (!SerialUSB.available()) { + if (!serial->available()) + continue; + serial->print(serial->read()); + } +} + +static uint16 init_all_timers_prescale = 0; + +static void set_prescale(timer_dev *dev) { + timer_set_prescaler(dev, init_all_timers_prescale); +} + +void init_all_timers(uint16 prescale) { + init_all_timers_prescale = prescale; + timer_foreach(set_prescale); +} + +void enable_usarts(void) { + Serial1.begin(BAUD); + Serial2.begin(BAUD); + Serial3.begin(BAUD); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) + Serial4.begin(BAUD); + Serial5.begin(BAUD); +#endif +} + +void disable_usarts(void) { + Serial1.end(); + Serial2.end(); + Serial3.end(); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) + Serial4.end(); + Serial5.end(); +#endif +} + +void print_board_array(const char* msg, const uint8 arr[], int len) { + SerialUSB.print("\t"); + SerialUSB.print(msg); + SerialUSB.print(" ("); + SerialUSB.print(len); + SerialUSB.print("): "); + for (int i = 0; i < len; i++) { + SerialUSB.print(arr[i], DEC); + if (i < len - 1) SerialUSB.print(", "); + } + SerialUSB.println(); +} + +// -- premain() and main() ---------------------------------------------------- + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (1) { + loop(); + } + return 0; +} diff --git a/tests/test-spi-roundtrip.cpp b/tests/test-spi-roundtrip.cpp new file mode 100644 index 0000000..ddc9875 --- /dev/null +++ b/tests/test-spi-roundtrip.cpp @@ -0,0 +1,192 @@ +/* + * Polling SPI loopback test. + * + * Bob is nowhere to be found, so Alice decides to talk to herself. + * + * Instructions: Connect SPI2 (Alice) to herself (i.e., MISO to MOSI). + * Connect to Alice via SerialUSB. Press any key to start. + * + * Alice will talk to herself for a little while. The sketch will + * report if Alice can't hear anything she says. She'll then start + * talking forever at various frequencies, bit orders, and modes. Use + * an oscilloscope to make sure she's not trying to lie about any of + * those things. + * + * This file is released into the public domain. + * + * Author: Marti Bolivar + */ + +#include + +HardwareSPI alice(2); + +#define NFREQS 8 +const SPIFrequency spi_freqs[] = { + SPI_140_625KHZ, + SPI_281_250KHZ, + SPI_562_500KHZ, + SPI_1_125MHZ, + SPI_2_25MHZ, + SPI_4_5MHZ, + SPI_9MHZ, + SPI_18MHZ, +}; + +#define TEST_BUF_SIZE 10 +uint8 test_buf[TEST_BUF_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + +void bad_assert(const char* file, int line, const char* exp) { + SerialUSB.println(); + SerialUSB.print("ERROR: FAILED ASSERT("); + SerialUSB.print(exp); + SerialUSB.print("): "); + SerialUSB.print(file); + SerialUSB.print(": "); + SerialUSB.println(line); + throb(); +} + +#undef ASSERT +#define ASSERT(exp) \ + if (exp) { \ + } else { \ + bad_assert(__FILE__, __LINE__, #exp); \ + } + +void haveConversation(uint32 bitOrder); +void soliloquies(uint32 bitOrder); + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + SerialUSB.read(); +} + +void loop() { + SerialUSB.println("** Having a conversation, MSB first"); + haveConversation(MSBFIRST); + + SerialUSB.println("** Having a conversation, LSB first"); + haveConversation(LSBFIRST); + + SerialUSB.println(); + SerialUSB.println("*** All done! It looks like everything worked."); + SerialUSB.println(); + + SerialUSB.println("** Alice will now wax eloquent in various styles. " + "Press any key for the next configuration."); + soliloquies(MSBFIRST); + soliloquies(LSBFIRST); + + while (true) + ; +} + +void printFrequencyString(SPIFrequency frequency); +void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode); + +void haveConversation(uint32 bitOrder) { + for (int f = 0; f < NFREQS; f++) { + for (int mode = 0; mode < 4; mode++) { + chat(spi_freqs[f], bitOrder, mode); + delay(10); + } + } +} + +void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { + SerialUSB.print("Having a chat.\tFrequency: "); + printFrequencyString(frequency); + SerialUSB.print(",\tbitOrder: "); + SerialUSB.print(bitOrder == MSBFIRST ? "MSB" : "LSB"); + SerialUSB.print(",\tmode: "); + SerialUSB.print(mode); + SerialUSB.print("."); + + SerialUSB.print(" [1] "); + alice.begin(frequency, bitOrder, mode); + + SerialUSB.print(" [2] "); + uint32 txed = 0; + while (txed < TEST_BUF_SIZE) { + ASSERT(alice.transfer(test_buf[txed]) == test_buf[txed]); + txed++; + } + + SerialUSB.print(" [3] "); + alice.end(); + + SerialUSB.println(" ok."); +} + +void soliloquy(SPIFrequency freq, uint32 bitOrder, uint32 mode); + +void soliloquies(uint32 bitOrder) { + for (int f = 0; f < NFREQS; f++) { + for (int mode = 0; mode < 4; mode++) { + soliloquy(spi_freqs[f], bitOrder, mode); + } + } +} + +void soliloquy(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { + const uint8 repeat = 0xAE; + SerialUSB.print("Alice is giving a soliloquy (repeating 0x"); + SerialUSB.print(repeat, HEX); + SerialUSB.print("). Frequency: "); + printFrequencyString(frequency); + SerialUSB.print(", bitOrder: "); + SerialUSB.print(bitOrder == MSBFIRST ? "big-endian" : "little-endian"); + SerialUSB.print(", SPI mode: "); + SerialUSB.println(mode); + + alice.begin(frequency, bitOrder, mode); + while (!SerialUSB.available()) { + alice.write(repeat); + delayMicroseconds(200); + } + SerialUSB.read(); +} + +void printFrequencyString(SPIFrequency frequency) { + switch (frequency) { + case SPI_18MHZ: + SerialUSB.print("18 MHz"); + break; + case SPI_9MHZ: + SerialUSB.print("9 MHz"); + break; + case SPI_4_5MHZ: + SerialUSB.print("4.5 MHz"); + break; + case SPI_2_25MHZ: + SerialUSB.print("2.25 MHZ"); + break; + case SPI_1_125MHZ: + SerialUSB.print("1.125 MHz"); + break; + case SPI_562_500KHZ: + SerialUSB.print("562.500 KHz"); + break; + case SPI_281_250KHZ: + SerialUSB.print("281.250 KHz"); + break; + case SPI_140_625KHZ: + SerialUSB.print("140.625 KHz"); + break; + } +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-systick.cpp b/tests/test-systick.cpp new file mode 100644 index 0000000..356f302 --- /dev/null +++ b/tests/test-systick.cpp @@ -0,0 +1,49 @@ +// Tests the SysTick enable/disable functions + +#include +#include + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); +} + +bool disable = true; +long time = 0; + +void loop() { + volatile int i = 0; + toggleLED(); + + // An artificial delay + for(i = 0; i < 150000; i++) + ; + + if (isButtonPressed()) { + if (disable) { + systick_disable(); + SerialUSB.println("Disabling SysTick"); + } else { + SerialUSB.println("Re-enabling SysTick"); + systick_enable(); + } + disable = !disable; + } + + SerialUSB.println(millis()); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated object that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-timers.cpp b/tests/test-timers.cpp new file mode 100644 index 0000000..e646916 --- /dev/null +++ b/tests/test-timers.cpp @@ -0,0 +1,537 @@ +// +// This is a mostly Wirish-free timer test. Wirish usage is minimized +// because this is a test of the C timer interface in +// , so it's good if it can be made to work even +// when most or all of Wirish is missing. Because of that, you may +// need to customize the following output configuration: +// +// Output is printed: +// - on COMM_USART, +// - via TX pin on port COMM_USART_PORT, bit COMM_USART_TX_BIT +// - via RX pin on port COMM_USART_PORT, bit COMM_USART_RX_BIT +// - at COMM_USART_BAUD baud. +#define COMM_USART USART6 +#define COMM_USART_BAUD 115200 +#define COMM_USART_PORT GPIOG +#define COMM_USART_TX_BIT 14 +#define COMM_USART_RX_BIT 9 +// Other optional configuration below. + +#include +#include +#include +#include +#include +#include + +// +// Configuration +// + +// More output if true +static bool verbose = true; + +// Timers to test +// FIXME use feature test macros for smaller MCUs +static timer_dev *timers[] = { + // Available on all currently supported MCUs + TIMER1, TIMER2, TIMER3, TIMER4, + // Available on F1 (HD and up), F2 + TIMER5, TIMER6, TIMER7, TIMER8, + // Available on F1 (XL), F2 + TIMER9, TIMER10, TIMER11, TIMER12, TIMER13, TIMER14, +}; + +// +// Test routines +// + +typedef void (*timer_test_t)(timer_dev *); + +static void runTest(const char description[], timer_test_t test); +static void runTests(void); + +static void testGetAndSetCount(timer_dev*); +static void testPauseAndResume(timer_dev*); +static void testTimerChannels(timer_dev*); + +// +// Helpers +// + +static void initTimer(timer_dev *dev); +static int timerNumber(timer_dev *dev); +// Hack: a systick-based delay, useful until delay_us() is fixed +static void _delay(uint32 msec); +// Wirish-less USART initialization routine +static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx); +// Return whether or not the timer has capture/compare channel `ch'. +// TODO: does something like this belong in the standard timer library? +static bool timer_has_cc_ch(timer_dev *dev, int ch); + +// Printing routines and variants for verbose mode +static void putstr(const char str[]); +static void println(void); +static void putstrln(const char str[]); +static void putudec(uint32 val); +static void puttimn(timer_dev *dev); +static void v_putstr(const char str[]); +static void v_println(); +static void v_putstrln(const char str[]); +static void v_putudec(uint32 val); +static void v_puttimn(timer_dev *dev); +// Used to visually separate output from different tests +static void printBanner(void); + +// +// Handler state +// + +static int count1 = 0; +static int count2 = 0; +static int count3 = 0; +static int count4 = 0; +static int timer_num; // Current timer we're considering + +// +// Timer capture/compare interrupt handlers +// +// These are shared between timers. The global variable timer_num +// controls which timer they affect. +// + +static void handler1(void); +static void handler2(void); +static void handler3(void); +static void handler4(void); +static voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; + +// +// setup() and loop() +// + +void setup() { + init_usart(COMM_USART, COMM_USART_PORT, + COMM_USART_TX_BIT, COMM_USART_RX_BIT); + _delay(5); + println(); + printBanner(); + putstr("Initializing timers...\r\n"); + timer_foreach(initTimer); + putstr("Done. Running tests.\r\n"); + runTests(); + printBanner(); + putstr("Done testing timers.\r\n"); +} + +void loop() { +} + +// +// Test routine implementations +// + +static void runTests(void) { + runTest("timer_get_count()/timer_set_count()", testGetAndSetCount); + runTest("timer_pause()/timer_resume()", testPauseAndResume); + runTest("capture/compare channels and interrupts", + testTimerChannels); +} + +static void runTest(const char description[], timer_test_t test) { + printBanner(); + putstr("Testing "); + putstr(description); + putstrln("."); + timer_foreach(test); +} + +static void testGetAndSetCount(timer_dev *dev) { + unsigned before, after; + unsigned val_to_set = 1234; + + timer_pause(dev); + before = timer_get_count(dev); + timer_set_count(dev, val_to_set); + after = timer_get_count(dev); + timer_resume(dev); + + if (after != val_to_set) { + puttimn(dev); + putstr(": "); + putstr("*** FAIL: get/set count for "); + puttimn(dev); + putstr("."); + putstr("Start count = "); + putudec(before); + putstr(". Count set to "); + putudec(val_to_set); + putstr(", and now count is = "); + putudec(after); + println(); + } else if (verbose) { + puttimn(dev); + putstr(": "); + putstrln("[ok]"); + } +} + +// This hack works on all currently supported STM32 series, but you +// may need to do something smarter in the future. The assertions +// ensure that our assumptions hold for your target. +static timer_dev *getDifferentTimerOnSameBusAs(timer_dev *dev) { + rcc_clk_domain dev_domain = rcc_dev_clk(dev->clk_id); + ASSERT(RCC_APB1 == dev_domain || RCC_APB2 == dev_domain); + ASSERT(rcc_dev_clk(TIMER1->clk_id) == RCC_APB2); + ASSERT(rcc_dev_clk(TIMER2->clk_id) == RCC_APB1); + ASSERT(rcc_dev_clk(TIMER8->clk_id) == RCC_APB2); + ASSERT(rcc_dev_clk(TIMER3->clk_id) == RCC_APB1); + + if (dev->clk_id == RCC_TIMER1) { + return TIMER8; + } + if (dev->clk_id == RCC_TIMER2) { + return TIMER3; + } + return dev_domain == RCC_APB2 ? TIMER1 : TIMER2; +} + +// Rough test of pause and resume. +// +// Approximately half the time, dev is in the "pause" state and the +// timer doesn't increment, while another timer (`base_dev') on the +// same bus continues. dev and base_dev have identical start counts +// and prescalers. +// +// Since dev and base_dev share a bus (and thus a base clock), and we +// configure them to have the same prescaler and start count, the +// ratio of their end counts should be approximately 1 : 2. We check +// to make sure this is true, up to tolerance `epsilon'. +static void testPauseAndResume(timer_dev *dev) { + timer_dev *base_dev = getDifferentTimerOnSameBusAs(dev); + unsigned start_count = 0, reload = 65535; + // This prescaler should be enough to ensure that we don't + // overflow, while still giving us a reasonably large number of + // timer ticks. + uint16 prescaler = CYCLES_PER_MICROSECOND * 50; + double epsilon = .02; + + if (rcc_dev_clk(base_dev->clk_id) != rcc_dev_clk(dev->clk_id)) { + putstrln("*** ERROR: cannot run test. Bus info is messed up."); + return; + } + + // Pause and set up timers + timer_pause(base_dev); + timer_pause(dev); + timer_set_count(base_dev, start_count); + timer_set_count(dev, start_count); + timer_set_reload(base_dev, reload); + timer_set_reload(dev, reload); + timer_set_prescaler(base_dev, prescaler); + timer_set_prescaler(dev, prescaler); + timer_generate_update(base_dev); + timer_generate_update(dev); + + // Resume the timers and run the test + ASSERT(timer_get_count(base_dev) == start_count); + ASSERT(timer_get_count(dev) == start_count); + timer_resume(base_dev); + timer_resume(dev); + _delay(1000); + timer_pause(dev); + _delay(1000); + timer_pause(base_dev); + + // Check the results + unsigned dev_count = timer_get_count(dev); + unsigned base_count = timer_get_count(base_dev); + double count_ratio = ((double)dev_count / base_count); + bool fail = false; + if (count_ratio > 0.5 + epsilon || count_ratio < 0.5 - epsilon) { + fail = true; + } + if (fail || verbose) { + puttimn(dev); + putstr(" vs. "); + puttimn(base_dev); + putstr(": "); + if (fail) putstr("*** FAIL: "); + else putstr("[ok] "); + putstr("(dev = "); + putudec(dev_count); + putstr(") / (base = "); + putudec(base_count); + putstr(") = "); + // hack hack hack + putudec((int)count_ratio); + count_ratio -= (int)count_ratio; + putstr("."); + int cr_x_100 = (int)(count_ratio * 100); + int hundredths = cr_x_100 % 10; + cr_x_100 /= 10; + int tenths = cr_x_100 % 10; + putudec(tenths); + putudec(hundredths); + println(); + } +} + +// This function touches every capture/compare channel of a given +// timer. The channel counts should be equal within a timer +// regardless of other interrupts on the system (note that this +// doesn't really test timers with only a single capture/compare +// channel; for that, you'll want to do visual inspection of timers +// that share a bus, in verbose mode). +static void testTimerChannels(timer_dev *dev) { + switch (dev->type) { + case TIMER_BASIC: + v_putstr("Skipping basic timer "); + v_puttimn(dev); + v_println(); + return; + case TIMER_ADVANCED: + case TIMER_GENERAL: + // Set up + v_puttimn(dev); + v_println(); + v_putstr("\tchannels: "); + + timer_num = timerNumber(dev); + timer_pause(dev); + count1 = 0; + count2 = 0; + count3 = 0; + count4 = 0; + timer_set_reload(dev, 0xFFFF); + timer_set_prescaler(dev, 1); + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + v_putudec(c); + v_putstr("\t"); + timer_set_compare(dev, c, 0xFFFF); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + timer_attach_interrupt(dev, c, handlers[c - 1]); + } + } + v_println(); + + // Run test + timer_generate_update(dev); + timer_resume(dev); + _delay(250); + timer_pause(dev); + + // Print results + v_putstr("\tcounts: "); + bool fail = false; + bool mismatched[4] = {false, false, false, false}; + int counts[4]; + counts[0] = count1; + counts[1] = count2; + counts[2] = count3; + counts[3] = count4; + bool first = true; + int first_count = -1; + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + if (first) { + first_count = counts[c - 1]; + first = false; + } + if (!first && (counts[c - 1] != first_count)) { + mismatched[c - 1] = true; + fail = true; + } + v_putudec(counts[c - 1]); + v_putstr("\t"); + } + } + v_println(); + if (fail) { + for (int i = 0; i < 4; i++) { + if (mismatched[i]) { + putstr("*** FAIL: mismatch on "); + puttimn(dev); + putstr(", channel "); + putudec(i + 1); + putstr(": expected "); + putudec(first_count); + putstr(", got "); + putudec(counts[i]); + println(); + } + } + } else { + puttimn(dev); + putstrln(" [ok]"); + } + v_println(); + + // Clean up + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + timer_set_mode(dev, c, TIMER_DISABLED); + } + } + break; + } +} + +// +// Helper implementations +// + +static void _delay(uint32 msec) { + uint32 end = systick_uptime() + msec; + while (systick_uptime() < end) + ; +} + +static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx) { + usart_config_gpios_async(dev, gdev, rx, gdev, tx, 0); + usart_init(dev); + usart_set_baud_rate(dev, USART_USE_PCLK, COMM_USART_BAUD); + usart_enable(dev); +} + +static bool timer_has_cc_ch(timer_dev *dev, int ch) { + ASSERT(1 <= ch && ch <= 4); + if (dev->type == TIMER_BASIC) + return false; + int tn = timerNumber(dev); + return (// TIM1-5 and 8 have all four channels + (tn <= 5 || tn == 8) || + // TIM9 and 12 only have channels 1 and 2 + ((tn == 9 || tn == 12) && ch <= 2) || + // All other general purpose timers only have channel 1 + (ch == 1)); +} + +static void putstr(const char str[]) { + usart_putstr(COMM_USART, str); +} + +static void println(void) { + putstr("\r\n"); +} + +static void putstrln(const char str[]) { + putstr(str); + println(); +} + +static void putudec(uint32 val) { + usart_putudec(COMM_USART, val); +} + +static void puttimn(timer_dev *dev) { + putstr("TIM"); + putudec(timerNumber(dev)); +} + +static void v_putstr(const char str[]) { + if (verbose) putstr(str); +} + +static void v_println() { + if (verbose) println(); +} + +__attribute__((unused)) /* (shut up, gcc) */ +static void v_putstrln(const char str[]) { + if (verbose) putstrln(str); +} + +static void v_putudec(uint32 val) { + if (verbose) putudec(val); +} + +static void v_puttimn(timer_dev *dev) { + if (verbose) puttimn(dev); +} + +// Used to visually separate output from different tests +static void printBanner(void) { + putstrln("-----------------------------------------------------"); +} + +static void initTimer(timer_dev *dev) { + v_puttimn(dev); + timer_init(dev); + switch (dev->type) { + case TIMER_ADVANCED: + case TIMER_GENERAL: + v_putstr(" channels "); + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + v_putudec(c); + v_putstr(" "); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + } + } + break; + case TIMER_BASIC: + break; + } + v_println(); +} + +static int timerNumber(timer_dev *dev) { + switch (dev->clk_id) { + case RCC_TIMER1: return 1; + case RCC_TIMER2: return 2; + case RCC_TIMER3: return 3; + case RCC_TIMER4: return 4; + case RCC_TIMER5: return 5; + case RCC_TIMER6: return 6; + case RCC_TIMER7: return 7; + case RCC_TIMER8: return 8; + case RCC_TIMER9: return 9; + case RCC_TIMER10: return 10; + case RCC_TIMER11: return 11; + case RCC_TIMER12: return 12; + case RCC_TIMER13: return 13; + case RCC_TIMER14: return 14; + default: + ASSERT(0); + return 0; + } +} + +// +// IRQ Handlers +// + +static void handler1(void) { + count1++; +} + +static void handler2(void) { + count2++; +} + +static void handler3(void) { + count3++; +} + +static void handler4(void) { + count4++; +} + +// +// init() and main() +// + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/tests/test-usart-dma.cpp b/tests/test-usart-dma.cpp new file mode 100644 index 0000000..7d6d8b9 --- /dev/null +++ b/tests/test-usart-dma.cpp @@ -0,0 +1,211 @@ +/** + * @file tests/test-usart-dma.cpp + * @author Marti Bolivar + * + * Simple test of DMA used with a USART receiver. + * + * Configures a USART receiver for use with DMA. Received bytes are + * placed into a buffer, with an interrupt firing when the buffer is + * full. At that point, the USART transmitter will print the contents + * of the byte buffer. The buffer is continually filled and refilled + * in this manner. + * + * This example isn't very robust; don't use it in production. In + * particular, since the buffer keeps filling (DMA_CIRC_MODE is set), + * if you keep sending characters after filling the buffer, you'll + * overwrite earlier bytes; this may happen before those earlier bytes + * are done printing. (Typing quickly and seeing how it affects the + * output is a fun way to make sense of how the interrupts and the + * main thread of execution interleave.) + * + * This code is released into the public domain. + */ + +#include +#include +#include + +#include + +/* + * Configuration and state + */ + +// Serial port and DMA configuration. You can change these to suit +// your purposes. +HardwareSerial *serial = &Serial2; +#define USART_DMA_DEV DMA1 +#if STM32_MCU_SERIES == STM32_SERIES_F1 +// On STM32F1 microcontrollers (like what's on Maple and Maple Mini), +// dma tubes are channels. +#define USART_RX_DMA_TUBE DMA_CH6 +#elif (STM32_MCU_SERIES == STM32_SERIES_F2 || \ + STM32_MCU_SERIES == STM32_SERIES_F4) +// On STM32F2 and STM32F4 microcontrollers (Maple 2 will have an F4), +// dma tubes are streams. +#define USART_RX_DMA_TUBE DMA_S5 +#else +#error "unsupported stm32 series" +#endif +// The serial port will make a DMA request each time it receives data. +// This is the dma_request_src we use to tell the DMA tube to handle +// that DMA request. +#define USART_DMA_REQ_SRC DMA_REQ_SRC_USART2_RX +#define BAUD 9600 + +// This will store the DMA configuration for USART RX. +dma_tube_config tube_config; + +// This will store received USART characters. +#define BUF_SIZE 20 +char rx_buf[BUF_SIZE]; + +// The interrupt handler, rx_dma_irq(), sets this to 1. +volatile uint32 irq_fired = 0; +// Used to store DMA interrupt status register (ISR) bits inside +// rx_dma_irq(). This helps explain what's going on inside loop(); see +// comments below. +volatile uint32 isr = 0; + +/* + * Helper functions + */ + +// This is our DMA interrupt handler. +void rx_dma_irq(void) { + irq_fired = 1; + isr = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); +} + +// Configure the USART receiver for use with DMA: +// 1. Turn it on. +// 2. Set the "DMA request on RX" bit in USART_CR3 (USART_CR3_DMAR). +void setup_usart(void) { + serial->begin(BAUD); + usart_dev *serial_dev = serial->c_dev(); + serial_dev->regs->CR3 = USART_CR3_DMAR; +} + +// Set up our dma_tube_config structure. (We could have done this +// above, when we declared tube_config, but having this function makes +// it easier to explain what's going on). +void setup_tube_config(void) { + // We're receiving from the USART data register. serial->c_dev() + // returns a pointer to the libmaple usart_dev for that serial + // port, so this is a pointer to its data register. + tube_config.tube_src = &serial->c_dev()->regs->DR; + // We're only interested in the bottom 8 bits of that data register. + tube_config.tube_src_size = DMA_SIZE_8BITS; + // We're storing to rx_buf. + tube_config.tube_dst = rx_buf; + // rx_buf is a char array, and a "char" takes up 8 bits on STM32. + tube_config.tube_dst_size = DMA_SIZE_8BITS; + // Only fill BUF_SIZE - 1 characters, to leave a null byte at the end. + tube_config.tube_nr_xfers = BUF_SIZE - 1; + // Flags: + // - DMA_CFG_DST_INC so we start at the beginning of rx_buf and + // fill towards the end. + // - DMA_CFG_CIRC so we go back to the beginning and start over when + // rx_buf fills up. + // - DMA_CFG_CMPLT_IE to turn on interrupts on transfer completion. + tube_config.tube_flags = DMA_CFG_DST_INC | DMA_CFG_CIRC | DMA_CFG_CMPLT_IE; + // Target data: none. It's important to set this to NULL if you + // don't have any special (microcontroller-specific) configuration + // in mind, which we don't. + tube_config.target_data = NULL; + // DMA request source. + tube_config.tube_req_src = USART_DMA_REQ_SRC; +} + +// Configure the DMA controller to serve DMA requests from the USART. +void setup_dma_xfer(void) { + // First, turn it on. + dma_init(USART_DMA_DEV); + // Next, configure it by calling dma_tube_cfg(), and check to make + // sure it succeeded. DMA tubes have many restrictions on their + // configuration, and there are configurations which work on some + // types of STM32 but not others. libmaple tries hard to make + // things just work, but checking the return status is important! + int status = dma_tube_cfg(USART_DMA_DEV, USART_RX_DMA_TUBE, &tube_config); + ASSERT(status == DMA_TUBE_CFG_SUCCESS); + // Now we'll perform any other configuration we want. For this + // example, we attach an interrupt handler. + dma_attach_interrupt(USART_DMA_DEV, USART_RX_DMA_TUBE, rx_dma_irq); + // Turn on the DMA tube. It will now begin serving requests. + dma_enable(USART_DMA_DEV, USART_RX_DMA_TUBE); +} + +/* + * setup() and loop() + */ + +void setup(void) { + pinMode(BOARD_LED_PIN, OUTPUT); + setup_tube_config(); + setup_dma_xfer(); + setup_usart(); +} + +void loop(void) { + toggleLED(); + delay(100); + + // See if the interrupt handler got called since the last time we + // checked. + if (irq_fired) { + serial->println("** IRQ **"); + // Notice how the interrupt status register (ISR) bits show + // transfer complete _and_ half-complete here, but the ISR + // bits we print next will be zero. That's because the + // variable "isr" gets set _inside_ rx_dma_irq(). After it + // exits, libmaple cleans up by clearing the tube's ISR + // bits. (If it didn't, and we forgot to, the interrupt would + // repeatedly fire forever.) + serial->print("ISR bits: 0x"); + serial->println(isr, HEX); + irq_fired = 0; + } + + // Print the ISR bits. + // + // Notice that the "transfer half-complete" ISR flag gets set when + // we reach the rx_buf half-way point. This is true even though we + // don't tell the DMA controller to interrupt us on a + // half-complete transfer. That is, the ISR bits get set at the + // right times no matter what; we just don't get interrupted + // unless we asked. (If an error or other problem occurs, the + // relevant ISR bits will get set in the same way). + serial->print("["); + serial->print(millis()); + serial->print("]\tISR bits: 0x"); + uint8 isr_bits = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); + serial->print(isr_bits, HEX); + + // Print the contents of rx_buf. If you keep typing after it fills + // up, the new characters will overwrite the old ones, thanks to + // DMA_CIRC_MODE. + serial->print("\tCharacter buffer contents: '"); + serial->print(rx_buf); + serial->println("'"); + if (isr_bits == 0x7) { + serial->println("** Clearing ISR bits."); + dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE); + } +} + +// ------- init() and main() -------------------------------------------------- + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} -- cgit v1.2.3