From e90ffb42667da287537c269c967cb9d723edb376 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Sun, 21 May 2017 07:30:26 -0700 Subject: [PATCH] Add mcp4725 module (#1966) --- app/include/user_modules.h | 1 + app/modules/mcp4725.c | 217 +++++++++++++++++++++++++++++++++++++ docs/en/modules/mcp4725.md | 146 +++++++++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 365 insertions(+) create mode 100644 app/modules/mcp4725.c create mode 100644 docs/en/modules/mcp4725.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 06714516..74bf1719 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -40,6 +40,7 @@ //#define LUA_USE_MODULES_HX711 #define LUA_USE_MODULES_I2C //#define LUA_USE_MODULES_L3G4200D +//#define LUA_USE_MODULES_MCP4725 //#define LUA_USE_MODULES_MDNS #define LUA_USE_MODULES_MQTT #define LUA_USE_MODULES_NET diff --git a/app/modules/mcp4725.c b/app/modules/mcp4725.c new file mode 100644 index 00000000..b9117746 --- /dev/null +++ b/app/modules/mcp4725.c @@ -0,0 +1,217 @@ +/* + * Driver for Microchip MCP4725 12-bit digital to analog converter. + */ + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "osapi.h" + +#define MCP4725_I2C_ADDR_BASE (0x60) +#define MCP4725_I2C_ADDR_A0_MASK (0x01) // user configurable +#define MCP4725_I2C_ADDR_A1_MASK (0x02) // hard wired at factory +#define MCP4725_I2C_ADDR_A2_MASK (0x04) // hard wired at factory + +#define MCP4725_COMMAND_WRITE_DAC (0x40) +#define MCP4725_COMMAND_WRITE_DAC_EEPROM (0x60) + +#define MCP4725_POWER_DOWN_NORMAL (0x00) +#define MCP4725_POWER_DOWN_RES_1K (0x02) +#define MCP4725_POWER_DOWN_RES_100K (0x04) +#define MCP4725_POWER_DOWN_RES_500K (0x06) + +static const unsigned mcp4725_i2c_id = 0; + +static uint8 get_address(lua_State* L, uint8 i2c_address){ + uint8 addr_temp = i2c_address; + uint16 temp_var = 0; + lua_getfield(L, 1, "A2"); + if (!lua_isnil(L, -1)) + { + if( lua_isnumber(L, -1) ) + { + temp_var = lua_tonumber(L, -1); + if(temp_var < 2){ + temp_var = MCP4725_I2C_ADDR_A2_MASK & (temp_var << 2); + addr_temp|=temp_var; + } + else + return luaL_argerror( L, 1, "A2: Must be 0 or 1" ); + } + else + { + return luaL_argerror( L, 1, "A2: Must be number" ); + } + } + lua_pop(L, 1); + + lua_getfield(L, 1, "A1"); + if (!lua_isnil(L, -1)) + { + if( lua_isnumber(L, -1) ) + { + temp_var = lua_tonumber(L, -1); + if(temp_var < 2){ + temp_var = MCP4725_I2C_ADDR_A1_MASK & (temp_var << 1); + addr_temp|=temp_var; + } + else + return luaL_argerror( L, 1, "A1: Must be 0 or 1" ); + } + else + { + return luaL_argerror( L, 1, "A1: Must be number" ); + } + } + lua_pop(L, 1); + + lua_getfield(L, 1, "A0"); + if (!lua_isnil(L, -1)) + { + if( lua_isnumber(L, -1) ) + { + temp_var = lua_tonumber(L, -1); + if(temp_var<2){ + temp_var = MCP4725_I2C_ADDR_A0_MASK & (temp_var); + addr_temp|=temp_var; + } + else + return luaL_argerror( L, 1, "A0: Must be 0 or 1" ); + } + else + { + return luaL_argerror( L, 1, "A0: Must be number" ); + } + } + lua_pop(L, 1); + + return addr_temp; +} + +static int mcp4725_write(lua_State* L){ + + uint8 i2c_address = MCP4725_I2C_ADDR_BASE; + uint16 dac_value = 0; + uint8 cmd_byte = 0; + + if(lua_istable(L, 1)) + { + i2c_address = get_address(L, i2c_address); + uint16 temp_var=0; + lua_getfield(L, 1, "value"); + if (!lua_isnil(L, -1)) + { + if( lua_isnumber(L, -1) ) + { + temp_var = lua_tonumber(L, -1); + if(temp_var >= 0 && temp_var<=4095){ + dac_value = temp_var<<4; + } + else + return luaL_argerror( L, 1, "value: Valid range 0-4095" ); + } + else + { + return luaL_argerror( L, 1, "value: Must be number" ); + } + } + else + { + return luaL_argerror( L, 1, "value: value is required" ); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "save"); + if (!lua_isnil(L, -1)) + { + if( lua_isboolean(L, -1) ) + { + if(lua_toboolean(L, -1)){ + cmd_byte |= MCP4725_COMMAND_WRITE_DAC_EEPROM; + } + else{ + cmd_byte |= MCP4725_COMMAND_WRITE_DAC; + } + } + else + { + return luaL_argerror( L, 1, "save: must be boolean" ); + } + } + else + { + cmd_byte |= MCP4725_COMMAND_WRITE_DAC; + } + lua_pop(L, 1); + + lua_getfield(L, 1, "pwrdn"); + if (!lua_isnil(L, -1)) + { + if( lua_isnumber(L, -1) ) + { + temp_var = lua_tonumber(L, -1); + if(temp_var >= 0 && temp_var <= 3){ + cmd_byte |= temp_var << 1; + } + else{ + return luaL_argerror( L, 1, "pwrdn: Valid range 0-3" ); + } + } + else + { + return luaL_argerror( L, 1, "pwrdn: Must be number" ); + } + } + lua_pop(L, 1); + + } + uint8 *dac_value_byte = (uint8*) & dac_value; + + platform_i2c_send_start(mcp4725_i2c_id); + platform_i2c_send_address(mcp4725_i2c_id, i2c_address, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(mcp4725_i2c_id, cmd_byte); + platform_i2c_send_byte(mcp4725_i2c_id, dac_value_byte[1]); + platform_i2c_send_byte(mcp4725_i2c_id, dac_value_byte[0]); + platform_i2c_send_stop(mcp4725_i2c_id); + + return 0; +} + +static int mcp4725_read(lua_State* L){ + uint8 i2c_address = MCP4725_I2C_ADDR_BASE; + uint8 recieve_buffer[5] = {0}; + + if(lua_istable(L, 1)) + { + i2c_address = get_address(L, i2c_address); + } + + platform_i2c_send_start(mcp4725_i2c_id); + platform_i2c_send_address(mcp4725_i2c_id, i2c_address, PLATFORM_I2C_DIRECTION_RECEIVER); + for(int i=0;i<5;i++){ + recieve_buffer[i] = platform_i2c_recv_byte(mcp4725_i2c_id, 1); + } + platform_i2c_send_stop(mcp4725_i2c_id); + + lua_pushnumber(L, (recieve_buffer[0] & 0x06)>>1); + lua_pushnumber(L, (recieve_buffer[1] << 4) | (recieve_buffer[2] >> 4)); + lua_pushnumber(L, (recieve_buffer[3] & 0x60) >> 5); + lua_pushnumber(L, ((recieve_buffer[3] & 0xf) << 8) | recieve_buffer[4]); + lua_pushnumber(L, (recieve_buffer[0] & 0x80) >> 7); + lua_pushnumber(L, (recieve_buffer[0] & 0x40) >> 6); + + return 6; +} + + +static const LUA_REG_TYPE mcp4725_map[] = { + { LSTRKEY( "write" ), LFUNCVAL( mcp4725_write ) }, + { LSTRKEY( "read" ), LFUNCVAL( mcp4725_read ) }, + { LSTRKEY( "PWRDN_NONE" ), LNUMVAL(MCP4725_POWER_DOWN_NORMAL) }, + { LSTRKEY( "PWRDN_1K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_1K)>>1) }, + { LSTRKEY( "PWRDN_100K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_100K)>>1) }, + { LSTRKEY( "PWRDN_500K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_500K)>>1) }, + { LNILKEY, LNILVAL} +}; + +NODEMCU_MODULE(MCP4725, "mcp4725", mcp4725_map, NULL); diff --git a/docs/en/modules/mcp4725.md b/docs/en/modules/mcp4725.md new file mode 100644 index 00000000..8db5b4c1 --- /dev/null +++ b/docs/en/modules/mcp4725.md @@ -0,0 +1,146 @@ +# MCP4725 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-05-10 | [dnc40085](https://github.com/dnc40085) | [dnc40085](https://github.com/dnc40085) | [mcp4725.c](../../../app/modules/mcp4725.c)| + + +This module provides access to the [MCP4725 12-bit Digital to Analog Converter](http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf). + +!!!important: + VDD is the power supply pin for the device. The voltage at the VDD pin is used as the supply input as well as the DAC reference input. The power supply at the VDD pin should be clean as possible for good DAC performance. + +!!!note: + The MCP4725 device address contains four fixed bits ( 1100 = device code) and three address bits (A2, A1, A0). The A2 and A1 bits are hard-wired during manufacturing, and A0 bit is determined by the logic state of A0 pin. The A0 pin can be connected to VDD or VSS , or actively driven by digital logic levels. The address pin(A0) can be actively driven by a GPIO to act as a chip select, allowing more than 2 devices to be used on the same bus. + +## mcp4725.read() +Gets contents of the dac register and EEPROM. + +#### Syntax +`mcp4725.read({[a0], [a1], [a2]})` + +#### Parameters +- `A0` Address bit 0. This bit is user configurable via MCP4725 pin 6(A0). (valid states: 0 or 1) (default: 0) +- `A1` Address bit 1. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0) + - Note: Modules purchased from Adafruit have this bit(A1) set high(1). +- `A2` Address bit 2. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0) + +#### Returns +* `cur_pwrdn` Current power down configuration value. +* `cur_val` Current value stored in dac register. +* `eeprom_pwrdn` Power down configuration stored in EEPROM. +* `eeprom_val` DAC value stored in EEPROM. +* `eeprom_state` EEPROM write status + * `0` EEPROM write is incomplete. + * `1` EEPROM write has completed +* `por_state` Power-On-Reset status; + * `0` The MCP4725 is performing reset and is not ready. + * `1` The MCP4725 has sucessfully performed reset. + +#### Example +```lua +-- Get current configuration using default i2c address 0x60(A0=0, A1=0, A2=0). +do +local ID = 0 +local SDA = 6 +local SCL = 5 + +i2c.setup(ID, SDA, SCL, i2c.SLOW) + +local cur_pwrdn, cur_val, eeprom_pwrdn, eeprom_val, eeprom_state, por_state = mcp4725.read() + +print("\n Current configuration:\n\tpower down value: "..cur_pwrdn.."\n\tdac value: "..cur_val) +print(" Configuration stored in EEPROM:\n\tpower down value: "..eeprom_pwrdn.."\n\tdac value: "..eeprom_val) +print(" EEPROM write state: "..(eeprom_state==1 and "Completed" or "incomplete")) +print(" Power-On-Reset state: "..(por_state==1 and "Completed" or "incomplete")) +end + +-- Get current configuration using default i2c address 0x60(A0=0, A1=0, A2=0). +-- The MCP4725's address pin(A0) is being driven with gpio 4(pin 2). +do +local ID = 0 +local SDA = 6 +local SCL = 5 +local mcp4725_chip_sel = 2 + +i2c.setup(ID, SDA, SCL, i2c.SLOW) +gpio.mode(mcp4725_chip_sel, gpio.OUTPUT, gpio.PULLUP) + +gpio.write(mcp4725_chip_sel, 1) +local cur_pwrdn, cur_val, eeprom_pwrdn, eeprom_val, eeprom_state, por_state = mcp4725.read({A0=1}) +gpio.write(mcp4725_chip_sel, 0) + +print("\n Current configuration:\n\tpower down value: "..cur_pwrdn.."\n\tdac value: "..cur_val) +print(" Configuration stored in EEPROM:\n\tpower down value: "..eeprom_pwrdn.."\n\tdac value: "..eeprom_val) +print(" EEPROM write state: "..(eeprom_state==1 and "Completed" or "incomplete")) +print(" Power-On-Reset state: "..(por_state==1 and "Completed" or "incomplete")) +end +``` +#### See also +- [`i2c.setup()`](i2c.md#i2csetup) + + +## mcp4725.write() +Write configuration to dac register or dac register and eeprom. + +#### Syntax +`mcp4725.write({[a0], [a1], [a2], value, [pwrdn], [save]})` + +#### Parameters +- `A0` Address bit 0. This bit is user configurable via MCP4725 pin 6(A0). (valid states: 0 or 1) (default: 0) +- `A1` Address bit 1. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0) + - Note: Modules purchased from Adafruit have this bit(A1) set high(1). +- `A2` Address bit 2. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0) +- `value` The value to be used to configure DAC (and EEPROM). (Range: 0 - 4095) +- `pwrdn` Set power down bits. + - `mcp4725.PWRDN_NONE` MCP4725 output enabled. (Default) + - `mcp4725.PWRDN_1K` MCP4725 output disabled, output pulled to ground via 1K restistor. + - `mcp4725.PWRDN_100K` MCP4725 output disabled, output pulled to ground via 100K restistor. + - `mcp4725.PWRDN_500K` MCP4725 output disabled, output pulled to ground via 500K restistor. +- `save` Save pwrdn and dac values to EEPROM. (Values are loaded on power-up or during reset.) + - `true` Save configuration to EEPROM. + - `false` Do not save configuration to EEPROM. (Default) + +#### Returns +nil + +#### Example +```lua + +-- Set current configuration using default i2c address 0x60(A0=0, A1=0, A2=0). +do + local ID = 0 + local SDA = 6 + local SCL = 5 + + i2c.setup(ID, SDA, SCL, i2c.SLOW) + mcp4725.write({value=2048}) +end + +-- Set current configuration and save to EEPROM using default i2c address 0x60(A0=0, A1=0, A2=0). +do + local ID = 0 + local SDA = 6 + local SCL = 5 + + i2c.setup(ID, SDA, SCL, i2c.SLOW) + mcp4725.write({value=2048, save=true}) +end + +-- Set current configuration using default i2c address 0x60(A0=0, A1=0, A2=0). +-- The MCP4725's address pin(A0) is being driven with gpio 4(pin 2). +do + local ID = 0 + local SDA = 6 + local SCL = 5 + local mcp4725_chip_sel = 2 + + i2c.setup(ID, SDA, SCL, i2c.SLOW) + gpio.mode(mcp4725_chip_sel, gpio.OUTPUT, gpio.PULLUP) + + gpio.write(mcp4725_chip_sel, 1) + mcp4725.read({A0=1, value}) + gpio.write(mcp4725_chip_sel, 0) +end +``` +#### See also +- [`i2c.setup()`](i2c.md#i2csetup) diff --git a/mkdocs.yml b/mkdocs.yml index 613cdd81..e200d50e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,7 @@ pages: - 'hx711' : 'en/modules/hx711.md' - 'i2c' : 'en/modules/i2c.md' - 'l3g4200d' : 'en/modules/l3g4200d.md' + - 'mcp4725': 'en/modules/mcp4725.md' - 'mdns': 'en/modules/mdns.md' - 'mqtt': 'en/modules/mqtt.md' - 'net': 'en/modules/net.md'