CuVoodoo STM32F1 firmware template
led_max7219.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
30 /* standard libraries */
31 #include <stdint.h> // standard integer types
32 #include <stdlib.h> // general utilities
33 #include <string.h> // string utilities
34 
35 /* STM32 (including CM3) libraries */
36 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
37 #include <libopencm3/stm32/rcc.h> // real-time control clock library
38 #include <libopencm3/stm32/gpio.h> // general purpose input output library
39 #include <libopencm3/stm32/spi.h> // SPI library
40 #include <libopencm3/stm32/timer.h> // timer library
41 
42 #include "global.h" // global utilities
43 #include "led_max7219.h" // MAX7219 header and definitions
44 
48 #define LED_MAX7219_LOAD_PORT B
49 #define LED_MAX7219_LOAD_PIN 12
55 #define LED_MAX7219_SPI 2
61 static const uint8_t ascii_7segments[] = {
62  0x00, // space
63  0x06, // ! (I)
64  0x22, // "
65  0x1d, // # (o)
66  0x5b, // $ (s)
67  0x25, // % (/)
68  0x5f, // & (6)
69  0x02, // '
70  0x4e, // ( ([)
71  0x78, // )
72  0x07, // *
73  0x31, // +
74  0x04, // ,
75  0x01, // -
76  0x04, // . (,)
77  0x25, // /
78  0x7e, // 0
79  0x30, // 1
80  0x6d, // 2
81  0x79, // 3
82  0x33, // 4
83  0x5b, // 5
84  0x5f, // 6
85  0x70, // 7
86  0x7f, // 8
87  0x7b, // 9
88  0x09, // : (=)
89  0x09, // ; (=)
90  0x0d, // <
91  0x09, // =
92  0x19, // >
93  0x65, // ?
94  0x6f, // @
95  0x77, // A
96  0x7f, // B
97  0x4e, // C
98  0x3d, // D
99  0x4f, // E
100  0x47, // F
101  0x5e, // G
102  0x37, // H
103  0x06, // I
104  0x3c, // J
105  0x37, // K
106  0x0e, // L
107  0x76, // M
108  0x76, // N
109  0x7e, // O
110  0x67, // P
111  0x6b, // Q
112  0x66, // R
113  0x5b, // S
114  0x0f, // T
115  0x3e, // U
116  0x3e, // V (U)
117  0x3e, // W (U)
118  0x37, // X (H)
119  0x3b, // Y
120  0x6d, // Z
121  0x4e, // [
122  0x13, // '\'
123  0x78, // /
124  0x62, // ^
125  0x08, // _
126  0x20, // `
127  0x7d, // a
128  0x1f, // b
129  0x0d, // c
130  0x3d, // d
131  0x6f, // e
132  0x47, // f
133  0x7b, // g
134  0x17, // h
135  0x04, // i
136  0x18, // j
137  0x37, // k
138  0x06, // l
139  0x15, // m
140  0x15, // n
141  0x1d, // o
142  0x67, // p
143  0x73, // q
144  0x05, // r
145  0x5b, // s
146  0x0f, // t
147  0x1c, // u
148  0x1c, // v (u)
149  0x1c, // w (u)
150  0x37, // x
151  0x3b, // y
152  0x6d, // z
153  0x4e, // { ([)
154  0x06, // |
155  0x78, // } ([)
156  0x01, // ~
157 };
158 
161 
166 static void led_max7219_write(uint16_t data, uint8_t display)
167 {
168  if (lex_max7219_displays<=display && 0xff!=display) { // display no in chain
169  return;
170  }
171 
172  gpio_clear(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // ensure load pin is low (data is put in MAX7219 register on rising edge)
173  for (uint8_t i=lex_max7219_displays; i>0; i--) { // go though all displays
174  while (SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_BSY); // wait until not busy
175  if (0xff==display || i==(display+1)) { // right display or broadcast message
176  spi_send(SPI(LED_MAX7219_SPI), data); // send data
177  } else {
178  spi_send(SPI(LED_MAX7219_SPI), 0x0000); // send no-op command to shift command to correct display or out
179  }
180  while (!(SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_TXE)); // wait until Tx is empty (reference manual says BSY should also cover this, but it doesn't)
181  while (SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_BSY); // wait until not busy (= transmission completed)
182  }
183  gpio_set(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // create rising edge on load pin for data to be set in MAX7219 register
184 }
185 
186 void led_max7219_setup(uint8_t displays)
187 {
188  // saved number of displays
189  lex_max7219_displays = displays;
190 
191  // configure GPIO for load line
192  rcc_periph_clock_enable(RCC_GPIO(LED_MAX7219_LOAD_PORT)); // enable clock for GPIO peripheral
193  gpio_clear(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // idle low (load on rising edge)
194  gpio_set_mode(GPIO(LED_MAX7219_LOAD_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_MAX7219_LOAD_PIN)); // set as output
195 
196  // configure SPI peripheral
197  rcc_periph_clock_enable(RCC_SPI_SCK_PORT(LED_MAX7219_SPI)); // enable clock for GPIO peripheral for clock signal
198  gpio_set_mode(SPI_SCK_PORT(LED_MAX7219_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(LED_MAX7219_SPI)); // set as output (max clock speed for MAX7219 is 10 MHz)
199  rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(LED_MAX7219_SPI)); // enable clock for GPIO peripheral for MOSI signal
200  gpio_set_mode(SPI_MOSI_PORT(LED_MAX7219_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(LED_MAX7219_SPI)); // set as output
201  rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
202  rcc_periph_clock_enable(RCC_SPI(LED_MAX7219_SPI)); // enable clock for SPI peripheral
203  spi_reset(SPI(LED_MAX7219_SPI)); // clear SPI values to default
204  spi_init_master(SPI(LED_MAX7219_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_16BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 8 since max MAX7219 clock is 10 MHz and max SPI PCLK clock is 72 Mhz, depending on which SPI is used, set clock polarity to idle low (as in the datasheet of the MAX7219, but not that important), set clock phase to go high when bit is set (depends on polarity) as data is stored on MAX7219 on rising edge), use 16 bits frames (as used by MAX7219), use MSB first
205  spi_set_unidirectional_mode(SPI(LED_MAX7219_SPI)); // we only need to transmit data
206  spi_enable(SPI(LED_MAX7219_SPI)); // enable SPI
207 }
208 
209 void led_max7219_on(uint8_t display)
210 {
211  led_max7219_write(0x0C01, display); // put in normal operation more (registers remain as set)
212 }
213 
214 void led_max7219_off(uint8_t display)
215 {
216  led_max7219_write(0x0C00, display); // put in shutdown mode (registers remain as set)
217 }
218 
219 void led_max7219_test(bool test, uint8_t display)
220 {
221  if (test) {
222  led_max7219_write(0x0F01, display); // go into display test mode
223  } else {
224  led_max7219_write(0x0F00, display); // go into normal operation mode
225  }
226 }
227 
228 void led_max7219_intensity(uint8_t intensity, uint8_t digits, uint8_t display)
229 {
230  if (intensity>15) { // intensity must be 0-15 (corresponds to (2*brightness+1)/32)
231  return;
232  }
233  if (digits<1 || digits>8) { // scan limit must bit 0-7
234  return;
235  }
236  led_max7219_write(0x0A00+intensity, display); // set brightness
237  led_max7219_write(0x0B00+digits-1, display); // set scan limit to display digits
238 }
239 
240 bool led_max7219_text(char* text, uint8_t display)
241 {
242  for (uint8_t i=0; i<8; i++) { // input text should only contain printable character (8th bit is used for dots)
243  if ((text[i]&0x7f)<' ' || (text[i]&0x7f)>=' '+LENGTH(ascii_7segments)) {
244  return false;
245  }
246  }
247  led_max7219_write(0x0900, display); // disable BCD decoding on all 7 digits
248  for (uint8_t i=0; i<8; i++) { // display text
249  led_max7219_write(((i+1)<<8)+(ascii_7segments[(text[7-i]&0x7f)-' '])+(text[7-i]&0x80), display); // send digit (in reverse order)
250  }
251  return true;
252 }
253 
254 void led_max7219_number(uint32_t number, uint8_t dots, uint8_t display)
255 {
256  led_max7219_write(0x09FF, display); // enable BCD decoding on all 7 digits
257  for (uint8_t digit=0; digit<8; digit++) { // go through digits
258  if (0==digit) { // display 0 on 0 only to first digit
259  led_max7219_write(((digit+1)<<8) + (number%10) + (((dots>>digit)&0x01)<<7), display); // display digit
260  } else if (0==number) { // display blank on other digits
261  led_max7219_write(((digit+1)<<8) + 0x0F + (((dots>>digit)&0x01)<<7), display); // display blank
262  } else {
263  led_max7219_write(((digit+1)<<8) + (number%10) + (((dots>>digit)&0x01)<<7), display); // display digit
264  }
265  number /= 10; // get next digit
266  }
267 }
#define SPI_MOSI_PORT(x)
get SPI port for MOSI signal based on SPI identifier
Definition: global.h:246
#define RCC_GPIO(x)
get RCC for GPIO based on GPIO identifier
Definition: global.h:41
uint8_t lex_max7219_displays
number of display in the chain
Definition: led_max7219.c:160
void led_max7219_setup(uint8_t displays)
setup communication with MAX7219 IC
Definition: led_max7219.c:186
bool led_max7219_text(char *text, uint8_t display)
display text
Definition: led_max7219.c:240
#define SPI_MOSI_PIN(x)
get SPI pin for MOSI signal based on SPI identifier
Definition: global.h:254
global definitions and methods (API)
#define GPIO(x)
get GPIO based on GPIO identifier
Definition: global.h:39
#define RCC_SPI_MOSI_PORT(x)
get RCC for GPIO port for SPI MOSI signals
Definition: global.h:235
#define RCC_SPI(x)
get RCC for SPI based on SPI identifier
Definition: global.h:218
void led_max7219_test(bool test, uint8_t display)
switch display in test or normal operation mode
Definition: led_max7219.c:219
#define SPI_SCK_PORT(x)
get SPI port for SCK signal based on SPI identifier
Definition: global.h:242
void led_max7219_on(uint8_t display)
switch display on
Definition: led_max7219.c:209
void led_max7219_number(uint32_t number, uint8_t dots, uint8_t display)
display number
Definition: led_max7219.c:254
static void led_max7219_write(uint16_t data, uint8_t display)
write data on SPI bus and handle load signal
Definition: led_max7219.c:166
void led_max7219_off(uint8_t display)
switch display off
Definition: led_max7219.c:214
#define SPI(x)
get SPI based on SPI identifier
Definition: global.h:216
#define RCC_SPI_SCK_PORT(x)
get RCC for GPIO port for SPI SCK signals
Definition: global.h:225
void led_max7219_intensity(uint8_t intensity, uint8_t digits, uint8_t display)
set display intensity
Definition: led_max7219.c:228
static const uint8_t ascii_7segments[]
ASCII characters encoded for the 7 segments digit block.
Definition: led_max7219.c:61
library to communicate with a Maxim MAX7219 IC attached to a 8-digit 7-segment (API) ...
#define LENGTH(x)
get the length of an array
Definition: global.h:26
#define LED_MAX7219_SPI
SPI to send data.
Definition: led_max7219.c:55
#define LED_MAX7219_LOAD_PIN
pin for load line
Definition: led_max7219.c:49
#define LED_MAX7219_LOAD_PORT
port for load line
Definition: led_max7219.c:48
#define SPI_SCK_PIN(x)
get SPI pin for SCK signal based on SPI identifier
Definition: global.h:250