IonizationChamber/Software/Firmware/Driver/mcp3425.c

179 wiersze
5.8 KiB
C
Executable File

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* mcp3425.c — Driver for the MCP3425 16bit I2C ADC
* Target MCU : STM8S003F3P6 (SDCC)
*
*/
#include "mcp3425.h"
#include "pinout_conf.h"
#include "stm8s.h"
#include "stm8s_clk.h"
#include "stm8s_gpio.h"
#include "stm8s_i2c.h"
#include <stdint.h>
#include <stdbool.h>
/* -------------------------------------------------------------------------
* I2C addresses & speed
* -------------------------------------------------------------------------*/
#define I2C_MASTER_ADDRESS 0x10U /* our 7bit address */
#define I2C_SLAVE_ADDRESS 0x68U /* MCP3425 address */
#define I2C_SPEED_STANDARD_HZ 100000UL /* 100 kHz SCL */
#define HZ_TO_MHZ(freq_hz) ((uint8_t)((freq_hz) / 1000000UL))
/* -------------------------------------------------------------------------
* MCP3425 configuration bits
* -------------------------------------------------------------------------*/
#define MCP3425_RDY_BIT (1U << 7) /* 1 = conversion not ready */
#define MCP3425_MODE_BIT (1U << 4) /* 1 = oneshot; 0 = continuous */
#define MCP3425_SPS_BIT (1U << 3) /* 1 = 16bit @ 15 SPS */
#define MCP3425_GAIN_BIT (0U) /* 00 = gain ×1 */
#define MCP3425_CONFIG (MCP3425_RDY_BIT | MCP3425_MODE_BIT | MCP3425_SPS_BIT | MCP3425_GAIN_BIT)
/* -------------------------------------------------------------------------
* Misc. constants
* -------------------------------------------------------------------------*/
#define I2C_TIMEOUT 10000UL /* loop-cycle timeout */
/* -------------------------------------------------------------------------
* Busywait macro with timeout
* -------------------------------------------------------------------------*/
#define BUSY_AWAIT_CONDITION(cond, cnt, max, on_to) do {\
(cnt) = (uint16_t)(max);\
while (!(cond)) {\
if (--(cnt) == 0U) {\
on_to;\
}\
}\
} while (0)
/* Private prototypes */
static void GPIO_setup(void);
static void I2C_setup(void);
static bool write_config(uint8_t config);
static AcqResult_t read_result(uint8_t *msb, uint8_t *lsb, uint8_t *conf);
/* -------------------------------------------------------------------------
* Public API
* -------------------------------------------------------------------------*/
void mcp3425_init(void)
{
GPIO_setup();
I2C_setup();
}
AcqResult_t mcp3425_get_value(MeasurementBytes_t *measurement)
{
if (!write_config(MCP3425_CONFIG))
{
return ACQ_ERROR; // bus or timeout failure
}
return read_result(&measurement->msb, &measurement->lsb, &measurement->conf);
}
/* -------------------------------------------------------------------------
* Private Functions
* -------------------------------------------------------------------------*/
static void GPIO_setup(void)
{
GPIO_Init(PORT_I2C, PIN_I2C_SCL, GPIO_MODE_OUT_OD_HIZ_FAST);
GPIO_Init(PORT_I2C, PIN_I2C_SDA, GPIO_MODE_OUT_OD_HIZ_FAST);
}
static void I2C_setup(void)
{
/* Enable the I2C peripheral clock is done in clk_conf_init() */
I2C_DeInit();
I2C_Init(I2C_SPEED_STANDARD_HZ,
I2C_MASTER_ADDRESS,
I2C_DUTYCYCLE_2,
I2C_ACK_CURR,
I2C_ADDMODE_7BIT,
HZ_TO_MHZ(CLK_GetClockFreq()));
I2C_Cmd(ENABLE);
}
static bool write_config(const uint8_t config)
{
uint16_t timeout_counter = I2C_TIMEOUT;
while (I2C_GetFlagStatus(I2C_FLAG_BUSBUSY))
{
if (--timeout_counter == 0)
{
//i2c_recover(); // todo : implement recovery function
return false;
}
}
I2C_GenerateSTART(ENABLE);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT),
timeout_counter, I2C_TIMEOUT, return false);
I2C_Send7bitAddress(I2C_SLAVE_ADDRESS << 1, I2C_DIRECTION_TX);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED),
timeout_counter, I2C_TIMEOUT, return false);
I2C_SendData(config);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED),
timeout_counter, I2C_TIMEOUT, return false);
I2C_GenerateSTOP(ENABLE);
return true;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity): Hardware sequence is clearer unbroken.
static AcqResult_t read_result(uint8_t *msb, uint8_t *lsb, uint8_t *conf)
{
uint16_t timeout_counter = I2C_TIMEOUT;
while (I2C_GetFlagStatus(I2C_FLAG_BUSBUSY))
{
if (--timeout_counter == 0)
{
return ACQ_READ_BUSSY;
}
}
I2C_GenerateSTART(ENABLE);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT),
timeout_counter, I2C_TIMEOUT, return ACQ_READ_FAILURE);
I2C_Send7bitAddress((I2C_SLAVE_ADDRESS << 1) | 0x01, I2C_DIRECTION_RX);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED),
timeout_counter, I2C_TIMEOUT, return ACQ_READ_FAILURE);
// Read MSB
I2C_AcknowledgeConfig(I2C_ACK_CURR);
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED),
timeout_counter, I2C_TIMEOUT, return ACQ_READ_FAILURE);
*msb = I2C_ReceiveData();
// Read LSB
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED),
timeout_counter, I2C_TIMEOUT, return ACQ_READ_FAILURE);
*lsb = I2C_ReceiveData();
// BEFORE reading config byte, prepare to stop
I2C_AcknowledgeConfig(I2C_ACK_NONE);
I2C_GenerateSTOP(ENABLE); // Set STOP immediately to avoid over-read
// NOW read config byte (no ACK will be sent)
BUSY_AWAIT_CONDITION(I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED),
timeout_counter, I2C_TIMEOUT, return ACQ_READ_FAILURE);
*conf = I2C_ReceiveData();
if ((*conf & MCP3425_RDY_BIT) != 0U)
{
return ACQ_RDY_BIT;
}
return ACQ_SUCCESS;
}