|
3 | 3 |
|
4 | 4 | #include "module.h"
|
5 | 5 | #include "lauxlib.h"
|
| 6 | +#include "lmem.h" |
6 | 7 | #include "platform.h"
|
7 | 8 | #include <stdlib.h>
|
8 | 9 | #include <string.h>
|
9 | 10 | #include "user_interface.h"
|
10 | 11 | static uint8_t data_pin;
|
11 | 12 | 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 |
12 | 42 |
|
13 | 43 | /*Lua: hx711.init(clk_pin,data_pin)*/
|
14 | 44 | 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); |
17 | 47 | MOD_CHECK_ID( gpio, clk_pin );
|
18 | 48 | MOD_CHECK_ID( gpio, data_pin );
|
19 | 49 |
|
20 | 50 | platform_gpio_mode(clk_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
|
21 | 51 | platform_gpio_mode(data_pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_FLOAT);
|
22 | 52 | 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]; |
23 | 56 | return 0;
|
24 | 57 | }
|
25 | 58 |
|
| 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 | + |
26 | 266 | #define HX711_MAX_WAIT 1000000
|
27 | 267 | /*will only read chA@128gain*/
|
28 | 268 | /*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; |
32 | 271 | //TODO: double check init has happened first.
|
| 272 | + // |
33 | 273 |
|
34 |
| - //wakeup hx711 |
35 |
| - platform_gpio_write(clk_pin,0); |
| 274 | + uint32_t mode = luaL_optinteger(L, 1, 0); |
36 | 275 |
|
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" ); |
43 | 278 | }
|
44 | 279 |
|
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); |
48 | 283 | }
|
| 284 | +#endif |
| 285 | + |
| 286 | + //wakeup hx711 |
| 287 | + platform_gpio_write(clk_pin, 0); |
| 288 | + |
| 289 | + int32_t data; |
49 | 290 |
|
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!"); |
56 | 305 | }
|
| 306 | + |
| 307 | + data = read_sample(mode); |
57 | 308 | }
|
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); |
65 | 313 | return 1;
|
66 | 314 | }
|
67 | 315 |
|
68 | 316 | // Module function map
|
69 | 317 | LROT_BEGIN(hx711)
|
70 | 318 | LROT_FUNCENTRY( init, hx711_init )
|
71 | 319 | 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 |
72 | 327 | LROT_END( hx711, NULL, 0 )
|
73 | 328 |
|
74 | 329 |
|
75 | 330 | 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 |
77 | 334 | return 0;
|
78 | 335 | }
|
79 | 336 |
|
|
0 commit comments