Skip to content

Commit 4fc2b85

Browse files
authored
Streaming support for hx711 (#2915)
1 parent b179f30 commit 4fc2b85

File tree

2 files changed

+346
-32
lines changed

2 files changed

+346
-32
lines changed

app/modules/hx711.c

Lines changed: 288 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,77 +3,335 @@
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>
10+
#include "task/task.h"
911
#include "user_interface.h"
1012
static uint8_t data_pin;
1113
static uint8_t clk_pin;
14+
// The fields below are after the pin_num conversion
15+
static uint8_t pin_data_pin;
16+
static uint8_t pin_clk_pin;
17+
18+
#ifdef GPIO_INTERRUPT_ENABLE
19+
static task_handle_t tasknumber;
20+
21+
// HX711_STATUS can be defined to enable the hx711.status() function to get debug info
22+
#undef HX711_STATUS
23+
#define BUFFERS 2
24+
25+
typedef struct {
26+
char *buf[BUFFERS];
27+
uint32_t dropped[BUFFERS];
28+
uint32_t timestamp[BUFFERS];
29+
uint32_t interrupts;
30+
uint32_t hx711_interrupts;
31+
uint16_t buflen;
32+
uint16_t used;
33+
uint32_t nobuffer;
34+
uint8_t active; // slot of the active buffer
35+
uint8_t freed; // slot of the most recently freed buffer
36+
uint8_t mode;
37+
uint8_t dropping; // is non zero when there is no space
38+
int cb_ref;
39+
} CONTROL;
40+
41+
static CONTROL *control;
42+
#endif
1243

1344
/*Lua: hx711.init(clk_pin,data_pin)*/
1445
static int hx711_init(lua_State* L) {
15-
clk_pin = luaL_checkinteger(L,1);
16-
data_pin = luaL_checkinteger(L,2);
46+
clk_pin = luaL_checkint(L,1);
47+
data_pin = luaL_checkint(L,2);
1748
MOD_CHECK_ID( gpio, clk_pin );
1849
MOD_CHECK_ID( gpio, data_pin );
1950

2051
platform_gpio_mode(clk_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
2152
platform_gpio_mode(data_pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_FLOAT);
2253
platform_gpio_write(clk_pin,1);//put chip to sleep.
54+
55+
pin_data_pin = pin_num[data_pin];
56+
pin_clk_pin = pin_num[clk_pin];
2357
return 0;
2458
}
2559

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

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

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");
277+
if (mode > 2) {
278+
return luaL_argerror( L, 1, "Mode value out of range" );
43279
}
44280

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

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
292+
// read two samples if mode > 0. We discard the first read and return the
293+
// second value.
294+
for (j = (mode ? 1 : 0); j >= 0; j--) {
295+
uint32_t i;
296+
297+
//wait for data ready. or time out.
298+
system_soft_wdt_feed(); //clear WDT... this may take a while.
299+
for (i = 0; i<HX711_MAX_WAIT && platform_gpio_read(data_pin)==1;i++){
300+
asm ("nop");
301+
}
302+
303+
//Handle timeout error
304+
if (i >= HX711_MAX_WAIT) {
305+
return luaL_error( L, "ADC timeout!");
56306
}
307+
308+
data = read_sample(mode);
57309
}
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 );
310+
311+
//sleep -- unfortunately, this resets the mode to 0
312+
platform_gpio_write(clk_pin, 1);
313+
lua_pushinteger(L, data);
65314
return 1;
66315
}
67316

68317
// Module function map
69318
LROT_BEGIN(hx711)
70319
LROT_FUNCENTRY( init, hx711_init )
71320
LROT_FUNCENTRY( read, hx711_read )
321+
#ifdef GPIO_INTERRUPT_ENABLE
322+
LROT_FUNCENTRY( start, hx711_start )
323+
#ifdef HX711_STATUS
324+
LROT_FUNCENTRY( status, hx711_status )
325+
#endif
326+
LROT_FUNCENTRY( stop, hx711_stop )
327+
#endif
72328
LROT_END( hx711, NULL, 0 )
73329

74330

75331
int luaopen_hx711(lua_State *L) {
76-
// TODO: Make sure that the GPIO system is initialized
332+
#ifdef GPIO_INTERRUPT_ENABLE
333+
tasknumber = task_get_id(hx711_task);
334+
#endif
77335
return 0;
78336
}
79337

0 commit comments

Comments
 (0)