CuVoodoo STM32F1 firmware template
led_tm1637.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  */
32 /* standard libraries */
33 #include <stdint.h> // standard integer types
34 #include <stdlib.h> // memory utilities
35 #include <string.h> // string utilities
36 
37 /* STM32 (including CM3) libraries */
38 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
39 #include <libopencm3/stm32/rcc.h> // real-time control clock library
40 #include <libopencm3/stm32/gpio.h> // general purpose input output library
41 #include <libopencm3/stm32/timer.h> // timer library
42 
43 #include "global.h" // global utilities
44 #include "led_tm1637.h" // TM1637 header and definitions
45 
49 #define LED_TM1637_CLK_PORT B
50 #define LED_TM1637_CLK_PIN 6
51 #define LED_TM1637_DIO_PORT B
52 #define LED_TM1637_DIO_PIN 7
58 #define LED_TM1637_TIMER 3
62 enum led_tm1637_brightness_t display_brightness = LED_TM1637_14DIV16;
63 
67 static const uint8_t ascii_7segments[] = {
68  0x00, // 0b00000000 space
69  0x30, // 0b00110000 ! (I)
70  0x22, // 0b00100010 "
71  0x5c, // 0b01011100 # (o)
72  0x6d, // 0b01101101 $ (s)
73  0x52, // 0b01010010 % (/)
74  0x7d, // 0b01111101 & (6)
75  0x20, // 0b00100000 '
76  0x39, // 0b00111001 ( ([)
77  0x0f, // 0b00001111 )
78  0x70, // 0b01110000 *
79  0x46, // 0b01000110 +
80  0x10, // 0b00010000 ,
81  0x40, // 0b01000000 -
82  0x10, // 0b00010000 . (,)
83  0x52, // 0b01010010 /
84  0x3f, // 0b00111111 0
85  0x06, // 0b00000110 1
86  0x5b, // 0b01011011 2
87  0x4f, // 0b01001111 3
88  0x66, // 0b01100110 4
89  0x6d, // 0b01101101 5
90  0x7d, // 0b01111101 6
91  0x07, // 0b00000111 7
92  0x7f, // 0b01111111 8
93  0x6f, // 0b01101111 9
94  0x48, // 0b01001000 : (=)
95  0x48, // 0b01001000 ; (=)
96  0x58, // 0b01011000 <
97  0x48, // 0b01001000 =
98  0x4c, // 0b01001100 >
99  0x53, // 0b01010011 ?
100  0x7b, // 0b01111011 @
101  0x77, // 0b01110111 A
102  0x7f, // 0b01111111 B
103  0x39, // 0b00111001 C
104  0x5e, // 0b01011110 D
105  0x79, // 0b01111001 E
106  0x71, // 0b01110001 F
107  0x3d, // 0b00111101 G
108  0x76, // 0b01110110 H
109  0x30, // 0b00110000 I
110  0x1e, // 0b00011110 J
111  0x76, // 0b01110110 K
112  0x38, // 0b00111000 L
113  0x37, // 0b00110111 M
114  0x37, // 0b00110111 N
115  0x3f, // 0b00111111 O
116  0x73, // 0b01110011 P
117  0x6b, // 0b01101011 Q
118  0x33, // 0b00110011 R
119  0x6d, // 0b01101101 S
120  0x78, // 0b01111000 T
121  0x3e, // 0b00111110 U
122  0x3e, // 0b00111110 V (U)
123  0x3e, // 0b00111110 W (U)
124  0x76, // 0b01110110 X (H)
125  0x6e, // 0b01101110 Y
126  0x5b, // 0b01011011 Z
127  0x39, // 0b00111001 [
128  0x64, // 0b01100100 '\'
129  0x0f, // 0b00001111 /
130  0x23, // 0b00100011 ^
131  0x08, // 0b00001000 _
132  0x02, // 0b00000010 `
133  0x5f, // 0b01011111 a
134  0x7c, // 0b01111100 b
135  0x58, // 0b01011000 c
136  0x5e, // 0b01011110 d
137  0x7b, // 0b01111011 e
138  0x71, // 0b01110001 f
139  0x6f, // 0b01101111 g
140  0x74, // 0b01110100 h
141  0x10, // 0b00010000 i
142  0x0c, // 0b00001100 j
143  0x76, // 0b01110110 k
144  0x30, // 0b00110000 l
145  0x54, // 0b01010100 m
146  0x54, // 0b01010100 n
147  0x5c, // 0b01011100 o
148  0x73, // 0b01110011 p
149  0x67, // 0b01100111 q
150  0x50, // 0b01010000 r
151  0x6d, // 0b01101101 s
152  0x78, // 0b01111000 t
153  0x1c, // 0b00011100 u
154  0x1c, // 0b00011100 v (u)
155  0x1c, // 0b00011100 w (u)
156  0x76, // 0b01110110 x
157  0x6e, // 0b01101110 y
158  0x5b, // 0b01011011 z
159  0x39, // 0b00111001 { ([)
160  0x30, // 0b00110000 |
161  0x0f, // 0b00001111 } ([)
162  0x40, // 0b01000000 ~
163 };
164 
166 {
167  // configure GPIO for CLK and DIO signals
168  rcc_periph_clock_enable(RCC_GPIO(LED_TM1637_CLK_PORT)); // enable clock for GPIO peripheral
169  gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // idle high
170  gpio_set_mode(GPIO(LED_TM1637_CLK_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_CLK_PIN)); // master start the communication (capacitance is to large for open drain), only switch to input for ack from slave
171  rcc_periph_clock_enable(RCC_GPIO(LED_TM1637_DIO_PORT)); // enable clock for GPIO peripheral
172  gpio_set(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // idle high
173  gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // master start the communication (capacitance is to large for open drain), only switch to input for ack from slave
174  // first clock then data high also stands for stop condition
175 
176  // setup timer to create signal timing (each tick is used for a single GPIO transition)
177  rcc_periph_clock_enable(RCC_TIM(LED_TM1637_TIMER)); // enable clock for timer block
178  timer_reset(TIM(LED_TM1637_TIMER)); // reset timer state
179  timer_set_mode(TIM(LED_TM1637_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
180  timer_set_prescaler(TIM(LED_TM1637_TIMER), 0); // don't prescale to get most precise timing ( 1/(72E6/1/(2**16))=0.91 ms > 0.5 us )
181  timer_set_period(TIM(LED_TM1637_TIMER), rcc_ahb_frequency/50000/2); // set the clock frequency to 50 kHz (500 kHz is the maximum per datasheet, but 50 kHz is empirically more stable due to the line capacitance of the multiple displays)
182  timer_clear_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF); // clear flag
183  timer_update_on_overflow(TIM(LED_TM1637_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
184 }
185 
188 static inline void led_tm1637_tick(void)
189 {
190  while (!timer_get_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF)); // wait until counter overflow update event happens
191  timer_clear_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF); // clear event flag
192 }
193 
200 static bool led_tm1637_write(const uint8_t* data, uint8_t length)
201 {
202  bool to_return = true; // return if write succeeded
203  if (NULL==data || 0==length) { // verify there it data to be read
204  return false;
205  }
206 
207  // enable timer for signal generation
208  gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // ensure we can output to sent start condition
209  timer_set_counter(TIM(LED_TM1637_TIMER), 0); // reset timer counter
210  timer_enable_counter(TIM(LED_TM1637_TIMER)); // enable timer to generate timing
211  led_tm1637_tick(); // wait to enforce minimum time since last write
212 
213  // send start condition (DIO then CLK low)
214  gpio_clear(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO low
215  led_tm1637_tick(); // wait for next tick
216  gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low
217 
218  // send data bytes (MSb first)
219  for (uint8_t i=0; i<length; i++) { // send all bytes
220  uint8_t byte = data[i];
221  for (uint8_t b=0; b<8; b++) { // send all bits
222  if (byte&0x1) { // send a 1
223  gpio_set(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO high
224  } else {
225  gpio_clear(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO low
226  }
227  byte >>= 1; // shift data
228  led_tm1637_tick(); // wait for next tick
229  gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high
230  led_tm1637_tick(); // wait for next tick (no DIO transition when CLK is high)
231  gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low
232  }
233  gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(LED_TM1637_DIO_PIN)); // switch DIO as input to read ACK
234  led_tm1637_tick(); // wait for next tick (when the slave should ACK)
235  gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high
236  if (gpio_get(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN))) { // no ACK received
237  to_return = false; // remember there was an error
238  break; // stop sending bytes
239  }
240  led_tm1637_tick(); // wait for next tick
241  gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low
242  gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // switch DIO back to output
243  }
244 
245  // send stop condition
246  led_tm1637_tick(); // wait for next tick
247  gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high
248  led_tm1637_tick(); // wait for next tick
249  gpio_set(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO high
250  timer_disable_counter(TIM(LED_TM1637_TIMER)); // stop timer since it's not used anymore
251 
252  return to_return;
253 }
254 
260 static bool led_tm1637_send_data(uint8_t* address_command, uint8_t length)
261 {
262  // sanity check
263  if (0==length && NULL==address_command) {
264  return false;
265  }
266 
267  uint8_t data_command[] = { 0x40 }; // data command: write data, automatic address adding, normal
268  uint8_t display_command[] = { 0x88+(display_brightness&0x0f) }; // display command: turn display on and set brightness
269 
270  if (led_tm1637_write(data_command, LENGTH(data_command)) && led_tm1637_write(address_command, length) && led_tm1637_write(display_command, LENGTH(display_command))) { // send commands
271  return true;
272  }
273  return false;
274 }
275 
276 bool led_tm1637_off(void)
277 {
278  // note: while the TM1637 could recognize the display command thanks to the first two bits, we still need to send the data and address command first
279  uint8_t data_command[] = { 0x40 }; // data command: write data, automatic address adding, normal
280  uint8_t address_command[] = { 0xc0, 0x00, 0x00, 0x00, 0x00 }; // address command: clear all (if bit 7 of byte 4 is set switching off doesn't work correctly, I don't know why)
281  uint8_t display_command[] = { 0x80 }; // display command: turn display off (we don't care about the brightness since it's set when turned on)
282 
283  if (led_tm1637_write(data_command,LENGTH(data_command)) && led_tm1637_write(address_command, LENGTH(address_command)) && led_tm1637_write(display_command, LENGTH(display_command))) { // send commands
284  return true;
285  }
286  return false;
287 }
288 
290 {
291  display_brightness = (brightness&0x0f); // save brightness
292 }
293 
294 bool led_tm1637_number(uint16_t number)
295 {
296  uint8_t data[] = { 0xc0, ascii_7segments[((number/1000)%10)+'0'-' '], ascii_7segments[((number/100)%10)+'0'-' '], ascii_7segments[((number/10)%10)+'0'-' '], ascii_7segments[((number/1)%10)+'0'-' '] }; // encode number on 4 digits
297  return led_tm1637_send_data(data, LENGTH(data)); // transmit data
298 }
299 
300 bool led_tm1637_time(uint8_t hours, uint8_t minutes)
301 {
302  uint8_t data[] = { 0xc0, ascii_7segments[((hours/10)%10)+'0'-' '], ascii_7segments[((hours/1)%10)+'0'-' ']|0x80, ascii_7segments[((minutes/10)%10)+'0'-' '], ascii_7segments[((minutes/1)%10)+'0'-' '] }; // encode time on 4 digits
303  return led_tm1637_send_data(data, LENGTH(data)); // transmit data
304 }
305 
306 bool led_tm1637_text(char* text)
307 {
308  if (strlen(text)!=4) { // input text should have exactly 4 characters
309  return false;
310  }
311  for (uint8_t i=0; i<4; i++) { // input text should only contain printable character (8th bit is used for dots)
312  if ((text[i]&0x7f)<' ' || (text[i]&0x7f)>=' '+LENGTH(ascii_7segments)) {
313  return false;
314  }
315  }
316  uint8_t data[] = { 0xc0, ascii_7segments[(text[0]&0x7f)-' ']|(text[0]&0x80), ascii_7segments[(text[1]&0x7f)-' ']|(text[1]&0x80), ascii_7segments[(text[2]&0x7f)-' ']|(text[2]&0x80), ascii_7segments[(text[3]&0x7f)-' ']|(text[3]&0x80) }; // encode text on 7 digits
317  return led_tm1637_send_data(data, LENGTH(data)); // transmit data
318 }
static const uint8_t ascii_7segments[]
ASCII characters encoded for the 7 segments digit block.
Definition: led_tm1637.c:67
bool led_tm1637_number(uint16_t number)
display number
Definition: led_tm1637.c:294
#define RCC_GPIO(x)
get RCC for GPIO based on GPIO identifier
Definition: global.h:41
static void led_tm1637_tick(void)
wait until clock tick (timer overflow) occurred
Definition: led_tm1637.c:188
library to communicate with a Titan Micro TM1637 IC attached to a 7-segment displays (API) ...
static bool led_tm1637_write(const uint8_t *data, uint8_t length)
write data on bus
Definition: led_tm1637.c:200
enum led_tm1637_brightness_t display_brightness
display brightness
Definition: led_tm1637.c:62
global definitions and methods (API)
#define GPIO(x)
get GPIO based on GPIO identifier
Definition: global.h:39
#define LED_TM1637_CLK_PIN
pin for CLK signal
Definition: led_tm1637.c:50
#define RCC_TIM(x)
get RCC for timer based on TIM identifier
Definition: global.h:45
void led_tm1637_brightness(enum led_tm1637_brightness_t brightness)
set display brightness
Definition: led_tm1637.c:289
#define LED_TM1637_DIO_PORT
port for DIO signal
Definition: led_tm1637.c:51
led_tm1637_brightness_t
display brightness levels
Definition: led_tm1637.h:27
#define LED_TM1637_CLK_PORT
port for CLK signal
Definition: led_tm1637.c:49
void led_tm1637_setup(void)
setup communication with TM1637 IC
Definition: led_tm1637.c:165
bool led_tm1637_off(void)
switch display on
Definition: led_tm1637.c:276
#define LENGTH(x)
get the length of an array
Definition: global.h:26
#define LED_TM1637_DIO_PIN
pin for DIO signal
Definition: led_tm1637.c:52
bool led_tm1637_time(uint8_t hours, uint8_t minutes)
display time
Definition: led_tm1637.c:300
#define TIM(x)
get TIM based on TIM identifier
Definition: global.h:43
static bool led_tm1637_send_data(uint8_t *address_command, uint8_t length)
write commands on bus to send data
Definition: led_tm1637.c:260
#define LED_TM1637_TIMER
timer to create signal
Definition: led_tm1637.c:58
bool led_tm1637_text(char *text)
display text
Definition: led_tm1637.c:306