From dba5a9fe68ebb996eeee69fa9b573fe5a1561ce2 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Thu, 16 Oct 2014 20:20:20 -0700 Subject: refactor: move test-style examples to ./tests --- tests/test-timers.cpp | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 tests/test-timers.cpp (limited to 'tests/test-timers.cpp') diff --git a/tests/test-timers.cpp b/tests/test-timers.cpp new file mode 100644 index 0000000..e646916 --- /dev/null +++ b/tests/test-timers.cpp @@ -0,0 +1,537 @@ +// +// This is a mostly Wirish-free timer test. Wirish usage is minimized +// because this is a test of the C timer interface in +// , so it's good if it can be made to work even +// when most or all of Wirish is missing. Because of that, you may +// need to customize the following output configuration: +// +// Output is printed: +// - on COMM_USART, +// - via TX pin on port COMM_USART_PORT, bit COMM_USART_TX_BIT +// - via RX pin on port COMM_USART_PORT, bit COMM_USART_RX_BIT +// - at COMM_USART_BAUD baud. +#define COMM_USART USART6 +#define COMM_USART_BAUD 115200 +#define COMM_USART_PORT GPIOG +#define COMM_USART_TX_BIT 14 +#define COMM_USART_RX_BIT 9 +// Other optional configuration below. + +#include +#include +#include +#include +#include +#include + +// +// Configuration +// + +// More output if true +static bool verbose = true; + +// Timers to test +// FIXME use feature test macros for smaller MCUs +static timer_dev *timers[] = { + // Available on all currently supported MCUs + TIMER1, TIMER2, TIMER3, TIMER4, + // Available on F1 (HD and up), F2 + TIMER5, TIMER6, TIMER7, TIMER8, + // Available on F1 (XL), F2 + TIMER9, TIMER10, TIMER11, TIMER12, TIMER13, TIMER14, +}; + +// +// Test routines +// + +typedef void (*timer_test_t)(timer_dev *); + +static void runTest(const char description[], timer_test_t test); +static void runTests(void); + +static void testGetAndSetCount(timer_dev*); +static void testPauseAndResume(timer_dev*); +static void testTimerChannels(timer_dev*); + +// +// Helpers +// + +static void initTimer(timer_dev *dev); +static int timerNumber(timer_dev *dev); +// Hack: a systick-based delay, useful until delay_us() is fixed +static void _delay(uint32 msec); +// Wirish-less USART initialization routine +static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx); +// Return whether or not the timer has capture/compare channel `ch'. +// TODO: does something like this belong in the standard timer library? +static bool timer_has_cc_ch(timer_dev *dev, int ch); + +// Printing routines and variants for verbose mode +static void putstr(const char str[]); +static void println(void); +static void putstrln(const char str[]); +static void putudec(uint32 val); +static void puttimn(timer_dev *dev); +static void v_putstr(const char str[]); +static void v_println(); +static void v_putstrln(const char str[]); +static void v_putudec(uint32 val); +static void v_puttimn(timer_dev *dev); +// Used to visually separate output from different tests +static void printBanner(void); + +// +// Handler state +// + +static int count1 = 0; +static int count2 = 0; +static int count3 = 0; +static int count4 = 0; +static int timer_num; // Current timer we're considering + +// +// Timer capture/compare interrupt handlers +// +// These are shared between timers. The global variable timer_num +// controls which timer they affect. +// + +static void handler1(void); +static void handler2(void); +static void handler3(void); +static void handler4(void); +static voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; + +// +// setup() and loop() +// + +void setup() { + init_usart(COMM_USART, COMM_USART_PORT, + COMM_USART_TX_BIT, COMM_USART_RX_BIT); + _delay(5); + println(); + printBanner(); + putstr("Initializing timers...\r\n"); + timer_foreach(initTimer); + putstr("Done. Running tests.\r\n"); + runTests(); + printBanner(); + putstr("Done testing timers.\r\n"); +} + +void loop() { +} + +// +// Test routine implementations +// + +static void runTests(void) { + runTest("timer_get_count()/timer_set_count()", testGetAndSetCount); + runTest("timer_pause()/timer_resume()", testPauseAndResume); + runTest("capture/compare channels and interrupts", + testTimerChannels); +} + +static void runTest(const char description[], timer_test_t test) { + printBanner(); + putstr("Testing "); + putstr(description); + putstrln("."); + timer_foreach(test); +} + +static void testGetAndSetCount(timer_dev *dev) { + unsigned before, after; + unsigned val_to_set = 1234; + + timer_pause(dev); + before = timer_get_count(dev); + timer_set_count(dev, val_to_set); + after = timer_get_count(dev); + timer_resume(dev); + + if (after != val_to_set) { + puttimn(dev); + putstr(": "); + putstr("*** FAIL: get/set count for "); + puttimn(dev); + putstr("."); + putstr("Start count = "); + putudec(before); + putstr(". Count set to "); + putudec(val_to_set); + putstr(", and now count is = "); + putudec(after); + println(); + } else if (verbose) { + puttimn(dev); + putstr(": "); + putstrln("[ok]"); + } +} + +// This hack works on all currently supported STM32 series, but you +// may need to do something smarter in the future. The assertions +// ensure that our assumptions hold for your target. +static timer_dev *getDifferentTimerOnSameBusAs(timer_dev *dev) { + rcc_clk_domain dev_domain = rcc_dev_clk(dev->clk_id); + ASSERT(RCC_APB1 == dev_domain || RCC_APB2 == dev_domain); + ASSERT(rcc_dev_clk(TIMER1->clk_id) == RCC_APB2); + ASSERT(rcc_dev_clk(TIMER2->clk_id) == RCC_APB1); + ASSERT(rcc_dev_clk(TIMER8->clk_id) == RCC_APB2); + ASSERT(rcc_dev_clk(TIMER3->clk_id) == RCC_APB1); + + if (dev->clk_id == RCC_TIMER1) { + return TIMER8; + } + if (dev->clk_id == RCC_TIMER2) { + return TIMER3; + } + return dev_domain == RCC_APB2 ? TIMER1 : TIMER2; +} + +// Rough test of pause and resume. +// +// Approximately half the time, dev is in the "pause" state and the +// timer doesn't increment, while another timer (`base_dev') on the +// same bus continues. dev and base_dev have identical start counts +// and prescalers. +// +// Since dev and base_dev share a bus (and thus a base clock), and we +// configure them to have the same prescaler and start count, the +// ratio of their end counts should be approximately 1 : 2. We check +// to make sure this is true, up to tolerance `epsilon'. +static void testPauseAndResume(timer_dev *dev) { + timer_dev *base_dev = getDifferentTimerOnSameBusAs(dev); + unsigned start_count = 0, reload = 65535; + // This prescaler should be enough to ensure that we don't + // overflow, while still giving us a reasonably large number of + // timer ticks. + uint16 prescaler = CYCLES_PER_MICROSECOND * 50; + double epsilon = .02; + + if (rcc_dev_clk(base_dev->clk_id) != rcc_dev_clk(dev->clk_id)) { + putstrln("*** ERROR: cannot run test. Bus info is messed up."); + return; + } + + // Pause and set up timers + timer_pause(base_dev); + timer_pause(dev); + timer_set_count(base_dev, start_count); + timer_set_count(dev, start_count); + timer_set_reload(base_dev, reload); + timer_set_reload(dev, reload); + timer_set_prescaler(base_dev, prescaler); + timer_set_prescaler(dev, prescaler); + timer_generate_update(base_dev); + timer_generate_update(dev); + + // Resume the timers and run the test + ASSERT(timer_get_count(base_dev) == start_count); + ASSERT(timer_get_count(dev) == start_count); + timer_resume(base_dev); + timer_resume(dev); + _delay(1000); + timer_pause(dev); + _delay(1000); + timer_pause(base_dev); + + // Check the results + unsigned dev_count = timer_get_count(dev); + unsigned base_count = timer_get_count(base_dev); + double count_ratio = ((double)dev_count / base_count); + bool fail = false; + if (count_ratio > 0.5 + epsilon || count_ratio < 0.5 - epsilon) { + fail = true; + } + if (fail || verbose) { + puttimn(dev); + putstr(" vs. "); + puttimn(base_dev); + putstr(": "); + if (fail) putstr("*** FAIL: "); + else putstr("[ok] "); + putstr("(dev = "); + putudec(dev_count); + putstr(") / (base = "); + putudec(base_count); + putstr(") = "); + // hack hack hack + putudec((int)count_ratio); + count_ratio -= (int)count_ratio; + putstr("."); + int cr_x_100 = (int)(count_ratio * 100); + int hundredths = cr_x_100 % 10; + cr_x_100 /= 10; + int tenths = cr_x_100 % 10; + putudec(tenths); + putudec(hundredths); + println(); + } +} + +// This function touches every capture/compare channel of a given +// timer. The channel counts should be equal within a timer +// regardless of other interrupts on the system (note that this +// doesn't really test timers with only a single capture/compare +// channel; for that, you'll want to do visual inspection of timers +// that share a bus, in verbose mode). +static void testTimerChannels(timer_dev *dev) { + switch (dev->type) { + case TIMER_BASIC: + v_putstr("Skipping basic timer "); + v_puttimn(dev); + v_println(); + return; + case TIMER_ADVANCED: + case TIMER_GENERAL: + // Set up + v_puttimn(dev); + v_println(); + v_putstr("\tchannels: "); + + timer_num = timerNumber(dev); + timer_pause(dev); + count1 = 0; + count2 = 0; + count3 = 0; + count4 = 0; + timer_set_reload(dev, 0xFFFF); + timer_set_prescaler(dev, 1); + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + v_putudec(c); + v_putstr("\t"); + timer_set_compare(dev, c, 0xFFFF); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + timer_attach_interrupt(dev, c, handlers[c - 1]); + } + } + v_println(); + + // Run test + timer_generate_update(dev); + timer_resume(dev); + _delay(250); + timer_pause(dev); + + // Print results + v_putstr("\tcounts: "); + bool fail = false; + bool mismatched[4] = {false, false, false, false}; + int counts[4]; + counts[0] = count1; + counts[1] = count2; + counts[2] = count3; + counts[3] = count4; + bool first = true; + int first_count = -1; + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + if (first) { + first_count = counts[c - 1]; + first = false; + } + if (!first && (counts[c - 1] != first_count)) { + mismatched[c - 1] = true; + fail = true; + } + v_putudec(counts[c - 1]); + v_putstr("\t"); + } + } + v_println(); + if (fail) { + for (int i = 0; i < 4; i++) { + if (mismatched[i]) { + putstr("*** FAIL: mismatch on "); + puttimn(dev); + putstr(", channel "); + putudec(i + 1); + putstr(": expected "); + putudec(first_count); + putstr(", got "); + putudec(counts[i]); + println(); + } + } + } else { + puttimn(dev); + putstrln(" [ok]"); + } + v_println(); + + // Clean up + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + timer_set_mode(dev, c, TIMER_DISABLED); + } + } + break; + } +} + +// +// Helper implementations +// + +static void _delay(uint32 msec) { + uint32 end = systick_uptime() + msec; + while (systick_uptime() < end) + ; +} + +static void init_usart(usart_dev *dev, gpio_dev *gdev, uint8 tx, uint8 rx) { + usart_config_gpios_async(dev, gdev, rx, gdev, tx, 0); + usart_init(dev); + usart_set_baud_rate(dev, USART_USE_PCLK, COMM_USART_BAUD); + usart_enable(dev); +} + +static bool timer_has_cc_ch(timer_dev *dev, int ch) { + ASSERT(1 <= ch && ch <= 4); + if (dev->type == TIMER_BASIC) + return false; + int tn = timerNumber(dev); + return (// TIM1-5 and 8 have all four channels + (tn <= 5 || tn == 8) || + // TIM9 and 12 only have channels 1 and 2 + ((tn == 9 || tn == 12) && ch <= 2) || + // All other general purpose timers only have channel 1 + (ch == 1)); +} + +static void putstr(const char str[]) { + usart_putstr(COMM_USART, str); +} + +static void println(void) { + putstr("\r\n"); +} + +static void putstrln(const char str[]) { + putstr(str); + println(); +} + +static void putudec(uint32 val) { + usart_putudec(COMM_USART, val); +} + +static void puttimn(timer_dev *dev) { + putstr("TIM"); + putudec(timerNumber(dev)); +} + +static void v_putstr(const char str[]) { + if (verbose) putstr(str); +} + +static void v_println() { + if (verbose) println(); +} + +__attribute__((unused)) /* (shut up, gcc) */ +static void v_putstrln(const char str[]) { + if (verbose) putstrln(str); +} + +static void v_putudec(uint32 val) { + if (verbose) putudec(val); +} + +static void v_puttimn(timer_dev *dev) { + if (verbose) puttimn(dev); +} + +// Used to visually separate output from different tests +static void printBanner(void) { + putstrln("-----------------------------------------------------"); +} + +static void initTimer(timer_dev *dev) { + v_puttimn(dev); + timer_init(dev); + switch (dev->type) { + case TIMER_ADVANCED: + case TIMER_GENERAL: + v_putstr(" channels "); + for (int c = 1; c <= 4; c++) { + if (timer_has_cc_ch(dev, c)) { + v_putudec(c); + v_putstr(" "); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + } + } + break; + case TIMER_BASIC: + break; + } + v_println(); +} + +static int timerNumber(timer_dev *dev) { + switch (dev->clk_id) { + case RCC_TIMER1: return 1; + case RCC_TIMER2: return 2; + case RCC_TIMER3: return 3; + case RCC_TIMER4: return 4; + case RCC_TIMER5: return 5; + case RCC_TIMER6: return 6; + case RCC_TIMER7: return 7; + case RCC_TIMER8: return 8; + case RCC_TIMER9: return 9; + case RCC_TIMER10: return 10; + case RCC_TIMER11: return 11; + case RCC_TIMER12: return 12; + case RCC_TIMER13: return 13; + case RCC_TIMER14: return 14; + default: + ASSERT(0); + return 0; + } +} + +// +// IRQ Handlers +// + +static void handler1(void) { + count1++; +} + +static void handler2(void) { + count2++; +} + +static void handler3(void) { + count3++; +} + +static void handler4(void) { + count4++; +} + +// +// init() and main() +// + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} -- cgit v1.2.3