/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Copyright (c) 2016 King Kévin */ /* standard libraries */ #include // standard integer types #include // standard I/O facilities #include // standard utilities #include // standard streams #include // error number utilities /* STM32 (including CM3) libraries */ #include // real-time control clock library #include // general purpose input output library #include // vector table definition #include // Cortex M3 utilities /* own libraries */ #include "main.h" // board definitions #include "usart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities /* get the length of an array */ #define LENGTH(x) (sizeof(x) / sizeof((x)[0])) /* default output (i.e. for printf) */ int _write(int file, char *ptr, int len) { int i; if (file == STDOUT_FILENO || file == STDERR_FILENO) { for (i = 0; i < len; i++) { if (ptr[i] == '\n') { // add carrier return before line feed. this is recommended for most UART terminals usart_putchar_nonblocking('\r'); // a second line feed doesn't break the display cdcacm_putchar('\r'); // a second line feed doesn't break the display } usart_putchar_nonblocking(ptr[i]); // send byte over USART cdcacm_putchar(ptr[i]); // send byte over USB } return i; } errno = EIO; return -1; } static void gpio_setup(void) { rcc_periph_clock_enable(LED_RCC); //enable clock for LED gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_PIN); // set LED pin to 'output push-pull' rcc_periph_clock_enable(VFD_RCC); //enable clock for VFD gpio_set_mode(VFD_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, VFD_STR); // set VFD pin to 'output push-pull' gpio_set_mode(VFD_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, VFD_NLE); // set VFD pin to 'output push-pull' gpio_set_mode(VFD_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, VFD_CLK); // set VFD pin to 'output push-pull' gpio_set_mode(VFD_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, VFD_DIN); // set VFD pin to 'output push-pull' } /* the ten seven segments + dot displays * actually they also have a comma and underline, but we want to save space */ //static uint8_t digits[10]; /* the twelve 5x7 dot matrix displays * the last column dot/bit is not used, making them byte aligned */ //static uint8_t dots[12][5]; /* the three 32 bit to be shifted out to the VFD controller */ static uint32_t vfd_data[3]; /* shift out the VFD data */ static void vfd_shift(void) { gpio_clear(VFD_PORT, VFD_NLE); // do not latch data gpio_set(VFD_PORT, VFD_CLK); // clock is idle high for (uint8_t i=0; i 0b01010011, // ? 0b01111011, // @ 0b01110111, // A 0b01111111, // B 0b00111001, // C 0b01011110, // D 0b01111001, // E 0b01110001, // F 0b00111101, // G 0b01110110, // H 0b00110000, // I 0b00011110, // J 0b01110110, // K 0b00111000, // L 0b00110111, // M 0b00110111, // N 0b00111111, // O 0b01110011, // P 0b01101011, // Q 0b00110011, // R 0b01101101, // S 0b01111000, // T 0b00111110, // U 0b00111110, // V (U) 0b00111110, // W (U) 0b01110110, // X (H) 0b01101110, // Y 0b01011011, // Z 0b00111001, // [ 0b01100100, // '\' 0b00001111, // / 0b00100011, // ^ 0b00001000, // _ 0b00000010, // ` 0b01011111, // a 0b01111100, // b 0b01011000, // c 0b01011110, // d 0b01111011, // e 0b01110001, // f 0b01101111, // g 0b01110100, // h 0b00010000, // i 0b00001100, // j 0b01110110, // k 0b00110000, // l 0b01010100, // m 0b01010100, // n 0b01011100, // o 0b01110011, // p 0b01100111, // q 0b01010000, // r 0b01101101, // s 0b01111000, // t 0b00011100, // u 0b00011100, // v (u) 0b00011100, // w (u) 0b01110110, // x 0b01101110, // y 0b01011011, // z 0b00111001, // { ([) 0b00110000, // | 0b00001111, // } ([) 0b01000000, // ~ }; /* put digit into memory */ static void vfd_digit(uint8_t nb, char c) { // there are only 10 digits if (nb>9) { return; } vfd_data[0] = 0; vfd_data[1] = 1<<(4+(9-nb)); // select digit /* encode segment * here the bit order (classic 7 segment + underline and dot) * 3_ * 8|9_|4 * 7|6_|5.1 * 0_2, * */ if (false) { // add the underline (not encoded) vfd_data[1] |= (1<<(14)); } if (c&0x80) { // add the dot (encoded in the 8th bit) vfd_data[1] |= (1<<(15)); } if (false) { // add the comma (not encoded) vfd_data[1] |= (1<<(16)); } if (((c&0x7f)<' ') || ((c&0x7f)>' '+LENGTH(ascii_7segments)-1)) { // unknown ASCII character vfd_data[1] = 0; // add empty segments to memory } else { vfd_data[1] |= (ascii_7segments[c-' ']<<(17)); // add encoded segments to memory } vfd_data[2] = 0; } /* font for the 5x7 dot matrix display * from http://sunge.awardspace.com/glcd-sd/node4.html * first value is left-most line * LSB is top dot, MSB is not used */ static const uint8_t ascii_font5x7[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00}, // (space) {0x00, 0x00, 0x5F, 0x00, 0x00}, // ! {0x00, 0x07, 0x00, 0x07, 0x00}, // " {0x14, 0x7F, 0x14, 0x7F, 0x14}, // # {0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $ {0x23, 0x13, 0x08, 0x64, 0x62}, // % {0x36, 0x49, 0x55, 0x22, 0x50}, // & {0x00, 0x05, 0x03, 0x00, 0x00}, // ' {0x00, 0x1C, 0x22, 0x41, 0x00}, // ( {0x00, 0x41, 0x22, 0x1C, 0x00}, // ) {0x08, 0x2A, 0x1C, 0x2A, 0x08}, // * {0x08, 0x08, 0x3E, 0x08, 0x08}, // + {0x00, 0x50, 0x30, 0x00, 0x00}, // , {0x08, 0x08, 0x08, 0x08, 0x08}, // - {0x00, 0x60, 0x60, 0x00, 0x00}, // . {0x20, 0x10, 0x08, 0x04, 0x02}, // / {0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 {0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 {0x42, 0x61, 0x51, 0x49, 0x46}, // 2 {0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 {0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 {0x27, 0x45, 0x45, 0x45, 0x39}, // 5 {0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6 {0x01, 0x71, 0x09, 0x05, 0x03}, // 7 {0x36, 0x49, 0x49, 0x49, 0x36}, // 8 {0x06, 0x49, 0x49, 0x29, 0x1E}, // 9 {0x00, 0x36, 0x36, 0x00, 0x00}, // : {0x00, 0x56, 0x36, 0x00, 0x00}, // ; {0x00, 0x08, 0x14, 0x22, 0x41}, // < {0x14, 0x14, 0x14, 0x14, 0x14}, // = {0x41, 0x22, 0x14, 0x08, 0x00}, // > {0x02, 0x01, 0x51, 0x09, 0x06}, // ? {0x32, 0x49, 0x79, 0x41, 0x3E}, // @ {0x7E, 0x11, 0x11, 0x11, 0x7E}, // A {0x7F, 0x49, 0x49, 0x49, 0x36}, // B {0x3E, 0x41, 0x41, 0x41, 0x22}, // C {0x7F, 0x41, 0x41, 0x22, 0x1C}, // D {0x7F, 0x49, 0x49, 0x49, 0x41}, // E {0x7F, 0x09, 0x09, 0x01, 0x01}, // F {0x3E, 0x41, 0x41, 0x51, 0x32}, // G {0x7F, 0x08, 0x08, 0x08, 0x7F}, // H {0x00, 0x41, 0x7F, 0x41, 0x00}, // I {0x20, 0x40, 0x41, 0x3F, 0x01}, // J {0x7F, 0x08, 0x14, 0x22, 0x41}, // K {0x7F, 0x40, 0x40, 0x40, 0x40}, // L {0x7F, 0x02, 0x04, 0x02, 0x7F}, // M {0x7F, 0x04, 0x08, 0x10, 0x7F}, // N {0x3E, 0x41, 0x41, 0x41, 0x3E}, // O {0x7F, 0x09, 0x09, 0x09, 0x06}, // P {0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q {0x7F, 0x09, 0x19, 0x29, 0x46}, // R {0x46, 0x49, 0x49, 0x49, 0x31}, // S {0x01, 0x01, 0x7F, 0x01, 0x01}, // T {0x3F, 0x40, 0x40, 0x40, 0x3F}, // U {0x1F, 0x20, 0x40, 0x20, 0x1F}, // V {0x7F, 0x20, 0x18, 0x20, 0x7F}, // W {0x63, 0x14, 0x08, 0x14, 0x63}, // X {0x03, 0x04, 0x78, 0x04, 0x03}, // Y {0x61, 0x51, 0x49, 0x45, 0x43}, // Z {0x00, 0x00, 0x7F, 0x41, 0x41}, // [ {0x02, 0x04, 0x08, 0x10, 0x20}, // '\' {0x41, 0x41, 0x7F, 0x00, 0x00}, // ] {0x04, 0x02, 0x01, 0x02, 0x04}, // ^ {0x40, 0x40, 0x40, 0x40, 0x40}, // _ {0x00, 0x01, 0x02, 0x04, 0x00}, // ` {0x20, 0x54, 0x54, 0x54, 0x78}, // a {0x7F, 0x48, 0x44, 0x44, 0x38}, // b {0x38, 0x44, 0x44, 0x44, 0x20}, // c {0x38, 0x44, 0x44, 0x48, 0x7F}, // d {0x38, 0x54, 0x54, 0x54, 0x18}, // e {0x08, 0x7E, 0x09, 0x01, 0x02}, // f {0x08, 0x14, 0x54, 0x54, 0x3C}, // g {0x7F, 0x08, 0x04, 0x04, 0x78}, // h {0x00, 0x44, 0x7D, 0x40, 0x00}, // i {0x20, 0x40, 0x44, 0x3D, 0x00}, // j {0x00, 0x7F, 0x10, 0x28, 0x44}, // k {0x00, 0x41, 0x7F, 0x40, 0x00}, // l {0x7C, 0x04, 0x18, 0x04, 0x78}, // m {0x7C, 0x08, 0x04, 0x04, 0x78}, // n {0x38, 0x44, 0x44, 0x44, 0x38}, // o {0x7C, 0x14, 0x14, 0x14, 0x08}, // p {0x08, 0x14, 0x14, 0x18, 0x7C}, // q {0x7C, 0x08, 0x04, 0x04, 0x08}, // r {0x48, 0x54, 0x54, 0x54, 0x20}, // s {0x04, 0x3F, 0x44, 0x40, 0x20}, // t {0x3C, 0x40, 0x40, 0x20, 0x7C}, // u {0x1C, 0x20, 0x40, 0x20, 0x1C}, // v {0x3C, 0x40, 0x30, 0x40, 0x3C}, // w {0x44, 0x28, 0x10, 0x28, 0x44}, // x {0x0C, 0x50, 0x50, 0x50, 0x3C}, // y {0x44, 0x64, 0x54, 0x4C, 0x44}, // z {0x00, 0x08, 0x36, 0x41, 0x00}, // { {0x00, 0x00, 0x7F, 0x00, 0x00}, // | {0x00, 0x41, 0x36, 0x08, 0x00}, // } {0b00001000, 0b00000100, 0b00001100, 0b00001000, 0b00000100} // ~ }; /* pictures for the 5x7 dot matrix display * first value is left-most line * LSB is top dot, MSB is not used */ static const uint8_t ascii_pict5x7[][5] = { {0x08, 0x08, 0x2A, 0x1C, 0x08}, // -> {0x08, 0x1C, 0x2A, 0x08, 0x08} // <- }; static void vfd_matrix(uint8_t nb, char c) { // there are only 12 matrix if (nb>11) { return; } else if (nb<4) { vfd_data[0] = 0; vfd_data[1] = 1<<(3-nb); // select digit } else { vfd_data[0] = 1<<(35-nb); // select digit vfd_data[1] = 0; } if (c<0x80) { uint8_t i = c-' '; vfd_data[2] = 0; if (i