514 lines
12 KiB
C
514 lines
12 KiB
C
/*******************************************************************************
|
|
* @file mcp4725_i2c.c
|
|
* @author CandyPops Co.
|
|
* @version V1.0.0
|
|
* @date 2022-09-05
|
|
* @brief
|
|
******************************************************************************/
|
|
|
|
/* board driver */
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
#include "nrf.h"
|
|
#include "app_error.h"
|
|
#include "boards.h"
|
|
#include "nrfx_gpiote.h"
|
|
|
|
#include "mcp4725_i2c.h"
|
|
#include "nrf_delay.h"
|
|
#include "debug_print.h"
|
|
|
|
/* I2C number and slave address for MCP4725 */
|
|
#define MCP4725_I2C_ADDR_7bit 0x66
|
|
uint8_t MCP4725_I2C_ADDR = MCP4725_I2C_ADDR_7bit << 1;
|
|
|
|
uint16_t _lastValue;
|
|
uint8_t _powerDownMode;
|
|
uint32_t _lastWriteEEPROM;
|
|
|
|
|
|
void mcp4725_i2c_initialize(void)
|
|
{
|
|
SCL_OUT();
|
|
SDA_OUT();
|
|
SCL_H();
|
|
SDA_H();
|
|
}
|
|
|
|
|
|
static uint8_t mcp4725_i2c_write(uint8_t data)
|
|
{
|
|
for(uint8_t i = 0; i < 8; i++)
|
|
{
|
|
// MSB first
|
|
if(data & 0x80) SDA_H();
|
|
else SDA_L();
|
|
i2c_clock();
|
|
data = data << 1;
|
|
}
|
|
|
|
// read ACK
|
|
SDA_H(); // leave SDA HI
|
|
SDA_IN(); // change direction to input on SDA line
|
|
|
|
i2c_delay();
|
|
SCL_H(); // clock back up
|
|
i2c_delay();
|
|
uint8_t ack = SDA_READ(); // get the ACK bit
|
|
SCL_L();
|
|
|
|
SDA_OUT(); // change direction back to output
|
|
|
|
if(ack) {
|
|
DBG_PRINTF("ACK Extra=%d,Data=%d\r\n", ack,data);
|
|
}
|
|
|
|
return ack;
|
|
}
|
|
|
|
|
|
static uint8_t mcp4725_i2c_read(bool ack)
|
|
{
|
|
uint8_t data;
|
|
|
|
SDA_H(); // leave SDA HI
|
|
SDA_IN(); // change direction to input on SDA line
|
|
|
|
data = 0;
|
|
for(uint8_t i = 0; i < 8; i++)
|
|
{
|
|
// MSB first
|
|
data = data << 1;
|
|
|
|
SCL_H();
|
|
do{
|
|
}while(SCL_READ() != 0); // Wait for any SCL clock stratching
|
|
|
|
i2c_delay();
|
|
if(SDA_READ()) // get the Data bit
|
|
data |= 1;
|
|
else
|
|
data |= 0;
|
|
SCL_L(); // clock LO
|
|
i2c_delay();
|
|
}
|
|
|
|
SDA_OUT(); // change direction back to output
|
|
|
|
// send ACK
|
|
if(ack == ACK) SDA_L();
|
|
else if(ack == NACK) SDA_H();
|
|
i2c_clock();
|
|
SDA_H(); // leave with SDA HI
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
void mcp4725_writeFastMode(const uint16_t value)
|
|
{
|
|
uint8_t low = value & 0xFF;
|
|
uint8_t high = ((value >> 8) & 0x0F); // set C2,c1 == 0,0(FastMode Write) pd1,pd0 == 0,0 (Normal Mode)
|
|
|
|
i2c_start();
|
|
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
|
|
|
//uint8_t address = MCP4725_I2C_ADDR | TWI_WRITE;
|
|
//DBG_PRINTF("[I2C] Write to 0x%02X\r\n", address); //oxCC
|
|
|
|
mcp4725_i2c_write(high); // value1 ~ 3
|
|
mcp4725_i2c_write(low);
|
|
i2c_stop();
|
|
}
|
|
|
|
|
|
void mcp4725_generalCall(const uint8_t gc)
|
|
{
|
|
i2c_start();
|
|
mcp4725_i2c_write(0x00); // First Byte 0x00
|
|
mcp4725_i2c_write(gc); // Second Byte, General Call Reset = 0x06, General Call Wake-Up = 0x09
|
|
i2c_stop();
|
|
}
|
|
|
|
|
|
void mcp4725_init(void)
|
|
{
|
|
mcp4725_i2c_initialize();
|
|
}
|
|
|
|
// DAC value is reset to EEPROM value
|
|
// need to reflect this in cached value
|
|
void mcp4725_powerOnReset(void)
|
|
{
|
|
mcp4725_generalCall(MCP4725_GC_RESET);
|
|
}
|
|
|
|
|
|
// _powerDownMode DAC resets to 0 -- PDM EEPROM stays same !!!
|
|
// need to reflect this in cached value
|
|
void mcp4725_powerOnWakeUp(void)
|
|
{
|
|
mcp4725_generalCall(MCP4725_GC_WAKEUP);
|
|
}
|
|
|
|
/* 현재 DAC값 읽어서 그 값에 Power Down 넣고 Write */
|
|
void mcp4725_PowerDownMode(void)
|
|
{
|
|
uint8_t low = 0x00; //read_value & 0xFF;
|
|
uint8_t high = 0x00; //((read_value >> 8) & 0x0F);
|
|
high = high | (MCP4725_PDMODE_1K << 4); // set C2,c1 == 0,0(FastMode Write) pd1,pd0 == 0,1 (Power Down Mode, 1Kohm to GND)
|
|
|
|
i2c_start();
|
|
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
|
mcp4725_i2c_write(high); // value
|
|
mcp4725_i2c_write(low);
|
|
i2c_stop();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
uint16_t mcp4725_readDAC(void)
|
|
{
|
|
while(!mcp4725_ready());
|
|
uint8_t buffer[3];
|
|
mcp4725_readRegister(buffer, 3);
|
|
uint16_t value = buffer[1];
|
|
value = value << 4;
|
|
value = value + (buffer[2] >> 4);
|
|
return value;
|
|
}
|
|
|
|
|
|
// ready checks if the last write to EEPROM has been written.
|
|
// until ready all writes to the MCP4725 are ignored!
|
|
bool mcp4725_ready(void)
|
|
{
|
|
uint8_t buffer[1];
|
|
mcp4725_readRegister(buffer, 1);
|
|
return ((buffer[0] & 0x80) > 0);
|
|
}
|
|
|
|
|
|
void mcp4725_writeRegisterMode(const uint16_t value, uint8_t reg)
|
|
{
|
|
uint8_t high = (value / 16);
|
|
uint8_t low = (value & 0x0F) << 4;
|
|
reg = reg | (_powerDownMode << 1);
|
|
|
|
i2c_start();
|
|
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_WRITE); // slave address
|
|
mcp4725_i2c_write(reg); // configuration
|
|
mcp4725_i2c_write(high); // value1 ~ 3
|
|
mcp4725_i2c_write(low);
|
|
i2c_stop();
|
|
}
|
|
|
|
|
|
void mcp4725_readRegister(uint8_t* buffer, const uint8_t length)
|
|
{
|
|
i2c_start();
|
|
mcp4725_i2c_write(MCP4725_I2C_ADDR|TWI_READ); // slave address with READ
|
|
switch(length) {
|
|
case 1:
|
|
buffer[0] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
|
break;
|
|
|
|
case 2:
|
|
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[1] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
|
break;
|
|
|
|
case 3:
|
|
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[2] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
|
break;
|
|
|
|
case 4:
|
|
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[2] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[3] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
|
break;
|
|
|
|
case 5:
|
|
buffer[0] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[1] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[2] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[3] = mcp4725_i2c_read(ACK); // ACK, SDA = 0;
|
|
buffer[4] = mcp4725_i2c_read(NACK); // NACK, SDA = 1;
|
|
break;
|
|
|
|
default:
|
|
#if FEATURE_PRINTF
|
|
DBG_PRINTF("ERR!! mcp4725_i2c_readregister\r\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
i2c_stop();
|
|
|
|
}
|
|
|
|
|
|
void mcp4725_setValue(const uint16_t value)
|
|
{
|
|
if (value == _lastValue) return;
|
|
if (value > MCP4725_MAXVALUE) return;
|
|
mcp4725_writeFastMode(value);
|
|
_lastValue = value;
|
|
}
|
|
|
|
|
|
uint16_t mcp4725_getValue(void)
|
|
{
|
|
return _lastValue;
|
|
}
|
|
|
|
|
|
void mcp4725_setPercentage(float percentage)
|
|
{
|
|
if ((percentage > 100) || (percentage < 0))
|
|
DBG_PRINTF("ERR!!! mcp4725 percentage error\r\n");
|
|
mcp4725_setValue(round(percentage * (0.01 * MCP4725_MAXVALUE)));
|
|
}
|
|
|
|
|
|
// unfortunately it is not possible to write a different value
|
|
// to the DAC and EEPROM simultaneously or write EEPROM only.
|
|
void mcp4725_writeDAC(const uint16_t value, const bool EEPROM)
|
|
{
|
|
if (value > MCP4725_MAXVALUE)
|
|
#if FEATURE_PRINTF
|
|
DBG_PRINTF("ERR!!! mcp4725 writeDAC error\r\n");
|
|
#endif
|
|
while(!mcp4725_ready());
|
|
mcp4725_writeRegisterMode(value, EEPROM ? MCP4725_DACEEPROM : MCP4725_DAC);
|
|
_lastValue = value;
|
|
}
|
|
|
|
|
|
uint16_t mcp4725_readEEPROM(void)
|
|
{
|
|
while(!mcp4725_ready());
|
|
uint8_t buffer[5];
|
|
mcp4725_readRegister(buffer, 5);
|
|
uint16_t value = buffer[3] & 0x0F;
|
|
value = value << 8;
|
|
value = value + buffer[4];
|
|
return value;
|
|
}
|
|
|
|
|
|
// depending on bool EEPROM the value of PDM is written to
|
|
// (false) DAC or
|
|
// (true) DAC & EEPROM,
|
|
void mcp4725_writePowerDownMode(const uint8_t PDM, const bool EEPROM)
|
|
{
|
|
_powerDownMode = (PDM & 0x03); // mask PDM bits only (written later low level)
|
|
|
|
_lastValue = mcp4725_readDAC();
|
|
_powerDownMode = mcp4725_readPowerDownModeDAC();
|
|
mcp4725_writeDAC(_lastValue, EEPROM);
|
|
}
|
|
|
|
|
|
uint8_t mcp4725_readPowerDownModeEEPROM(void)
|
|
{
|
|
while(!mcp4725_ready());
|
|
uint8_t buffer[4];
|
|
mcp4725_readRegister(buffer, 4);
|
|
uint8_t value = (buffer[3] >> 5) & 0x03;
|
|
return value;
|
|
}
|
|
|
|
|
|
uint8_t mcp4725_readPowerDownModeDAC(void)
|
|
{
|
|
while(!mcp4725_ready()); // TODO needed?
|
|
uint8_t buffer[1];
|
|
mcp4725_readRegister(buffer, 1);
|
|
uint8_t value = (buffer[0] >> 1) & 0x03;
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ds_i2c_start(void) {
|
|
SDA_H();//nrf_gpio_pin_set(I2C_SDA_PIN);
|
|
SCL_H();//nrf_gpio_pin_set(I2C_SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SDA_L();//nrf_gpio_pin_clear(I2C_SDA_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SCL_L();//nrf_gpio_pin_clear(I2C_SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
}
|
|
|
|
void ds_i2c_stop(void) {
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SDA_L();//nrf_gpio_pin_clear(I2C_SDA_PIN);
|
|
SCL_H();// nrf_gpio_pin_set(I2C_SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SDA_H();//nrf_gpio_pin_set(I2C_SDA_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void i2c_write_bit(uint8_t bit) {
|
|
if (bit) {
|
|
SDA_H();
|
|
} else {
|
|
SDA_L();
|
|
}
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SCL_H(); //nrf_gpio_pin_set(SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SCL_L();//nrf_gpio_pin_clear(SCL_PIN);
|
|
}
|
|
|
|
uint8_t i2c_read_bit(void) {
|
|
uint8_t bit;
|
|
SDA_IN();//nrf_gpio_cfg_input(SDA_PIN, NRF_GPIO_PIN_NOPULL);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SCL_H(); ////nrf_gpio_pin_set(SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
bit = SDA_READ();//nrf_gpio_pin_read(SDA_PIN);
|
|
SCL_L(); //nrf_gpio_pin_clear(SCL_PIN);
|
|
SDA_OUT();//nrf_gpio_cfg_output(SDA_PIN);
|
|
return bit;
|
|
}
|
|
uint8_t i2c_write_byte(uint8_t byte) {
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
i2c_write_bit(byte & 0x80);
|
|
byte <<= 1;
|
|
}
|
|
return i2c_read_bit(); // Read ACK/NACK
|
|
}
|
|
|
|
uint8_t i2c_read_byte(uint8_t ack) {
|
|
uint8_t byte = 0;
|
|
SDA_IN();//nrf_gpio_cfg_input(SDA_PIN, NRF_GPIO_PIN_NOPULL);
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
byte <<= 1;
|
|
byte |= i2c_read_bit();
|
|
}
|
|
SDA_OUT();//nrf_gpio_cfg_output(SDA_PIN);
|
|
i2c_write_bit(!ack); // Send ACK/NACK
|
|
return byte;
|
|
}
|
|
void i2c_repeated_start(void) {
|
|
SDA_H();//nrf_gpio_pin_set(SDA_PIN);
|
|
SCL_H();//nrf_gpio_pin_set(SCL_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SDA_L();////nrf_gpio_pin_clear(SDA_PIN);
|
|
i2c_delay();//nrf_delay_us(I2C_DELAY_US);
|
|
SCL_L();//nrf_gpio_pin_clear(SCL_PIN);
|
|
}
|
|
|
|
// i2c_start();
|
|
// i2c_write_byte(I2C_ADDRESS << 1); // Write address
|
|
// i2c_write_byte(0x00); // Register address
|
|
// i2c_write_byte(0xFF); // Data
|
|
// i2c_stop();
|
|
|
|
// // Read example
|
|
// i2c_start();
|
|
// i2c_write_byte((I2C_ADDRESS << 1) | 1); // Read address
|
|
// uint8_t data = i2c_read_byte(0); // Read data with NACK
|
|
// i2c_stop();
|
|
|
|
|
|
void DS3930_write(uint8_t id, uint8_t addr, uint8_t wdata)
|
|
{
|
|
//i2c_start();
|
|
// ds_i2c_stop();
|
|
ds_i2c_start();
|
|
i2c_write_byte(id << 1); // Write address
|
|
i2c_write_byte(addr); // Register address
|
|
i2c_write_byte(wdata); // Data
|
|
|
|
// mcp4725_i2c_write(id << 1);
|
|
// mcp4725_i2c_write(addr);
|
|
// mcp4725_i2c_write(wdata);
|
|
//
|
|
// }
|
|
//i2c_delay();
|
|
//i2c_stop();
|
|
ds_i2c_stop();
|
|
}
|
|
|
|
|
|
|
|
uint8_t DS3930_read(uint8_t id, uint8_t addr, uint8_t* rdata)
|
|
{
|
|
ds_i2c_start();
|
|
i2c_write_byte(id << 1); // Write address
|
|
i2c_write_byte(addr); // Register address
|
|
i2c_repeated_start();
|
|
i2c_write_byte((id << 1) | 1); // Read address
|
|
uint8_t data = i2c_read_byte(0); // Read data with NACK
|
|
ds_i2c_stop();
|
|
rdata[0] = data;
|
|
DBG_PRINTF("Data 0x%x . \r\n", data);
|
|
//i2c_stop();
|
|
return data;
|
|
}
|
|
|
|
|
|
|
|
|
|
//void CAT24_write(uint8_t id, uint8_t addr, uint8_t* wdata)
|
|
//{
|
|
////i2c_start();
|
|
//// ds_i2c_stop();
|
|
// ds_i2c_start();
|
|
// i2c_write_byte(id << 1); // Write address
|
|
// i2c_write_byte(addr); // Register address
|
|
// i2c_write_byte(wdata); // Data
|
|
//
|
|
//// mcp4725_i2c_write(id << 1);
|
|
//// mcp4725_i2c_write(addr);
|
|
//// mcp4725_i2c_write(wdata);
|
|
////
|
|
//// }
|
|
////i2c_delay();
|
|
////i2c_stop();
|
|
// ds_i2c_stop();
|
|
//}
|
|
|
|
|
|
//uint8_t CAT24_read(uint8_t id, uint8_t addr, uint8_t* rdata,uint8_t length)
|
|
//{
|
|
// ds_i2c_start();
|
|
// i2c_write_byte(id << 1); // Write address
|
|
// i2c_write_byte(addr); // Register address
|
|
// i2c_repeated_start();
|
|
// i2c_write_byte((id << 1) | 1); // Read address
|
|
// uint8_t data = i2c_read_byte(0); // Read data with NACK
|
|
// ds_i2c_stop();
|
|
// rdata[0] = data;
|
|
// DBG_PRINTF("Data 0x%x . \r\n", data);
|
|
// //i2c_stop();
|
|
// return data;
|
|
//}
|
|
|