diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/blinky.cpp | 15 | ||||
-rw-r--r-- | examples/debug-dtrrts.cpp | 24 | ||||
-rw-r--r-- | examples/fsmc-stress-test.cpp | 216 | ||||
-rw-r--r-- | examples/qa-slave-shield.cpp | 67 | ||||
-rw-r--r-- | examples/spi_master.cpp | 72 | ||||
-rw-r--r-- | examples/test-bkp.cpp | 80 | ||||
-rw-r--r-- | examples/test-dac.cpp | 96 | ||||
-rw-r--r-- | examples/test-fsmc.cpp | 214 | ||||
-rw-r--r-- | examples/test-print.cpp | 181 | ||||
-rw-r--r-- | examples/test-ring-buffer-insertion.cpp | 114 | ||||
-rw-r--r-- | examples/test-serial-flush.cpp | 45 | ||||
-rw-r--r-- | examples/test-serialusb.cpp | 89 | ||||
-rw-r--r-- | examples/test-servo.cpp | 152 | ||||
-rw-r--r-- | examples/test-session.cpp | 998 | ||||
-rw-r--r-- | examples/test-spi-roundtrip.cpp | 194 | ||||
-rw-r--r-- | examples/test-systick.cpp | 55 | ||||
-rw-r--r-- | examples/test-timers.cpp | 399 | ||||
-rw-r--r-- | examples/test-usart-dma.cpp | 127 | ||||
-rw-r--r-- | examples/vga-leaf.cpp | 201 | ||||
-rw-r--r-- | examples/vga-scope.cpp | 222 |
20 files changed, 2508 insertions, 1053 deletions
diff --git a/examples/blinky.cpp b/examples/blinky.cpp index 5611987..91d1a47 100644 --- a/examples/blinky.cpp +++ b/examples/blinky.cpp @@ -1,12 +1,9 @@ -// Blinks the LED, pin 13
+// Blinks the built-in LED
#include "wirish.h"
-// Use the pin attached to the built-in LED
-#define PIN BOARD_LED_PIN
-
void setup() {
- pinMode(PIN, OUTPUT);
+ pinMode(BOARD_LED_PIN, OUTPUT);
}
int toggle = 1;
@@ -14,21 +11,21 @@ int toggle = 1; void loop() {
// You could just use toggleLED() instead, but this illustrates
// the use of digitalWrite():
- digitalWrite(PIN, toggle);
+ digitalWrite(BOARD_LED_PIN, toggle);
toggle ^= 1;
delay(100);
}
// 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() {
+// Otherwise, statically allocated objects that need libmaple may fail.
+__attribute__((constructor)) void premain() {
init();
}
int main(void) {
setup();
- while (1) {
+ while (true) {
loop();
}
return 0;
diff --git a/examples/debug-dtrrts.cpp b/examples/debug-dtrrts.cpp index f9f8b96..3829208 100644 --- a/examples/debug-dtrrts.cpp +++ b/examples/debug-dtrrts.cpp @@ -1,16 +1,11 @@ -// Sample main.cpp file. Blinks an LED, sends a message out USART2 -// and turns on PWM on pin 2 +// Test sketch for figuring out DTR/RTS behavior on different platforms. #include "wirish.h" #include "usb.h" -#define LED_PIN 13 -#define PWM_PIN 2 - -void setup() -{ +void setup() { /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); + pinMode(BOARD_LED_PIN, OUTPUT); /* Send a message out USART2 */ Serial2.begin(9600); @@ -18,12 +13,10 @@ void setup() } -int toggle = 0; - void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(100); + Serial2.print("DTR: "); Serial2.print(usbGetDTR(), DEC); Serial2.print("\tRTS: "); @@ -31,13 +24,12 @@ void loop() { } // 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() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); while (1) { diff --git a/examples/fsmc-stress-test.cpp b/examples/fsmc-stress-test.cpp new file mode 100644 index 0000000..12e8650 --- /dev/null +++ b/examples/fsmc-stress-test.cpp @@ -0,0 +1,216 @@ +/* + + A low-level stress test of SRAM functionality. Uses slow-ish timing + by default (DATAST = ADDSET = 0xF). + + Copyright 2011 LeafLabs, LLC. + + This code is released into the public domain. + + */ + +#include <stdio.h> +#include <stddef.h> + +#include "wirish.h" +#include "rcc.h" +#include "fsmc.h" + +// -- SRAM config ------------------------------------------------------------- + +// Timing configuration +#define DATAST 0xF +#define ADDSET 0xF + +// Number of SRAM chips to test +#define N 1 + +// How much of each to test +#define MEM_SIZE 0x3FFF + +// Their start addresses in FSMC bank 1 +__io uint16 *const starts[N] = { + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION1, + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION2, + (__io uint16 *const)FSMC_NOR_PSRAM_REGION3, + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION4, +}; + +// Corresponding FSMC configuration registers +__io uint32 *const bcrs[N] = { + // &FSMC_NOR_PSRAM1_BASE->BCR, + // &FSMC_NOR_PSRAM2_BASE->BCR, + &FSMC_NOR_PSRAM3_BASE->BCR, + // &FSMC_NOR_PSRAM4_BASE->BCR, +}; + +// Corresponding FSMC timing registers +__io uint32 *const btrs[N] = { + // &FSMC_NOR_PSRAM1_BASE->BTR, + // &FSMC_NOR_PSRAM2_BASE->BTR, + &FSMC_NOR_PSRAM3_BASE->BTR, + // &FSMC_NOR_PSRAM4_BASE->BTR, +}; + +// -- Pseudorandom number generation ----------------------------------------- + +const uint32 seed = 0xDEADBEEF; + +uint32 num_rand_calls = 0; + +uint32 rand(long n) { + num_rand_calls++; + return random(n); +} + +// -- Printing ---------------------------------------------------------------- + +// For snprintf() +char snprintf_buf[200]; + +#define ERR(fmt, ...) do { \ + snprintf(snprintf_buf, sizeof snprintf_buf, \ + "ERROR: " fmt " (seed %d, ncalls %d, line %d)", \ + __VA_ARGS__, seed, num_rand_calls, __LINE__); \ + Serial1.println(snprintf_buf); \ + } while (0) + +// -- setup()/loop() ---------------------------------------------------------- + +void setup() { + fsmc_sram_init_gpios(); + rcc_clk_enable(RCC_FSMC); + + for (int i = 0; i < N; i++) { + *bcrs[i] = (FSMC_BCR_WREN | + FSMC_BCR_MTYP_SRAM | + FSMC_BCR_MWID_16BITS | + FSMC_BCR_MBKEN); + *btrs[i] = (DATAST << 8) | ADDSET; + } + + Serial1.begin(115200); + randomSeed(seed); +} + +// stress_test() and simple_roundtrip() are the available test routines +bool stress_test(void); +bool simple_roundtrip(void); + +void loop() { + uint32 count = 0; + bool ok = true; + bool (*test)(void) = stress_test; + + while (true) { + count++; + bool result = test(); + ok = ok && result; + if (ok) { + snprintf(snprintf_buf, sizeof snprintf_buf, + "everything ok so far, timestamp %d ms", millis()); + Serial1.println(snprintf_buf); + } + } +} + +// -- Test routines ----------------------------------------------------------- + +bool random_trips(); +bool sequential_trips(); + +bool stress_test(void) { + static int i = 0; + i = !i; + + switch (i) { + case 0: + return random_trips(); + default: + return sequential_trips(); + } +} + +bool simple_roundtrip(void) { + uint16 wval = 0xAB; + + for (int i = 0; i < N; i++) { + __io uint16 *addr = starts[i] + 4; + snprintf(snprintf_buf, sizeof snprintf_buf, "round-trip 0x%x at %p", + wval, addr); + Serial1.println(snprintf_buf); + + *addr = wval; + uint16 rval = *addr; + + if (rval != wval) { + ERR("wrote 0x%x, read 0x%x, timestamp %d", wval, rval, millis()); + return false; + } else { + snprintf(snprintf_buf, sizeof snprintf_buf, "got back 0x%x", rval); + Serial1.println(snprintf_buf); + } + } + + return true; +} + +bool random_trips(void) { + for (int n = 0; n < N; n++) { + __io uint16 *const start = starts[n]; + + for (int i = 0; i < 1000; i++) { + uint32 offset = rand(MEM_SIZE); + uint32 wval = rand(0xFFFF); + + *(start + offset) = wval; + uint32 rval = *(start + offset); + + if (rval != wval) { + ERR("wrote 0x%x to 0x%x, read 0x%x", wval, offset, rval); + return false; + } + } + } + return true; +} + +bool sequential_trips(void) { + static const uint32 seq_length = 300; + + for (int n = 0; n < N; n++) { + __io uint16 *const start = starts[n]; + + for (int i = 0; i < 100; i++) { + uint32 start_offset = rand(MEM_SIZE - seq_length); + + for (uint32 w = 0; w < seq_length; w++) { + uint32 offset = start_offset + w; + + *(start + offset) = w; + uint32 r = *(start + offset); + + if (w != r) { + ERR("wrote 0x%x to 0x%x, read 0x%x", w, offset, r); + return false; + } + } + } + } + return true; +} + +// ---------------------------------------------------------------------------- + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + while (true) { + loop(); + } + return 0; +} + diff --git a/examples/qa-slave-shield.cpp b/examples/qa-slave-shield.cpp index b395cca..2da1c04 100644 --- a/examples/qa-slave-shield.cpp +++ b/examples/qa-slave-shield.cpp @@ -1,53 +1,64 @@ -// slave mode for QA shield +// Slave mode for Quality Assurance test #include "wirish.h" -#define LED_PIN 13 -#define NUM_GPIO 38 // not the number of the max... +#define INTER_TOGGLE_DELAY_NORMAL 5 +#define INTER_TOGGLE_DELAY_SLOW 80 -int i; +void interToggleDelay(void); -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, 1); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); - for(i=0; i<NUM_GPIO; i++) { - if(i==13) { continue; } + // All unused pins start out low. + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, OUTPUT); - digitalWrite(i,0); + digitalWrite(i, LOW); } - //delay(5000); SerialUSB.println("OK, starting..."); - } void loop() { - digitalWrite(LED_PIN,1); + toggleLED(); delay(100); - digitalWrite(LED_PIN,0); - - for(i=0; i<NUM_GPIO; i++) { - if(i==13) { continue; } - digitalWrite(i,1); - delay(5); - digitalWrite(i,0); - delay(5); + toggleLED(); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + + // Bring just this pin high. + digitalWrite(i, HIGH); + // Give the master time to detect if any other pins also went high. + interToggleDelay(); + // Bring this pin back low again; all pins should now be low. + digitalWrite(i, LOW); + // Give the master time to detect if any pins are still high. + interToggleDelay(); } } +void interToggleDelay(void) { + if (digitalRead(BOARD_BUTTON_PIN)) { // don't pay the debouncing time + delay(INTER_TOGGLE_DELAY_SLOW); + } else { + delay(INTER_TOGGLE_DELAY_NORMAL); + } + } + // 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() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/spi_master.cpp b/examples/spi_master.cpp index 9f6d81b..af3e709 100644 --- a/examples/spi_master.cpp +++ b/examples/spi_master.cpp @@ -1,75 +1,75 @@ -/* *****************************************************************************
+/******************************************************************************
* The MIT License
*
* Copyright (c) 2010 LeafLabs LLC.
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- * ****************************************************************************/
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
/**
- * @brief Sample main.cpp file. Sends "Hello world!" out SPI1.
+ * @brief Sample main.cpp file. Sends "Hello world!" out SPI1.
*
- * SPI1 is set up to be a master transmitter at 4.5MHz, little endianness,
- * and SPI mode 0.
- *
- * Pin 10 is used as Chip Select
+ * SPI1 is set up to be a master transmitter at 4.5MHz, little
+ * endianness, and SPI mode 0.
*
+ * Pin 10 is used as slave select.
*/
#include "wirish.h"
-#define CS 10
+#define NSS 10
byte buf[] = "Hello world!";
HardwareSPI spi1(1);
void setup() {
- /* Set up chip select as output */
- pinMode(CS, OUTPUT);
+ /* Set up chip select as output */
+ pinMode(NSS, OUTPUT);
- /* CS is usually active low, so initialize it high */
- digitalWrite(CS, HIGH);
+ /* NSS is usually active LOW, so initialize it HIGH */
+ digitalWrite(NSS, HIGH);
- /* Initialize SPI */
+ /* Initialize SPI */
spi1.begin(SPI_4_5MHZ, LSBFIRST, 0);
}
void loop() {
- /* Send message */
- digitalWrite(CS, LOW);
- spi1.send(buf, sizeof buf);
- digitalWrite(CS,HIGH);
+ /* Send message */
+ digitalWrite(NSS, LOW);
+ spi1.write(buf, sizeof buf);
+ digitalWrite(NSS, HIGH);
delay(1000);
}
// 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() {
+// Otherwise, statically allocated objects that need libmaple may fail.
+__attribute__((constructor)) void premain() {
init();
}
-int main(void)
-{
+int main(void) {
setup();
- while (1) {
+ while (true) {
loop();
}
return 0;
diff --git a/examples/test-bkp.cpp b/examples/test-bkp.cpp new file mode 100644 index 0000000..f5957b7 --- /dev/null +++ b/examples/test-bkp.cpp @@ -0,0 +1,80 @@ +#include <stdio.h> // for snprintf() + +#include "wirish.h" +#include "bkp.h" +#include "iwdg.h" + +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 index 3a699e2..40ae5d5 100644 --- a/examples/test-dac.cpp +++ b/examples/test-dac.cpp @@ -1,45 +1,51 @@ -
-#include "wirish.h"
-#include "fsmc.h"
-#include "rcc.h"
-#include "gpio.h"
-#include "dac.h"
-
-uint16 count = 0;
-
-void setup() {
-
- pinMode(BOARD_LED_PIN, OUTPUT);
- digitalWrite(BOARD_LED_PIN,1);
-
- Serial1.begin(9600);
- Serial1.println("**** Beginning DAC test");
-
- Serial1.print("Init... ");
- dac_init();
- Serial1.println("Done.");
-}
-
-void loop() {
- toggleLED();
- delay(100);
-
- count += 100;
- if(count > 4095) {
- count = 0;
- }
-
- dac_write(1, 2048);
- dac_write(2, count);
-}
-
-int main(void) {
- init();
- setup();
-
- while (1) {
- loop();
- }
- return 0;
-}
-
+/* + * Simple DAC test. + * + * Author: Marti Bolivar <mbolivar@leaflabs.com> + * + * This file is released into the public domain. + */ + +#include "wirish.h" +#include "dac.h" + +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 index f4fd068..7db3bb7 100644 --- a/examples/test-fsmc.cpp +++ b/examples/test-fsmc.cpp @@ -1,119 +1,125 @@ +#include <stddef.h> // for ptrdiff_t
#include "wirish.h"
#include "fsmc.h"
-#define LED_PIN 23 // hack for maple native
-#define DISC_PIN 14 // hack for USB on native
-
-// System control block registers
-#define SCB_BASE (SCB_Reg*)(0xE000ED00)
-
-// This stuff should ultimately get moved to util.h or scb.h or w/e.
-// Also in interactive test program and the HardFault IRQ handler.
-typedef struct {
- volatile uint32 CPUID;
- volatile uint32 ICSR;
- volatile uint32 VTOR;
- volatile uint32 AIRCR;
- volatile uint32 SCR;
- volatile uint32 CCR;
- volatile uint32 SHPR1;
- volatile uint32 SHPR2;
- volatile uint32 SHPR3;
- volatile uint32 SHCRS;
- volatile uint32 CFSR;
- volatile uint32 HFSR;
- uint32 pad1;
- volatile uint32 MMAR;
- volatile uint32 BFAR;
-} SCB_Reg;
-
-SCB_Reg *scb;
-
-uint16 *ptr;
-int toggle = 0;
-int count = 0;
+#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*)0x60080000;
+
+void test_single_write(void);
+void test_all_addresses(void);
void setup() {
- uint32 id;
- scb = (SCB_Reg*)SCB_BASE;
-
- pinMode(LED_PIN, OUTPUT);
- pinMode(DISC_PIN, OUTPUT);
- digitalWrite(DISC_PIN,1);
- digitalWrite(LED_PIN,1);
-
- Serial1.begin(9600);
- Serial1.println("Hello World!");
-
- Serial1.print("Init... ");
- fsmc_native_sram_init();
- Serial1.println("Done.");
-
- // Start of channel1 SRAM bank (through to 0x63FFFFFF, though only a chunk
- // of this is valid)
- ptr = (uint16*)(0x60000000);
-
- Serial1.print("Writing... ");
- *ptr = 0x1234;
- Serial1.println("Done.");
-
- Serial1.print("Reading... ");
- id = *ptr;
- Serial1.print("Done: "); // shouldn't be 0xFFFFFFFF
- Serial1.println(id,BIN);
-
- Serial1.println("Dumping System Control Block Registers");
- Serial1.print("CPUID: ");
- id = scb->CPUID;
- Serial1.println(id,BIN);
- Serial1.print("ICSR: ");
- id = scb->ICSR;
- Serial1.println(id,BIN);
- Serial1.print("CFSR: ");
- id = scb->CFSR;
- Serial1.println(id,BIN);
- Serial1.print("HFSR: ");
- id = scb->HFSR;
- Serial1.println(id,BIN);
- Serial1.print("MMAR: ");
- id = scb->MMAR;
- Serial1.println(id,BIN);
- Serial1.print("BFAR: ");
- id = scb->BFAR;
- Serial1.println(id,BIN);
-
- Serial1.println("Now testing all memory addresses... (will hardfault at the end)");
- delay(3000);
+ pinMode(BOARD_LED_PIN, OUTPUT);
+ digitalWrite(BOARD_LED_PIN, HIGH);
+
+ Serial1.begin(115200);
+ Serial1.println("*** Beginning RAM chip test");
+
+ test_single_write();
+ test_all_addresses();
+
+ Serial1.println("Tests pass, finished.");
}
void loop() {
- digitalWrite(LED_PIN, toggle);
- toggle ^= 1;
- delay(1);
-
- for(int i = 0; i<100; i++) { // modify this to speed things up
- count++;
- ptr++;
- //delay(10); // tweak this to test SRAM resiliance over time
- *ptr = (0x0000FFFF & count);
- if(*ptr != (0x0000FFFF & count)) {
- Serial1.println("ERROR: mismatch, halting");
- while(1) { }
+}
+
+void test_single_write() {
+ uint16 *ptr = sram_start;
+ uint16 tmp;
+
+ Serial1.print("Writing 0x1234... ");
+ *ptr = 0x1234;
+ Serial1.println("Done.");
+
+ Serial1.print("Reading... ");
+ tmp = *ptr;
+ Serial1.print("Done: 0x");
+ Serial1.println(tmp, HEX);
+
+ if (tmp != 0x1234) {
+ Serial1.println("Mismatch; abort.");
+ ASSERT(0);
+ }
+}
+
+void test_all_addresses() {
+ uint32 start, end;
+ uint16 count = 0;
+ uint16 *ptr;
+
+ Serial1.println("Now writing all memory addresses (unrolled loop)");
+ // Turn off the USB interrupt, as it interferes most with timing
+ // (don't turn off SysTick, or we won't get micros()).
+ SerialUSB.end();
+ 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.begin();
+ Serial1.print("Done. Elapsed time (us): ");
+ Serial1.println(end - start);
+
+ Serial1.println("Validating writes.");
+ for (ptr = sram_start, count = 0; ptr < sram_end; ptr++, count++) {
+ uint16 value = *ptr;
+ if (value != count) {
+ Serial1.print("mismatch: 0x");
+ Serial1.print((uint32)ptr);
+ Serial1.print(" = 0x");
+ Serial1.print(value, HEX);
+ Serial1.print(", should be 0x");
+ Serial1.print(count, HEX);
+ Serial1.println(".");
+ ASSERT(0);
}
- }
-
- Serial1.print((uint32)(ptr),HEX);
- Serial1.print(": ");
- Serial1.println(*ptr,BIN);
+ }
+ Serial1.println("Done; all writes seem valid.");
+
+ ptrdiff_t nwrites = sram_end - sram_start;
+ double us_per_write = double(end-start) / double(nwrites);
+ Serial1.print("Number of writes = ");
+ Serial1.print(nwrites);
+ Serial1.print("; avg. time per write = ");
+ Serial1.print(us_per_write);
+ Serial1.print(" us (");
+ Serial1.print(1 / us_per_write);
+ Serial1.println(" MHz)");
+}
+
+__attribute__((constructor)) void premain() {
+ init();
}
int main(void) {
- init();
- setup();
+ setup();
+
+ while (true) {
+ loop();
+ }
- while (1) {
- loop();
- }
- return 0;
+ return 0;
}
diff --git a/examples/test-print.cpp b/examples/test-print.cpp new file mode 100644 index 0000000..ff3c219 --- /dev/null +++ b/examples/test-print.cpp @@ -0,0 +1,181 @@ +/* + * 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 "wirish.h" +#undef min +#undef max + +// For snprintf() +#include <stdio.h> +// The <limits.h> that comes with newlib is missing LLONG_MAX, etc. +#include <limits> + +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.println("unsigned char: "); + // prevent Print from treating it as an (extended) ASCII character: + SerialUSB.println((uint32)numeric_limits<unsigned char>::max()); + + SerialUSB.print("int: "); + SerialUSB.print(numeric_limits<int>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<int>::max()); + + SerialUSB.print("unsigned int: "); + SerialUSB.print(numeric_limits<unsigned int>::max()); + SerialUSB.println(); + + SerialUSB.print("long: "); + SerialUSB.print(numeric_limits<long>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<long>::max()); + + SerialUSB.print("long long: "); + SerialUSB.print(numeric_limits<long long>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<long long>::max()); + + SerialUSB.print("unsigned long long: "); + SerialUSB.println(numeric_limits<unsigned long long>::max()); +} + +void test_base_arithmetic(void) { + SerialUSB.println("Base arithmetic:"); + + SerialUSB.print("Binary: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), BIN); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), BIN); + + SerialUSB.print("Octal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), OCT); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), OCT); + + SerialUSB.print("Decimal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), DEC); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), DEC); + + SerialUSB.print("Hexadecimal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), HEX); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), HEX); +} + +void test_floating_point(void) { + double dmax = numeric_limits<double>::max(); + + SerialUSB.println("Floating point:"); + + SerialUSB.print("sizeof double: "); + SerialUSB.println(sizeof(double)); + SerialUSB.print("println(-5.67): "); + SerialUSB.println(-5.67); + SerialUSB.print("println((double)(LLONG_MAX - 10)): "); + SerialUSB.println((double)(numeric_limits<long long>::max() - 10)); + SerialUSB.print("println((double)(LLONG_MAX - 10)) from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits<long long>::max() - 10)); + SerialUSB.println(buf); + SerialUSB.print("println((double)LLONG_MAX / 2): "); + SerialUSB.println((double)(numeric_limits<long long>::max()) / 2); + SerialUSB.print("println((double)LLONG_MAX / 2) from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits<long long>::max()) / 2); + SerialUSB.println(buf); + SerialUSB.print("DBL_MAX: "); + SerialUSB.println(dmax); + SerialUSB.print("DBL_MAX from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", dmax); + SerialUSB.println(buf); + SerialUSB.print("-DBL_MAX / 2: "); + SerialUSB.println(-dmax / 2.0); + SerialUSB.print("-DBL_MAX / 2 from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", -dmax / 2.0); + SerialUSB.println(buf); + SerialUSB.print("Double epsilon, round error: "); + SerialUSB.print(numeric_limits<double>::epsilon()); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<double>::round_error()); + + SerialUSB.println(); + + float fmax = numeric_limits<float>::max(); + + SerialUSB.print("sizeof float: "); + SerialUSB.println(sizeof(float)); + SerialUSB.print("println(-5.67f): "); + SerialUSB.println(-5.67f); + SerialUSB.print("Float max: "); + SerialUSB.println(fmax); + SerialUSB.print("Float epsilon, round error: "); + SerialUSB.print(numeric_limits<float>::epsilon()); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<float>::round_error()); +} + +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 new file mode 100644 index 0000000..e86372a --- /dev/null +++ b/examples/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 "wirish.h" + +#include "ring_buffer.h" + +#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 index d7fbf7a..adc9c3e 100644 --- a/examples/test-serial-flush.cpp +++ b/examples/test-serial-flush.cpp @@ -1,44 +1,37 @@ -// Tests the "flush" Serial function +/* + * Tests the "flush" Serial function. + */ #include "wirish.h" -void setup() -{ - /* Send a message out USART2 */ - Serial2.begin(9600); - Serial2.println("Hello world!"); +void setup() { + Serial1.begin(9600); + Serial1.println("Hello world!"); } -int toggle = 0; - void loop() { - - Serial2.println("This is the first line."); - Serial2.end(); - Serial2.println("This is the second line."); - - Serial2.begin(9600); - Serial2.println("Waiting for multiple input..."); - while(Serial2.available() < 5) { } - Serial2.println(Serial2.read()); - Serial2.println(Serial2.read()); - Serial2.flush(); - if(Serial2.available()) { - Serial2.println("FAIL! Still had junk in the buffer..."); + 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 object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-serialusb.cpp b/examples/test-serialusb.cpp index 9d0a862..15ab913 100644 --- a/examples/test-serialusb.cpp +++ b/examples/test-serialusb.cpp @@ -1,81 +1,74 @@ -// Sample main.cpp file. Blinks an LED, sends a message out USART2 -// and turns on PWM on pin 2 +// Tests SerialUSB functionality. #include "wirish.h" #include "usb.h" -#define LED_PIN 13 -#define BUT_PIN 38 - -uint32 state = 0; #define QUICKPRINT 0 #define BIGSTUFF 1 #define NUMBERS 2 #define SIMPLE 3 #define ONOFF 4 +uint32 state = 0; -void setup() -{ +void setup() { /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - - /* Set up the Button */ - pinMode(BUT_PIN, INPUT_PULLUP); + pinMode(BOARD_LED_PIN, OUTPUT); + /* Set up Serial2 for use as a debug channel */ Serial2.begin(9600); - Serial2.println("Hello world! This is the debug channel."); + Serial2.println("This is the debug channel. Press any key."); + while (!Serial2.available()) + ; + Serial2.read(); } -int toggle = 0; - uint8 c1 = '-'; void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(1000); - if(digitalRead(BUT_PIN)) { - while(digitalRead(BUT_PIN)) {}; + if (Serial2.available()) { + Serial2.read(); state++; } - - switch(state) { + + switch (state) { case QUICKPRINT: - for(int i = 0; i<30; i++) { - usbSendBytes(&c1,1); + for (int i = 0; i < 30; i++) { + usbSendBytes(&c1, 1); SerialUSB.print('.'); SerialUSB.print('|'); } - Serial2.println(SerialUSB.pending(),DEC); + Serial2.println(SerialUSB.pending(), DEC); SerialUSB.println(); break; case BIGSTUFF: - SerialUSB.println("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); - SerialUSB.println((uint32)123456789,DEC); + SerialUSB.println("0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "012345678901234567890"); + SerialUSB.println((int64)123456789, DEC); SerialUSB.println(3.1415926535); - Serial2.println(SerialUSB.pending(),DEC); + 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(1, DEC); + Serial2.println(1, DEC); + SerialUSB.println(-1, DEC); + Serial2.println(-1, DEC); SerialUSB.println(3.14159265); Serial2.println(3.14159265); - SerialUSB.println(3.14159265,9); - Serial2.println(3.14159265,9); - SerialUSB.println(123456789,DEC); - Serial2.println(123456789,DEC); - SerialUSB.println(-123456789,DEC); - Serial2.println(-123456789,DEC); - SerialUSB.println(65535,HEX); - Serial2.println(65535,HEX); + 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')"); @@ -92,8 +85,8 @@ void loop() { SerialUSB.print("hij\n\r"); SerialUSB.write(' '); SerialUSB.println(); - Serial2.println("Trying println(123456789,DEC)"); - SerialUSB.println(123456789); + 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\")"); @@ -103,12 +96,12 @@ void loop() { Serial2.println("Shutting down..."); SerialUSB.println("Shutting down..."); SerialUSB.end(); - Serial2.println("Waiting 4seconds..."); + Serial2.println("Waiting 4 seconds..."); delay(4000); Serial2.println("Starting up..."); SerialUSB.begin(); SerialUSB.println("Hello World!"); - Serial2.println("Waiting 4seconds..."); + Serial2.println("Waiting 4 seconds..."); delay(4000); state++; break; @@ -118,18 +111,16 @@ void loop() { } // 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() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; } - diff --git a/examples/test-servo.cpp b/examples/test-servo.cpp new file mode 100644 index 0000000..b6b8cd5 --- /dev/null +++ b/examples/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 <stdio.h> + +#include "wirish.h" + +#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 index 845547d..959c1b8 100644 --- a/examples/test-session.cpp +++ b/examples/test-session.cpp @@ -1,112 +1,93 @@ // Interactive Test Session for LeafLabs Maple // Copyright (c) 2010 LeafLabs LLC. // -// Useful for testing Maple features and troubleshooting. Select a COMM port -// (SerialUSB or Serial2) before compiling and then enter 'h' at the prompt -// for a list of commands. +// Useful for testing Maple features and troubleshooting. +// Communicates over SerialUSB. #include "wirish.h" -#define LED_PIN BOARD_LED_PIN -#define PWM_PIN 3 - -// choose your weapon -#define COMM SerialUSB -//#define COMM Serial2 -//#define COMM Serial3 - - +// ASCII escape character #define ESC ((uint8)27) -int rate = 0; - -#if defined(BOARD_maple) -const uint8 pwm_pins[] = - {0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 24, 25, 27, 28}; -const uint8 adc_pins[] = - {0, 1, 2, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 27, 28}; +// Default USART baud rate +#define BAUD 9600 -#elif defined(BOARD_maple_mini) -const uint8 pwm_pins[] = {3, 4, 5, 8, 9, 10, 11, 15, 16, 25, 26, 27}; -const uint8 adc_pins[] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 33}; // NB: 33 is LED +uint8 gpio_state[BOARD_NR_GPIO_PINS]; -#elif defined(BOARD_maple_native) -const uint8 pwm_pins[] = {12, 13, 14, 15, 22, 23, 24, 25, 37, 38, 45, - 46, 47, 48, 49, 50, 53, 54}; -const uint8 adc_pins[] = {6, 7, 8, 9, 10, 11, - /* the following are on ADC3, which lacks support: - 39, 40, 41, 42, 43, 45, */ - 46, 47, 48, 49, 50, 51, 52, 53, 54}; - -#else -#error "Board type has not been selected correctly" - -#endif - -uint8 gpio_state[NR_GPIO_PINS]; - -const char* const dummy_dat = ("qwertyuiopasdfghjklzxcvbnmmmmmm,./1234567890-=" - "qwertyuiopasdfghjklzxcvbnm,./1234567890"); +const char* dummy_data = ("qwertyuiopasdfghjklzxcvbnmmmmmm,./1234567890-=" + "qwertyuiopasdfghjklzxcvbnm,./1234567890"); +// 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_writes(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_pwm_sweep(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 do_serials(HardwareSerial **serials, int n, unsigned baud); +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(9600); - Serial2.begin(9600); - Serial3.begin(9600); - - // Send a message out over COMM interface - COMM.println(" "); - COMM.println(" __ __ _ _"); - COMM.println(" | \\/ | __ _ _ __ | | ___| |"); - COMM.println(" | |\\/| |/ _` | '_ \\| |/ _ \\ |"); - COMM.println(" | | | | (_| | |_) | | __/_|"); - COMM.println(" |_| |_|\\__,_| .__/|_|\\___(_)"); - COMM.println(" |_|"); - COMM.println(" by leaflabs"); - COMM.println(""); - COMM.println(""); - COMM.println("Maple interactive test program (type '?' for help)"); - COMM.println("----------------------------------------------------------"); - COMM.print("> "); + 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(100); + delay(250); - while(COMM.available()) { - uint8 input = COMM.read(); - COMM.println(input); + while (SerialUSB.available()) { + uint8 input = SerialUSB.read(); + SerialUSB.println(input); switch(input) { case '\r': break; case ' ': - COMM.println("spacebar, nice!"); + SerialUSB.println("spacebar, nice!"); break; case '?': @@ -128,8 +109,12 @@ void loop () { cmd_serial1_serial3(); break; + case 'E': + cmd_serial1_echo(); + break; + case '.': - while(!COMM.available()) { + while (!SerialUSB.available()) { Serial1.print("."); Serial2.print("."); Serial3.print("."); @@ -150,33 +135,45 @@ void loop () { break; case 'W': - while(!COMM.available()) { - Serial1.print(dummy_dat); - Serial2.print(dummy_dat); - Serial3.print(dummy_dat); + while (!SerialUSB.available()) { + Serial1.print(dummy_data); + Serial2.print(dummy_data); + Serial3.print(dummy_data); } break; case 'U': - COMM.println("Dumping data to USB. Press any key."); - while(!COMM.available()) { - SerialUSB.print(dummy_dat); + SerialUSB.println("Dumping data to USB. Press any key."); + while (!SerialUSB.available()) { + SerialUSB.print(dummy_data); } break; case 'g': - cmd_sequential_gpio_writes(); + 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': - COMM.println("Wiggling D4 as fast as possible in bursts. " + SerialUSB.println("Wiggling D4 as fast as possible in bursts. " "Press any key."); - pinMode(4,OUTPUT); - while(!COMM.available()) { + pinMode(4, OUTPUT); + while (!SerialUSB.available()) { fast_gpio(4); delay(1); } @@ -186,19 +183,19 @@ void loop () { cmd_sequential_pwm_test(); break; - case 'P': - cmd_pwm_sweep(); - break; - case '_': - COMM.println("Delaying for 5 seconds..."); + 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': @@ -206,26 +203,30 @@ void loop () { break; case 'd': - COMM.println("Pulling down D4, D22. Press any key."); - pinMode(22,INPUT_PULLDOWN); - pinMode(4,INPUT_PULLDOWN); - while(!COMM.available()) { + SerialUSB.println("Pulling down D4, D22. Press any key."); + pinMode(22, INPUT_PULLDOWN); + pinMode(4, INPUT_PULLDOWN); + while (!SerialUSB.available()) { continue; } - COMM.println("Pulling up D4, D22. Press any key."); - pinMode(22,INPUT_PULLUP); - pinMode(4,INPUT_PULLUP); - while(!COMM.available()) { + SerialUSB.println("Pulling up D4, D22. Press any key."); + pinMode(22, INPUT_PULLUP); + pinMode(4, INPUT_PULLUP); + while (!SerialUSB.available()) { continue; } - COMM.read(); - pinMode(4,OUTPUT); + SerialUSB.read(); + pinMode(4, OUTPUT); 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': @@ -236,403 +237,662 @@ void loop () { cmd_sequential_adc_reads(); break; + case 'b': + cmd_board_info(); + break; + case '+': cmd_gpio_qa(); break; default: // ------------------------------- - COMM.print("Unexpected: "); - COMM.print(input); - COMM.println(", press h for help."); + SerialUSB.print("Unexpected byte: 0x"); + SerialUSB.print((int)input, HEX); + SerialUSB.println(", press h for help."); } - COMM.print("> "); + SerialUSB.print("> "); } } -void cmd_print_help(void) { - COMM.println(""); - //COMM.println("Command Listing\t(# means any digit)"); - COMM.println("Command Listing"); - COMM.println("\t?: print this menu"); - COMM.println("\th: print this menu"); - COMM.println("\tw: print Hello World on all 3 USARTS"); - COMM.println("\tn: measure noise and do statistics"); - COMM.println("\tN: measure noise and do statistics with background stuff"); - COMM.println("\ta: show realtime ADC info"); - COMM.println("\t.: echo '.' until new input"); - COMM.println("\tu: print Hello World on USB"); - COMM.println("\t_: do as little as possible for a couple seconds (delay)"); - COMM.println("\tp: test all PWM channels sequentially"); - COMM.println("\tW: dump data as fast as possible on all 3 USARTS"); - COMM.println("\tU: dump data as fast as possible on USB"); - COMM.println("\tg: toggle all GPIOs sequentialy"); - COMM.println("\tG: toggle all GPIOs at the same time"); - COMM.println("\tf: toggle GPIO D4 as fast as possible in bursts"); - COMM.println("\tP: simultaneously test all PWM channels with different " - "speeds/sweeps"); - COMM.println("\tr: Monitor and print GPIO status changes"); - COMM.println("\ts: output a sweeping servo PWM on all PWM channels"); - COMM.println("\tm: output data on USART1 and USART3 with various rates"); - COMM.println("\t+: test shield mode (for QA, will disrupt Serial2!)"); - - COMM.println("Unimplemented:"); - COMM.println("\te: do everything all at once until new input"); - COMM.println("\tt: output a 1khz squarewave on all GPIOs"); - COMM.println("\tT: output a 1hz squarewave on all GPIOs"); - COMM.println("\ti: print out a bunch of info about system state"); - COMM.println("\tI: print out status of all headers"); -} - -void measure_adc_noise(uint8 pin) { // TODO - uint16 data[100]; - float mean = 0; - //float stddev = 0; - float delta = 0; - float M2 = 0; - pinMode(pin, INPUT_ANALOG); +// -- Commands ---------------------------------------------------------------- - // variance algorithm from knuth; see wikipedia - // checked against python - for(int i = 0; i<100; i++) { - data[i] = analogRead(pin); - delta = data[i] - mean; - mean = mean + delta/(i+1); - M2 = M2 + delta*(data[i] - mean); - } - - //sqrt is broken? - //stddev = sqrt(variance); - COMM.print("header: D"); COMM.print(pin,DEC); - COMM.print("\tn: "); COMM.print(100,DEC); - COMM.print("\tmean: "); COMM.print(mean); - COMM.print("\tvariance: "); COMM.println(M2/99.0); - pinMode(pin, OUTPUT); +void cmd_print_help(void) { + SerialUSB.println(""); + SerialUSB.println("Command Listing"); + SerialUSB.println("\t?: print this menu"); + 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 on USB"); + 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) { - COMM.println("Taking ADC noise stats..."); + SerialUSB.println("Taking ADC noise stats."); digitalWrite(BOARD_LED_PIN, 0); - for(uint32 i = 0; i<sizeof(adc_pins); i++) { + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { delay(5); - measure_adc_noise(adc_pins[i]); + measure_adc_noise(boardADCPins[i]); } } void cmd_stressful_adc_stats(void) { - COMM.println("Taking ADC noise stats under duress..."); - digitalWrite(BOARD_LED_PIN, 0); - for(uint32 i = 0; i<sizeof(adc_pins); i++) { - // spool up PWM - for(uint32 j = 2; j<(uint32)sizeof(pwm_pins); j++) { - if(adc_pins[i] != pwm_pins[j]) { - pinMode(pwm_pins[j],PWM); - pwmWrite(pwm_pins[j], 1000 + i); + SerialUSB.println("Taking ADC noise stats under duress."); + + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], PWM); + pwmWrite(boardPWMPins[j], 1000 + i); } } - SerialUSB.print(dummy_dat); - SerialUSB.print(dummy_dat); - measure_adc_noise(adc_pins[i]); - for(uint32 j = 2; j<(uint32)sizeof(pwm_pins); j++) { - if(adc_pins[i] != pwm_pins[j]) { - pinMode(pwm_pins[j],OUTPUT); - digitalWrite(pwm_pins[j],0); + + Serial1.print(dummy_data); + + measure_adc_noise(boardADCPins[i]); + + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], OUTPUT); + digitalWrite(boardPWMPins[j], LOW); } } } } 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 - COMM.println("(unimplemented)"); -} - -void fast_gpio(int maple_pin) { - GPIO_Port *port = PIN_MAP[maple_pin].port; - uint32 pin = PIN_MAP[maple_pin].pin; - - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); + SerialUSB.println("Unimplemented."); } void cmd_serial1_serial3(void) { HardwareSerial *serial_1_and_3[] = {&Serial1, &Serial3}; - COMM.println("Testing 57600 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 57600); - COMM.read(); + 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(); - COMM.println("Testing 115200 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 115200); - COMM.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(); - COMM.println("Testing 9600 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 9600); - COMM.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(); - COMM.println("Resetting USART1 and USART3..."); - Serial1.begin(9600); - Serial3.begin(9600); + SerialUSB.println("Resetting USART1 and USART3..."); + Serial1.begin(BAUD); + Serial3.begin(BAUD); } -void do_serials(HardwareSerial **serials, int n, unsigned baud) { - for (int i = 0; i < n; i++) { - serials[i]->begin(9600); - } - while (!COMM.available()) { - for (int i = 0; i < n; i++) { - serials[i]->println(dummy_dat); - if (serials[i]->available()) { - serials[i]->println(serials[i]->read()); - delay(1000); - } - } - } +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) { - COMM.println("Monitoring GPIO read state changes. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(int i = 2; i<NR_GPIO_PINS; i++) { + 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(!COMM.available()) { - for(int i = 2; i<NR_GPIO_PINS; 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]) { - COMM.print("State change on header D"); - COMM.print(i,DEC); - if(current_state) COMM.println(":\tHIGH"); - else COMM.println(":\tLOW"); + 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 = 2; i<NR_GPIO_PINS; i++) { + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, OUTPUT); } } void cmd_sequential_adc_reads(void) { - COMM.print("Sequentially reading each ADC port."); - COMM.println("Press any key for next port, or ESC to stop."); - digitalWrite(LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(adc_pins); i++) { - COMM.print("Reading on header D"); - COMM.print(adc_pins[i], DEC); - COMM.println("..."); - pinMode(adc_pins[i], INPUT_ANALOG); - while(!COMM.available()) { - int sample = analogRead(adc_pins[i]); - COMM.print(adc_pins[i],DEC); - COMM.print("\t"); - COMM.print(sample,DEC); - COMM.print("\t"); - COMM.print("|"); - for(int j = 0; j<4096; j+= 100) { - if(sample >= j) COMM.print("#"); - else COMM.print(" "); + 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(" "); + } } - COMM.print("| "); - for(int j = 0; j<12; j++) { - if(sample & (1 << (11-j))) COMM.print("1"); - else COMM.print("0"); + SerialUSB.print("| "); + for (int j = 0; j < 12; j++) { + if (sample & (1 << (11 - j))) { + SerialUSB.print("1"); + } else { + SerialUSB.print("0"); + } } - COMM.println(""); + 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; } - pinMode(adc_pins[i], OUTPUT); - digitalWrite(adc_pins[i], 0); - if((uint8)COMM.read() == ESC) break; } + return false; } void cmd_gpio_qa(void) { - COMM.println("Doing QA testing for most GPIO pins..."); - digitalWrite(BOARD_LED_PIN, 0); - for(int i = 0; i<NR_GPIO_PINS; i++) { + 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); - gpio_state[i] = 0; } - COMM.println("Waiting to start..."); - while(digitalRead(0) != 1 && !COMM.available()) { - continue; - } - for(int i=0; i<38; i++) { - if(i == BOARD_LED_PIN) { - COMM.println("Not checking LED"); + + 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; } - COMM.print("Checking D"); - COMM.print(i,DEC); - while(digitalRead(i) == 0) continue; - for(int j=0; j<NR_GPIO_PINS; j++) { - if(digitalRead(j) && j!=i) { - COMM.print(": FAIL ########################### D"); - COMM.println(j, DEC); - break; - } + 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; } - while(digitalRead(i) == 1) continue; - for(int j=0; j<NR_GPIO_PINS; j++) { - if(digitalRead(j) && j!=i) { - COMM.print(": FAIL ########################### D"); - COMM.println(j, DEC); - break; - } + + pin_ok = pin_ok && test_single_pin_is_high(not_a_pin, "is still HIGH"); + + if (pin_ok) { + SerialUSB.println(": ok"); } - COMM.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<NR_GPIO_PINS; i++) { + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + pinMode(i, OUTPUT); - digitalWrite(i, 0); + digitalWrite(i, LOW); + gpio_state[i] = 0; } } -void cmd_sequential_gpio_writes(void) { - COMM.print("Sequentially toggling all pins except D0, D1. "); - COMM.println("Anything for next, ESC to stop."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { - COMM.print("GPIO write out on header D"); - COMM.print(i, DEC); - COMM.println("..."); +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(!COMM.available()); - digitalWrite(i, 0); - if((uint8)COMM.read() == ESC) break; + } while (!SerialUSB.available()); + + digitalWrite(i, LOW); + if (SerialUSB.read() == ESC) + break; } } void cmd_gpio_toggling(void) { - COMM.println("Toggling all GPIOs simultaneously. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { + 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(!COMM.available()) { - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { + + while (!SerialUSB.available()) { + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; togglePin(i); } } - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { - digitalWrite(i, 0); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + digitalWrite(i, LOW); } } -void cmd_sequential_pwm_test(void) { - COMM.println("Sequentially testing PWM on all possible headers " - "except D0 and D1."); - COMM.println("Press any key for next, ESC to stop."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - COMM.print("PWM out on header D"); - COMM.print(pwm_pins[i], DEC); - COMM.println("..."); - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 16000); - while(!COMM.available()) { delay(10); } - pinMode(pwm_pins[i], OUTPUT); - digitalWrite(pwm_pins[i], 0); - if((uint8)COMM.read() == ESC) break; - } -} - -void cmd_pwm_sweep(void) { - COMM.println("Testing all PWM ports with a sweep. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX pins - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 4000); +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); } - while(!COMM.available()) { - rate += 20; - if(rate > 65500) rate = 0; - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pwmWrite(pwm_pins[i], rate); + + 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]); } - delay(1); } - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], OUTPUT); + + 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) { - COMM.println("Testing all PWM headers with a servo sweep. Press any key."); - COMM.println(); - digitalWrite(BOARD_LED_PIN, 0); + SerialUSB.println("Testing all PWM headers with a servo sweep. " + "Press any key to stop."); + SerialUSB.println(); + + disable_usarts(); init_all_timers(21); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 4000); + + 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 - rate = 4096; - while(!COMM.available()) { + int rate = 4096; + while (!SerialUSB.available()) { rate += 20; - if(rate > 5734) rate = 4096; - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pwmWrite(pwm_pins[i], rate); + 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 = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], OUTPUT); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(boardPWMPins[i], OUTPUT); } init_all_timers(1); - Serial2.begin(9600); - COMM.println("(reset serial port)"); + 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) { + uint16 data[100]; + 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 i = 0; i < 100; i++) { + data[i] = analogRead(pin); + delta = data[i] - mean; + mean = mean + delta / (i + 1); + M2 = M2 + delta * (data[i] - mean); + } + + SerialUSB.print("header: D"); + SerialUSB.print(pin, DEC); + SerialUSB.print("\tn: "); + SerialUSB.print(100, DEC); + SerialUSB.print("\tmean: "); + SerialUSB.print(mean); + SerialUSB.print("\tvariance: "); + SerialUSB.println(M2 / 99.0); + 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) { - timer_init(TIMER1, prescale); - timer_init(TIMER2, prescale); - timer_init(TIMER3, prescale); -#if NR_TIMERS >= 4 - timer_init(TIMER4, prescale); -#elif NR_TIMERS >= 8 // TODO test this on maple native - timer_init(TIMER5, prescale); - timer_init(TIMER6, prescale); - timer_init(TIMER7, prescale); - timer_init(TIMER8, 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 object that need libmaple may fail. -__attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); while (1) { diff --git a/examples/test-spi-roundtrip.cpp b/examples/test-spi-roundtrip.cpp new file mode 100644 index 0000000..8fe97b9 --- /dev/null +++ b/examples/test-spi-roundtrip.cpp @@ -0,0 +1,194 @@ +/* + * 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 <mbolivar@leaflabs.com> + */ + +#include "wirish.h" + +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); + while (!SerialUSB.available()) + ; + 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 index 247892d..78c7307 100644 --- a/examples/test-systick.cpp +++ b/examples/test-systick.cpp @@ -1,59 +1,48 @@ // Tests the SysTick enable/disable functions -// + #include "wirish.h" #include "systick.h" -#define LED_PIN 13 -#define PWM_PIN 2 -#define BUT 38 - -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - - /* Turn on PWM on pin PWM_PIN */ - pinMode(PWM_PIN, PWM); - pwmWrite(PWM_PIN, 0x8000); - - pinMode(BUT, INPUT_PULLDOWN); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); } -int toggle = 0; +bool disable = true; long time = 0; void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + volatile int i = 0; + toggleLED(); // An artificial delay - int16 i = 1; - float j = 1; - for(i=0; i<6553; i++) { - j = sqrt(j) + 1; - } - - if(digitalRead(BUT)) { - systick_disable(); - } else { - systick_resume(); + 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(micros()); // there is a bug with this 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() { +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp index ccba251..247cc57 100644 --- a/examples/test-timers.cpp +++ b/examples/test-timers.cpp @@ -1,8 +1,7 @@ -// Program to test the wirish timers implementation +// Program to test the timer.h implementation's essential functionality. #include "wirish.h" - -#define LED_PIN 13 +#include "timer.h" void handler1(void); void handler2(void); @@ -12,7 +11,6 @@ void handler4(void); void handler3b(void); void handler4b(void); -int toggle = 0; int t; int count1 = 0; @@ -28,222 +26,271 @@ uint16 val2 = 10000; uint16 val3 = 10000; uint16 val4 = 10000; -HardwareTimer Timers[] = {Timer1, Timer2, Timer3, Timer4}; +// FIXME [0.1.0] high density timer test (especially basic timers + DAC) +timer_dev *timers[] = {TIMER1, TIMER2, TIMER3, TIMER4}; +voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; + +void initTimer(timer_dev *dev); +void setTimerPeriod(timer_dev *dev, uint32 period_us); +void testSetTimerPeriod(uint32 period); +void testTimerChannels(timer_dev *dev); +int timerNumber(timer_dev *dev); -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); +void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); // Setup the button as input - pinMode(38, INPUT_PULLUP); - - /* Send a message out USART2 */ - //SerialUSB.begin(9600); - SerialUSB.println("Begining timer test..."); - for(int t=0; t<4; t++) { - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - } - - // Wait for user to attach... - delay(2000); + pinMode(BOARD_BUTTON_PIN, INPUT); + + // Send a message out Serial2 + Serial2.begin(115200); + Serial2.println("*** Initializing timers..."); + timer_foreach(initTimer); + Serial2.println("*** Done. Beginning timer test."); } void loop() { - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setCount/getCount"); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - SerialUSB.println("Timer1.setCount(1234)"); - Timer1.setCount(1234); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - // This tests whether the pause/resume functions work; when BUT is held - // down Timer4 is in the "pause" state and the timer doesn't increment, so - // the final counts should reflect the ratio of time that BUT was held down - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing Pause/Resume; button roughly controls Timer4"); + Serial2.println("-----------------------------------------------------"); + + Serial2.println("Testing timer_get_count()/timer_set_count()"); + Serial2.print("TIMER1 count = "); + Serial2.println(timer_get_count(TIMER1)); + Serial2.println("timer_set_count(TIMER1, 1234)"); + timer_set_count(TIMER1, 1234); + Serial2.print("timer_get_count(TIMER1) = "); + Serial2.println(timer_get_count(TIMER1)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing pause/resume; button roughly controls TIMER4"); + // when BUT is held down, TIMER4 is in the "pause" state and the + // timer doesn't increment, so the final counts should reflect the + // ratio of time that BUT was held down. count3 = 0; count4 = 0; - Timer3.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer3.pause(); - Timer4.pause(); - Timer3.setCount(0); - Timer4.setCount(0); - Timer3.setOverflow(30000); - Timer4.setOverflow(30000); - Timer3.setCompare1(1000); - Timer4.setCompare1(1000); - Timer3.attachCompare1Interrupt(handler3b); - Timer4.attachCompare1Interrupt(handler4b); - Timer3.resume(); - Timer4.resume(); - SerialUSB.println("~4 seconds..."); - for(int i = 0; i<4000; i++) { - if(digitalRead(38)) { - Timer4.pause(); + timer_set_mode(TIMER3, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_pause(TIMER3); + timer_pause(TIMER4); + timer_set_count(TIMER3, 0); + timer_set_count(TIMER4, 0); + timer_set_reload(TIMER3, 30000); + timer_set_reload(TIMER4, 30000); + timer_set_compare(TIMER3, 1, 1000); + timer_set_compare(TIMER4, 1, 1000); + timer_attach_interrupt(TIMER3, TIMER_CC1_INTERRUPT, handler3b); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + timer_resume(TIMER3); + timer_resume(TIMER4); + + Serial2.println("Testing for ~4 seconds..."); + for(int i = 0; i < 4000; i++) { + if (isButtonPressed()) { + timer_pause(TIMER4); } else { - Timer4.resume(); + timer_resume(TIMER4); } delay(1); } - Timer3.setChannel1Mode(TIMER_DISABLED); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - - // These test the setPeriod auto-configure functionality - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setPeriod"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(10); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 10ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/10ms = 200000)"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setPeriod(30000); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPeriod(300000); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 300000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/300000ms ~ 6.7)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPrescaleFactor(33); - Timer4.setOverflow(65454); - Timer4.pause(); - Timer4.setCount(0); - Timer4.setCompare1(1); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + + timer_set_mode(TIMER3, TIMER_CH1, TIMER_DISABLED); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + + Serial2.print("TIMER3 count: "); + Serial2.println(timer_get_count(TIMER3)); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing setTimerPeriod()"); + testSetTimerPeriod(10); + testSetTimerPeriod(30000); + testSetTimerPeriod(300000); + testSetTimerPeriod(30000); + + Serial2.println("Sanity check (with hand-coded reload and prescaler for " + "72 MHz timers):"); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_prescaler(TIMER4, 33); + timer_set_reload(TIMER4, 65454); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_set_compare(TIMER4, TIMER_CH1, 1); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period 30000ms, wait 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(30000); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(count4); + Serial2.println(" (Should be around 2sec/30000ms ~ 67)"); + + // Test all the individual timer channels + timer_foreach(testTimerChannels); +} + +void initTimer(timer_dev *dev) { + switch (dev->type) { + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Initializing timer "); + Serial2.println(timerNumber(dev)); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + } + Serial2.println("Done."); + break; + case TIMER_BASIC: + break; + } +} + +void testSetTimerPeriod(uint32 period) { + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_compare(TIMER4, TIMER_CH1, 1); + setTimerPeriod(TIMER4, period); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period "); + Serial2.print(period); + Serial2.print(" ms. Waiting 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - // This section is to touch every channel of every timer. The output - // ratios should reflect the ratios of the rate variables. Demonstrates - // that over time the actual timing rates get blown away by other system - // interrupts. - for(t=0; t<4; t++) { - toggle ^= 1; digitalWrite(LED_PIN, toggle); - delay(100); - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.print("Testing Timer "); SerialUSB.println(t+1); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + Serial2.print(" (Should be around 2 sec / "); + Serial2.print(period); + Serial2.print(" ms = "); + Serial2.print(double(2) / period * 1000); + Serial2.println(", modulo delays due to interrupts)"); +} + +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; +#ifdef STM32_HIGH_DENSITY + case RCC_TIMER5: + return 5; + case RCC_TIMER6: + return 6; + case RCC_TIMER7: + return 7; + case RCC_TIMER8: + return 8; +#endif + default: + ASSERT(0); + return 0; + } +} + +/* This function touches every channel of a given timer. The output + * ratios should reflect the ratios of the rate variables. It + * demonstrates that, over time, the actual timing rates get blown + * away by other system interrupts. */ +void testTimerChannels(timer_dev *dev) { + t = timerNumber(dev); + toggleLED(); + delay(100); + Serial2.println("-----------------------------------------------------"); + switch (dev->type) { + case TIMER_BASIC: + Serial2.print("NOT testing channels for basic timer "); + Serial2.println(t); + break; + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Testing channels for timer "); + Serial2.println(t); + timer_pause(dev); count1 = count2 = count3 = count4 = 0; - Timers[t].setOverflow(0xFFFF); - Timers[t].setPrescaleFactor(1); - Timers[t].setCompare1(65535); - Timers[t].setCompare2(65535); - Timers[t].setCompare3(65535); - Timers[t].setCompare4(65535); - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - Timers[t].attachCompare1Interrupt(handler1); - Timers[t].attachCompare2Interrupt(handler2); - Timers[t].attachCompare3Interrupt(handler3); - Timers[t].attachCompare4Interrupt(handler4); - Timers[t].resume(); + timer_set_reload(dev, 0xFFFF); + timer_set_prescaler(dev, 1); + for (int c = 1; c <= 4; c++) { + timer_set_compare(dev, c, 65535); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + timer_attach_interrupt(dev, c, handlers[c - 1]); + } + timer_resume(dev); delay(3000); - Timers[t].setChannel1Mode(TIMER_DISABLED); - Timers[t].setChannel2Mode(TIMER_DISABLED); - Timers[t].setChannel3Mode(TIMER_DISABLED); - Timers[t].setChannel4Mode(TIMER_DISABLED); - SerialUSB.print("Count1: "); SerialUSB.println(count1); - SerialUSB.print("Count2: "); SerialUSB.println(count2); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_DISABLED); + } + Serial2.print("Channel 1 count: "); Serial2.println(count1); + Serial2.print("Channel 2 count: "); Serial2.println(count2); + Serial2.print("Channel 3 count: "); Serial2.println(count3); + Serial2.print("Channel 4 count: "); Serial2.println(count4); + break; + } +} + +// FIXME [0.1.0] move some incarnation of this into timer.h +void setTimerPeriod(timer_dev *dev, uint32 period_us) { + if (!period_us) { + // FIXME handle this case + ASSERT(0); + return; } + uint32 cycles = period_us * CYCLES_PER_MICROSECOND; + uint16 pre = (uint16)((cycles >> 16) + 1); + timer_set_prescaler(dev, pre); + timer_set_reload(dev, cycles / pre - 1); } void handler1(void) { val1 += rate1; - Timers[t].setCompare1(val1); + timer_set_compare(timers[t], TIMER_CH1, val1); count1++; -} +} + void handler2(void) { val2 += rate2; - Timers[t].setCompare2(val2); + timer_set_compare(timers[t], TIMER_CH2, val2); count2++; -} +} + void handler3(void) { val3 += rate3; - Timers[t].setCompare3(val3); + timer_set_compare(timers[t], TIMER_CH3, val3); count3++; -} +} + void handler4(void) { val4 += rate4; - Timers[t].setCompare4(val4); + timer_set_compare(timers[t], TIMER_CH4, val4); count4++; -} +} void handler3b(void) { count3++; -} +} + void handler4b(void) { count4++; -} +} +__attribute__((constructor)) void premain() { + init(); +} int main(void) { - init(); setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-usart-dma.cpp b/examples/test-usart-dma.cpp new file mode 100644 index 0000000..b9c03f1 --- /dev/null +++ b/examples/test-usart-dma.cpp @@ -0,0 +1,127 @@ +/** + * @file test-usart-dma.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * + * 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 typing after filling the buffer, you'll overwrite + * earlier bytes; this may happen before those earlier bytes are done + * printing. + * + * This code is released into the public domain. + */ + +#include "dma.h" +#include "usart.h" +#include "gpio.h" + +#include "wirish.h" + +#define BAUD 9600 + +#define USART USART2 +#define USART_HWSER Serial2 +#define USART_DMA_DEV DMA1 +#define USART_RX_DMA_CHANNEL DMA_CH6 +#define USART_TX BOARD_USART2_TX_PIN +#define USART_RX BOARD_USART2_RX_PIN + +#define BUF_SIZE 8 +uint8 rx_buf[BUF_SIZE]; + +dma_irq_cause irq_cause; + +__io uint32 irq_fired = 0; + +void init_usart(void); +void init_dma_xfer(void); +void rx_dma_irq(void); + +void setup(void) { + pinMode(BOARD_LED_PIN, OUTPUT); + + init_dma_xfer(); + init_usart(); +} + +void loop(void) { + toggleLED(); + delay(100); + + dma_channel_reg_map *ch_regs = dma_channel_regs(USART_DMA_DEV, + USART_RX_DMA_CHANNEL); + if (irq_fired) { + USART_HWSER.println("** IRQ **"); + while (true) + ; + } + USART_HWSER.print("["); + USART_HWSER.print(millis()); + USART_HWSER.print("]\tISR bits: 0x"); + uint8 isr_bits = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_CHANNEL); + USART_HWSER.print((int32)isr_bits, HEX); + USART_HWSER.print("\tCCR: 0x"); + USART_HWSER.print((int64)ch_regs->CCR, HEX); + USART_HWSER.print("\tCNDTR: 0x"); + USART_HWSER.print((int64)ch_regs->CNDTR, HEX); + USART_HWSER.print("\tBuffer contents: "); + for (int i = 0; i < BUF_SIZE; i++) { + USART_HWSER.print('\''); + USART_HWSER.print(rx_buf[i]); + USART_HWSER.print('\''); + if (i < BUF_SIZE - 1) USART_HWSER.print(", "); + } + USART_HWSER.println(); + if (isr_bits == 0x7) { + USART_HWSER.println("** Clearing ISR bits."); + dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_CHANNEL); + } + + irq_fired = 0; +} + +/* Configure USART receiver for use with DMA */ +void init_usart(void) { + USART_HWSER.begin(BAUD); + USART->regs->CR3 = USART_CR3_DMAR; +} + +/* Configure DMA transmission */ +void init_dma_xfer(void) { + dma_init(USART_DMA_DEV); + dma_setup_transfer(USART_DMA_DEV, USART_RX_DMA_CHANNEL, + &USART->regs->DR, DMA_SIZE_8BITS, + rx_buf, DMA_SIZE_8BITS, + (DMA_MINC_MODE | DMA_CIRC_MODE | DMA_TRNS_CMPLT + )); + dma_set_num_transfers(USART_DMA_DEV, USART_RX_DMA_CHANNEL, BUF_SIZE); + // Currently not working: + // dma_attach_interrupt(USART_DMA_DEV, USART_RX_DMA_CHANNEL, rx_dma_irq); + dma_enable(USART_DMA_DEV, USART_RX_DMA_CHANNEL); +} + +void rx_dma_irq(void) { +} + +// 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/vga-leaf.cpp b/examples/vga-leaf.cpp index d1c6d7d..f31dc87 100644 --- a/examples/vga-leaf.cpp +++ b/examples/vga-leaf.cpp @@ -1,21 +1,21 @@ /* - Crude VGA Output + VGA Output - Outputs a red and white leaf to VGA. This implementation is crude and noisy, - but a fun demo. It should run most VGA monitors at 640x480, though it does - not follow the timing spec very carefully. Real twisted or shielded wires, - proper grounding, and not doing this on a breadboard are recommended (but - it seems to work ok without). + Outputs a red and white leaf to VGA. It should run most VGA monitors + at 640x480, though it does not follow the timing spec very + carefully. Real twisted or shielded wires, proper grounding, and not + doing this on a breadboard are recommended (but it seems to work ok + without). - SerialUSB is disabled to get rid of most interrupts (which mess with timing); - the SysTick is probably the source of the remaining flickers. This means that - you have to use perpetual bootloader or the reset button to flash new - programs. + SerialUSB and SysTick are disabled to get rid of the most frequently + occurring interrupts (which mess with timing). This means that you + have to use perpetual bootloader mode or the reset button to flash + new programs. How to wire this to a VGA port: - D5 via ~200ohms to VGA Red (1) - D6 via ~200ohms to VGA Green (2) - D7 via ~200ohms to VGA Blue (3) + D6 via ~200ohms to VGA Red (1) + D7 via ~200ohms to VGA Green (2) + D8 via ~200ohms to VGA Blue (3) D11 to VGA VSync (14) (swapped?) D12 to VGA HSync (13) (swapped?) GND to VGA Ground (5) @@ -24,48 +24,65 @@ See also: - http://pinouts.ru/Video/VGA15_pinout.shtml - http://www.epanorama.net/documents/pc/vga_timing.html - + Created 20 July 2010 By Bryan Newbold for LeafLabs This code is released with no strings attached. - */ -#include "wirish.h" +// FIXME: generalize for Native and Mini -#define LED_PIN 13 +#include "wirish.h" -// Pinouts -#define VGA_R 5 // STM32: B6 -#define VGA_G 6 // STM32: A8 -#define VGA_B 7 // STM32: A9 +// Pinouts -- you also must change the GPIO macros below if you change +// these +#define VGA_R 6 // STM32: A8 +#define VGA_G 7 // STM32: A9 +#define VGA_B 8 // STM32: A10 #define VGA_V 11 // STM32: A6 #define VGA_H 12 // STM32: A7 -// These low level macros make GPIO writes much faster -#define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) -#define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) -#define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) -#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) -#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) -#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) -#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) -#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) -#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) -#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) +// These low level (and STM32 specific) macros make GPIO writes much +// faster +#define ABSRR ((volatile uint32*)0x40010810) +#define ABRR ((volatile uint32*)0x40010814) + +#define RBIT 8 // (see pinouts) +#define GBIT 9 +#define BBIT 10 + +#define VGA_R_HIGH *ABSRR = BIT(RBIT) +#define VGA_R_LOW *ABRR = BIT(RBIT) +#define VGA_G_HIGH *ABSRR = BIT(GBIT) +#define VGA_G_LOW *ABRR = BIT(GBIT) +#define VGA_B_HIGH *ABSRR = BIT(BBIT) +#define VGA_B_LOW *ABRR = BIT(BBIT) + +#define ON_COLOR BIT(RBIT) +#define OFF_COLOR (BIT(RBIT) | BIT(GBIT) | BIT(BBIT)) + +// set has priority, so clear every bit and set some given bits: +#define VGA_COLOR(c) (*ABSRR = c | \ + BIT(RBIT+16) | BIT(GBIT+16) | BIT(BBIT+16)) + +#define VGA_V_HIGH *ABSRR = BIT(6) +#define VGA_V_LOW *ABRR = BIT(6) +#define VGA_H_HIGH *ABSRR = BIT(7) +#define VGA_H_LOW *ABRR = BIT(7) void isr_porch(void); void isr_start(void); void isr_stop(void); void isr_update(void); -uint8 toggle; uint16 x = 0; // X coordinate uint16 y = 0; // Y coordinate -uint8 v_active = 1; // Are we in the image? +uint16 logo_y = 0; // Y coordinate, mapped into valid logo index (for speed) +bool v_active = true; // Are we in the image? -// 1-bit! -uint8 logo[18][16] = { +const uint8 x_max = 16; +const uint8 y_max = 18; +uint32 logo[y_max][x_max] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, {0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,}, @@ -85,9 +102,11 @@ uint8 logo[18][16] = { {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, }; +HardwareTimer timer(4); + void setup() { // Setup our pins - pinMode(LED_PIN, OUTPUT); + pinMode(BOARD_LED_PIN, OUTPUT); pinMode(VGA_R, OUTPUT); pinMode(VGA_G, OUTPUT); pinMode(VGA_B, OUTPUT); @@ -99,91 +118,108 @@ void setup() { digitalWrite(VGA_H, HIGH); digitalWrite(VGA_V, HIGH); + // Fill the logo array with color patterns corresponding to its + // truth value. Note that we could get more tricky here, since + // there are 3 bits of color. + for (int y = 0; y < y_max; y++) { + for (int x = 0; x < x_max; x++) { + logo[y][x] = logo[y][x] ? ON_COLOR : OFF_COLOR; + } + } + // This gets rid of the majority of the interrupt artifacts; + // there's still a glitch for low values of y, but let's not worry + // about that. (Probably due to the hackish way vsync is done). SerialUSB.end(); - SystemTick.end(); + systick_disable(); // Configure - Timer4.pause(); // while we configure - Timer4.setPrescaleFactor(1); // Full speed - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE); - Timer4.setOverflow(2287); // Total line time - - Timer4.setCompare1(200); - Timer4.attachCompare1Interrupt(isr_porch); - Timer4.setCompare2(300); - Timer4.attachCompare2Interrupt(isr_start); - Timer4.setCompare3(2170); - Timer4.attachCompare3Interrupt(isr_stop); - Timer4.setCompare4(1); // Could be zero I guess - Timer4.attachCompare4Interrupt(isr_update); - - Timer4.setCount(0); // Ready... - Timer4.resume(); // Go! + timer.pause(); // while we configure + timer.setPrescaleFactor(1); // Full speed + timer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH3, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH4, TIMER_OUTPUT_COMPARE); + timer.setOverflow(2287); // Total line time + + timer.setCompare(TIMER_CH1, 200); + timer.attachInterrupt(TIMER_CH1, isr_porch); + timer.setCompare(TIMER_CH2, 300); + timer.attachInterrupt(TIMER_CH2, isr_start); + timer.setCompare(TIMER_CH3, 2170); + timer.attachInterrupt(TIMER_CH3, isr_stop); + timer.setCompare(TIMER_CH4, 1); // Could be zero, I guess + timer.attachInterrupt(TIMER_CH4, isr_update); + + timer.setCount(0); // Ready... + timer.resume(); // Go! } void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(100); // Everything happens in the interrupts! } - // This ISR will end horizontal sync for most of the image and // setup the vertical sync for higher line counts void isr_porch(void) { VGA_H_HIGH; y++; + logo_y = map(y, 0, 478, 0, y_max); // Back to the top - if(y>=523) { - y=1; - v_active = 1; + if (y >= 523) { + y = 1; + logo_y = 0; + v_active = true; return; } // Other vsync stuff below the image - if(y>=492) { + if (y >= 492) { VGA_V_HIGH; return; } - if(y>=490) { + if (y >= 490) { VGA_V_LOW; return; } - if(y>=479) { - v_active = 0; + if (y >= 479) { + v_active = false; return; } } // This is the main horizontal sweep -void isr_start(void) { +void isr_start(void) { // Skip if we're not in the image at all - if(!v_active) { return; } + if (!v_active) { + return; + } // Start Red VGA_R_LOW; VGA_R_HIGH; - // For each "pixel" (really 20 or so screen pixels?) go red or white - for(x=0; x<32; x++) { - if(logo[y/28][x/2]) { - VGA_G_HIGH; - VGA_B_HIGH; - } else { - VGA_G_LOW; - VGA_B_LOW; - } + // For each "pixel", go ON_COLOR or OFF_COLOR + for (x = 0; x < 16; x++) { + // setting the color several times is just an easy way to + // delay, so the image is wider. if you only do the following + // once, you'll be able to make the logo array bigger: + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); } } // End of the horizontal line void isr_stop(void) { - if(!v_active) { return; } + if (!v_active) { + return; + } VGA_R_LOW; VGA_G_LOW; VGA_B_LOW; @@ -194,11 +230,14 @@ void isr_update(void) { VGA_H_LOW; } -int main(void) { +__attribute__((constructor)) void premain() { init(); +} + +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/vga-scope.cpp b/examples/vga-scope.cpp index 0265f9c..b5fa8a5 100644 --- a/examples/vga-scope.cpp +++ b/examples/vga-scope.cpp @@ -1,144 +1,204 @@ -// Low-level, non-wirish demonstration of VGA -// -// Connect a microphone or something less to ANALOG_PIN +/* + VGA Oscilloscope demo. + + Connect a microphone or something like it to ANALOG_PIN (0V -- 3.3V + only; 0.2V -- 3.1V will probably look nicer); an attached VGA + monitor will display the signal roughly in real-time. + + The thick blue line corresponds roughly to 0V. + + This is a fairy crude hack, but it's fun to watch/toy around with. + + SerialUSB and SysTick are disabled to get rid of the most frequently + occurring interrupts (which mess with timing). This means that you + have to use perpetual bootloader mode or the reset button to flash + new programs. + + How to wire this to a VGA port: + D6 via ~200ohms to VGA Red (1) + D7 via ~200ohms to VGA Green (2) + D8 via ~200ohms to VGA Blue (3) + D11 to VGA VSync (14) (swapped?) + D12 to VGA HSync (13) (swapped?) + GND to VGA Ground (5) + GND to VGA Sync Ground (10) + + See also: + - http://pinouts.ru/Video/VGA15_pinout.shtml + - http://www.epanorama.net/documents/pc/vga_timing.html + + This code is released into the public domain. + + Authors: + + Bryan Newbold <bnewbold@leaflabs.com> + Marti Bolivar <mbolivar@leaflabs.com> + */ #include "wirish.h" +#include "systick.h" + +// FIXME: generalize for Native and Mini -#define LED_PIN 13 #define ANALOG_PIN 18 -#define VGA_R 5 // B6 -#define VGA_G 6 // A8 -#define VGA_B 7 // A9 -#define VGA_V 11 // A6 -#define VGA_H 12 // A7 -#define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) -#define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) -#define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) -#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) -#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) -#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) -#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) -#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) -#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) -#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) + +// Pinouts -- you also must change the GPIO macros below if you change +// these +#define VGA_R 6 // STM32: A8 +#define VGA_G 7 // STM32: A9 +#define VGA_B 8 // STM32: A10 +#define VGA_V 11 // STM32: A6 +#define VGA_H 12 // STM32: A7 + +// These low level (and STM32 specific) macros make GPIO writes much +// faster +#define ABSRR ((volatile uint32*)0x40010810) +#define ABRR ((volatile uint32*)0x40010814) + +#define RBIT 8 // (see pinouts) +#define GBIT 9 +#define BBIT 10 + +#define VGA_R_HIGH *ABSRR = BIT(RBIT) +#define VGA_R_LOW *ABRR = BIT(RBIT) +#define VGA_G_HIGH *ABSRR = BIT(GBIT) +#define VGA_G_LOW *ABRR = BIT(GBIT) +#define VGA_B_HIGH *ABSRR = BIT(BBIT) +#define VGA_B_LOW *ABRR = BIT(BBIT) + +#define COLOR_WHITE (BIT(RBIT) | BIT(GBIT) | BIT(BBIT)) +#define COLOR_BLACK 0 +#define COLOR_RED BIT(RBIT) +#define COLOR_GREEN BIT(GBIT) +#define COLOR_BLUE BIT(BBIT) + +#define BORDER_COLOR COLOR_BLUE + +// set has priority, so clear every bit and set some given bits: +#define VGA_COLOR(c) (*ABSRR = c | \ + BIT(RBIT + 16) | BIT(GBIT + 16) | BIT(BBIT + 16)) + +#define VGA_V_HIGH *ABSRR = BIT(6) +#define VGA_V_LOW *ABRR = BIT(6) +#define VGA_H_HIGH *ABSRR = BIT(7) +#define VGA_H_LOW *ABRR = BIT(7) void isr_porch(void); void isr_start(void); void isr_stop(void); void isr_update(void); -void setup() -{ - pinMode(LED_PIN, OUTPUT); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); pinMode(ANALOG_PIN, INPUT_ANALOG); - digitalWrite(LED_PIN, 1); + digitalWrite(BOARD_LED_PIN, 1); pinMode(VGA_R, OUTPUT); pinMode(VGA_G, OUTPUT); pinMode(VGA_B, OUTPUT); pinMode(VGA_V, OUTPUT); pinMode(VGA_H, OUTPUT); - /* Send a message out USART2 */ + // Send a message out USART2 Serial2.begin(9600); - Serial2.println("Video time..."); + Serial2.println("Time to kill the radio star..."); // This gets rid of the majority of the interrupt artifacts; - // a SysTick.end() is required as well + // there's still a glitch for low values of y, but let's not worry + // about that. (Probably due to the hackish way vsync is done). SerialUSB.end(); - + systick_disable(); + digitalWrite(VGA_R, 0); digitalWrite(VGA_G, 0); digitalWrite(VGA_B, 0); - digitalWrite(VGA_H,1); - digitalWrite(VGA_V,1); - - timer_set_prescaler(4,0); - timer_set_mode(4, 1, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 2, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 3, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 4, TIMER_OUTPUTCOMPARE); - timer_set_reload(4, 2287); - timer_set_compare_value(4,1,200); - timer_set_compare_value(4,2,300); - timer_set_compare_value(4,3,2170); // 2219 max... - timer_set_compare_value(4,4,1); - timer_attach_interrupt(4,1,isr_porch); - timer_attach_interrupt(4,2,isr_start); - timer_attach_interrupt(4,3,isr_stop); - timer_attach_interrupt(4,4,isr_update); - - timer_set_count(4,0); + digitalWrite(VGA_H, 1); + digitalWrite(VGA_V, 1); + + timer_pause(TIMER4); + timer_set_prescaler(TIMER4, 0); + timer_set_mode(TIMER4, 1, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 2, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 3, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 4, TIMER_OUTPUT_COMPARE); + timer_set_reload(TIMER4, 2287); + timer_set_compare(TIMER4, 1, 200); + timer_set_compare(TIMER4, 2, 250); + timer_set_compare(TIMER4, 3, 2170); // 2219 max... + timer_set_compare(TIMER4, 4, 1); + timer_attach_interrupt(TIMER4, 1, isr_porch); + timer_attach_interrupt(TIMER4, 2, isr_start); + timer_attach_interrupt(TIMER4, 3, isr_stop); + timer_attach_interrupt(TIMER4, 4, isr_update); + + timer_set_count(TIMER4, 0); + timer_resume(TIMER4); } -int toggle = 0; -uint16 x = 0; uint16 y = 0; uint16 val = 0; -uint8 v_active = 1; -GPIO_Port *portb = GPIOB_BASE; +bool v_active = true; +const uint16 x_max = 60; // empirically (and sloppily) determined void isr_porch(void) { VGA_H_HIGH; y++; - if(y>=523) { - y=1; - v_active = 1; + val = map(analogRead(ANALOG_PIN), 0, 4095, 0, x_max); + if (y >= 523) { + y = 1; + v_active = true; return; } - if(y>=492) { + if (y >= 492) { VGA_V_HIGH; return; } - if(y>=490) { + if (y >= 490) { VGA_V_LOW; return; } - if(y>=479) { // 479 - v_active = 0; + if (y >= 479) { + v_active = false; return; } } void isr_start(void) { - if(!v_active) { return; } - VGA_R_HIGH; - VGA_R_HIGH; - VGA_R_HIGH; - VGA_R_LOW; - //delayMicroseconds(2); - //gpio_write_bit(GPIOA_BASE, 8, 1); // VGA_G - for(x=0; x<(val>>6); x++) { - } - VGA_B_HIGH; - VGA_G_HIGH; - VGA_G_LOW; - VGA_B_LOW; - //VGA_R_HIGH; - //val = (val + analogRead(ANALOG_PIN))/2; - val = analogRead(ANALOG_PIN); - + if (!v_active) { + return; + } + VGA_COLOR(BORDER_COLOR); + for (int x = 0; x < val; x++) { + VGA_COLOR(COLOR_BLACK); + } + VGA_COLOR(COLOR_WHITE); + VGA_COLOR(COLOR_BLACK); } + void isr_stop(void) { - if(!v_active) { return; } - VGA_R_LOW; - VGA_G_LOW; - VGA_B_LOW; + if (!v_active) { + return; + } + VGA_COLOR(COLOR_BLACK); } + void isr_update(void) { VGA_H_LOW; } void loop() { - //val = analogRead(ANALOG_PIN); + toggleLED(); + delay(100); } +__attribute__((constructor)) void premain() { + init(); +} int main(void) { - init(); setup(); - while (1) { + while (true) { loop(); } return 0; |