1 /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
4 * 2019/8/14 - ROBIN KRENS
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.
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)
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).
21 * Another thing to note is that this chip uses BIG-ENDIANESS
30 #include <sys/robsys.h>
32 #include <lib/regfunc.h>
33 #include <lib/string.h>
34 #include <lib/tinyprintf.h>
36 #include <drivers/tm1637.h>
40 #define WRITE_CMD 0x20
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.
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*/
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
65 /* Reset the I2C channels and reinitialize */
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
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) {
86 printf("Offset incorrect");
89 // enable dot on display if desired
90 if (dot && (offset == 1 || offset == 2)) {
94 int start_pos_cmd = 0x03 | (offset & 0x01) << 7 | (offset & 0x2) << 5 ;
96 /* Initiate writing routine */
98 rwrite(I2C_DR, WRITE_CMD);
107 /* Set GRID offset */
109 rwrite(I2C_DR, start_pos_cmd);
118 int (*ack)() = &ack_recv;
119 if ((((value >> 4) & 0xF) == 0xF ) && !(value & 0x08)) {
123 /* Write value to segments */
125 rwrite(I2C_DR, value);
131 delay(); // wait for the chip to finish writing
137 /* Turns on/off the led display. The degree of brightness can set [0..7]*/
138 int set_display(bool on, uint8_t degree) {
140 int disp_cmd = 0x1; // off
142 disp_cmd = 0xF1; // TODO
146 rwrite(I2C_DR, disp_cmd);
148 printf("Can't switch on display!");
157 void tm1637_example() {
159 unsigned char dn[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6};
160 char love[4] = { 0x1C, 0xFC, 0x7C, 0x9E };
162 for (int i = 0; i < 4; i++) {
163 set_grid(i, love[i], NODOT);
168 /* HELPER ROUTINES */
170 static void start_condition() {
171 rsetbit(I2C_CR1, 8); //start bit
174 static void stop_condition() {
175 rsetbit(I2C_CR1, 9); //stop bit
178 /* Wait for an acknowledge from the peripheral */
181 while(!(*I2C_SR1 & 0x2)) {
184 printf("Error: no ack (addr bit) received \n");
188 uint32_t a = *I2C_SR2; // need to read SR2 register!
193 /* Similar, but SR2 register is not read */
196 while(!(*I2C_SR1 & 0x8)) {
199 printf("Error: no ack (addr10 bit) received \n");
207 /* Write delay chip */
208 static void delay() {
210 for (int i = 0; i < 500; i++)
215 /* Check if lines are idle (i.e. stop condition finished)*/
218 while(*I2C_SR2 & 0x2) {
221 printf("Error: busy state\n");