This package contains a simple test of tests for various elements of the SmartBoard hardware, which is a simple baseboard designed for easy embedding. It is able to run both a semi-automatic test suite as well as allow interactive testing.
Dependencies: EthernetNetIf NTPClient_NetServices mbed
This program is most of what you need to test your SmartBoard baseboard hardware. It provides a means to test the following:
- Two channels of CAN (with a loopback cable)
- RS-232 Ports
- Analog inputs
- PWM outputs
- Ethernet port
- Real time clock
- micro SD
- USB Host port
SmartBoard_Tester.cpp
- Committer:
- WiredHome
- Date:
- 2011-03-31
- Revision:
- 4:ca93a8d4874d
- Parent:
- 3:2b4fe31e8d15
File content as of revision 4:ca93a8d4874d:
/// @file SmartBoard_Tester.cpp is the simple test framework /// /// This file contains the startup, interactive, and test code /// to evaluate the SmartBoard baseboard. /// /// @note Copyright © 2011 by Smartware Computing, all rights reserved. /// This software may be used to derive new software, as long as /// this copyright statement remains in the source file. /// @author David Smart /// #include "mbed.h" #include "SmartBoard.h" #include "ShowTime.h" #include "EthernetNetIf.h" #include "NTPClient.h" #include "SDFileSystem.h" #include "MSCFileSystem.h" #include "Watchdog.h" extern "C" void HardFault_Handler() { printf("Hard Fault!\n"); while (1); } Watchdog wdt; Serial pc(USBTX, USBRX); ///!< Used as the console for interactively reporting progress const char * TicTocServer = "ntp.okstate.edu"; ///!< time server since it is closer than "0.uk.pool.ntp.org" const int tzOffsetHr = -6; ///!< time zone offset hours to print time in local time const int tzOffsetMin = 0; ///!< time zone offset minutes to print time in local time void LED_Tests(void); void PWM_Tests(void); void AnalogIn_Tests(void); void RTC_Tests(void); void RTC_Set(void); void MicroSD_Tests(void); void RS_232_Tests(void); void CAN_Tests(void); void Ethernet_Tests(void); void USBHost_Tests(void); void FileSystem_Tests(void); /// TestVector will execute a given test, based on the parameter /// /// It can show the list of available commands, as for an interactive /// test session, or it can simply execute the chosen test. This is /// used for both the automated testing and the interactive testing, /// so a couple of the commands for interactive would not be used /// for the automated testing. /// '?' causes it to display the available commands. /// /// @param i contains the single character value indicating the operation /// to perform. /// @returns false if the input paramter was 'X'. /// @returns true if the input parameters was not 'X'. /// bool TestVector(int i) { bool r = true; ///!< expect to return true switch (i) { default: case '?': pc.printf("Commands:\r\n" " L LED_Tests(); // Blink each in turn\r\n" " P PWM_Tests(); // Ramps the PWM channels\r\n" " A AnalogIn_Tests(); // Measures voltage on each\r\n" " R RTC_Tests(); // Saves current time, alters it, restores it\r\n" " M MicroSD_Tests(); // Writes and Reads file on an installed card\r\n" " S RS_232_Tests(); // Outputs simple text\r\n" " C CAN_Tests(); // Requires CAN1 wired to CAN2, loops messages\r\n" " E Ethernet_Tests(); // Sets the clock from a time server\r\n" " U USBHost_Tests(); // Writes and Reads file on a memory stick\r\n" " F FileSystems_Tests(); // Writes and Reads file on internal file system\r\n" " X eXit to automatic testing\r\n"); break; case 'X': r = false; break; case 'L': LED_Tests(); break; case 'P': PWM_Tests(); break; case 'A': AnalogIn_Tests(); break; case 'R': RTC_Tests(); break; case 'r': RTC_Set(); break; case 'M': MicroSD_Tests(); break; case 'S': RS_232_Tests(); break; case 'C': CAN_Tests(); break; case 'E': Ethernet_Tests(); break; case 'U': USBHost_Tests(); break; case 'F': FileSystem_Tests(); break; } return r; } /// main is the main startup code. /// /// This initializes the test environment, shows a banner, /// and starts the automated testing. /// It also detects if the user is attempting to interact, and /// between each test category there is the possibility to transfer /// to the interactive test mode. /// When in interactive test mode, the user determines which test /// to run. The user can also exit interactive mode back to the /// automated test mode. /// /// @returns never /// int main() { bool init = true; ///!< init is slightly different bool interactive = false; ///!< track when in interactive mode int test = 0; ///!< which test to run char TestList[] = "XLPARrMSCEU"; ///!< list of valid test commands AUTOTESTS are uppercase pc.printf("Set your baudrate from 9600 to 921600\r\n"); pc.baud(921600); if (wdt.WatchdogCausedReset()) pc.printf("Watchdog caused reset. WD is armed for 30s cycle.\r\n"); wdt.Configure(30.0); // longest test should be under this interval wait(3.0); // just wait a little while in case they want to inject a '?' while (1) { wdt.Service(); // do this often enough if (pc.readable() || init) { pc.printf("\r\n\r\n"); pc.printf("SmartBoard Hardware Tester [" __DATE__ " " __TIME__ "]\r\n"); pc.printf(" SmartBoard Hardware v0.05\r\n"); pc.printf(" SmartBoard Software v0.13\r\n"); pc.printf("\r\n"); pc.printf(" [USB] [Eth/USB] \r\n"); pc.printf(" +---------------+------------+---+-------+---+\r\n"); pc.printf(" |O [RS232 1-2] | | | | | | O|\r\n"); pc.printf(" | | |microSD| | | | |\r\n"); pc.printf(" |S | | | | | | C|\r\n"); pc.printf(" |P | +-------+ | | | A|\r\n"); pc.printf(" |I | | |Yl Gr| N|\r\n"); pc.printf(" |1 | | +-------+ 1|\r\n"); pc.printf(" |- | | -|\r\n"); pc.printf(" |2 | RTC | 2|\r\n"); pc.printf(" | | (Battery) | |\r\n"); pc.printf(" | | | |\r\n"); pc.printf(" | | 1 2 3 4 | |\r\n"); pc.printf(" | +------------+ |\r\n"); pc.printf(" |O[Analog In ] O [PWM Out] O|\r\n"); pc.printf(" +--------------------------------------------+\r\n"); pc.printf("\r\n"); init = false; } if (pc.readable()) { interactive = true; while (pc.readable()) (void)pc.getc(); while (interactive) { wdt.Service(); // do this often enough pc.printf("> "); while (!pc.readable()) wdt.Service(); int i = pc.getc(); pc.putc(i); pc.putc('\r'); pc.putc('\n'); interactive = TestVector(i); } } else { if (test == 0) pc.printf("\x07"); // Bell character indicating start of tests if (TestList[test] >= 'A' && TestList[test] <= 'Z') // Tests are UPPER-case only TestVector(TestList[test]); test++; if (TestList[test] == '\0') test = 0; wait(5.0); // Extra pause } } } /// LED_Tests performs some simple digital output to the /// LEDs. /// /// It will attempt to exercise the LEDs on the Ethernet ports /// as well, but by jumper configuration these may not be available. /// void LED_Tests(void) { int l; int i; struct { const char * name; DigitalOut led; } Leds[] = { {"Ethernet Green", ETHERGREEN}, {"Ethernet Yellow", ETHERYELLOW}, {"Led 1", LED1}, {"Led 2", LED2}, {"Led 3", LED3}, {"Led 4", LED4} }; const int numLeds = sizeof(Leds) / sizeof(Leds[0]); printf("LED Test:\r\n"); for (l=0; l<numLeds; l++) { wdt.Service(); printf(" Blink %s LED 3 times\r\n", Leds[l].name); for (i=0; i<3; i++) { Leds[l].led = true; wait(0.4); Leds[l].led = false; wait(0.4); } } } /// PWM_Tests performs some simple pwm output to the /// PWM channels and the LEDs. /// /// It will attempt to exercise the outputs with a simple ramping /// signal, but by jumper configuration these may not be available. /// void PWM_Tests(void) { int l; int i; float f; struct { const char * name; PwmOut pwm; } Pwms[] = { {"PWM 1", p21}, {"PWM 2", p22}, {"PWM 3", p23}, {"PWM 4", p24}, {"PWM 5", p25}, {"PWM 6", p26}, {"Led 1", LED1}, {"Led 2", LED2}, {"Led 3", LED3}, {"Led 4", LED4} }; const int numPwms = sizeof(Pwms) / sizeof(Pwms[0]); printf("PWM Test:\r\n"); for (l=0; l<numPwms; l++) { wdt.Service(); printf(" Ramp %s PWM 3 times\r\n", Pwms[l].name); for (i=0; i<3; i++) { for (f=0.0; f<=1.0; f+= 0.1) { Pwms[l].pwm = f; wait(0.1); } } Pwms[l].pwm = 0; // off when done } } /// AnalogIn_Tests takes a few sample measurements on each channel /// /// It samples each channel a number of times and presents the /// converted results on the console. /// void AnalogIn_Tests(void) { int l; int i; const int samples = 20; struct { const char * name; AnalogIn in; } Analogs[] = { {"Ain 1", p15}, {"Ain 2", p16}, {"Ain 3", p17}, {"Ain 4", p18}, {"Ain 5", p19}, {"Ain 6", p20} }; const int numAnalogs = sizeof(Analogs) / sizeof(Analogs[0]); printf("Analog Test:\r\n"); for (l=0; l<numAnalogs; l++) { wdt.Service(); for (i=0; i<samples; i++) { uint16_t raw = Analogs[l].in.read_u16(); float flt = Analogs[l].in.read(); printf(" Analog %i is %04X, %3.2f, %3.2fv\r", l, raw, flt, flt*3.3); wait(0.1); } printf("\n"); } } /// RTC_Tests will perform simple tests on the Real Time Clock /// /// It will first sample the time from the RTC and later restore /// it as best it can. /// In the middle of that it will set the clock, then simply show /// the time once per second for 5 seconds. After this it /// will restore the clock at best it can. /// void RTC_Tests(void) { time_t x; int i; const int oldTime = 1256729737; printf("RTC Test:\r\n"); ShowTime(tzOffsetHr, tzOffsetMin); x = time(NULL); // Save the time before the test printf(" Saving current time(%d)\r\n", x); set_time(oldTime); // Set RTC time to Wed, 28 Oct 2009 11:35:37 printf(" Set time to Wed, 28 Oct 2009 11:35:37\r\n"); for (i=0; i<5; i++) { ShowTime(); wait(1.0); } set_time(x + time(NULL) - 1256729737); // Approximately restored ShowTime(tzOffsetHr, tzOffsetMin); wait(1.0); ShowTime(tzOffsetHr, tzOffsetMin); } /// GetNumber will get from the user a number using the /// specified number of digits. /// /// They can enter a number from 0 to 10^(digits-1) /// /// @param digits is the number of digits to enter /// @param pValue is a pointer to where to store the result /// @returns true if a number was entered /// @returns false if they entered a non-digit /// int GetNumber(int digits, int * pValue) { int tempValue = 0; int i; while (digits--) { while (!pc.readable()) wdt.Service(); i = pc.getc(); if (i == ' ') i = '0'; // special case for leading blank if (i >= '0' && i <= '9') { pc.putc(i); tempValue = tempValue * 10 + (i - '0'); } else return false; } *pValue = tempValue; return true; } /// RTC_Set will interactively set the Real Time Clock /// /// It will allow you to enter the date and time information /// and then create a timestamp. After this, it will set /// the RTC to that timestamp. /// void RTC_Set(void) { time_t seconds = time(NULL); struct tm *t = localtime(&seconds); int i; pc.printf("RTC Set:\r\n"); ShowTime(seconds, tzOffsetHr, tzOffsetMin); pc.printf(" Enter the time MM/DD/YYYY HH:MM:SS\r\n"); while (1) { int _m, _d, _y, _H, _M, _S; printf(" > "); if (GetNumber(2, &_m)) { pc.putc('/'); if (!GetNumber(2, &_d)) continue; pc.putc('/'); if (!GetNumber(4, &_y)) continue; t->tm_mon = _m - 1; t->tm_mday = _d; t->tm_year = _y - 1900; } else { pc.printf("%02d/%02d/%04d", t->tm_mon+1, t->tm_mday, t->tm_year+1900); } pc.putc(' '); if (GetNumber(2, &_H)) { pc.putc(':'); if (!GetNumber(2, &_M)) continue; pc.putc(':'); if (!GetNumber(2, &_S)) continue; t->tm_hour = _H; t->tm_min = _M; t->tm_sec = _S; pc.printf("\r\n"); pc.printf("%02d/%02d/%04d ", t->tm_mon+1, t->tm_mday, t->tm_year+1900); pc.printf("%02d:%02d:%02d\r\n", t->tm_hour, t->tm_min, t->tm_sec); // convert to timestamp and display (1256729737) time_t seconds = mktime(t); seconds = seconds - (time_t)(tzOffsetHr * 3600 + tzOffsetMin * 60); set_time(seconds); break; } else { pc.printf("%02d:%02d:%02d\r\n", t->tm_hour, t->tm_min, t->tm_sec); break; // they can bail here } } for (i=0; i<5; i++) { ShowTime(tzOffsetHr, tzOffsetMin); wait(1.0); } } /// Ethernet_Tests will attempt to test the Ethernet interface /// /// It will connect to the network - if possible, then it will /// try to connect to a network time server and set the clock, /// using hard coded time server and time zone offset values. /// /// It appears that the Ethernet interface cannot be instantiated, /// destroyed, and later instantiated again (it would reliably "hang"). /// So, this test is "runonce" protected. /// void Ethernet_Tests(void) { EthernetNetIf eth; NTPClient ntp; static bool runonce = true; printf("Ethernet Test:\r\n"); if (runonce) { EthernetErr ethErr = eth.setup(); if (ethErr) { printf("Error %d in setup.\r\n", ethErr); return; } printf(" Ethernet Setup OK\r\n"); ShowTime(0, tzOffsetHr, tzOffsetMin); printf(" Setting clock to %s\r\n", TicTocServer); Host server(IpAddr(), 123, TicTocServer); ntp.setTime(server); printf(" Clock was set.\r\n"); wait(1.0); ShowTime(0, tzOffsetHr, tzOffsetMin); runonce = false; } else { printf(" only runs once per cold-boot.\r\n"); } } /// isPrint tests the passed in character for being 'printable' /// /// It will evaluate the character on a simple ASCII range of /// characters 0 to 127. /// /// @param i is the character to test /// @returns true if the character is in the printable set (including <space>) /// @returns false if the character is not printable (may be control code or extended character) /// bool isPrint(char i) { if (i >= ' ' && i <= '~') return true; else return false; } /// common file system test to write and then read a moderately large file /// /// This will create a folder and then open a file for write. It will write a /// text string to the file and defined number of times and close the file. /// It will then report the performance metrics in bytes/sec write. /// It will then open the same file for read and read the file back, one record /// at a time. It will then report the performance metrics for the read in bytes/sec. /// /// @param folder is the folder name to create /// @param file is the filename to create /// @param textMessage is the text message to use in the test /// @param COUNTLIMIT is the number of times the text is written to the file /// @returns nothing /// void RunFileSystemTest(const char * folder, const char * file, const char * textMessage, int COUNTLIMIT) { FILE *fp; Timer timer; char * buffer = (char *)malloc(strlen(textMessage)+2); // just a bit bigger if (strlen(folder)) mkdir(folder, 0777); // Write test printf(" Starting write test of %i chars.\r\n", strlen(textMessage) * COUNTLIMIT); int begin = timer.read_us(); fp = fopen(file, "w"); if (fp == NULL) { printf(" Could not open file for write\r\n"); } else { int counter; for (counter=0; counter<COUNTLIMIT; counter++) { fprintf(fp, textMessage); } fclose(fp); int end = timer.read_us(); printf(" Closed file. Wrote %i chars in %i uSec.\r\n", strlen(textMessage) * COUNTLIMIT, end - begin); printf(" %6.0f bytes/sec.\r\n", 1.0e6f * (float)(strlen(textMessage) * COUNTLIMIT) / (end - begin)); // read test printf(" Read test.\r\n"); begin = timer.read_us(); fp = fopen(file, "r"); if (fp == NULL) { printf(" could not open file for read\r\n"); } else { while (fgets(buffer, sizeof(buffer), fp)) { while (strlen(buffer) > 0 && !isPrint(buffer[strlen(buffer)-1])) buffer[strlen(buffer)-1] = '\0'; // chomp the <LF> //printf(" Read: {%s}\r\n", buffer); } fclose(fp); end = timer.read_us(); printf(" Closed file. Read %i chars in %i uSec.\r\n", strlen(textMessage) * COUNTLIMIT, end - begin); printf(" %6.0f bytes/sec.\r\n", 1.0e6f * (float)(strlen(textMessage) * COUNTLIMIT) / (end - begin)); } } free(buffer); } /// MicroSD_Tests attempts to access and write a file on the micro SD card /// /// It will mount the file system, then attempt to write a simple /// file on the micro SD card. /// void MicroSD_Tests(void) { SDFileSystem sd(p5, p6, p7, p8, "ud"); // the pinout on the mbed Cool Components workshop board char folder[] = "/ud/testDir"; char file[] = "/ud/testDir/sdtest.txt"; DigitalIn cardIn(p11); const int COUNTLIMIT = 10000; const char textMessage[] = "Write a message to the micro SD card! 50 chars\r\n"; printf("SD File System Tests: [installed microSD card required]\r\n"); printf(" Card Detection: %s\r\n", (cardIn) ? "in" : "out"); if (cardIn) { RunFileSystemTest(folder, file, textMessage, COUNTLIMIT); } printf(" test complete!\r\n"); } /// USBHost_Tests attempts to access and write a file on USB stick /// /// It will mount the file system, then attempt to write a simple /// file on the USB interface. /// void USBHost_Tests(void) { MSCFileSystem fs ("fs"); char folder[] = "/fs/testDir"; char file[] = "/fs/testDir/sdtest.txt"; const int COUNTLIMIT = 10000; const char textMessage[] = "Write a message to the USB Memory stick 50 chars\r\n"; printf("USB Host Tests: [installed memory stick required]\r\n"); RunFileSystemTest(folder, file, textMessage, COUNTLIMIT); printf(" test complete!\r\n"); } /// FileSystem_Tests attempts to access and write a file on the local file system /// /// It will mount the file system, then attempt to write a simple /// file on the interface. While file handles are open, the USB drive will /// be unavailable. /// void FileSystem_Tests(void) { LocalFileSystem local("local"); char folder[] = ""; char file[] = "/local/fstest.txt"; const int COUNTLIMIT = 10000; const char textMessage[] = "Write a message to the Local Files 50 chars\r\n"; printf("Local File System Tests: [mbed onboard file system]\r\n"); RunFileSystemTest(folder, file, textMessage, COUNTLIMIT); printf(" test complete!\r\n"); } /// FormatMicroseconds will format a number of microseconds int ascii seconds. /// /// It will support formatting the number with decimal and thousands /// separators. /// /// @param value to format /// @returns the formatted string "0034.123456" /// char * FormatMicroseconds(int value) { static char result[15] = ""; int uSec = value % 1000000; int Sec = value / 1000000; sprintf(result, "%04i.%06i", Sec, uSec); return result; } /// ShowCANMessage will print to the console as specified /// /// This format is used in other tools, and is not explained /// here beyond what you see /// /// +--- 'r'eceive or 't'ransmit /// | +--- 'nrm' 11 bit identifier, 'xtd' 29 bit identifier /// | | +--- channel '1' to '2' /// | | | +--- identifier in hex /// | | | | +-- dlc - data length control /// | | | | | +--------------------+ data bytes 1 to 8 /// | | | | | | +--- fixed zero for compatibility with other tools /// | | | | | | | +-- fixed zero for compat /// | | | | | | | | +--- timestamp /// | | | | | | | | | /// _ ___ _ ________ __ _______________________ _ ___ ___________ /// r xtd 2 1CF00400 08 11 22 33 44 55 66 77 88 0 0 1234.567890 /// t xtd 1 18EAFF03 03 EE EE 00 0 0 1235.654321 /// /// @param tx indicates it is a transmit message when non-zero, receive otherwise /// @param ch is the communication channel of this message /// @msg is the CAN message to be shown /// @uSec is the timestamp in microseconds /// @returns nothing /// void ShowCANMessage(int tx, int ch, CANMessage msg, int uSec) { pc.printf("%c %s %d %08X %02X ", (tx) ? 't' : 'r', (msg.format == CANExtended) ? "xtd" : "nrm", ch, msg.id, msg.len); for (int d=0; d<8; d++) { if (d < msg.len) pc.printf("%02X ", msg.data[d]); else pc.printf(" "); } pc.printf("0 0 %s\r\n", FormatMicroseconds(uSec)); } /// CAN_Tests will send some packets on one CAN port and expect them on the other /// /// It will attempt to send 10 messages on one port and expect that /// all 10 messages were received on the other port. The two ports should /// be wired from one to the other with a loop-back cable and a termination /// resistor. /// void CAN_Tests(void) { CAN can1(p9, p10); CAN can2(p30, p29); char Txcounter = 0; char Rxcounter = 0; CANMessage tMsg; CANMessage rMsg; Timer timer; int i; pc.printf("CAN Tests:\r\n"); i = can1.frequency(250000); pc.printf(" can1 frequency set: %i\r\n", i); i = can2.frequency(250000); pc.printf(" can2 frequency set: %i\r\n", i); timer.start(); for (i=0; i<25; i++) { // gets a few more passes to receive the messages for (int x=0; x<8; x++) tMsg.data[x] = (char)(rand()); tMsg.data[0] = Txcounter; tMsg.id = rand(); tMsg.len = rand() % 9; tMsg.type = CANData; tMsg.format = (rand() & 1) ? CANExtended : CANStandard; if (Txcounter < 10 && can1.write(tMsg)) { pc.printf(" "); ShowCANMessage(1, 1, tMsg, timer.read_us()); Txcounter++; //wait(0.05); } if (can2.read(rMsg)) { Rxcounter++; pc.printf(" "); ShowCANMessage(0, 2, rMsg, timer.read_us()); } wait(0.005); } if (Txcounter == Rxcounter) printf(" passed.\r\n"); else printf(" **** Txcounter (%d) != Rxcounter (%d) ****\r\n", Txcounter, Rxcounter); } /// RS_232_Tests will say hello on each of the RS-232 channels /// /// It will print a hello text string out each of the ports. /// void RS_232_Tests(void) { Serial s1(p13, p14); Serial s2(p28, p27); pc.printf("RS-232 Tests:\r\n"); s1.printf(" Hello going out S1\r\n"); s2.printf(" Hello going out S2\r\n"); pc.printf(" end tests.\r\n"); }