Skip to content

Commit 32ad759

Browse files
pjsgmarcelstoer
authored andcommitted
Add streaming support for hx711 device (#2793)
1 parent fff9f95 commit 32ad759

File tree

2 files changed

+345
-32
lines changed

2 files changed

+345
-32
lines changed

app/modules/hx711.c

Lines changed: 287 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,77 +3,334 @@
33

44
#include "module.h"
55
#include "lauxlib.h"
6+
#include "lmem.h"
67
#include "platform.h"
78
#include <stdlib.h>
89
#include <string.h>
910
#include "user_interface.h"
1011
static uint8_t data_pin;
1112
static uint8_t clk_pin;
13+
// The fields below are after the pin_num conversion
14+
static uint8_t pin_data_pin;
15+
static uint8_t pin_clk_pin;
16+
17+
#ifdef GPIO_INTERRUPT_ENABLE
18+
static task_handle_t tasknumber;
19+
20+
// HX711_STATUS can be defined to enable the hx711.status() function to get debug info
21+
#undef HX711_STATUS
22+
#define BUFFERS 2
23+
24+
typedef struct {
25+
char *buf[BUFFERS];
26+
uint32_t dropped[BUFFERS];
27+
uint32_t timestamp[BUFFERS];
28+
uint32_t interrupts;
29+
uint32_t hx711_interrupts;
30+
uint16_t buflen;
31+
uint16_t used;
32+
uint32_t nobuffer;
33+
uint8_t active; // slot of the active buffer
34+
uint8_t freed; // slot of the most recently freed buffer
35+
uint8_t mode;
36+
uint8_t dropping; // is non zero when there is no space
37+
int cb_ref;
38+
} CONTROL;
39+
40+
static CONTROL *control;
41+
#endif
1242

1343
/*Lua: hx711.init(clk_pin,data_pin)*/
1444
static int hx711_init(lua_State* L) {
15-
clk_pin = luaL_checkinteger(L,1);
16-
data_pin = luaL_checkinteger(L,2);
45+
clk_pin = luaL_checkint(L,1);
46+
data_pin = luaL_checkint(L,2);
1747
MOD_CHECK_ID( gpio, clk_pin );
1848
MOD_CHECK_ID( gpio, data_pin );
1949

2050
platform_gpio_mode(clk_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
2151
platform_gpio_mode(data_pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_FLOAT);
2252
platform_gpio_write(clk_pin,1);//put chip to sleep.
53+
54+
pin_data_pin = pin_num[data_pin];
55+
pin_clk_pin = pin_num[clk_pin];
2356
return 0;
2457
}
2558

59+
static int32_t ICACHE_RAM_ATTR read_sample(char mode) {
60+
int i;
61+
int32_t data = 0;
62+
63+
for (i = 0; i < 24 ; i++){ //clock in the 24 bits
64+
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << pin_clk_pin);
65+
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << pin_clk_pin);
66+
data = data << 1;
67+
if (GPIO_REG_READ(GPIO_IN_ADDRESS) & (1 << pin_data_pin)) {
68+
data = i == 0 ? -1 : data | 1; //signextend the first bit
69+
}
70+
}
71+
//add 25th-27th clock pulse to prevent protocol error
72+
for (i = 0; i <= mode; i++) {
73+
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << pin_clk_pin);
74+
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << pin_clk_pin);
75+
}
76+
77+
return data;
78+
}
79+
80+
#ifdef GPIO_INTERRUPT_ENABLE
81+
static void ICACHE_RAM_ATTR hx711_data_available() {
82+
if (!control) {
83+
return;
84+
}
85+
uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS);
86+
if (bits & (1 << pin_data_pin)) {
87+
return; // not ready
88+
}
89+
90+
// Read a sample
91+
int32_t data = read_sample(control->mode);
92+
93+
if (control->dropping) {
94+
if (control->active == control->freed) {
95+
// still can't advance
96+
control->nobuffer++;
97+
return;
98+
}
99+
// Advance
100+
control->active = (1 + control->active) % BUFFERS;
101+
control->dropping = 0;
102+
}
103+
104+
// insert into the active buffer
105+
char *dest = control->buf[control->active] + control->used;
106+
*dest++ = data;
107+
*dest++ = data >> 8;
108+
*dest++ = data >> 16;
109+
110+
control->used += 3;
111+
if (control->used == control->buflen) {
112+
control->used = 0;
113+
control->timestamp[control->active] = system_get_time();
114+
control->dropped[control->active] = control->nobuffer;
115+
control->nobuffer = 0;
116+
// post task
117+
task_post_medium(tasknumber, control->active);
118+
119+
uint8_t next_active = (1 + control->active) % BUFFERS;
120+
121+
if (control->active == control->freed) {
122+
// We can't advance to the buffer
123+
control->dropping = 1;
124+
} else {
125+
// flip to other buffer
126+
control->active = next_active;
127+
}
128+
}
129+
}
130+
131+
static uint32_t ICACHE_RAM_ATTR hx711_interrupt(uint32_t ret_gpio_status)
132+
{
133+
// This function really is running at interrupt level with everything
134+
// else masked off. It should take as little time as necessary.
135+
//
136+
//
137+
138+
// This gets the set of pins which have changed status
139+
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
140+
141+
int pin_mask = 1 << pin_data_pin;
142+
int i;
143+
144+
control->interrupts++;
145+
146+
if (gpio_status & pin_mask) {
147+
uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS);
148+
control->hx711_interrupts++;
149+
if (!(bits & pin_mask)) {
150+
// is now ready to read
151+
hx711_data_available();
152+
}
153+
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & pin_mask);
154+
}
155+
156+
return gpio_status & ~pin_mask;
157+
}
158+
159+
// Lua: hx711.start( mode, samples, callback )
160+
static int hx711_start( lua_State* L )
161+
{
162+
uint32_t mode = luaL_checkint( L, 1 );
163+
uint32_t samples = luaL_checkint( L, 2 );
164+
165+
if (mode > 2) {
166+
return luaL_argerror( L, 1, "Mode value out of range" );
167+
}
168+
169+
if (!samples || samples > 400) {
170+
return luaL_argerror( L, 2, "Samples value out of range (1-400)" );
171+
}
172+
173+
if (control) {
174+
return luaL_error( L, "Already running" );
175+
}
176+
177+
int buflen = 3 * samples;
178+
179+
control = (CONTROL *) luaM_malloc(L, sizeof(CONTROL) + BUFFERS * buflen);
180+
if (!control) {
181+
return luaL_error( L, "Failed to allocate memory" );
182+
}
183+
184+
int cb_ref;
185+
186+
if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) {
187+
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
188+
cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
189+
} else {
190+
luaM_free(L, control);
191+
control = NULL;
192+
return luaL_argerror( L, 3, "Not a callback function" );
193+
}
194+
195+
memset(control, 0, sizeof(*control));
196+
control->buf[0] = (char *) (control + 1);
197+
control->buflen = buflen;
198+
int i;
199+
200+
for (i = 1; i < BUFFERS; i++) {
201+
control->buf[i] = control->buf[i - 1] + buflen;
202+
}
203+
control->mode = mode;
204+
control->cb_ref = cb_ref;
205+
control->freed = BUFFERS - 1;
206+
207+
// configure data_pin as interrupt input
208+
platform_gpio_register_intr_hook(1 << pin_data_pin, hx711_interrupt);
209+
platform_gpio_mode(data_pin, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT);
210+
platform_gpio_intr_init(data_pin, GPIO_PIN_INTR_NEGEDGE);
211+
212+
213+
// Wake up chip
214+
platform_gpio_write(clk_pin, 0);
215+
216+
return 0;
217+
}
218+
219+
// Lua: hx711.stop( )
220+
static int hx711_stop( lua_State* L )
221+
{
222+
if (control) {
223+
platform_gpio_mode(data_pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_FLOAT);
224+
CONTROL *to_free = control;
225+
control = NULL;
226+
luaL_unref(L, LUA_REGISTRYINDEX, to_free->cb_ref);
227+
luaM_free(L, to_free);
228+
}
229+
230+
return 0;
231+
}
232+
233+
static int hx711_status( lua_State* L )
234+
{
235+
if (control) {
236+
lua_pushlstring(L, (char *) control, sizeof(*control));
237+
return 1;
238+
}
239+
240+
return 0;
241+
}
242+
243+
static void hx711_task(os_param_t param, uint8_t prio)
244+
{
245+
(void) prio;
246+
if (!control) {
247+
return;
248+
}
249+
250+
lua_State *L = lua_getstate();
251+
252+
if (control->cb_ref != LUA_NOREF) {
253+
lua_rawgeti(L, LUA_REGISTRYINDEX, control->cb_ref);
254+
255+
lua_pushlstring(L, control->buf[param], control->buflen);
256+
lua_pushinteger(L, control->timestamp[param]);
257+
lua_pushinteger(L, control->dropped[param]);
258+
259+
control->freed = param;
260+
261+
lua_call(L, 3, 0);
262+
}
263+
}
264+
#endif
265+
26266
#define HX711_MAX_WAIT 1000000
27267
/*will only read chA@128gain*/
28268
/*Lua: result = hx711.read()*/
29-
static int ICACHE_FLASH_ATTR hx711_read(lua_State* L) {
30-
uint32_t i;
31-
int32_t data = 0;
269+
static int hx711_read(lua_State* L) {
270+
int j;
32271
//TODO: double check init has happened first.
272+
//
33273

34-
//wakeup hx711
35-
platform_gpio_write(clk_pin,0);
274+
uint32_t mode = luaL_optinteger(L, 1, 0);
36275

37-
//wait for data ready. or time out.
38-
//TODO: set pin inturrupt and come back to it. This may take up to 1/10 sec
39-
// or maybe just make an async version too and have both available.
40-
system_soft_wdt_feed(); //clear WDT... this may take a while.
41-
for (i = 0; i<HX711_MAX_WAIT && platform_gpio_read(data_pin)==1;i++){
42-
asm ("nop");
276+
if (mode > 2) {
277+
return luaL_argerror( L, 1, "Mode value out of range" );
43278
}
44279

45-
//Handle timeout error
46-
if (i>=HX711_MAX_WAIT) {
47-
return luaL_error( L, "ADC timeout!", ( unsigned )0 );
280+
#ifdef GPIO_INTERRUPT_ENABLE
281+
if (control) {
282+
hx711_stop(L);
48283
}
284+
#endif
285+
286+
//wakeup hx711
287+
platform_gpio_write(clk_pin, 0);
288+
289+
int32_t data;
49290

50-
for (i = 0; i<24 ; i++){ //clock in the 24 bits
51-
platform_gpio_write(clk_pin,1);
52-
platform_gpio_write(clk_pin,0);
53-
data = data<<1;
54-
if (platform_gpio_read(data_pin)==1) {
55-
data = i==0 ? -1 : data|1; //signextend the first bit
291+
// read two samples if mode > 0. We discard the first read and return the
292+
// second value.
293+
for (j = (mode ? 1 : 0); j >= 0; j--) {
294+
uint32_t i;
295+
296+
//wait for data ready. or time out.
297+
system_soft_wdt_feed(); //clear WDT... this may take a while.
298+
for (i = 0; i<HX711_MAX_WAIT && platform_gpio_read(data_pin)==1;i++){
299+
asm ("nop");
300+
}
301+
302+
//Handle timeout error
303+
if (i >= HX711_MAX_WAIT) {
304+
return luaL_error( L, "ADC timeout!");
56305
}
306+
307+
data = read_sample(mode);
57308
}
58-
//add 25th clock pulse to prevent protocol error (probably not needed
59-
// since we'll go to sleep immediately after and reset on wakeup.)
60-
platform_gpio_write(clk_pin,1);
61-
platform_gpio_write(clk_pin,0);
62-
//sleep
63-
platform_gpio_write(clk_pin,1);
64-
lua_pushinteger( L, data );
309+
310+
//sleep -- unfortunately, this resets the mode to 0
311+
platform_gpio_write(clk_pin, 1);
312+
lua_pushinteger(L, data);
65313
return 1;
66314
}
67315

68316
// Module function map
69317
LROT_BEGIN(hx711)
70318
LROT_FUNCENTRY( init, hx711_init )
71319
LROT_FUNCENTRY( read, hx711_read )
320+
#ifdef GPIO_INTERRUPT_ENABLE
321+
LROT_FUNCENTRY( start, hx711_start )
322+
#ifdef HX711_STATUS
323+
LROT_FUNCENTRY( status, hx711_status )
324+
#endif
325+
LROT_FUNCENTRY( stop, hx711_stop )
326+
#endif
72327
LROT_END( hx711, NULL, 0 )
73328

74329

75330
int luaopen_hx711(lua_State *L) {
76-
// TODO: Make sure that the GPIO system is initialized
331+
#ifdef GPIO_INTERRUPT_ENABLE
332+
tasknumber = task_get_id(hx711_task);
333+
#endif
77334
return 0;
78335
}
79336

0 commit comments

Comments
 (0)