/* 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 /* 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; i9) { 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)); } uint8_t segment = 0; switch (c&0x7f) { case '0': segment = 0b00111111; break; case '1': segment = 0b00000110; break; case '2': segment = 0b01011011; break; case '3': segment = 0b01001111; break; case '4': segment = 0b01100110; break; case '5': segment = 0b01101101; break; case '6': segment = 0b01111101; break; case '7': segment = 0b00000111; break; case '8': segment = 0b01111111; break; case '9': segment = 0b01101111; break; case 'a': segment = 0b01011111; break; case 'b': segment = 0b01111100; break; case 'c': segment = 0b01011000; break; case 'd': segment = 0b01011110; break; case 'e': segment = 0b01111011; break; case 'f': segment = 0b01110001; break; case 'g': segment = 0b01101111; break; case 'h': segment = 0b01110100; break; case 'i': segment = 0b00010000; break; case 'j': segment = 0b00001100; break; case 'k': segment = 0b01110110; break; case 'l': segment = 0b00110000; break; case 'm': segment = 0b01010100; break; case 'n': segment = 0b01010100; break; case 'o': segment = 0b01011100; break; case 'p': segment = 0b01110011; break; case 'q': segment = 0b01100111; break; case 'r': segment = 0b01010000; break; case 's': segment = 0b01101101; break; case 't': segment = 0b01111000; break; case 'u': segment = 0b00011100; break; case 'v': segment = 0b00011100; break; case 'w': segment = 0b00011100; break; case 'x': segment = 0b01110110; break; case 'y': segment = 0b01101110; break; case 'z': segment = 0b01011011; break; case 'A': segment = 0b01110111; break; case 'B': segment = 0b01111111; break; case 'C': segment = 0b00111001; break; case 'D': segment = 0b01011110; break; case 'E': segment = 0b01111001; break; case 'F': segment = 0b01110001; break; case 'G': segment = 0b00111101; break; case 'H': segment = 0b01110110; break; case 'I': segment = 0b00110000; break; case 'J': segment = 0b00011110; break; case 'K': segment = 0b01110110; break; case 'L': segment = 0b00111000; break; case 'M': segment = 0b00110111; break; case 'N': segment = 0b00110111; break; case 'O': segment = 0b00111111; break; case 'P': segment = 0b01110011; break; case 'Q': segment = 0b01101011; break; case 'R': segment = 0b00110011; break; case 'S': segment = 0b01101101; break; case 'T': segment = 0b01111000; break; case 'U': segment = 0b00111110; break; case 'V': segment = 0b00111110; break; case 'W': segment = 0b00111110; break; case 'X': segment = 0b01110110; break; case 'Y': segment = 0b01101110; break; case 'Z': segment = 0b01011011; break; case '_': segment = 0b00001000; break; case '-': segment = 0b01000000; break; case ' ': segment = 0b00000000; break; case '\'': segment = 0b00100000; break; case '"': segment = 0b00100010; break; case '/': segment = 0b01010010; break; case '\\': segment = 0b01100100; break; case '=': segment = 0b01001000; break; case ',': segment = 0b00010000; break; case '>': segment = 0b01001100; break; case '<': segment = 0b01011000; break; case '(': case '[': case '{': segment = 0b00111001; break; case ')': case ']': case '}': segment = 0b00001111; break; case '@': segment = 0b01111011; break; case '^': segment = 0b00100011; break; case '`': segment = 0b00000010; break; case '|': segment = 0b00110000; break; case '~': segment = 0b01000000; break; default: segment = 0x00; } vfd_data[1] |= (segment<<(17)); // add the segment to memory vfd_data[2] = 0; } int main(void) { SCB_VTOR = (uint32_t) 0x08002000; // relocate vector table because of the bootloader rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock gpio_setup(); // setup main inputs/outputs usart_setup(); // setup USART (for printing) cdcacm_setup(); // setup USB CDC ACM (for printing) setbuf(stdout, NULL); // set standard out buffer to NULL to immediately print setbuf(stderr, NULL); // set standard error buffer to NULL to immediately print printf("welcome to the STM32F1 CuVoodoo display driver\n"); gpio_set(VFD_PORT, VFD_STR); // disable HV output gpio_clear(VFD_PORT, VFD_NLE); // do not latch data gpio_set(VFD_PORT, VFD_CLK); // clock is idle high bool vfd_transmit = false; uint8_t digit = 0; uint8_t c = 0; /* blink the LED with every transmitted character */ while (1) { while (usart_received) { // echo every received character //gpio_toggle(LED_PORT, LED_PIN); // toggle LED printf("%c",usart_getchar()); // transmit receive character vfd_transmit = true; } while (cdcacm_received) { // echo every received character //gpio_toggle(LED_PORT, LED_PIN); // toggle LED printf("%c",cdcacm_getchar()); // transmit receive character vfd_transmit = true; } while (vfd_transmit) { c = digit+'0'; vfd_digit(digit,c); vfd_shift(); digit = (digit+1)%10; // let the fluorescence glow up a bit for (uint32_t i = 0; i < 0x2000; i++) { __asm__("nop"); } } __WFI(); // go to sleep } return 0; }