Skip to content

Commit 9954883

Browse files
dnc40085Konrad Eisele
authored andcommitted
Add mcp4725 module (nodemcu#1966)
1 parent 8fc57a5 commit 9954883

File tree

4 files changed

+365
-0
lines changed

4 files changed

+365
-0
lines changed

app/include/user_modules.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
//#define LUA_USE_MODULES_HX711
4141
#define LUA_USE_MODULES_I2C
4242
//#define LUA_USE_MODULES_L3G4200D
43+
//#define LUA_USE_MODULES_MCP4725
4344
//#define LUA_USE_MODULES_MDNS
4445
#define LUA_USE_MODULES_MQTT
4546
#define LUA_USE_MODULES_NET

app/modules/mcp4725.c

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Driver for Microchip MCP4725 12-bit digital to analog converter.
3+
*/
4+
5+
#include "module.h"
6+
#include "lauxlib.h"
7+
#include "platform.h"
8+
#include "osapi.h"
9+
10+
#define MCP4725_I2C_ADDR_BASE (0x60)
11+
#define MCP4725_I2C_ADDR_A0_MASK (0x01) // user configurable
12+
#define MCP4725_I2C_ADDR_A1_MASK (0x02) // hard wired at factory
13+
#define MCP4725_I2C_ADDR_A2_MASK (0x04) // hard wired at factory
14+
15+
#define MCP4725_COMMAND_WRITE_DAC (0x40)
16+
#define MCP4725_COMMAND_WRITE_DAC_EEPROM (0x60)
17+
18+
#define MCP4725_POWER_DOWN_NORMAL (0x00)
19+
#define MCP4725_POWER_DOWN_RES_1K (0x02)
20+
#define MCP4725_POWER_DOWN_RES_100K (0x04)
21+
#define MCP4725_POWER_DOWN_RES_500K (0x06)
22+
23+
static const unsigned mcp4725_i2c_id = 0;
24+
25+
static uint8 get_address(lua_State* L, uint8 i2c_address){
26+
uint8 addr_temp = i2c_address;
27+
uint16 temp_var = 0;
28+
lua_getfield(L, 1, "A2");
29+
if (!lua_isnil(L, -1))
30+
{
31+
if( lua_isnumber(L, -1) )
32+
{
33+
temp_var = lua_tonumber(L, -1);
34+
if(temp_var < 2){
35+
temp_var = MCP4725_I2C_ADDR_A2_MASK & (temp_var << 2);
36+
addr_temp|=temp_var;
37+
}
38+
else
39+
return luaL_argerror( L, 1, "A2: Must be 0 or 1" );
40+
}
41+
else
42+
{
43+
return luaL_argerror( L, 1, "A2: Must be number" );
44+
}
45+
}
46+
lua_pop(L, 1);
47+
48+
lua_getfield(L, 1, "A1");
49+
if (!lua_isnil(L, -1))
50+
{
51+
if( lua_isnumber(L, -1) )
52+
{
53+
temp_var = lua_tonumber(L, -1);
54+
if(temp_var < 2){
55+
temp_var = MCP4725_I2C_ADDR_A1_MASK & (temp_var << 1);
56+
addr_temp|=temp_var;
57+
}
58+
else
59+
return luaL_argerror( L, 1, "A1: Must be 0 or 1" );
60+
}
61+
else
62+
{
63+
return luaL_argerror( L, 1, "A1: Must be number" );
64+
}
65+
}
66+
lua_pop(L, 1);
67+
68+
lua_getfield(L, 1, "A0");
69+
if (!lua_isnil(L, -1))
70+
{
71+
if( lua_isnumber(L, -1) )
72+
{
73+
temp_var = lua_tonumber(L, -1);
74+
if(temp_var<2){
75+
temp_var = MCP4725_I2C_ADDR_A0_MASK & (temp_var);
76+
addr_temp|=temp_var;
77+
}
78+
else
79+
return luaL_argerror( L, 1, "A0: Must be 0 or 1" );
80+
}
81+
else
82+
{
83+
return luaL_argerror( L, 1, "A0: Must be number" );
84+
}
85+
}
86+
lua_pop(L, 1);
87+
88+
return addr_temp;
89+
}
90+
91+
static int mcp4725_write(lua_State* L){
92+
93+
uint8 i2c_address = MCP4725_I2C_ADDR_BASE;
94+
uint16 dac_value = 0;
95+
uint8 cmd_byte = 0;
96+
97+
if(lua_istable(L, 1))
98+
{
99+
i2c_address = get_address(L, i2c_address);
100+
uint16 temp_var=0;
101+
lua_getfield(L, 1, "value");
102+
if (!lua_isnil(L, -1))
103+
{
104+
if( lua_isnumber(L, -1) )
105+
{
106+
temp_var = lua_tonumber(L, -1);
107+
if(temp_var >= 0 && temp_var<=4095){
108+
dac_value = temp_var<<4;
109+
}
110+
else
111+
return luaL_argerror( L, 1, "value: Valid range 0-4095" );
112+
}
113+
else
114+
{
115+
return luaL_argerror( L, 1, "value: Must be number" );
116+
}
117+
}
118+
else
119+
{
120+
return luaL_argerror( L, 1, "value: value is required" );
121+
}
122+
lua_pop(L, 1);
123+
124+
lua_getfield(L, 1, "save");
125+
if (!lua_isnil(L, -1))
126+
{
127+
if( lua_isboolean(L, -1) )
128+
{
129+
if(lua_toboolean(L, -1)){
130+
cmd_byte |= MCP4725_COMMAND_WRITE_DAC_EEPROM;
131+
}
132+
else{
133+
cmd_byte |= MCP4725_COMMAND_WRITE_DAC;
134+
}
135+
}
136+
else
137+
{
138+
return luaL_argerror( L, 1, "save: must be boolean" );
139+
}
140+
}
141+
else
142+
{
143+
cmd_byte |= MCP4725_COMMAND_WRITE_DAC;
144+
}
145+
lua_pop(L, 1);
146+
147+
lua_getfield(L, 1, "pwrdn");
148+
if (!lua_isnil(L, -1))
149+
{
150+
if( lua_isnumber(L, -1) )
151+
{
152+
temp_var = lua_tonumber(L, -1);
153+
if(temp_var >= 0 && temp_var <= 3){
154+
cmd_byte |= temp_var << 1;
155+
}
156+
else{
157+
return luaL_argerror( L, 1, "pwrdn: Valid range 0-3" );
158+
}
159+
}
160+
else
161+
{
162+
return luaL_argerror( L, 1, "pwrdn: Must be number" );
163+
}
164+
}
165+
lua_pop(L, 1);
166+
167+
}
168+
uint8 *dac_value_byte = (uint8*) & dac_value;
169+
170+
platform_i2c_send_start(mcp4725_i2c_id);
171+
platform_i2c_send_address(mcp4725_i2c_id, i2c_address, PLATFORM_I2C_DIRECTION_TRANSMITTER);
172+
platform_i2c_send_byte(mcp4725_i2c_id, cmd_byte);
173+
platform_i2c_send_byte(mcp4725_i2c_id, dac_value_byte[1]);
174+
platform_i2c_send_byte(mcp4725_i2c_id, dac_value_byte[0]);
175+
platform_i2c_send_stop(mcp4725_i2c_id);
176+
177+
return 0;
178+
}
179+
180+
static int mcp4725_read(lua_State* L){
181+
uint8 i2c_address = MCP4725_I2C_ADDR_BASE;
182+
uint8 recieve_buffer[5] = {0};
183+
184+
if(lua_istable(L, 1))
185+
{
186+
i2c_address = get_address(L, i2c_address);
187+
}
188+
189+
platform_i2c_send_start(mcp4725_i2c_id);
190+
platform_i2c_send_address(mcp4725_i2c_id, i2c_address, PLATFORM_I2C_DIRECTION_RECEIVER);
191+
for(int i=0;i<5;i++){
192+
recieve_buffer[i] = platform_i2c_recv_byte(mcp4725_i2c_id, 1);
193+
}
194+
platform_i2c_send_stop(mcp4725_i2c_id);
195+
196+
lua_pushnumber(L, (recieve_buffer[0] & 0x06)>>1);
197+
lua_pushnumber(L, (recieve_buffer[1] << 4) | (recieve_buffer[2] >> 4));
198+
lua_pushnumber(L, (recieve_buffer[3] & 0x60) >> 5);
199+
lua_pushnumber(L, ((recieve_buffer[3] & 0xf) << 8) | recieve_buffer[4]);
200+
lua_pushnumber(L, (recieve_buffer[0] & 0x80) >> 7);
201+
lua_pushnumber(L, (recieve_buffer[0] & 0x40) >> 6);
202+
203+
return 6;
204+
}
205+
206+
207+
static const LUA_REG_TYPE mcp4725_map[] = {
208+
{ LSTRKEY( "write" ), LFUNCVAL( mcp4725_write ) },
209+
{ LSTRKEY( "read" ), LFUNCVAL( mcp4725_read ) },
210+
{ LSTRKEY( "PWRDN_NONE" ), LNUMVAL(MCP4725_POWER_DOWN_NORMAL) },
211+
{ LSTRKEY( "PWRDN_1K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_1K)>>1) },
212+
{ LSTRKEY( "PWRDN_100K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_100K)>>1) },
213+
{ LSTRKEY( "PWRDN_500K" ), LNUMVAL((MCP4725_POWER_DOWN_RES_500K)>>1) },
214+
{ LNILKEY, LNILVAL}
215+
};
216+
217+
NODEMCU_MODULE(MCP4725, "mcp4725", mcp4725_map, NULL);

docs/en/modules/mcp4725.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# MCP4725 Module
2+
| Since | Origin / Contributor | Maintainer | Source |
3+
| :----- | :-------------------- | :---------- | :------ |
4+
| 2017-05-10 | [dnc40085](https://github.com/dnc40085) | [dnc40085](https://github.com/dnc40085) | [mcp4725.c](../../../app/modules/mcp4725.c)|
5+
6+
7+
This module provides access to the [MCP4725 12-bit Digital to Analog Converter](http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf).
8+
9+
!!!important:
10+
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.
11+
12+
!!!note:
13+
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.
14+
15+
## mcp4725.read()
16+
Gets contents of the dac register and EEPROM.
17+
18+
#### Syntax
19+
`mcp4725.read({[a0], [a1], [a2]})`
20+
21+
#### Parameters
22+
- `A0` Address bit 0. This bit is user configurable via MCP4725 pin 6(A0). (valid states: 0 or 1) (default: 0)
23+
- `A1` Address bit 1. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0)
24+
- Note: Modules purchased from Adafruit have this bit(A1) set high(1).
25+
- `A2` Address bit 2. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0)
26+
27+
#### Returns
28+
* `cur_pwrdn` Current power down configuration value.
29+
* `cur_val` Current value stored in dac register.
30+
* `eeprom_pwrdn` Power down configuration stored in EEPROM.
31+
* `eeprom_val` DAC value stored in EEPROM.
32+
* `eeprom_state` EEPROM write status
33+
* `0` EEPROM write is incomplete.
34+
* `1` EEPROM write has completed
35+
* `por_state` Power-On-Reset status;
36+
* `0` The MCP4725 is performing reset and is not ready.
37+
* `1` The MCP4725 has sucessfully performed reset.
38+
39+
#### Example
40+
```lua
41+
-- Get current configuration using default i2c address 0x60(A0=0, A1=0, A2=0).
42+
do
43+
local ID = 0
44+
local SDA = 6
45+
local SCL = 5
46+
47+
i2c.setup(ID, SDA, SCL, i2c.SLOW)
48+
49+
local cur_pwrdn, cur_val, eeprom_pwrdn, eeprom_val, eeprom_state, por_state = mcp4725.read()
50+
51+
print("\n Current configuration:\n\tpower down value: "..cur_pwrdn.."\n\tdac value: "..cur_val)
52+
print(" Configuration stored in EEPROM:\n\tpower down value: "..eeprom_pwrdn.."\n\tdac value: "..eeprom_val)
53+
print(" EEPROM write state: "..(eeprom_state==1 and "Completed" or "incomplete"))
54+
print(" Power-On-Reset state: "..(por_state==1 and "Completed" or "incomplete"))
55+
end
56+
57+
-- Get current configuration using default i2c address 0x60(A0=0, A1=0, A2=0).
58+
-- The MCP4725's address pin(A0) is being driven with gpio 4(pin 2).
59+
do
60+
local ID = 0
61+
local SDA = 6
62+
local SCL = 5
63+
local mcp4725_chip_sel = 2
64+
65+
i2c.setup(ID, SDA, SCL, i2c.SLOW)
66+
gpio.mode(mcp4725_chip_sel, gpio.OUTPUT, gpio.PULLUP)
67+
68+
gpio.write(mcp4725_chip_sel, 1)
69+
local cur_pwrdn, cur_val, eeprom_pwrdn, eeprom_val, eeprom_state, por_state = mcp4725.read({A0=1})
70+
gpio.write(mcp4725_chip_sel, 0)
71+
72+
print("\n Current configuration:\n\tpower down value: "..cur_pwrdn.."\n\tdac value: "..cur_val)
73+
print(" Configuration stored in EEPROM:\n\tpower down value: "..eeprom_pwrdn.."\n\tdac value: "..eeprom_val)
74+
print(" EEPROM write state: "..(eeprom_state==1 and "Completed" or "incomplete"))
75+
print(" Power-On-Reset state: "..(por_state==1 and "Completed" or "incomplete"))
76+
end
77+
```
78+
#### See also
79+
- [`i2c.setup()`](i2c.md#i2csetup)
80+
81+
82+
## mcp4725.write()
83+
Write configuration to dac register or dac register and eeprom.
84+
85+
#### Syntax
86+
`mcp4725.write({[a0], [a1], [a2], value, [pwrdn], [save]})`
87+
88+
#### Parameters
89+
- `A0` Address bit 0. This bit is user configurable via MCP4725 pin 6(A0). (valid states: 0 or 1) (default: 0)
90+
- `A1` Address bit 1. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0)
91+
- Note: Modules purchased from Adafruit have this bit(A1) set high(1).
92+
- `A2` Address bit 2. This bit is hard-wired during manufacture. (valid states: 0 or 1) (default: 0)
93+
- `value` The value to be used to configure DAC (and EEPROM). (Range: 0 - 4095)
94+
- `pwrdn` Set power down bits.
95+
- `mcp4725.PWRDN_NONE` MCP4725 output enabled. (Default)
96+
- `mcp4725.PWRDN_1K` MCP4725 output disabled, output pulled to ground via 1K restistor.
97+
- `mcp4725.PWRDN_100K` MCP4725 output disabled, output pulled to ground via 100K restistor.
98+
- `mcp4725.PWRDN_500K` MCP4725 output disabled, output pulled to ground via 500K restistor.
99+
- `save` Save pwrdn and dac values to EEPROM. (Values are loaded on power-up or during reset.)
100+
- `true` Save configuration to EEPROM.
101+
- `false` Do not save configuration to EEPROM. (Default)
102+
103+
#### Returns
104+
nil
105+
106+
#### Example
107+
```lua
108+
109+
-- Set current configuration using default i2c address 0x60(A0=0, A1=0, A2=0).
110+
do
111+
local ID = 0
112+
local SDA = 6
113+
local SCL = 5
114+
115+
i2c.setup(ID, SDA, SCL, i2c.SLOW)
116+
mcp4725.write({value=2048})
117+
end
118+
119+
-- Set current configuration and save to EEPROM using default i2c address 0x60(A0=0, A1=0, A2=0).
120+
do
121+
local ID = 0
122+
local SDA = 6
123+
local SCL = 5
124+
125+
i2c.setup(ID, SDA, SCL, i2c.SLOW)
126+
mcp4725.write({value=2048, save=true})
127+
end
128+
129+
-- Set current configuration using default i2c address 0x60(A0=0, A1=0, A2=0).
130+
-- The MCP4725's address pin(A0) is being driven with gpio 4(pin 2).
131+
do
132+
local ID = 0
133+
local SDA = 6
134+
local SCL = 5
135+
local mcp4725_chip_sel = 2
136+
137+
i2c.setup(ID, SDA, SCL, i2c.SLOW)
138+
gpio.mode(mcp4725_chip_sel, gpio.OUTPUT, gpio.PULLUP)
139+
140+
gpio.write(mcp4725_chip_sel, 1)
141+
mcp4725.read({A0=1, value})
142+
gpio.write(mcp4725_chip_sel, 0)
143+
end
144+
```
145+
#### See also
146+
- [`i2c.setup()`](i2c.md#i2csetup)

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pages:
5959
- 'hx711' : 'en/modules/hx711.md'
6060
- 'i2c' : 'en/modules/i2c.md'
6161
- 'l3g4200d' : 'en/modules/l3g4200d.md'
62+
- 'mcp4725': 'en/modules/mcp4725.md'
6263
- 'mdns': 'en/modules/mdns.md'
6364
- 'mqtt': 'en/modules/mqtt.md'
6465
- 'net': 'en/modules/net.md'

0 commit comments

Comments
 (0)