rtc example with tm1637 driver
[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 DOT true
41 #define NODOT false
42
43 #define WRITE_CMD 0x20
44
45 /* STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their
46 GPIOs must be configured as open-drain. So, you have to add two additional resistors to
47 pull-up I2C lines. Something between 4K and 10K is a proven value.
48 */
49
50 void tm1637_init() {
51
52  /* Program the peripheral input clock generate correct timings.
53   * Configure the clock control registers CCR
54   * Configure the rise time register TRIS
55   * Program the I2C_CR1 register to enable the peripheral
56   * Enable GPIOB6 and B7*/
57  
58  rsetbit(RCC_APB1ENR, 21); // enable GPIOB
59  rsetbit(RCC_APB2ENR, 3); // enable I2C
60  rwrite(GPIOB_CRL, 0xEE444444); // open-drain
61  rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz 
62  rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1)
63  rwrite(I2C_CCR, 0x000A); // standard mode
64  rsetbit(I2C_CR1, 0); // enable
65
66 }
67
68 /* Reset the I2C channels and reinitialize */
69 void tm1637_reset() {
70
71  rsetbit(I2C_CR1, 15); // master is busy, reset
72  rsetbit(RCC_APB1RSTR, 21); // reset I2C
73  rwrite(RCC_APB1RSTR, 0x00000000); // clear reset
74  rsetbit(RCC_APB1ENR, 21);
75  rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz 
76  rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1)
77  rwrite(I2C_CCR, 0x000A); // standard mode
78  rsetbit(I2C_CR1, 0); // enable
79
80
81 }
82
83 /* Write value to grid, if no offset is given it writes to the first
84  * grid. Grid 1 an 2 have an additional dot that can be set */
85 int set_grid(uint8_t offset, char value, bool dot) {
86
87
88         if (offset > 3) {
89                 printf("Offset incorrect");
90         }
91
92         // enable dot on display if desired
93         if (dot && (offset == 1 || offset == 2)) {
94                 value = value | 0x1;
95         }
96
97         int start_pos_cmd  = 0x03  | (offset & 0x01) << 7 | (offset & 0x2) << 5 ;
98
99         /* Initiate writing routine */
100         start_condition();
101         rwrite(I2C_DR, WRITE_CMD); 
102         if(!ack_recv())
103                 return -1;
104
105         stop_condition();
106         
107         if(!idle())
108                 return -1;
109
110         /* Set GRID offset */
111         start_condition();
112         rwrite(I2C_DR, start_pos_cmd); 
113         if(!ack_recv())
114                 return -1;
115
116         stop_condition();
117
118         tm1637_reset();
119
120
121         int (*ack)() = &ack_recv;
122         if ((((value >> 4) & 0xF) == 0xF ) && !(value & 0x08)) {
123                 ack = &ack10_recv;
124         }
125         
126         /* Write value to segments */
127         start_condition();
128         rwrite(I2C_DR, value);
129         if(!ack()) {
130         //      return -1;
131         }
132
133         stop_condition(); 
134         delay(); // wait for the chip to finish writing
135         tm1637_reset();
136
137         return 0;
138 }
139
140 /* Turns on/off the led display. The degree of brightness can set [0..7]*/
141 int set_display(bool on, uint8_t degree) {
142
143         int disp_cmd = 0x1; // off
144         if (on) {
145                 disp_cmd = 0xF1; // TODO 
146         }       
147
148         start_condition();
149         rwrite(I2C_DR, disp_cmd);
150         if(!ack_recv()) {
151                 printf("Can't switch on display!");
152                 return -1;
153         }
154         stop_condition();
155         tm1637_reset();
156
157         return 0;
158 }
159
160 void tm1637_example() {
161
162         unsigned char dn[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6};
163         char love[4]  = { 0x1C, 0xFC, 0x7C, 0x9E };
164                 
165         for (int i = 0; i < 4; i++) {
166                 set_grid(i, love[i], NODOT);
167         }
168
169 }
170
171 /* HELPER ROUTINES */
172
173 static void start_condition() {
174         rsetbit(I2C_CR1, 8); //start bit
175 }
176
177 static void stop_condition() {
178         rsetbit(I2C_CR1, 9); //stop bit
179 }
180
181 /* Wait for an acknowledge from the peripheral */
182 int ack_recv() {
183         int cnt = 0;
184         while(!(*I2C_SR1 & 0x2)) {
185                 cnt++;
186                 if (cnt > TIMEOUT) {
187                         printf("Error: no ack (addr bit) received \n");
188                         return 0;
189                 }
190         }
191         uint32_t a = *I2C_SR2; // need to read SR2 register!
192         return 1;
193
194 }
195
196 /* Similar, but SR2 register is not read */
197 int ack10_recv() {
198         int cnt = 0;
199         while(!(*I2C_SR1 & 0x8)) {
200                 cnt++;
201                 if (cnt > TIMEOUT) {
202                         printf("Error: no ack (addr10 bit) received \n");
203                         return 0;
204                 }
205         }
206         return 1;
207
208 }
209
210 /* Write delay chip */
211 static void delay() {
212         int a = 0;
213         for (int i = 0; i < 500; i++)
214                 a++;
215 }
216
217
218 /* Check if lines are idle (i.e. stop condition finished)*/
219 int idle() {
220         int cnt = 0;
221         while(*I2C_SR2 & 0x2) {
222                 cnt++;
223                 if (cnt > TIMEOUT) {
224                         printf("Error: busy state\n");
225                         return 0;
226                 }
227         }
228
229         return 1;
230 }
231
232