From: Robin Krens Date: Tue, 13 Aug 2019 19:05:58 +0000 (+0800) Subject: rewrite register function, code formatting X-Git-Url: https://robinkrens.nl/gitweb/?a=commitdiff_plain;h=9b56cc6635c4ca73be6d14a3492d7ce05bbd028e;p=cortex-from-scratch rewrite register function, code formatting --- diff --git a/drivers/tm1637.c b/drivers/tm1637.c index 8c8ce5b..b6453ae 100644 --- a/drivers/tm1637.c +++ b/drivers/tm1637.c @@ -1,17 +1,24 @@ /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL * * $LOG$ - * 2019/7/25 - ROBIN KRENS + * 2019/8/14 - ROBIN KRENS * Initial version * * $DESCRIPTION$ - * Basic driver for the TM1637. The TM1637 is 8 segment - * ledclock peripheral. Communication is similar to I2C, - * but not completely. There is no address selecting. + * Driver for the TM1637 for STM32 boards. + * The TM1637 is (4 digit) 8 segment ledclock peripheral. Communication + * is similar to I2C, but not completely. There is no device address + * selecting. Instead, you can directly send a command / write data. * - * Alternative I2C protocol : (W)rite or (R)ead - * | Start | ADDRESS | W | ACK/NACK | DATA | ACK/NACK | (d+a*n) | St - * | Start | ADDRESS | R | ACK/NACK | DATA | ACK/NACK | (d+a*n) | St + * Most libraries for this chip that I have seen use 'Bit banging'. + * Manually adjusting the clock and data pin (not using the I2C) + * + * This driver is different and uses STM32 I2C hardware. A problem of the + * TM1637 chip, however, is that commands sometimes set bit 1 (LSB). Setting + * the LSB will make the STM32 MCU switch to receiver mode. (A better design + * choice for commands would have to keep the LSB reserved). + * + * Another thing to note is that this chip uses BIG-ENDIANESS * * */ @@ -24,7 +31,7 @@ #include #include -#include +#include #include @@ -33,90 +40,142 @@ #define DOT true #define NODOT false - -/* -0 = 0x00 -1 = 0x60 -2 = 0xDA -3 = 0xF2 -4 = 0x66 -5 = 0xB6 -6 = 0xBE -7 = 0xE0 -8 = 0xFF -9 = 0xF6 -*/ +#define WRITE_CMD 0x20 /* STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their GPIOs must be configured as open-drain. So, you have to add two additional resistors to pull-up I2C lines. Something between 4K and 10K is a proven value. */ -/* BIG ENDIAN! */ - void tm1637_init() { - /* Program the peripheral input clock in I2C_CR2 Register in order to generate correct timings - Configure the clock control registers CCR - Configure the rise time register TRIS - Program the I2C_CR1 register to enable the peripheral + /* Program the peripheral input clock generate correct timings. + * Configure the clock control registers CCR + * Configure the rise time register TRIS + * Program the I2C_CR1 register to enable the peripheral + * Enable GPIOB6 and B7*/ + + rsetbit(RCC_APB1ENR, 21); // enable GPIOB + rsetbit(RCC_APB2ENR, 3); // enable I2C + rwrite(GPIOB_CRL, 0xEE444444); // open-drain + rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz + rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1) + rwrite(I2C_CCR, 0x000A); // standard mode + rsetbit(I2C_CR1, 0); // enable - ENABLE GPIOB6 and B7*/ +} - regw_u32(RCC_APB1ENR, 0x1, 21, SETBIT); - regw_u32(RCC_APB2ENR, 0x1, 3, SETBIT); - // //regw_u8(AFIO_EVCR, 0x89, 0, SETBIT);// set event control register, output on ? - - regw_u32(GPIOB_CRL, 0xEE444444, 0, OWRITE); +/* Reset the I2C channels and reinitialize */ +void tm1637_reset() { + + rsetbit(I2C_CR1, 15); // master is busy, reset + rsetbit(RCC_APB1RSTR, 21); // reset I2C + rwrite(RCC_APB1RSTR, 0x00000000); // clear reset + rsetbit(RCC_APB1ENR, 21); + rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz + rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1) + rwrite(I2C_CCR, 0x000A); // standard mode + rsetbit(I2C_CR1, 0); // enable - regw_u32(I2C_CR2, 0x2, 0, OWRITE); //2 MHz - regw_u8(I2C_TRISE, 0x3, 0, OWRITE); // MAX = 1000ns, TPCLK1 = 500ns (+1) - regw_u32(I2C_CCR, 0x000A, 0, OWRITE); // standard mode, output 100 kHz (100hz* / perip) - - regw_u32(I2C_CR1, 0x1, 0, OWRITE); // enable } -void tm1637_reset() { +/* Write value to grid, if no offset is given it writes to the first + * grid. Grid 1 an 2 have an additional dot that can be set */ +int set_grid(uint8_t offset, char value, bool dot) { - regw_u32(RCC_APB1RSTR, 0x1, 21, SETBIT); + //int (*ack)() = ack_recv; /* Scary function pointer :D */ - regw_u32(RCC_APB1RSTR, 0x00000000, 0, OWRITE); // clr - //regw_u32(RCC_APB2ENR, 0x1, 3, SETBIT); - // //regw_u8(AFIO_EVCR, 0x89, 0, SETBIT);// set event control register, output on ? - - regw_u32(RCC_APB1ENR, 0x1, 21, SETBIT); - //regw_u32(GPIOB_CRL, 0xEE444444, 0, OWRITE); + if (offset > 3) { + printf("Offset incorrect"); + } + + // enable dot on display if desired + if (dot && (offset == 1 || offset == 2)) { + value = value | 0x1; + } + + int start_pos_cmd = 0x03 | (offset & 0x01) << 7 | (offset & 0x2) << 5 ; + + /* Initiate writing routine */ + start_condition(); + rwrite(I2C_DR, WRITE_CMD); + if(!ack_recv()) + return -1; + + stop_condition(); + + if(!idle()) + return -1; + + /* Set GRID offset */ + start_condition(); + rwrite(I2C_DR, start_pos_cmd); + if(!ack_recv()) + return -1; + + stop_condition(); - regw_u32(I2C_CR2, 0x2, 0, OWRITE); //2 MHz - regw_u8(I2C_TRISE, 0x3, 0, OWRITE); // MAX = 1000ns, TPCLK1 = 500ns (+1) - regw_u32(I2C_CCR, 0x000A, 0, OWRITE); // standard mode, output 100 kHz (100hz* / perip) + tm1637_reset(); - regw_u32(I2C_CR1, 0x1, 0, OWRITE); // enable + /* Write value to segments */ + start_condition(); + rwrite(I2C_DR, value); + if(!ack_recv()) + return -1; + stop_condition(); + tm1637_reset(); + return 0; } -static void start_condition() { +/* Turns on/off the led display. The degree of brightness can set [0..7]*/ +int set_display(bool on, uint8_t degree) { + + int disp_cmd = 0x1; // off + if (on) { + disp_cmd = 0xF1; // TODO + } - regw_u32(I2C_CR1, 0x1, 8, SETBIT); //start + start_condition(); + rwrite(I2C_DR, disp_cmd); + if(!ack_recv()) { + printf("Can't switch on display!"); + return -1; + } + stop_condition(); + + tm1637_reset(); + + return 0; } -static void stop_condition() { - regw_u32(I2C_CR1, 0x1, 9, SETBIT); //stop +void tm1637_example() { + +unsigned char display_number[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6}; + + + char love[4] = { 0x1C, 0xFC, 0x7C, 0x9E }; + + for (int i = 0; i < 4; i++) { + set_grid(i, love[i], NODOT); + } + + set_display(true, 0); + } -static int buf_empty() { - int cnt = 0; - while(!(*I2C_SR1 & 0x80)) { - cnt++; - if (cnt > TIMEOUT) { - return 0; - } - } - return 1; +/* HELPER ROUTINES */ + +static void start_condition() { + rsetbit(I2C_CR1, 8); //start bit +} + +static void stop_condition() { + rsetbit(I2C_CR1, 9); //stop bit } /* Wait for an acknowledge from the peripheral */ @@ -124,8 +183,10 @@ int ack_recv() { int cnt = 0; while(!(*I2C_SR1 & 0x2)) { cnt++; - if (cnt > TIMEOUT) + if (cnt > TIMEOUT) { + printf("Error: no ack (addr bit) received \n"); return 0; + } } uint32_t a = *I2C_SR2; // need to read SR2 register! return 1; @@ -137,110 +198,27 @@ int ack10_recv() { int cnt = 0; while(!(*I2C_SR1 & 0x8)) { cnt++; - if (cnt > TIMEOUT) + if (cnt > TIMEOUT) { + printf("Error: no ack (addr10 bit) received \n"); return 0; + } } return 1; } +/* Check if lines are idle (i.e. stop condition finished)*/ int idle() { int cnt = 0; while(*I2C_SR2 & 0x2) { cnt++; - if (cnt > TIMEOUT) + if (cnt > TIMEOUT) { + printf("Error: busy state\n"); return 0; + } } return 1; } -int delay() { - - int a = 0; - for (int i = 0; i < TIMEOUT; i++) - a++; -} - -void set_display(bool on, uint8_t degree) { - - start_condition(); - - regw_u32(I2C_DR, 0xF1, 0, OWRITE); - if(!ack_recv()) - cputs("Can't switch on display!"); - stop_condition(); - - // reset bus - regw_u32(I2C_CR1, 0x1, 15, SETBIT); - -} - - -void set_segment(int offset, char value, bool dot) { - - int (*ack)() = ack_recv; /* Scary function pointer :D */ - - - if (offset > 3) { - cputs("Offset incorrect"); - } - - if (dot) { - value = value | 0x1; - } - int start_pos_cmd = 0x03 | (offset & 0x01) << 7 | (offset & 0x2) << 5 ; - - start_condition(); - regw_u32(I2C_DR, 0x20, 0, OWRITE); - if(!ack_recv()) - cputs("Error: initiating write for start segment \n"); - - stop_condition(); - - if(!idle()) - cputs("Error: timeout"); - - - start_condition(); - regw_u32(I2C_DR, start_pos_cmd, 0, OWRITE); - if(!ack()) - cputs("Error: Can't set start segment \n"); - - stop_condition(); - regw_u32(I2C_CR1, 0x1, 15, SETBIT); - - tm1637_reset(); - -// if (value & 0xF0) -// ack = &ack10_recv; - - start_condition(); - regw_u32(I2C_DR, value, 0, OWRITE); // use ack10 if higher - if(!ack_recv()) - cputs("Error: can't set location\n"); - stop_condition(); - - regw_u32(I2C_CR1, 0x1, 15, SETBIT); - - tm1637_reset(); -} - - - -void tm1637_start() { - -unsigned char display_number[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6}; - - - char love[4] = { 0x1C, 0xFC, 0x7C, 0x9E }; - - for (int i = 0; i < 4; i++) { - set_segment(i, love[i], NODOT); - } - - set_display(true, 0); - -} - diff --git a/include/drivers/tm1637.h b/include/drivers/tm1637.h index 4ad572a..e537cdf 100644 --- a/include/drivers/tm1637.h +++ b/include/drivers/tm1637.h @@ -1,8 +1,16 @@ #ifndef __TM1637_H #define __TM1637_H +/* HELPER SUBROUTINES DECLARATIONS */ +static void start_condition(); +static void stop_condition(); +static int ack_recv(); +static int ack10_recv(); +static int idle(); + extern void tm1637_init(); -extern void tm1637_start(); -extern void tm1637_stop(); +extern int set_grid(uint8_t offset, char value, bool dot); +extern int set_display(bool on, uint8_t degree); +extern void tm1637_example(); #endif diff --git a/include/sys/robsys.h b/include/sys/robsys.h index 9a19a47..6962770 100644 --- a/include/sys/robsys.h +++ b/include/sys/robsys.h @@ -8,7 +8,7 @@ * These values are used throughout the code to calculator desired baud * rates etc. */ -//#define ENABLE_HSE +//#define ENABLE_HSE //efine CRYSTAL_MHZ 8 //efine CLKSPEED_MHZ 72 extern void clock_init(); diff --git a/main.c b/main.c index 8455b46..68eaa03 100644 --- a/main.c +++ b/main.c @@ -69,10 +69,10 @@ void main() led_init(); // eeprom_at24c_init(); // eeprom_test(); -// rtc_init(); + rtc_init(); tm1637_init(); - tm1637_start(); + tm1637_example(); //uint32_t test = hextoreg("12345678");