2017-02-07 11:06:18 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** library to communicate with a Titan Micro TM1637 IC attached to a 4-digit 7-segment (code)
* @ file led_tm1637 . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2017
* @ note peripherals used : I2C @ ref led_tm1637_i2c
* @ warning no interrupts or sleep modes are used
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/i2c.h> // I2C library
# include <libopencm3/cm3/nvic.h> // interrupt handler
# include "global.h" // global utilities
# include "led_tm1637.h" // TM1637 header and definitions
/** @defgroup led_tm1637_i2c I2C peripheral used to communication with TM1637 IC
* @ {
*/
/** I2C peripheral */
# define LED_TM1637_I2C 1 /**< I2C peripheral */
/** display brightness */
static enum led_tm1637_brightness_t display_brightness = LED_TM1637_11DIV16 ;
/** if display is on */
static bool display_on = false ;
//#define RTC_DS1307_I2C_RCC RCC_I2C1 /**< I2C peripheral clock */
//#define RTC_DS1307_I2C_PORT_RCC RCC_GPIOB /**< I2C I/O peripheral clock */
//#define RTC_DS1307_I2C_PORT GPIOB /**< I2C I/O peripheral port */
//#define RTC_DS1307_I2C_PIN_SDA GPIO_I2C1_SDA /**< I2C peripheral data pin (PB7) */
//#define RTC_DS1307_I2C_PIN_SCL GPIO_I2C1_SCL /**< I2C peripheral clock pin (PB6) */
//#define RTC_DS1307_I2C_ADDR 0x68 /**< DS1307 I2C address (fixed to 0b1101000) */
/** @} */
void led_tm1637_setup ( void )
{
// configure GPIO for I2C peripheral (this cause a small low pulse on the lines, but I didn't figure out why)
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for alternate function
rcc_periph_clock_enable ( RCC_I2C_PORT ( LED_TM1637_I2C ) ) ; // all I2C pin are on port B
2017-02-07 11:11:54 +01:00
gpio_set_mode ( I2C_SCL_PORT ( LED_TM1637_I2C ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , I2C_SCL_PIN ( LED_TM1637_I2C ) ) ; // clock line will be an output since we will be master
gpio_set_mode ( I2C_SDA_PORT ( LED_TM1637_I2C ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , I2C_SDA_PIN ( LED_TM1637_I2C ) ) ; // data line will be an output since we will be master
2017-02-07 11:06:18 +01:00
// configure I2C peripheral
rcc_periph_clock_enable ( RCC_I2C ( LED_TM1637_I2C ) ) ; // enable clock for I2C peripheral
i2c_reset ( I2C ( LED_TM1637_I2C ) ) ; // reset configuration
i2c_peripheral_disable ( I2C ( LED_TM1637_I2C ) ) ; // I2C needs to be disable to be configured
i2c_set_clock_frequency ( I2C ( LED_TM1637_I2C ) , rcc_apb1_frequency / 1000000 ) ; // configure the peripheral clock to the APB1 freq (where it is connected to)
2017-02-07 11:12:36 +01:00
i2c_set_fast_mode ( I2C ( LED_TM1637_I2C ) ) ;
2017-02-07 11:06:18 +01:00
i2c_set_ccr ( I2C ( LED_TM1637_I2C ) , rcc_apb1_frequency / ( 400000 * 2 ) ) ; // set Thigh/Tlow to generate frequency of 400 kHz
i2c_set_trise ( I2C ( LED_TM1637_I2C ) , ( 300 / ( 1000 / ( rcc_apb1_frequency / 1000000 ) ) ) + 1 ) ; // max rise time for 300 kHz is 300 ns
i2c_peripheral_enable ( I2C ( LED_TM1637_I2C ) ) ; // enable I2C after configuration completed
}
/** write data on I2C bus
* @ param [ in ] data bytes to write
* @ param [ in ] length number of bytes to write
* @ return if write succeeded
* @ note includes start and stop conditions
*/
static bool led_tm1637_write ( const uint8_t * data , size_t length )
{
bool to_return = false ; // return if write succeeded
if ( data = = NULL | | length = = 0 ) { // verify there it data to be read
return false ;
}
i2c_send_start ( I2C ( LED_TM1637_I2C ) ) ; // send start condition to start transaction
while ( ! ( I2C_SR1 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR1_SB ) ) ; // wait until start condition is transmitted
if ( ! ( I2C_SR2 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR2_MSL ) ) { // verify if in master mode
goto error ;
}
// in I2C the first byte is the slave address and has special handling
i2c_send_data ( I2C ( LED_TM1637_I2C ) , data [ 0 ] ) ; // send first byte (don't use the i2c_send_7bit_address since we don't really care about the 7-bit address or if it's a read or write command)
while ( ! ( I2C_SR1 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR1_ADDR ) ) ; // wait until address is transmitted
if ( length > 1 & & ! ( ( I2C_SR2 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR2_TRA ) ) ) { // we can only send further byte if the first byte is an I2C write command and put us in transmit mode)
goto error ; // we are not it transmit mode and can't send further bytes
}
for ( size_t i = 1 ; i < length ; i + + ) {
i2c_send_data ( I2C ( LED_TM1637_I2C ) , data [ i ] ) ; // send data
while ( ! ( I2C_SR1 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR1_TxE ) ) ; // wait until byte has been transmitted and ACKed
}
to_return = true ;
error :
if ( I2C_SR2 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR2_BUSY ) { // release bus if busy
i2c_send_stop ( I2C ( LED_TM1637_I2C ) ) ; // send stop to release bus
}
while ( I2C_SR2 ( I2C ( LED_TM1637_I2C ) ) & I2C_SR2_MSL ) ; // wait until bus released (non master mode)
return to_return ;
}
bool led_tm1637_on ( void )
{
uint8_t data [ ] = { 0x88 + display_brightness } ; // command to turn display on (use set brightness)
bool to_return = false ; // result to return
if ( led_tm1637_write ( data , LENGTH ( data ) ) ) { // send command
display_on = true ; // remember display is on
to_return = true ; // command succeeded
}
return to_return ; // return result
}
bool led_tm1637_off ( void )
{
const uint8_t data [ ] = { 0x80 + display_brightness } ; // command to turn display off (use set brightness)
bool to_return = false ; // result to return
if ( led_tm1637_write ( data , LENGTH ( data ) ) ) { // send command
display_on = false ; // remember display is off
to_return = true ; // command succeeded
}
return to_return ; // return result
}
bool led_tm1637_brightness ( enum led_tm1637_brightness_t brightness )
{
bool to_return = false ; // result to return
display_brightness = brightness ; // save brightness
if ( display_on ) { // adjust brightness if display is on
to_return = led_tm1637_on ( ) ; // adjust brightness
} else {
to_return = true ; // command succeeded
}
return to_return ;
}
bool led_tm1637_number ( uint16_t number )
{
( void ) number ;
bool to_return = false ; // result to return
const uint8_t write_data [ ] = { 0x40 } ; // command: write data, automatic address adding, normal
2017-02-07 11:11:54 +01:00
uint8_t data_0 [ ] = { 0xc0 , 0x00 , 0xff } ; // set address C0H and add data
uint8_t data_2 [ ] = { 0xc2 , 0x00 , 0xff } ;
uint8_t data_4 [ ] = { 0xc4 , 0x00 , 0xff } ;
2017-02-07 11:06:18 +01:00
2017-02-07 11:11:54 +01:00
if ( led_tm1637_write ( write_data , LENGTH ( write_data ) ) & & led_tm1637_write ( data_0 , LENGTH ( data_0 ) ) & & led_tm1637_write ( data_2 , LENGTH ( data_2 ) ) & & led_tm1637_write ( data_4 , LENGTH ( data_4 ) ) ) { // send commands
2017-02-07 11:06:18 +01:00
to_return = true ;
}
return to_return ;
}
/*
bool led_tm1637_time ( uint8_t hours , uint8_t minutes )
{
return true ;
}
bool led_tm1637_text ( char * text )
{
return true ;
}
*/