added commenting, removed deprecated functions
[cortex-from-scratch] / drivers / tm1637.c
1 /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
2  * 
3  * $LOG$
4  * 2019/8/14 - ROBIN KRENS      
5  * Initial version 
6  * 
7  * $DESCRIPTION$
8  * Driver for the TM1637 for STM32 boards. 
9  * The TM1637 is (4 digit) 8 segment ledclock peripheral. Communication 
10  * is similar to I2C, but not completely. There is no device address
11  * selecting. Instead, you can directly send a command / write data. 
12  *
13  * Most libraries for this chip that I have seen use 'Bit banging'.
14  * Manually adjusting the clock and data pin (not using the I2C)
15  *
16  * This driver is different and uses STM32 I2C hardware. A problem of the
17  * TM1637 chip, however, is that commands sometimes set bit 1 (LSB). Setting
18  * the LSB will make the STM32 MCU switch to receiver mode. (A better design 
19  * choice for commands would have to keep the LSB reserved). 
20  *
21  * Another thing to note is that this chip uses BIG-ENDIANESS
22  *
23  * */
24
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28
29 #include <sys/mmap.h>
30 #include <sys/robsys.h>
31
32 #include <lib/regfunc.h>
33 #include <lib/string.h>
34 #include <lib/tinyprintf.h>
35
36 #include <drivers/tm1637.h>
37
38 #define TIMEOUT 5000
39
40 #define WRITE_CMD 0x20
41
42 /* STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their
43 GPIOs must be configured as open-drain. So, you have to add two additional resistors to
44 pull-up I2C lines. Something between 4K and 10K is a proven value.
45 */
46
47 void tm1637_init() {
48
49  /* Program the peripheral input clock generate correct timings.
50   * Configure the clock control registers CCR
51   * Configure the rise time register TRIS
52   * Program the I2C_CR1 register to enable the peripheral
53   * Enable GPIOB6 and B7*/
54  
55  rsetbit(RCC_APB1ENR, 21); // enable GPIOB
56  rsetbit(RCC_APB2ENR, 3); // enable I2C
57  rwrite(GPIOB_CRL, 0xEE444444); // open-drain
58  rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz 
59  rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1)
60  rwrite(I2C_CCR, 0x000A); // standard mode
61  rsetbit(I2C_CR1, 0); // enable
62
63 }
64
65 /* Reset the I2C channels and reinitialize */
66 void tm1637_reset() {
67
68  rsetbit(I2C_CR1, 15); // master is busy, reset
69  rsetbit(RCC_APB1RSTR, 21); // reset I2C
70  rwrite(RCC_APB1RSTR, 0x00000000); // clear reset
71  rsetbit(RCC_APB1ENR, 21);
72  rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz 
73  rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1)
74  rwrite(I2C_CCR, 0x000A); // standard mode
75  rsetbit(I2C_CR1, 0); // enable
76
77
78 }
79
80 /* Write value to grid, if no offset is given it writes to the first
81  * grid. Grid 1 an 2 have an additional dot that can be set */
82 int set_grid(uint8_t offset, char value, bool dot) {
83
84
85         if (offset > 3) {
86                 printf("Offset incorrect");
87         }
88
89         // enable dot on display if desired
90         if (dot && (offset == 1 || offset == 2)) {
91                 value = value | 0x1;
92         }
93
94         int start_pos_cmd  = 0x03  | (offset & 0x01) << 7 | (offset & 0x2) << 5 ;
95
96         /* Initiate writing routine */
97         start_condition();
98         rwrite(I2C_DR, WRITE_CMD); 
99         if(!ack_recv())
100                 return -1;
101
102         stop_condition();
103         
104         if(!idle())
105                 return -1;
106
107         /* Set GRID offset */
108         start_condition();
109         rwrite(I2C_DR, start_pos_cmd); 
110         if(!ack_recv())
111                 return -1;
112
113         stop_condition();
114
115         tm1637_reset();
116
117
118         int (*ack)() = &ack_recv;
119         if ((((value >> 4) & 0xF) == 0xF ) && !(value & 0x08)) {
120                 ack = &ack10_recv;
121         }
122         
123         /* Write value to segments */
124         start_condition();
125         rwrite(I2C_DR, value);
126         if(!ack()) {
127         //      return -1;
128         }
129
130         stop_condition(); 
131         delay(); // wait for the chip to finish writing
132         tm1637_reset();
133
134         return 0;
135 }
136
137 /* Turns on/off the led display. The degree of brightness can set [0..7]*/
138 int set_display(bool on, uint8_t degree) {
139
140         int disp_cmd = 0x1; // off
141         if (on) {
142                 disp_cmd = 0xF1; // TODO 
143         }       
144
145         start_condition();
146         rwrite(I2C_DR, disp_cmd);
147         if(!ack_recv()) {
148                 printf("Can't switch on display!");
149                 return -1;
150         }
151         stop_condition();
152         tm1637_reset();
153
154         return 0;
155 }
156
157 void tm1637_example() {
158
159         unsigned char dn[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6};
160         char love[4]  = { 0x1C, 0xFC, 0x7C, 0x9E };
161                 
162         for (int i = 0; i < 4; i++) {
163                 set_grid(i, love[i], NODOT);
164         }
165
166 }
167
168 /* HELPER ROUTINES */
169
170 static void start_condition() {
171         rsetbit(I2C_CR1, 8); //start bit
172 }
173
174 static void stop_condition() {
175         rsetbit(I2C_CR1, 9); //stop bit
176 }
177
178 /* Wait for an acknowledge from the peripheral */
179 int ack_recv() {
180         int cnt = 0;
181         while(!(*I2C_SR1 & 0x2)) {
182                 cnt++;
183                 if (cnt > TIMEOUT) {
184                         printf("Error: no ack (addr bit) received \n");
185                         return 0;
186                 }
187         }
188         uint32_t a = *I2C_SR2; // need to read SR2 register!
189         return 1;
190
191 }
192
193 /* Similar, but SR2 register is not read */
194 int ack10_recv() {
195         int cnt = 0;
196         while(!(*I2C_SR1 & 0x8)) {
197                 cnt++;
198                 if (cnt > TIMEOUT) {
199                         printf("Error: no ack (addr10 bit) received \n");
200                         return 0;
201                 }
202         }
203         return 1;
204
205 }
206
207 /* Write delay chip */
208 static void delay() {
209         int a = 0;
210         for (int i = 0; i < 500; i++)
211                 a++;
212 }
213
214
215 /* Check if lines are idle (i.e. stop condition finished)*/
216 int idle() {
217         int cnt = 0;
218         while(*I2C_SR2 & 0x2) {
219                 cnt++;
220                 if (cnt > TIMEOUT) {
221                         printf("Error: busy state\n");
222                         return 0;
223                 }
224         }
225
226         return 1;
227 }
228
229