aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:24:49 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:32:57 -0400
commitf005bd3a5c087e3d5559f2858a1e7898a4f92a8d (patch)
tree0701628a68056f7b5f92d5a5af5f281f58e6a71e /examples
parent761e059962e8f53f3cceef61d65bf2bf3025319a (diff)
parentc6073e4886da4606679bc3e9d770c9cff9390597 (diff)
downloadlibrambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.tar.gz
librambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.zip
Merge branch 'wip-family-support'
Merge the long-lived (too long; future changes like these will need to proceed more incrementally) development branch of libmaple, containing experimental STM32F2 and STM32F1 value line support, into master. This required many changes to the structure of the library. The most important structural reorganizations occurred in: - 954f9e5: moves public headers to include directories - 3efa313: uses "series" instead of "family" - c0d60e3: adds board files to the build system, to make it easier to add new boards - 096d86c: adds build logic for targeting different STM32 series (e.g. STM32F1, STM32F2) This last commit in particular (096d86c) is the basis for the repartitioning of libmaple into portable sections, which work on all supported MCUs, and nonportable sections, which are segregated into separate directories and contain all series-specific code. Moving existing STM32F1-only code into libmaple/stm32f1 and wirish/stm32f1, along with adding equivalents under .../stm32f2 directories, was the principal project of this branch. Important API changes occur in several places. Existing code is still expected to work on STM32F1 targets, but there have been many deprecations. A detailed changelog explaining the situation needs to be prepared. F2 and F1 value line support is not complete; the merge is proceeding prematurely in this respect. We've been getting more libmaple patches from the community lately, and I'm worried that the merge conflicts with the old tree structure will become painful to manage. Conflicts: Makefile Resolved Makefile conflicts manually; this required propagating -Xlinker usage into support/make/target-config.mk. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'examples')
-rw-r--r--examples/blinky.cpp2
-rw-r--r--examples/debug-dtrrts.cpp12
-rw-r--r--examples/freertos-blinky.cpp2
-rw-r--r--examples/fsmc-stress-test.cpp6
-rw-r--r--examples/i2c-mcp4725-dac.cpp145
-rw-r--r--examples/mini-exti-test.cpp2
-rw-r--r--examples/qa-slave-shield.cpp2
-rw-r--r--examples/serial-echo.cpp30
-rw-r--r--examples/spi_master.cpp2
-rw-r--r--examples/test-bkp.cpp6
-rw-r--r--examples/test-dac.cpp4
-rw-r--r--examples/test-fsmc.cpp4
-rw-r--r--examples/test-print.cpp2
-rw-r--r--examples/test-ring-buffer-insertion.cpp4
-rw-r--r--examples/test-serial-flush.cpp2
-rw-r--r--examples/test-serialusb.cpp6
-rw-r--r--examples/test-servo.cpp2
-rw-r--r--examples/test-session.cpp2
-rw-r--r--examples/test-spi-roundtrip.cpp2
-rw-r--r--examples/test-systick.cpp4
-rw-r--r--examples/test-timers.cpp710
-rw-r--r--examples/test-usart-dma.cpp220
-rw-r--r--examples/vga-leaf.cpp2
-rw-r--r--examples/vga-scope.cpp4
24 files changed, 839 insertions, 338 deletions
diff --git a/examples/blinky.cpp b/examples/blinky.cpp
index dd72514..fad71f8 100644
--- a/examples/blinky.cpp
+++ b/examples/blinky.cpp
@@ -1,6 +1,6 @@
// Blinks the built-in LED
-#include "wirish.h"
+#include <wirish/wirish.h>
void setup() {
pinMode(BOARD_LED_PIN, OUTPUT);
diff --git a/examples/debug-dtrrts.cpp b/examples/debug-dtrrts.cpp
index 3829208..75eceef 100644
--- a/examples/debug-dtrrts.cpp
+++ b/examples/debug-dtrrts.cpp
@@ -1,7 +1,7 @@
// Test sketch for figuring out DTR/RTS behavior on different platforms.
-#include "wirish.h"
-#include "usb.h"
+#include <wirish/wirish.h>
+#include "usb_cdcacm.h"
void setup() {
/* Set up the LED to blink */
@@ -10,7 +10,6 @@ void setup() {
/* Send a message out USART2 */
Serial2.begin(9600);
Serial2.println("Debugging DTR/RTS...");
-
}
void loop() {
@@ -18,9 +17,9 @@ void loop() {
delay(100);
Serial2.print("DTR: ");
- Serial2.print(usbGetDTR(), DEC);
+ Serial2.print(usb_cdcacm_get_dtr(), DEC);
Serial2.print("\tRTS: ");
- Serial2.println(usbGetRTS(), DEC);
+ Serial2.println(usb_cdcacm_get_rts(), DEC);
}
// Force init to be called *first*, i.e. before static object allocation.
@@ -32,9 +31,8 @@ __attribute__((constructor)) void premain() {
int main(void) {
setup();
- while (1) {
+ while (true) {
loop();
}
return 0;
}
-
diff --git a/examples/freertos-blinky.cpp b/examples/freertos-blinky.cpp
index 6f82d71..2e7c7f7 100644
--- a/examples/freertos-blinky.cpp
+++ b/examples/freertos-blinky.cpp
@@ -1,4 +1,4 @@
-#include "wirish.h"
+#include <wirish/wirish.h>
#include "libraries/FreeRTOS/MapleFreeRTOS.h"
static void vLEDFlashTask(void *pvParameters) {
diff --git a/examples/fsmc-stress-test.cpp b/examples/fsmc-stress-test.cpp
index 509a02f..20d3fa7 100644
--- a/examples/fsmc-stress-test.cpp
+++ b/examples/fsmc-stress-test.cpp
@@ -12,9 +12,9 @@
#include <stdio.h>
#include <stddef.h>
-#include "wirish.h"
-#include "rcc.h"
-#include "fsmc.h"
+#include <wirish/wirish.h>
+#include <libmaple/rcc.h>
+#include <libmaple/fsmc.h>
// -- SRAM config -------------------------------------------------------------
diff --git a/examples/i2c-mcp4725-dac.cpp b/examples/i2c-mcp4725-dac.cpp
new file mode 100644
index 0000000..da9a34e
--- /dev/null
+++ b/examples/i2c-mcp4725-dac.cpp
@@ -0,0 +1,145 @@
+// i2c-mcp4725-dac.cpp
+//
+// Written by Andrew Meyer <ajm@leaflabs.com>
+// Modified by Marti Bolivar <mbolivar@leaflabs.com>
+//
+// Simple program showing how to control an MCP4725 DAC using the
+// libmaple I2C interface. There's an MCP4725 breakout board available
+// on SparkFun:
+//
+// http://www.sparkfun.com/products/8736
+//
+// How to use:
+//
+// 1. Connect the DAC SDA and SCL pins to I2C2, with a pullup
+// resistor (1 KOhm should work) to VCC.
+// 2. Load the sketch and connect to SerialUSB.
+// 3. Press the button.
+//
+// The program then makes sure the DAC is connected properly (during
+// setup()), then has the DAC output a sawtooth wave (with loop()).
+
+#include <wirish/wirish.h>
+#include <libmaple/i2c.h>
+
+#define MCP_ADDR 0x60
+#define MCP_WRITE_DAC 0b01000000
+#define MCP_WRITE_EEPROM 0b01100000
+#define MCP_PD_NORMAL 0b00000000
+#define MCP_PD_1K 0b00000010
+#define MCP_PD_100K 0b00000100
+#define MCP_PD_500K 0b00000110
+
+static uint8 write_msg_data[3];
+static i2c_msg write_msg;
+
+static uint8 read_msg_data[5];
+static i2c_msg read_msg;
+
+/*
+ * DAC control routines
+ */
+
+void mcp_i2c_setup(void) {
+ write_msg.addr = MCP_ADDR;
+ write_msg.flags = 0; // write, 7 bit address
+ write_msg.length = sizeof(write_msg_data);
+ write_msg.xferred = 0;
+ write_msg.data = write_msg_data;
+
+ read_msg.addr = MCP_ADDR;
+ read_msg.flags = I2C_MSG_READ;
+ read_msg.length = sizeof(read_msg_data);
+ read_msg.xferred = 0;
+ read_msg.data = read_msg_data;
+}
+
+void mcp_write_val(uint16 val) {
+ write_msg_data[0] = MCP_WRITE_DAC | MCP_PD_NORMAL;
+ uint16 tmp = val >> 4;
+ write_msg_data[1] = tmp;
+ tmp = (val << 4) & 0x00FF;
+ write_msg_data[2] = tmp;
+
+ i2c_master_xfer(I2C2, &write_msg, 1, 0);
+}
+
+uint16 mcp_read_val() {
+ uint16 tmp = 0;
+
+ i2c_master_xfer(I2C2, &read_msg, 1, 2);
+
+ /* We don't care about the status and EEPROM bytes (0, 3, and 4). */
+ tmp = (read_msg_data[1] << 4);
+ tmp += (read_msg_data[2] >> 4);
+ return tmp;
+}
+
+int mcp_test() {
+ uint16 val;
+ uint16 test_val = 0x0101;
+
+ SerialUSB.println("Testing the MCP4725...");
+ /* Read the value of the register (should be 0x0800 if factory fresh) */
+ val = mcp_read_val();
+ SerialUSB.print("DAC Register = 0x");
+ SerialUSB.println(val, HEX);
+
+ mcp_write_val(test_val);
+ SerialUSB.print("Wrote 0x");
+ SerialUSB.print(test_val, HEX);
+ SerialUSB.println(" to the DAC");
+
+ val = mcp_read_val();
+ SerialUSB.print("DAC Register = 0x");
+ SerialUSB.println(val, HEX);
+
+ if (val != test_val) {
+ SerialUSB.println("ERROR: MCP4725 not responding correctly");
+ return 0;
+ }
+
+ SerialUSB.println("MCP4725 seems to be working");
+ return 1;
+}
+
+/*
+ * setup() and loop()
+ */
+
+void setup() {
+ pinMode(BOARD_BUTTON_PIN, INPUT);
+ i2c_master_enable(I2C2, 0);
+ mcp_i2c_setup();
+
+ waitForButtonPress();
+ ASSERT(mcp_test());
+
+ SerialUSB.println("Starting sawtooth wave");
+}
+
+void loop() {
+ static uint16 dout = 0;
+
+ mcp_write_val(dout);
+
+ dout += 50;
+ if (dout >= 32768) {
+ dout = 0;
+ }
+}
+
+// -- init() and main() -------------------------------------------------------
+
+__attribute__((constructor)) void premain() {
+ init();
+}
+
+int main(void) {
+ setup();
+
+ while (true) {
+ loop();
+ }
+ return 0;
+}
diff --git a/examples/mini-exti-test.cpp b/examples/mini-exti-test.cpp
index 84b323e..54a4dd0 100644
--- a/examples/mini-exti-test.cpp
+++ b/examples/mini-exti-test.cpp
@@ -10,7 +10,7 @@
#include <stdio.h>
#include <string.h>
-#include "wirish.h"
+#include <wirish/wirish.h>
// test routines
void run_exti_test(void);
diff --git a/examples/qa-slave-shield.cpp b/examples/qa-slave-shield.cpp
index 2da1c04..ec25e49 100644
--- a/examples/qa-slave-shield.cpp
+++ b/examples/qa-slave-shield.cpp
@@ -1,6 +1,6 @@
// Slave mode for Quality Assurance test
-#include "wirish.h"
+#include <wirish/wirish.h>
#define INTER_TOGGLE_DELAY_NORMAL 5
#define INTER_TOGGLE_DELAY_SLOW 80
diff --git a/examples/serial-echo.cpp b/examples/serial-echo.cpp
new file mode 100644
index 0000000..204f011
--- /dev/null
+++ b/examples/serial-echo.cpp
@@ -0,0 +1,30 @@
+// Simple serial port "echo". Send back any received data.
+
+#include <wirish/wirish.h>
+
+// Note: you can change "Serial1" to any other serial port you have on
+// your board.
+
+void setup() {
+ Serial1.begin(115200);
+}
+
+void loop() {
+ while (Serial1.available()) {
+ Serial1.write(Serial1.read());
+ }
+}
+
+// Force init() to be called before anything else.
+__attribute__((constructor)) void premain() {
+ init();
+}
+
+int main(void) {
+ setup();
+
+ while (true) {
+ loop();
+ }
+ return 0;
+}
diff --git a/examples/spi_master.cpp b/examples/spi_master.cpp
index 100fc53..ea6c990 100644
--- a/examples/spi_master.cpp
+++ b/examples/spi_master.cpp
@@ -33,7 +33,7 @@
* Pin 10 is used as slave select.
*/
-#include "wirish.h"
+#include <wirish/wirish.h>
#define NSS 10
diff --git a/examples/test-bkp.cpp b/examples/test-bkp.cpp
index f5957b7..719cac7 100644
--- a/examples/test-bkp.cpp
+++ b/examples/test-bkp.cpp
@@ -1,8 +1,8 @@
#include <stdio.h> // for snprintf()
-#include "wirish.h"
-#include "bkp.h"
-#include "iwdg.h"
+#include <wirish/wirish.h>
+#include <libmaple/bkp.h>
+#include <libmaple/iwdg.h>
void print_bkp_contents();
void write_to_bkp(uint16 val);
diff --git a/examples/test-dac.cpp b/examples/test-dac.cpp
index 40ae5d5..af188cc 100644
--- a/examples/test-dac.cpp
+++ b/examples/test-dac.cpp
@@ -6,8 +6,8 @@
* This file is released into the public domain.
*/
-#include "wirish.h"
-#include "dac.h"
+#include <wirish/wirish.h>
+#include <libmaple/dac.h>
uint16 count = 0;
diff --git a/examples/test-fsmc.cpp b/examples/test-fsmc.cpp
index 22f6975..1621317 100644
--- a/examples/test-fsmc.cpp
+++ b/examples/test-fsmc.cpp
@@ -1,7 +1,7 @@
#include <stddef.h> // for ptrdiff_t
-#include "wirish.h"
-#include "fsmc.h"
+#include <wirish/wirish.h>
+#include <libmaple/fsmc.h>
#ifndef BOARD_maple_native
#error "Sorry, this example only works on Maple Native."
diff --git a/examples/test-print.cpp b/examples/test-print.cpp
index 5477512..bdc1894 100644
--- a/examples/test-print.cpp
+++ b/examples/test-print.cpp
@@ -8,7 +8,7 @@
* This file is released into the public domain.
*/
-#include "wirish.h"
+#include <wirish/wirish.h>
#undef min
#undef max
diff --git a/examples/test-ring-buffer-insertion.cpp b/examples/test-ring-buffer-insertion.cpp
index e86372a..2188b03 100644
--- a/examples/test-ring-buffer-insertion.cpp
+++ b/examples/test-ring-buffer-insertion.cpp
@@ -12,9 +12,9 @@
* This file is released into the public domain.
*/
-#include "wirish.h"
+#include <wirish/wirish.h>
-#include "ring_buffer.h"
+#include <libmaple/ring_buffer.h>
#define BUF_SIZE 64
ring_buffer ring_buf;
diff --git a/examples/test-serial-flush.cpp b/examples/test-serial-flush.cpp
index adc9c3e..409d1f9 100644
--- a/examples/test-serial-flush.cpp
+++ b/examples/test-serial-flush.cpp
@@ -2,7 +2,7 @@
* Tests the "flush" Serial function.
*/
-#include "wirish.h"
+#include <wirish/wirish.h>
void setup() {
Serial1.begin(9600);
diff --git a/examples/test-serialusb.cpp b/examples/test-serialusb.cpp
index 15ab913..098e445 100644
--- a/examples/test-serialusb.cpp
+++ b/examples/test-serialusb.cpp
@@ -1,7 +1,7 @@
// Tests SerialUSB functionality.
-#include "wirish.h"
-#include "usb.h"
+#include <wirish/wirish.h>
+#include "usb_cdcacm.h"
#define QUICKPRINT 0
#define BIGSTUFF 1
@@ -37,7 +37,7 @@ void loop() {
switch (state) {
case QUICKPRINT:
for (int i = 0; i < 30; i++) {
- usbSendBytes(&c1, 1);
+ usb_cdcacm_putc((char)c1, 1);
SerialUSB.print('.');
SerialUSB.print('|');
}
diff --git a/examples/test-servo.cpp b/examples/test-servo.cpp
index b6b8cd5..6f6e3ba 100644
--- a/examples/test-servo.cpp
+++ b/examples/test-servo.cpp
@@ -29,7 +29,7 @@
#include <stdio.h>
-#include "wirish.h"
+#include <wirish/wirish.h>
#include "libraries/Servo/Servo.h"
diff --git a/examples/test-session.cpp b/examples/test-session.cpp
index 6c7cfff..4316cda 100644
--- a/examples/test-session.cpp
+++ b/examples/test-session.cpp
@@ -4,7 +4,7 @@
// Useful for testing Maple features and troubleshooting.
// Communicates over SerialUSB.
-#include "wirish.h"
+#include <wirish/wirish.h>
// ASCII escape character
#define ESC ((uint8)27)
diff --git a/examples/test-spi-roundtrip.cpp b/examples/test-spi-roundtrip.cpp
index 71ae658..ddc9875 100644
--- a/examples/test-spi-roundtrip.cpp
+++ b/examples/test-spi-roundtrip.cpp
@@ -17,7 +17,7 @@
* Author: Marti Bolivar <mbolivar@leaflabs.com>
*/
-#include "wirish.h"
+#include <wirish/wirish.h>
HardwareSPI alice(2);
diff --git a/examples/test-systick.cpp b/examples/test-systick.cpp
index 78c7307..356f302 100644
--- a/examples/test-systick.cpp
+++ b/examples/test-systick.cpp
@@ -1,7 +1,7 @@
// Tests the SysTick enable/disable functions
-#include "wirish.h"
-#include "systick.h"
+#include <wirish/wirish.h>
+#include <libmaple/systick.h>
void setup() {
pinMode(BOARD_LED_PIN, OUTPUT);
diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp
index 247cc57..e646916 100644
--- a/examples/test-timers.cpp
+++ b/examples/test-timers.cpp
@@ -1,288 +1,528 @@
-// Program to test the timer.h implementation's essential functionality.
-
-#include "wirish.h"
-#include "timer.h"
-
-void handler1(void);
-void handler2(void);
-void handler3(void);
-void handler4(void);
-
-void handler3b(void);
-void handler4b(void);
-
-int t;
-
-int count1 = 0;
-int count2 = 0;
-int count3 = 0;
-int count4 = 0;
-uint16 rate1 = 1000;
-uint16 rate2 = 2000;
-uint16 rate3 = 4000;
-uint16 rate4 = 8000;
-uint16 val1 = 10000;
-uint16 val2 = 10000;
-uint16 val3 = 10000;
-uint16 val4 = 10000;
-
-// 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);
+//
+// This is a mostly Wirish-free timer test. Wirish usage is minimized
+// because this is a test of the C timer interface in
+// <libmaple/timer.h>, so it's good if it can be made to work even
+// when most or all of Wirish is missing. Because of that, you may
+// need to customize the following output configuration:
+//
+// Output is printed:
+// - on COMM_USART,
+// - via TX pin on port COMM_USART_PORT, bit COMM_USART_TX_BIT
+// - via RX pin on port COMM_USART_PORT, bit COMM_USART_RX_BIT
+// - at COMM_USART_BAUD baud.
+#define COMM_USART USART6
+#define COMM_USART_BAUD 115200
+#define COMM_USART_PORT GPIOG
+#define COMM_USART_TX_BIT 14
+#define COMM_USART_RX_BIT 9
+// Other optional configuration below.
+
+#include <libmaple/libmaple.h>
+#include <libmaple/gpio.h>
+#include <libmaple/usart.h>
+#include <libmaple/systick.h>
+#include <libmaple/timer.h>
+#include <wirish/boards.h>
+
+//
+// Configuration
+//
+
+// More output if true
+static bool verbose = true;
+
+// Timers to test
+// FIXME use feature test macros for smaller MCUs
+static timer_dev *timers[] = {
+ // Available on all currently supported MCUs
+ TIMER1, TIMER2, TIMER3, TIMER4,
+ // Available on F1 (HD and up), F2
+ TIMER5, TIMER6, TIMER7, TIMER8,
+ // Available on F1 (XL), F2
+ TIMER9, TIMER10, TIMER11, TIMER12, TIMER13, TIMER14,
+};
+
+//
+// Test routines
+//
+
+typedef void (*timer_test_t)(timer_dev *);
+
+static void runTest(const char description[], timer_test_t test);
+static void runTests(void);
+
+static void testGetAndSetCount(timer_dev*);
+static void testPauseAndResume(timer_dev*);
+static void testTimerChannels(timer_dev*);
+
+//
+// Helpers
+//
+
+static void initTimer(timer_dev *dev);
+static int timerNumber(timer_dev *dev);
+// Hack: a systick-based delay, useful until delay_us() is fixed
+static void _delay(uint32 msec);
+// Wirish-less USART initialization routine
+static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx);
+// Return whether or not the timer has capture/compare channel `ch'.
+// TODO: does something like this belong in the standard timer library?
+static bool timer_has_cc_ch(timer_dev *dev, int ch);
+
+// Printing routines and variants for verbose mode
+static void putstr(const char str[]);
+static void println(void);
+static void putstrln(const char str[]);
+static void putudec(uint32 val);
+static void puttimn(timer_dev *dev);
+static void v_putstr(const char str[]);
+static void v_println();
+static void v_putstrln(const char str[]);
+static void v_putudec(uint32 val);
+static void v_puttimn(timer_dev *dev);
+// Used to visually separate output from different tests
+static void printBanner(void);
+
+//
+// Handler state
+//
+
+static int count1 = 0;
+static int count2 = 0;
+static int count3 = 0;
+static int count4 = 0;
+static int timer_num; // Current timer we're considering
+
+//
+// Timer capture/compare interrupt handlers
+//
+// These are shared between timers. The global variable timer_num
+// controls which timer they affect.
+//
+
+static void handler1(void);
+static void handler2(void);
+static void handler3(void);
+static void handler4(void);
+static voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4};
+
+//
+// setup() and loop()
+//
void setup() {
- // Set up the LED to blink
- pinMode(BOARD_LED_PIN, OUTPUT);
-
- // Setup the button as input
- pinMode(BOARD_BUTTON_PIN, INPUT);
-
- // Send a message out Serial2
- Serial2.begin(115200);
- Serial2.println("*** Initializing timers...");
+ init_usart(COMM_USART, COMM_USART_PORT,
+ COMM_USART_TX_BIT, COMM_USART_RX_BIT);
+ _delay(5);
+ println();
+ printBanner();
+ putstr("Initializing timers...\r\n");
timer_foreach(initTimer);
- Serial2.println("*** Done. Beginning timer test.");
+ putstr("Done. Running tests.\r\n");
+ runTests();
+ printBanner();
+ putstr("Done testing timers.\r\n");
}
void loop() {
- 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;
- 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 {
- timer_resume(TIMER4);
- }
- delay(1);
- }
+}
- 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;
- timer_resume(TIMER4);
- delay(2000);
- 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);
+//
+// Test routine implementations
+//
+
+static void runTests(void) {
+ runTest("timer_get_count()/timer_set_count()", testGetAndSetCount);
+ runTest("timer_pause()/timer_resume()", testPauseAndResume);
+ runTest("capture/compare channels and interrupts",
+ testTimerChannels);
}
-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;
+static void runTest(const char description[], timer_test_t test) {
+ printBanner();
+ putstr("Testing ");
+ putstr(description);
+ putstrln(".");
+ timer_foreach(test);
+}
+
+static void testGetAndSetCount(timer_dev *dev) {
+ unsigned before, after;
+ unsigned val_to_set = 1234;
+
+ timer_pause(dev);
+ before = timer_get_count(dev);
+ timer_set_count(dev, val_to_set);
+ after = timer_get_count(dev);
+ timer_resume(dev);
+
+ if (after != val_to_set) {
+ puttimn(dev);
+ putstr(": ");
+ putstr("*** FAIL: get/set count for ");
+ puttimn(dev);
+ putstr(".");
+ putstr("Start count = ");
+ putudec(before);
+ putstr(". Count set to ");
+ putudec(val_to_set);
+ putstr(", and now count is = ");
+ putudec(after);
+ println();
+ } else if (verbose) {
+ puttimn(dev);
+ putstr(": ");
+ putstrln("[ok]");
}
}
-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;
- timer_resume(TIMER4);
- delay(2000);
- 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)");
+// This hack works on all currently supported STM32 series, but you
+// may need to do something smarter in the future. The assertions
+// ensure that our assumptions hold for your target.
+static timer_dev *getDifferentTimerOnSameBusAs(timer_dev *dev) {
+ rcc_clk_domain dev_domain = rcc_dev_clk(dev->clk_id);
+ ASSERT(RCC_APB1 == dev_domain || RCC_APB2 == dev_domain);
+ ASSERT(rcc_dev_clk(TIMER1->clk_id) == RCC_APB2);
+ ASSERT(rcc_dev_clk(TIMER2->clk_id) == RCC_APB1);
+ ASSERT(rcc_dev_clk(TIMER8->clk_id) == RCC_APB2);
+ ASSERT(rcc_dev_clk(TIMER3->clk_id) == RCC_APB1);
+
+ if (dev->clk_id == RCC_TIMER1) {
+ return TIMER8;
+ }
+ if (dev->clk_id == RCC_TIMER2) {
+ return TIMER3;
+ }
+ return dev_domain == RCC_APB2 ? TIMER1 : TIMER2;
}
-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;
+// Rough test of pause and resume.
+//
+// Approximately half the time, dev is in the "pause" state and the
+// timer doesn't increment, while another timer (`base_dev') on the
+// same bus continues. dev and base_dev have identical start counts
+// and prescalers.
+//
+// Since dev and base_dev share a bus (and thus a base clock), and we
+// configure them to have the same prescaler and start count, the
+// ratio of their end counts should be approximately 1 : 2. We check
+// to make sure this is true, up to tolerance `epsilon'.
+static void testPauseAndResume(timer_dev *dev) {
+ timer_dev *base_dev = getDifferentTimerOnSameBusAs(dev);
+ unsigned start_count = 0, reload = 65535;
+ // This prescaler should be enough to ensure that we don't
+ // overflow, while still giving us a reasonably large number of
+ // timer ticks.
+ uint16 prescaler = CYCLES_PER_MICROSECOND * 50;
+ double epsilon = .02;
+
+ if (rcc_dev_clk(base_dev->clk_id) != rcc_dev_clk(dev->clk_id)) {
+ putstrln("*** ERROR: cannot run test. Bus info is messed up.");
+ return;
+ }
+
+ // Pause and set up timers
+ timer_pause(base_dev);
+ timer_pause(dev);
+ timer_set_count(base_dev, start_count);
+ timer_set_count(dev, start_count);
+ timer_set_reload(base_dev, reload);
+ timer_set_reload(dev, reload);
+ timer_set_prescaler(base_dev, prescaler);
+ timer_set_prescaler(dev, prescaler);
+ timer_generate_update(base_dev);
+ timer_generate_update(dev);
+
+ // Resume the timers and run the test
+ ASSERT(timer_get_count(base_dev) == start_count);
+ ASSERT(timer_get_count(dev) == start_count);
+ timer_resume(base_dev);
+ timer_resume(dev);
+ _delay(1000);
+ timer_pause(dev);
+ _delay(1000);
+ timer_pause(base_dev);
+
+ // Check the results
+ unsigned dev_count = timer_get_count(dev);
+ unsigned base_count = timer_get_count(base_dev);
+ double count_ratio = ((double)dev_count / base_count);
+ bool fail = false;
+ if (count_ratio > 0.5 + epsilon || count_ratio < 0.5 - epsilon) {
+ fail = true;
+ }
+ if (fail || verbose) {
+ puttimn(dev);
+ putstr(" vs. ");
+ puttimn(base_dev);
+ putstr(": ");
+ if (fail) putstr("*** FAIL: ");
+ else putstr("[ok] ");
+ putstr("(dev = ");
+ putudec(dev_count);
+ putstr(") / (base = ");
+ putudec(base_count);
+ putstr(") = ");
+ // hack hack hack
+ putudec((int)count_ratio);
+ count_ratio -= (int)count_ratio;
+ putstr(".");
+ int cr_x_100 = (int)(count_ratio * 100);
+ int hundredths = cr_x_100 % 10;
+ cr_x_100 /= 10;
+ int tenths = cr_x_100 % 10;
+ putudec(tenths);
+ putudec(hundredths);
+ println();
}
}
-/* This function touches every 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("-----------------------------------------------------");
+// This function touches every capture/compare channel of a given
+// timer. The channel counts should be equal within a timer
+// regardless of other interrupts on the system (note that this
+// doesn't really test timers with only a single capture/compare
+// channel; for that, you'll want to do visual inspection of timers
+// that share a bus, in verbose mode).
+static void testTimerChannels(timer_dev *dev) {
switch (dev->type) {
case TIMER_BASIC:
- Serial2.print("NOT testing channels for basic timer ");
- Serial2.println(t);
- break;
+ v_putstr("Skipping basic timer ");
+ v_puttimn(dev);
+ v_println();
+ return;
case TIMER_ADVANCED:
case TIMER_GENERAL:
- Serial2.print("Testing channels for timer ");
- Serial2.println(t);
+ // Set up
+ v_puttimn(dev);
+ v_println();
+ v_putstr("\tchannels: ");
+
+ timer_num = timerNumber(dev);
timer_pause(dev);
- count1 = count2 = count3 = count4 = 0;
+ count1 = 0;
+ count2 = 0;
+ count3 = 0;
+ count4 = 0;
timer_set_reload(dev, 0xFFFF);
timer_set_prescaler(dev, 1);
for (int c = 1; c <= 4; c++) {
- timer_set_compare(dev, c, 65535);
- timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE);
- timer_attach_interrupt(dev, c, handlers[c - 1]);
+ if (timer_has_cc_ch(dev, c)) {
+ v_putudec(c);
+ v_putstr("\t");
+ timer_set_compare(dev, c, 0xFFFF);
+ timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE);
+ timer_attach_interrupt(dev, c, handlers[c - 1]);
+ }
}
+ v_println();
+
+ // Run test
+ timer_generate_update(dev);
timer_resume(dev);
- delay(3000);
+ _delay(250);
+ timer_pause(dev);
+
+ // Print results
+ v_putstr("\tcounts: ");
+ bool fail = false;
+ bool mismatched[4] = {false, false, false, false};
+ int counts[4];
+ counts[0] = count1;
+ counts[1] = count2;
+ counts[2] = count3;
+ counts[3] = count4;
+ bool first = true;
+ int first_count = -1;
+ for (int c = 1; c <= 4; c++) {
+ if (timer_has_cc_ch(dev, c)) {
+ if (first) {
+ first_count = counts[c - 1];
+ first = false;
+ }
+ if (!first && (counts[c - 1] != first_count)) {
+ mismatched[c - 1] = true;
+ fail = true;
+ }
+ v_putudec(counts[c - 1]);
+ v_putstr("\t");
+ }
+ }
+ v_println();
+ if (fail) {
+ for (int i = 0; i < 4; i++) {
+ if (mismatched[i]) {
+ putstr("*** FAIL: mismatch on ");
+ puttimn(dev);
+ putstr(", channel ");
+ putudec(i + 1);
+ putstr(": expected ");
+ putudec(first_count);
+ putstr(", got ");
+ putudec(counts[i]);
+ println();
+ }
+ }
+ } else {
+ puttimn(dev);
+ putstrln(" [ok]");
+ }
+ v_println();
+
+ // Clean up
for (int c = 1; c <= 4; c++) {
- timer_set_mode(dev, c, TIMER_DISABLED);
+ if (timer_has_cc_ch(dev, 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;
- }
+//
+// Helper implementations
+//
- 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);
+static void _delay(uint32 msec) {
+ uint32 end = systick_uptime() + msec;
+ while (systick_uptime() < end)
+ ;
}
-void handler1(void) {
- val1 += rate1;
- timer_set_compare(timers[t], TIMER_CH1, val1);
- count1++;
+static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx) {
+ usart_config_gpios_async(dev, gdev, rx, gdev, tx, 0);
+ usart_init(dev);
+ usart_set_baud_rate(dev, USART_USE_PCLK, COMM_USART_BAUD);
+ usart_enable(dev);
}
-void handler2(void) {
- val2 += rate2;
- timer_set_compare(timers[t], TIMER_CH2, val2);
- count2++;
+static bool timer_has_cc_ch(timer_dev *dev, int ch) {
+ ASSERT(1 <= ch && ch <= 4);
+ if (dev->type == TIMER_BASIC)
+ return false;
+ int tn = timerNumber(dev);
+ return (// TIM1-5 and 8 have all four channels
+ (tn <= 5 || tn == 8) ||
+ // TIM9 and 12 only have channels 1 and 2
+ ((tn == 9 || tn == 12) && ch <= 2) ||
+ // All other general purpose timers only have channel 1
+ (ch == 1));
}
-void handler3(void) {
- val3 += rate3;
- timer_set_compare(timers[t], TIMER_CH3, val3);
- count3++;
+static void putstr(const char str[]) {
+ usart_putstr(COMM_USART, str);
}
-void handler4(void) {
- val4 += rate4;
- timer_set_compare(timers[t], TIMER_CH4, val4);
- count4++;
+static void println(void) {
+ putstr("\r\n");
}
-void handler3b(void) {
+static void putstrln(const char str[]) {
+ putstr(str);
+ println();
+}
+
+static void putudec(uint32 val) {
+ usart_putudec(COMM_USART, val);
+}
+
+static void puttimn(timer_dev *dev) {
+ putstr("TIM");
+ putudec(timerNumber(dev));
+}
+
+static void v_putstr(const char str[]) {
+ if (verbose) putstr(str);
+}
+
+static void v_println() {
+ if (verbose) println();
+}
+
+__attribute__((unused)) /* (shut up, gcc) */
+static void v_putstrln(const char str[]) {
+ if (verbose) putstrln(str);
+}
+
+static void v_putudec(uint32 val) {
+ if (verbose) putudec(val);
+}
+
+static void v_puttimn(timer_dev *dev) {
+ if (verbose) puttimn(dev);
+}
+
+// Used to visually separate output from different tests
+static void printBanner(void) {
+ putstrln("-----------------------------------------------------");
+}
+
+static void initTimer(timer_dev *dev) {
+ v_puttimn(dev);
+ timer_init(dev);
+ switch (dev->type) {
+ case TIMER_ADVANCED:
+ case TIMER_GENERAL:
+ v_putstr(" channels ");
+ for (int c = 1; c <= 4; c++) {
+ if (timer_has_cc_ch(dev, c)) {
+ v_putudec(c);
+ v_putstr(" ");
+ timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE);
+ }
+ }
+ break;
+ case TIMER_BASIC:
+ break;
+ }
+ v_println();
+}
+
+static int timerNumber(timer_dev *dev) {
+ switch (dev->clk_id) {
+ case RCC_TIMER1: return 1;
+ case RCC_TIMER2: return 2;
+ case RCC_TIMER3: return 3;
+ case RCC_TIMER4: return 4;
+ case RCC_TIMER5: return 5;
+ case RCC_TIMER6: return 6;
+ case RCC_TIMER7: return 7;
+ case RCC_TIMER8: return 8;
+ case RCC_TIMER9: return 9;
+ case RCC_TIMER10: return 10;
+ case RCC_TIMER11: return 11;
+ case RCC_TIMER12: return 12;
+ case RCC_TIMER13: return 13;
+ case RCC_TIMER14: return 14;
+ default:
+ ASSERT(0);
+ return 0;
+ }
+}
+
+//
+// IRQ Handlers
+//
+
+static void handler1(void) {
+ count1++;
+}
+
+static void handler2(void) {
+ count2++;
+}
+
+static void handler3(void) {
count3++;
}
-void handler4b(void) {
+static void handler4(void) {
count4++;
}
+//
+// init() and main()
+//
+
__attribute__((constructor)) void premain() {
init();
}
diff --git a/examples/test-usart-dma.cpp b/examples/test-usart-dma.cpp
index 5ff5b86..d10dc68 100644
--- a/examples/test-usart-dma.cpp
+++ b/examples/test-usart-dma.cpp
@@ -1,5 +1,5 @@
/**
- * @file test-usart-dma.cpp
+ * @file examples/test-usart-dma.cpp
* @author Marti Bolivar <mbolivar@leaflabs.com>
*
* Simple test of DMA used with a USART receiver.
@@ -12,100 +12,188 @@
*
* 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.
+ * if you keep sending characters after filling the buffer, you'll
+ * overwrite earlier bytes; this may happen before those earlier bytes
+ * are done printing. (Typing quickly and seeing how it affects the
+ * output is a fun way to make sense of how the interrupts and the
+ * main thread of execution interleave.)
*
* This code is released into the public domain.
*/
-#include "dma.h"
-#include "usart.h"
-#include "gpio.h"
+#include <libmaple/dma.h>
+#include <libmaple/usart.h>
+#include <libmaple/gpio.h>
-#include "wirish.h"
+#include <wirish/wirish.h>
-#define BAUD 9600
+/*
+ * Configuration and state
+ */
-#define USART USART2
-#define USART_HWSER Serial2
+// Serial port and DMA configuration. You can change these to suit
+// your purposes.
+HardwareSerial *serial = &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
+#if STM32_MCU_SERIES == STM32_SERIES_F1
+// On STM32F1 microcontrollers (like what's on Maple and Maple Mini),
+// dma tubes are channels.
+#define USART_RX_DMA_TUBE DMA_CH6
+#elif (STM32_MCU_SERIES == STM32_SERIES_F2 || \
+ STM32_MCU_SERIES == STM32_SERIES_F4)
+// On STM32F2 and STM32F4 microcontrollers (Maple 2 will have an F4),
+// dma tubes are streams.
+#define USART_RX_DMA_TUBE DMA_S5
+#else
+#error "unsupported stm32 series"
+#endif
+// The serial port will make a DMA request each time it receives data.
+// This is the dma_request_src we use to tell the DMA tube to handle
+// that DMA request.
+#define USART_DMA_REQ_SRC DMA_REQ_SRC_USART2_RX
+#define BAUD 9600
-#define BUF_SIZE 8
-uint8 rx_buf[BUF_SIZE];
+// This will store the DMA configuration for USART RX.
+dma_tube_config tube_config;
-dma_irq_cause irq_cause;
+// This will store received USART characters.
+#define BUF_SIZE 20
+char rx_buf[BUF_SIZE];
+// The interrupt handler, rx_dma_irq(), sets this to 1.
volatile uint32 irq_fired = 0;
+// Used to store DMA interrupt status register (ISR) bits inside
+// rx_dma_irq(). This helps explain what's going on inside loop(); see
+// comments below.
+volatile uint32 isr = 0;
+
+/*
+ * Helper functions
+ */
+
+// This is our DMA interrupt handler.
+void rx_dma_irq(void) {
+ irq_fired = 1;
+ isr = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE);
+}
+
+// Configure the USART receiver for use with DMA:
+// 1. Turn it on.
+// 2. Set the "DMA request on RX" bit in USART_CR3 (USART_CR3_DMAR).
+void setup_usart(void) {
+ serial->begin(BAUD);
+ usart_dev *serial_dev = serial->c_dev();
+ serial_dev->regs->CR3 = USART_CR3_DMAR;
+}
+
+// Set up our dma_tube_config structure. (We could have done this
+// above, when we declared tube_config, but having this function makes
+// it easier to explain what's going on).
+void setup_tube_config(void) {
+ // We're receiving from the USART data register. serial->c_dev()
+ // returns a pointer to the libmaple usart_dev for that serial
+ // port, so this is a pointer to its data register.
+ tube_config.tube_src = &serial->c_dev()->regs->DR;
+ // We're only interested in the bottom 8 bits of that data register.
+ tube_config.tube_src_size = DMA_SIZE_8BITS;
+ // We're storing to rx_buf.
+ tube_config.tube_dst = rx_buf;
+ // rx_buf is a char array, and a "char" takes up 8 bits on STM32.
+ tube_config.tube_dst_size = DMA_SIZE_8BITS;
+ // Only fill BUF_SIZE - 1 characters, to leave a null byte at the end.
+ tube_config.tube_nr_xfers = BUF_SIZE - 1;
+ // Flags:
+ // - DMA_CFG_DST_INC so we start at the beginning of rx_buf and
+ // fill towards the end.
+ // - DMA_CFG_CIRC so we go back to the beginning and start over when
+ // rx_buf fills up.
+ // - DMA_CFG_CMPLT_IE to turn on interrupts on transfer completion.
+ tube_config.tube_flags = DMA_CFG_DST_INC | DMA_CFG_CIRC | DMA_CFG_CMPLT_IE;
+ // Target data: none. It's important to set this to NULL if you
+ // don't have any special (microcontroller-specific) configuration
+ // in mind, which we don't.
+ tube_config.target_data = NULL;
+ // DMA request source.
+ tube_config.tube_req_src = USART_DMA_REQ_SRC;
+}
+
+// Configure the DMA controller to serve DMA requests from the USART.
+void setup_dma_xfer(void) {
+ // First, turn it on.
+ dma_init(USART_DMA_DEV);
+ // Next, configure it by calling dma_tube_cfg(), and check to make
+ // sure it succeeded. DMA tubes have many restrictions on their
+ // configuration, and there are configurations which work on some
+ // types of STM32 but not others. libmaple tries hard to make
+ // things just work, but checking the return status is important!
+ int status = dma_tube_cfg(USART_DMA_DEV, USART_RX_DMA_TUBE, &tube_config);
+ ASSERT(status == DMA_TUBE_CFG_SUCCESS);
+ // Now we'll perform any other configuration we want. For this
+ // example, we attach an interrupt handler.
+ dma_attach_interrupt(USART_DMA_DEV, USART_RX_DMA_TUBE, rx_dma_irq);
+ // Turn on the DMA tube. It will now begin serving requests.
+ dma_enable(USART_DMA_DEV, USART_RX_DMA_TUBE);
+}
-void init_usart(void);
-void init_dma_xfer(void);
-void rx_dma_irq(void);
+/*
+ * setup() and loop()
+ */
void setup(void) {
pinMode(BOARD_LED_PIN, OUTPUT);
-
- init_dma_xfer();
- init_usart();
+ setup_tube_config();
+ setup_dma_xfer();
+ setup_usart();
}
void loop(void) {
toggleLED();
delay(100);
- dma_channel_reg_map *ch_regs = dma_channel_regs(USART_DMA_DEV,
- USART_RX_DMA_CHANNEL);
+ // See if the interrupt handler got called since the last time we
+ // checked.
if (irq_fired) {
- USART_HWSER.println("** IRQ **");
+ serial->println("** IRQ **");
+ // Notice how the interrupt status register (ISR) bits show
+ // transfer complete _and_ half-complete here, but the ISR
+ // bits we print next will be zero. That's because the
+ // variable "isr" gets set _inside_ rx_dma_irq(). After it
+ // exits, libmaple cleans up by clearing the tube's ISR
+ // bits. (If it didn't, and we forgot to, the interrupt would
+ // repeatedly fire forever.)
+ serial->print("ISR bits: 0x");
+ serial->println(isr, HEX);
irq_fired = 0;
}
- 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(isr_bits, HEX);
- USART_HWSER.print("\tCCR: 0x");
- USART_HWSER.print(ch_regs->CCR, HEX);
- USART_HWSER.print("\tCNDTR: 0x");
- USART_HWSER.print(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();
+
+ // Print the ISR bits.
+ //
+ // Notice that the "transfer half-complete" ISR flag gets set when
+ // we reach the rx_buf half-way point. This is true even though we
+ // don't tell the DMA controller to interrupt us on a
+ // half-complete transfer. That is, the ISR bits get set at the
+ // right times no matter what; we just don't get interrupted
+ // unless we asked. (If an error or other problem occurs, the
+ // relevant ISR bits will get set in the same way).
+ serial->print("[");
+ serial->print(millis());
+ serial->print("]\tISR bits: 0x");
+ uint8 isr_bits = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE);
+ serial->print(isr_bits, HEX);
+
+ // Print the contents of rx_buf. If you keep typing after it fills
+ // up, the new characters will overwrite the old ones, thanks to
+ // DMA_CIRC_MODE.
+ serial->print("\tCharacter buffer contents: '");
+ serial->print(rx_buf);
+ serial->println("'");
if (isr_bits == 0x7) {
- USART_HWSER.println("** Clearing ISR bits.");
- dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_CHANNEL);
+ serial->println("** Clearing ISR bits.");
+ dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_TUBE);
}
}
-/* 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);
- 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) {
- irq_fired = true;
-}
+// ------- init() and main() --------------------------------------------------
// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need libmaple may fail.
diff --git a/examples/vga-leaf.cpp b/examples/vga-leaf.cpp
index f31dc87..5159956 100644
--- a/examples/vga-leaf.cpp
+++ b/examples/vga-leaf.cpp
@@ -32,7 +32,7 @@
// FIXME: generalize for Native and Mini
-#include "wirish.h"
+#include <wirish/wirish.h>
// Pinouts -- you also must change the GPIO macros below if you change
// these
diff --git a/examples/vga-scope.cpp b/examples/vga-scope.cpp
index b5fa8a5..8730cf0 100644
--- a/examples/vga-scope.cpp
+++ b/examples/vga-scope.cpp
@@ -35,8 +35,8 @@
Marti Bolivar <mbolivar@leaflabs.com>
*/
-#include "wirish.h"
-#include "systick.h"
+#include <wirish/wirish.h>
+#include <libmaple/systick.h>
// FIXME: generalize for Native and Mini