217 lines
5.6 KiB
C
217 lines
5.6 KiB
C
#include <stdio.h>
|
|
#include "ql_gpio.h"
|
|
#include "ql_rtos.h"
|
|
#include "ql_uart.h"
|
|
#include "ql_application.h"
|
|
#include "ql_type.h"
|
|
//#include "global_types.h"
|
|
#include "ql_func.h"
|
|
|
|
#define NO_ERROR 0
|
|
#define STATUS_FAIL -1
|
|
|
|
#define SW_I2C_CLOCK_PERIOD_USEC 10
|
|
|
|
#define DELAY_USEC (SW_I2C_CLOCK_PERIOD_USEC / 2)
|
|
|
|
/**
|
|
* Initialize all hard- and software components that are needed to set the
|
|
* SDA and SCL pins.
|
|
*/
|
|
void sw_i2c_init_pins(void) {
|
|
// IMPLEMENT
|
|
/*SDA*/
|
|
ql_gpio_init(GPIO_PIN_NO_50,PIN_DIRECTION_OUT,PIN_PULL_PU,PIN_LEVEL_HIGH);
|
|
/*SCL*/
|
|
ql_gpio_init(GPIO_PIN_NO_49,PIN_DIRECTION_OUT,PIN_PULL_PU,PIN_LEVEL_HIGH);
|
|
}
|
|
|
|
/**
|
|
* Release all resources initialized by sensirion_init_pins()
|
|
*/
|
|
void sw_i2c_release_pins(void) {
|
|
// IMPLEMENT or leave empty if no resources need to be freed
|
|
ql_gpio_deinit(GPIO_PIN_NO_49);
|
|
ql_gpio_deinit(GPIO_PIN_NO_50);
|
|
}
|
|
|
|
/**
|
|
* Configure the SDA pin as an input. With an external pull-up resistor the line
|
|
* should be left floating, without external pull-up resistor, the input must be
|
|
* configured to use the internal pull-up resistor.
|
|
*/
|
|
void sw_i2c_SDA_in(void) {
|
|
// IMPLEMENT
|
|
ql_gpio_deinit(GPIO_PIN_NO_50);
|
|
ql_gpio_init(GPIO_PIN_NO_50,PIN_DIRECTION_IN,PIN_PULL_DISABLE,PIN_LEVEL_HIGH);
|
|
}
|
|
|
|
/**
|
|
* Configure the SDA pin as an output and drive it low or set to logical false.
|
|
*/
|
|
void sw_i2c_SDA_out(void) {
|
|
// IMPLEMENT
|
|
ql_gpio_deinit(GPIO_PIN_NO_50);
|
|
ql_gpio_init(GPIO_PIN_NO_50,PIN_DIRECTION_OUT,PIN_PULL_DISABLE,PIN_LEVEL_LOW);
|
|
ql_gpio_set_level(GPIO_PIN_NO_50,PIN_LEVEL_LOW);
|
|
}
|
|
|
|
/**
|
|
* Read the value of the SDA pin.
|
|
* @returns 0 if the pin is low and 1 otherwise.
|
|
*/
|
|
uint8_t sw_i2c_SDA_read(void) {
|
|
// IMPLEMENT
|
|
PIN_LEVEL_E vv ;
|
|
ql_gpio_get_level(GPIO_PIN_NO_50,&vv);
|
|
return vv;
|
|
}
|
|
|
|
/**
|
|
* Configure the SCL pin as an input. With an external pull-up resistor the line
|
|
* should be left floating, without external pull-up resistor, the input must be
|
|
* configured to use the internal pull-up resistor.
|
|
*/
|
|
void sw_i2c_SCL_in(void) {
|
|
ql_gpio_deinit(GPIO_PIN_NO_49);
|
|
ql_gpio_init(GPIO_PIN_NO_49,PIN_DIRECTION_IN,PIN_PULL_DISABLE,PIN_LEVEL_HIGH);
|
|
}
|
|
|
|
/**
|
|
* Configure the SCL pin as an output and drive it low or set to logical false.
|
|
*/
|
|
void sw_i2c_SCL_out(void) {
|
|
// IMPLEMENT
|
|
ql_gpio_deinit(GPIO_PIN_NO_49);
|
|
ql_gpio_init(GPIO_PIN_NO_49,PIN_DIRECTION_OUT,PIN_PULL_DISABLE,PIN_LEVEL_LOW);
|
|
ql_gpio_set_level(GPIO_PIN_NO_49,PIN_LEVEL_LOW);
|
|
}
|
|
|
|
/**
|
|
* Read the value of the SCL pin.
|
|
* @returns 0 if the pin is low and 1 otherwise.
|
|
*/
|
|
uint8_t sw_i2c_SCL_read(void) {
|
|
PIN_LEVEL_E vv ;
|
|
ql_gpio_get_level(GPIO_PIN_NO_49,&vv);
|
|
return vv;
|
|
}
|
|
|
|
/**
|
|
* Sleep for a given number of microseconds. The function should delay the
|
|
* execution approximately, but no less than, the given time.
|
|
*
|
|
* The precision needed depends on the desired i2c frequency, i.e. should be
|
|
* exact to about half a clock cycle (defined in
|
|
* `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_arch_config.h`).
|
|
*
|
|
* Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
|
|
*
|
|
* @param useconds the sleep time in microseconds
|
|
*/
|
|
void sw_i2c_sleep_usec(u32 useconds) {
|
|
volatile uint_32 i;
|
|
for(i=0;i<useconds*100;i++)
|
|
i=i+1;
|
|
}
|
|
|
|
static int8 sw_i2c_wait_while_clock_stretching(void) {
|
|
/* Maximal timeout of 150ms (SCD30) in sleep polling cycles */
|
|
u32 timeout_cycles = 150000 / SW_I2C_CLOCK_PERIOD_USEC;
|
|
|
|
while (--timeout_cycles) {
|
|
if (sw_i2c_SCL_read())
|
|
return NO_ERROR;
|
|
sw_i2c_sleep_usec(SW_I2C_CLOCK_PERIOD_USEC);
|
|
}
|
|
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
int8 sw_i2c_write_byte(uint8_t data) {
|
|
int8 nack, i;
|
|
for (i = 7; i >= 0; i--) {
|
|
sw_i2c_SCL_out();
|
|
if ((data >> i) & 0x01)
|
|
sw_i2c_SDA_in();
|
|
else
|
|
sw_i2c_SDA_out();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
if (sw_i2c_wait_while_clock_stretching())
|
|
return STATUS_FAIL;
|
|
}
|
|
sw_i2c_SCL_out();
|
|
sw_i2c_SDA_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_in();
|
|
if (sw_i2c_wait_while_clock_stretching())
|
|
return STATUS_FAIL;
|
|
nack = (sw_i2c_SDA_read() != 0);
|
|
sw_i2c_SCL_out();
|
|
|
|
return nack;
|
|
}
|
|
|
|
uint8_t sw_i2c_read_byte(uint8_t ack) {
|
|
int8 i;
|
|
uint8_t data = 0;
|
|
sw_i2c_SDA_in();
|
|
for (i = 7; i >= 0; i--) {
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_in();
|
|
if (sw_i2c_wait_while_clock_stretching())
|
|
return 0xFF; // return 0xFF on error
|
|
data |= (sw_i2c_SDA_read() != 0) << i;
|
|
sw_i2c_SCL_out();
|
|
}
|
|
if (ack)
|
|
sw_i2c_SDA_out();
|
|
else
|
|
sw_i2c_SDA_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
if (sw_i2c_wait_while_clock_stretching())
|
|
return 0xFF; // return 0xFF on error
|
|
sw_i2c_SCL_out();
|
|
sw_i2c_SDA_in();
|
|
|
|
return data;
|
|
}
|
|
|
|
int8 sw_i2c_start(void) {
|
|
sw_i2c_SCL_in();
|
|
if (sw_i2c_wait_while_clock_stretching())
|
|
return STATUS_FAIL;
|
|
|
|
sw_i2c_SDA_out();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_out();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void sw_i2c_stop(void) {
|
|
sw_i2c_SDA_out();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SCL_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
sw_i2c_SDA_in();
|
|
sw_i2c_sleep_usec(DELAY_USEC);
|
|
}
|
|
|
|
void sw_i2c_init(void) {
|
|
sw_i2c_init_pins();
|
|
sw_i2c_SCL_in();
|
|
sw_i2c_SDA_in();
|
|
}
|
|
|
|
void sw_i2c_release(void) {
|
|
sw_i2c_SCL_in();
|
|
sw_i2c_SDA_in();
|
|
sw_i2c_release_pins();
|
|
}
|
|
|