diff --git a/Makefile b/Makefile index 019432e..afabae9 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,6 @@ DEFS = -DUSE_STDPERIPH_DRIVER -DSTM32F7XX -DARM_MATH_CM7 -DHSE_VALUE=8000000 DEFS += -DSTM32F722xx -DUSE_HAL_DRIVER STARTUP = $(CUBE)/CMSIS/Device/ST/STM32F7xx/Source/Templates/gcc/startup_stm32f722xx.s -# MCFLAGS = -march=armv4e-m -mthumb -# MCFLAGS = -mthumb -march=armv4e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 MCFLAGS = -mthumb -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 STM32_INCLUDES = \ @@ -49,13 +47,12 @@ OPTIMIZE = -O2 CFLAGS += -std=c99 CFLAGS += -Wall -CFLAGS += -Wno-unused-function +CFLAGS += -Wno-unused-function -Wno-unused-value CFLAGS += $(MCFLAGS) CFLAGS += $(OPTIMIZE) CFLAGS += $(DEFS) -I. -I./ $(STM32_INCLUDES) CFLAGS += -fsingle-precision-constant -Wdouble-promotion CFLAGS += -DLUA_32BITS -# CFLAGS += -DLUA_32BITS -DLUA_COMPAT_5_2 CFLAGS += -fno-common CFLAGS += -DVERSION=\"$(GIT_VERSION)\" CFLAGS += -ffunction-sections -fdata-sections # provides majority of LTO binary size reduction @@ -129,39 +126,25 @@ all: $(TARGET).hex $(BIN) # fennel script conversion to lua FNL_SRC = $(wildcard lua/*.fnl) \ -#FNL_SRC = $(wildcard util/*.fnl) \ - FNL_PP = $(FNL_SRC:%.fnl=%.lua) # i2c descriptors II_SRCD = lua/ii II_SRC = $(wildcard $(II_SRCD)/*.lua) -II_TARGET = $(addprefix $(BUILD_DIR)/ii_, $(notdir $(II_SRC))) - -$(II_TARGET): util/ii_lua_module.lua - -$(BUILD_DIR)/ii_%.lua: $(II_SRCD)/%.lua util/ii_lua_module.lua | $(BUILD_DIR) - @lua util/ii_lua_module.lua $< $@ - @echo "ii-lua-module $< -> $@" - -$(BUILD_DIR)/iihelp.lua: $(II_SRC) util/ii_lua_help.lua | $(BUILD_DIR) - @lua util/ii_lua_help.lua $(II_SRCD) $@ - @echo "ii-lua-help $@" $(BUILD_DIR)/ii_c_layer.h: $(II_SRC) util/ii_c_layer.lua | $(BUILD_DIR) @lua util/ii_c_layer.lua $(II_SRCD) $@ @echo "ii-c-layer $@" -$(BUILD_DIR)/ii_lualink.h: $(II_SRC) util/ii_lualinker.lua | $(BUILD_DIR) - @lua util/ii_lualinker.lua $(II_SRCD) $@ - @echo "ii-lualinker $@" +$(BUILD_DIR)/ii_mod_gen.h: $(II_SRC) util/ii_mod_gen.lua | $(BUILD_DIR) + @lua util/ii_mod_gen.lua $(II_SRCD) $@ + @echo "ii-mod-gen $@" ### destination sources # lua srcs: these get converted to bytecode strings wrapped in c-headers -# LUA_SRC = $(wildcard lua/*.lua) LUA_SRC += lua/asl.lua LUA_SRC += lua/asllib.lua LUA_SRC += lua/calibrate.lua @@ -177,8 +160,6 @@ LUA_SRC += lua/quote.lua LUA_SRC += lua/sequins.lua LUA_SRC += lua/timeline.lua LUA_SRC += lua/hotswap.lua -LUA_SRC += $(BUILD_DIR)/iihelp.lua -LUA_SRC += $(II_TARGET) LUA_PP = $(LUA_SRC:%.lua=%.lua.h) LUA_PP: $(LUA_SRC) @@ -200,9 +181,9 @@ OBJS += Startup.o $(OBJS): $(LUA_PP) # specific objects that require built dependencies (ii) -$(OBJDIR)/lib/l_bootstrap.o: $(LUA_PP) $(BUILD_DIR)/ii_lualink.h -# $(OBJDIR)/lib/lualink.o: $(LUA_PP) $(BUILD_DIR)/ii_lualink.h +$(OBJDIR)/lib/l_bootstrap.o: $(LUA_PP) #$(BUILD_DIR)/ii_lualink.h $(OBJDIR)/lib/ii.o: $(BUILD_DIR)/ii_c_layer.h +$(OBJDIR)/lib/l_ii_mod.o: $(BUILD_DIR)/ii_mod_gen.h # generate the build directory $(BUILD_DIR): @@ -232,7 +213,6 @@ tests: # include all DEP files in the makefile # will rebuild elements if dependent C headers are changed -# FIXME: currently causes compiler warning due to missing .lua.h files -include $(DEP) $(TARGET).hex: $(EXECUTABLE) @@ -259,7 +239,6 @@ flash: $(BIN) debug: make flash TRACE=1 - #st-flash write $(BIN) 0x08020000 stlink-trace -c 216 dfu: $(BIN) @@ -272,7 +251,6 @@ dfureset: pydfu: $(TARGET).dfu $(BIN) @python3 util/pydfu.py -u $< -# @python3 util/pydfu.py --vid 0x0483 --pid 0xDF11 -u $< $(TARGET).dfu: $(BIN) python3 util/dfu.py -D 0x0483:0xDF11 -b 0x08020000:$^ $@ diff --git a/lib/caw.c b/lib/caw.c index 42b9159..9f87d92 100644 --- a/lib/caw.c +++ b/lib/caw.c @@ -127,8 +127,11 @@ static C_cmd_t _find_cmd( char* str, uint32_t len ) return C_none; } -static uint8_t _is_multiline( char* first_char ) +static uint8_t _is_multiline( char* first_char, uint32_t len ) { + // need at least 3 chars to be a valid multiline sequence + if(len < 3){ return 0; } + // buffer must lead with the backticks if( *first_char++ == '`' ){ if( *first_char++ == '`' ){ if( *first_char == '`' ){ return 1; } @@ -161,7 +164,7 @@ C_cmd_t Caw_try_receive( void ) retcmd = C_none; // no action goto exit; } - if( _is_multiline( (char*)buf ) ){ + if( _is_multiline( (char*)buf, len ) ){ multiline ^= 1; if(!multiline){ retcmd = C_repl; @@ -176,7 +179,7 @@ C_cmd_t Caw_try_receive( void ) } } } - if( pReader + len > USB_RX_BUFFER ){ // overflow protection + if( (pReader + len) >= USB_RX_BUFFER ){ // overflow protection pReader = 0; Caw_send_luachunk("!chunk too long!"); printf("!chunk too long!\n"); diff --git a/lib/ii.c b/lib/ii.c index 204dd7c..f39b68d 100644 --- a/lib/ii.c +++ b/lib/ii.c @@ -171,7 +171,10 @@ uint8_t ii_leader_enqueue_bytes( uint8_t address void ii_leader_process( void ) { + static bool retrying = false; // flag to monitor if this is a retry + if( !I2C_is_ready() ){ return; } // I2C lib is busy + int ix = queue_front(l_qix); if( ix < 0 ){ return; } // queue is empty! ii_q_t* q = &l_iq[ix]; @@ -187,7 +190,7 @@ void ii_leader_process( void ) )) ){ if( error & 0x6 ){ error_action( 1 ); } printf("leadRx failed %i\n",error); - return; // EARLY RETURN. DOESN'T POP QUEUE. WILL RETRY + goto retry; // attempt retry, or abandon } } else { if( (error = I2C_LeadTx( q->address @@ -196,10 +199,21 @@ void ii_leader_process( void ) )) ){ if( error & 2 ){ error_action( 1 ); } printf("leadTx failed %i\n",error); - return; // EARLY RETURN. DOESN'T POP QUEUE. WILL RETRY + goto retry; // attempt retry, or abandon } } - queue_dequeue(l_qix); // pop the value as it was used + queue_dequeue(l_qix); // pop the value from queue as it was successfully used + return; + +retry: + if(retrying){ + retrying = false; + queue_dequeue(l_qix); // pop the value, abandoning this message + } else { + retrying = true; + // DON'T POP QUEUE. we'll try it next time + } + return; } @@ -318,7 +332,7 @@ static void error_action( int error_code ) { switch( error_code ){ case 0: // Ack Failed - printf("I2C_ERROR_AF\n"); // means can't find device + // printf("I2C_ERROR_AF\n"); // means can't find device // TODO make this a global variable which can be checked by user // becomes a basic way to ask "was the message received" break; diff --git a/lib/l_bootstrap.c b/lib/l_bootstrap.c index 9dee345..5563f19 100644 --- a/lib/l_bootstrap.c +++ b/lib/l_bootstrap.c @@ -1,6 +1,7 @@ #include "l_bootstrap.h" #include +#include #include "l_crowlib.h" @@ -14,14 +15,21 @@ #include "build/input.h" #include "build/output.h" #include "build/ii.h" -#include "build/iihelp.h" // generated lua stub for loading i2c modules +// #include "build/iihelp.h" // generated lua stub for loading i2c modules #include "build/calibrate.h" #include "build/sequins.h" #include "build/quote.h" #include "build/timeline.h" #include "build/hotswap.h" -#include "build/ii_lualink.h" // generated C header for linking to lua +// #include "build/ii_lualink.h" // generated C header for linking to lua + +struct lua_lib_locator{ + const char* name; + const unsigned char* addr_of_luacode; + const bool stripped; + const unsigned int len; +}; static int _open_lib( lua_State *L, const struct lua_lib_locator* lib, const char* name ); static void lua_full_gc(lua_State* L); @@ -37,7 +45,7 @@ const struct lua_lib_locator Lua_libs[] = , { "lua_output" , build_output_lc , true, build_output_lc_len} , { "lua_public" , build_public_lc , true, build_public_lc_len} , { "lua_ii" , build_ii_lc , true, build_ii_lc_len} - , { "build_iihelp" , build_iihelp_lc , true, build_iihelp_lc_len} + // , { "build_iihelp" , build_iihelp_lc , true, build_iihelp_lc_len} , { "lua_calibrate" , build_calibrate_lc , true, build_calibrate_lc_len} , { "lua_sequins" , build_sequins_lc , true, build_sequins_lc_len} , { "lua_quote" , build_quote_lc , true, build_quote_lc_len} @@ -60,6 +68,10 @@ void l_bootstrap_init(lua_State* L){ // _c = dofile('lua/crowlib.lua') lua_pushliteral(L, "lua/crowlib.lua"); l_bootstrap_dofile(L); // hotrod without l_call + lua_settop(L, 0); + + // _c = {} + lua_newtable(L); lua_setglobal(L, "_c"); // crow = _c @@ -110,11 +122,11 @@ int l_bootstrap_dofile(lua_State* L) case 1: lua_full_gc(L); return 1; default: break; } - switch( _open_lib( L, Lua_ii_libs, cname ) ){ - case -1: goto fail; - case 1: lua_full_gc(L); return 1; - default: break; - } + // switch( _open_lib( L, Lua_ii_libs, cname ) ){ + // case -1: goto fail; + // case 1: lua_full_gc(L); return 1; + // default: break; + // } printf("can't open library: %s\n", (char*)cname); fail: diff --git a/lib/l_crowlib.c b/lib/l_crowlib.c index 1e89950..60d0302 100644 --- a/lib/l_crowlib.c +++ b/lib/l_crowlib.c @@ -2,17 +2,282 @@ #include +#include "l_bootstrap.h" // l_bootstrap_dofile +#include "l_ii_mod.h" // l_ii_mod_preload +#include "../ll/random.h" // Random_Get() +#include "lib/ii.h" // ii_*() +#include "lib/ashapes.h" // AShaper_get_state +#include "lib/caw.h" // Caw_printf() +#include "lib/io.h" // IO_GetADC() + #define L_CL_MIDDLEC (261.63f) #define L_CL_MIDDLEC_INV (1.0f/L_CL_MIDDLEC) #define L_CL_JIVOLT (1.0f/logf(2.f)) +static int _ii_follow_reset( lua_State* L ); +static int _random_arity_n( lua_State* L ); +static int _tell_get_out( lua_State* L ); +static int _tell_get_cv( lua_State* L ); +static int _crow_reset( lua_State* L ); +static int _lua_void_function( lua_State* L ); +static int _delay( lua_State* L ); + +// function() end +// useful as a do-nothing callback +static int _lua_void_function( lua_State* L ){ + lua_settop(L, 0); + return 0; +} + +static void _load_lib(lua_State* L, char* filename, char* luaname){ + lua_pushfstring(L, "lua/%s.lua", filename); + l_bootstrap_dofile(L); + lua_setglobal(L, luaname); + lua_settop(L, 0); +} + // called after crowlib lua file is loaded // here we add any additional globals and such -void l_crowlib_init(lua_State* L){} +void l_crowlib_init(lua_State* L){ + + //////// load all libraries + _load_lib(L, "input", "Input"); + _load_lib(L, "output", "Output"); + _load_lib(L, "asl", "asl"); + _load_lib(L, "asllib", "asllib"); + _load_lib(L, "metro", "metro"); + + // load C funcs into lua env first + l_ii_mod_preload(L); + _load_lib(L, "ii", "ii"); + + _load_lib(L, "calibrate", "cal"); + _load_lib(L, "public", "public"); + _load_lib(L, "clock", "clock"); + _load_lib(L, "sequins", "sequins"); + _load_lib(L, "quote", "quote"); + _load_lib(L, "timeline", "timeline"); + _load_lib(L, "hotswap", "hotswap"); + + + //////// crow.reset + lua_getglobal(L, "crow"); // @1 + lua_pushcfunction(L, _crow_reset); + lua_setfield(L, 1, "reset"); + lua_settop(L, 0); + + + //////// tell + // C.tell = tell + lua_getglobal(L, "crow"); // @1 + lua_getglobal(L, "tell"); // @2 + lua_setfield(L, 1, "tell"); + lua_settop(L, 0); + + + //////// get_out & get_cv + lua_pushcfunction(L, _tell_get_out); + lua_setglobal(L, "get_out"); + lua_pushcfunction(L, _tell_get_cv); + lua_setglobal(L, "get_cv"); + lua_settop(L, 0); + + + //////// input + + // -- Input + // input = {1,2} + // for chan = 1, #input do + // input[chan] = Input.new( chan ) + // end + lua_createtable(L, 2, 0); // 4 array elements + lua_setglobal(L, "input"); // -> @0 + + lua_getglobal(L, "input"); // @1 + for(int i=1; i<=2; i++){ + lua_getglobal(L, "Input"); // @2 + lua_getfield(L, 2, "new"); // Output.new @3 + lua_pushinteger(L, i); // push the key + lua_call(L, 1, 1); // Output.new(chan) -> replace key with value -> @3 + lua_pushinteger(L, i); // push the key + lua_rotate(L, -2, 1); // swap top 2 elements + lua_settable(L, 1); // output[chan] = result + lua_settop(L, 1); // discard everything except _G.output + } + lua_settop(L, 0); + + + //////// output / asl + + // -- Output + // output = {1,2,3,4} + // for chan = 1, #output do + // output[chan] = Output.new( chan ) + // end + lua_createtable(L, 4, 0); // 4 array elements + lua_setglobal(L, "output"); // -> @0 + + lua_getglobal(L, "output"); // @1 + for(int i=1; i<=4; i++){ + lua_getglobal(L, "Output"); // @2 + lua_getfield(L, 2, "new"); // Output.new @3 + lua_pushinteger(L, i); // push the key + lua_call(L, 1, 1); // Output.new(chan) -> replace key with value -> @3 + lua_pushinteger(L, i); // push the key + lua_rotate(L, -2, 1); // swap top 2 elements + lua_settable(L, 1); // output[chan] = result + lua_settop(L, 1); // discard everything except _G.output + } + lua_settop(L, 0); + + + // LL_get_state = get_state + lua_getglobal(L, "get_state"); + lua_setglobal(L, "LL_get_state"); + lua_settop(L, 0); + + + //////// ii follower default actions + + // install the reset function + lua_pushcfunction(L, _ii_follow_reset); + lua_setglobal(L, "ii_follow_reset"); + + // call it to reset immediately + lua_getglobal(L, "ii_follow_reset"); + lua_call(L, 0, 0); + lua_settop(L, 0); + + + //////// ii.pullup(true) + ii_set_pullups(1); + + + //////// RANDOM + + // hook existing math.random into math.srandom + lua_getglobal(L, "math"); // 1 + lua_getfield(L, 1, "random"); // 2 + lua_setfield(L, 1, "srandom"); + lua_settop(L, 1); // abandon anything above _G.math + // _G.math is still at stack position 1 + lua_getfield(L, 1, "randomseed"); + lua_setfield(L, 1, "srandomseed"); + lua_settop(L, 0); + + // set math.random to the c-func for true random + lua_getglobal(L, "math"); + lua_pushcfunction(L, _random_arity_n); + lua_setfield(L, -2, "random"); + lua_settop(L, 0); + + + //////// DELAY + // creates a closure, so this is just way easier + luaL_dostring(L,"function delay(action, time, repeats)\n" + "local r = repeats or 0\n" + "return clock.run(function()\n" + "for i=1,1+r do\n" + "clock.sleep(time)\n" + "action(i)\n" + "end\n" + "end)\n" + "end\n"); + + //////// empty init + lua_pushcfunction(L, _lua_void_function); + lua_setglobal(L, "init"); +} + + +/////// static declarations + +static int _crow_reset( lua_State* L ){ + printf("crow.reset()\n\r"); + + lua_getglobal(L, "input"); // @1 + for(int i=1; i<=2; i++){ + lua_settop(L, 1); // _G.input is TOS @1 + lua_pushinteger(L, i); // @2 + lua_gettable(L, 1); // replace @2 with: input[n] + + // input[n].mode = 'none' + lua_pushstring(L, "none"); // @3 + lua_setfield(L, 2, "mode"); // pops 'none' -> @2 + + // input[n].reset_events(input[n]) -- aka void method call + lua_getfield(L, 2, "reset_events"); // @3 + lua_pushvalue(L, 2); // @4 copy of input[n] + lua_call(L, 1, 0); + } + lua_settop(L, 0); + + lua_getglobal(L, "output"); // @1 + for(int i=1; i<=4; i++){ + lua_settop(L, 1); // _G.output is TOS @1 + lua_pushinteger(L, i); // @2 + lua_gettable(L, 1); // replace @2 with: output[n] + + // output[n].slew = 0 + lua_pushnumber(L, 0.0); // @3 + lua_setfield(L, 2, "slew"); // pops 'none' -> @2 + // output[n].volts = 0 + lua_pushnumber(L, 0.0); // @3 + lua_setfield(L, 2, "volts"); // pops 'none' -> @2 + // output[n].scale('none') + lua_getfield(L, 2, "scale"); + lua_pushstring(L, "none"); + lua_call(L, 1, 0); + // output[n].done = function() end + lua_pushcfunction(L, _lua_void_function); // @3 + lua_setfield(L, 2, "done"); // pops 'none' -> @2 + // output[n]:clock('none') + lua_getfield(L, 2, "clock"); // @3 + lua_pushvalue(L, 2); // @4 copy of output[n] + lua_pushstring(L, "none"); + lua_call(L, 2, 0); + } + lua_settop(L, 0); + + // ii.reset_events(ii.self) + lua_getglobal(L, "ii"); // @1 + lua_getfield(L, 1, "reset_events"); // @2 + lua_getfield(L, 1, "self"); // @3 + lua_call(L, 1, 0); + lua_settop(L, 0); + + // ii_follow_reset() -- resets forwarding to output libs + lua_getglobal(L, "ii_follow_reset"); + lua_call(L, 0, 0); + lua_settop(L, 0); + + // metro.free_all() + lua_getglobal(L, "metro"); // @1 + lua_getfield(L, 1, "free_all"); + lua_call(L, 0, 0); + lua_settop(L, 0); + + // if public then public.clear() end + lua_getglobal(L, "public"); // @1 + if(!lua_isnil(L, 1)){ // if public is not nil + lua_getfield(L, 1, "clear"); + lua_call(L, 0, 0); + } + lua_settop(L, 0); + + // clock.cleanup() + lua_getglobal(L, "clock"); // @1 + lua_getfield(L, 1, "cleanup"); + lua_call(L, 0, 0); + lua_settop(L, 0); + + return 0; +} // Just Intonation calculators +// included in lualink.c as global lua functions static int justvolts(lua_State* L, float mul); @@ -96,3 +361,174 @@ static int justvolts(lua_State* L, float mul){ } return nresults; } + +/// true random + +static int _random_arity_n( lua_State* L ) +{ + int nargs = lua_gettop(L); + switch(nargs){ + case 0:{ + float r = Random_Float(); + lua_settop(L, 0); + lua_pushnumber(L, r); + break;} + case 1:{ + int r = Random_Int(1, luaL_checknumber(L, 1)); + lua_settop(L, 0); + lua_pushinteger(L, r); + break;} + default:{ + int r = Random_Int(luaL_checknumber(L, 1) + ,luaL_checknumber(L, 2)); + lua_settop(L, 0); + lua_pushinteger(L, r); + break;} + } + return 1; +} + +// ii follower default actions + +// function(chan,val) output[chan].volts = val end +static int _ii_self_volts( lua_State* L ){ + int chan = luaL_checknumber(L, 1); + float val = luaL_checknumber(L, 2); + lua_settop(L, 0); + lua_getglobal(L, "output"); // 1 + lua_pushnumber(L, chan); // 2 + lua_gettable(L, -2); // output[chan] onto stack @2 + lua_pushnumber(L, val); // 3 + lua_setfield(L, 2, "volts"); + lua_settop(L, 0); + return 0; +} + +// function(chan,val) output[chan].volts = val end +static int _ii_self_slew( lua_State* L ){ + int chan = luaL_checknumber(L, 1); + float slew = luaL_checknumber(L, 2); + lua_settop(L, 0); + lua_getglobal(L, "output"); // 1 + lua_pushnumber(L, chan); // 2 + lua_gettable(L, -2); // output[chan] onto stack @2 + lua_pushnumber(L, slew); // 3 + lua_setfield(L, 2, "slew"); + lua_settop(L, 0); + return 0; +} + +// function() crow.reset() end +static int _ii_self_reset( lua_State* L ){ + lua_getglobal(L, "crow"); // 1 + lua_getfield(L, 1, "reset"); + lua_call(L, 0, 0); + lua_settop(L, 0); + return 0; +} + +// function(chan,ms,volts,pol) output[chan](pulse(ms,volts,pol)) end +static int _ii_self_pulse( lua_State* L ){ + int chan = luaL_checknumber(L, 1); + float ms = luaL_checknumber(L, 2); + float volts = luaL_checknumber(L, 3); + float pol = luaL_checknumber(L, 4); + lua_settop(L, 0); + + lua_getglobal(L, "output"); // 1 + lua_pushnumber(L, chan); // 2 + lua_gettable(L, -2); // output[chan] onto stack @2 + + lua_getglobal(L, "pulse"); // 3 + lua_pushnumber(L, ms); + lua_pushnumber(L, volts); + lua_pushnumber(L, pol); + lua_call(L, 3, 1); // calls 'ramp' and leaves asl table @3 + lua_call(L, 1, 0); // calls output[chan]({asl-table}) + lua_settop(L, 0); + return 0; +} + +// function(chan,atk,rel,volts) output[chan](ar(atk,rel,volts)) end +static int _ii_self_ar( lua_State* L ){ + int chan = luaL_checknumber(L, 1); + float atk = luaL_checknumber(L, 2); + float rel = luaL_checknumber(L, 3); + float volts = luaL_checknumber(L, 4); + lua_settop(L, 0); + + lua_getglobal(L, "output"); // 1 + lua_pushnumber(L, chan); // 2 + lua_gettable(L, -2); // output[chan] onto stack @2 + + lua_getglobal(L, "ar"); // 3 + lua_pushnumber(L, atk); + lua_pushnumber(L, rel); + lua_pushnumber(L, volts); + lua_call(L, 3, 1); // calls 'ar' and leaves asl table @3 + lua_call(L, 1, 0); // calls output[chan]({asl-table}) + lua_settop(L, 0); + return 0; +} + + +// -- convert freq to seconds where freq==0 is 1Hz +// function(chan,freq,level,skew) output[chan](ramp(math.pow(2,-freq),skew,level)) end +static int _ii_self_lfo( lua_State* L ){ + int chan = luaL_checknumber(L, 1); + float freq = luaL_checknumber(L, 2); + float level = luaL_checknumber(L, 3); + float skew = luaL_checknumber(L, 4); + lua_settop(L, 0); + + lua_getglobal(L, "output"); // 1 + lua_pushnumber(L, chan); // 2 + lua_gettable(L, -2); // output[chan] onto stack @2 + + lua_getglobal(L, "ramp"); // 3 + lua_pushnumber(L, powf(2.0, -freq)); + lua_pushnumber(L, skew); + lua_pushnumber(L, level); + lua_call(L, 3, 1); // calls 'ramp' and leaves asl table @3 + lua_call(L, 1, 0); // calls output[chan]({asl-table}) + lua_settop(L, 0); + return 0; +} + +static int _ii_follow_reset( lua_State* L ){ + lua_getglobal(L, "ii"); // @1 + lua_getfield(L, 1, "self"); // @2 + + lua_pushcfunction(L, _ii_self_volts); // @3 + lua_setfield(L, 2, "volts"); + lua_pushcfunction(L, _ii_self_slew); + lua_setfield(L, 2, "slew"); + lua_pushcfunction(L, _ii_self_reset); + lua_setfield(L, 2, "reset"); + lua_pushcfunction(L, _ii_self_pulse); + lua_setfield(L, 2, "pulse"); + lua_pushcfunction(L, _ii_self_ar); + lua_setfield(L, 2, "ar"); + lua_pushcfunction(L, _ii_self_lfo); + lua_setfield(L, 2, "lfo"); + + lua_settop(L, 0); + return 0; +} + + +// C.tell( 'output', channel, get_state( channel )) +static int _tell_get_out( lua_State* L ){ + int chan = luaL_checknumber(L, -1); + Caw_printf( "^^output(%i,%f)", chan, (double)AShaper_get_state(chan-1)); + lua_settop(L, 0); + return 0; +} + +// C.tell( 'stream', channel, io_get_input( channel )) +static int _tell_get_cv( lua_State* L ){ + int chan = luaL_checknumber(L, -1); + Caw_printf( "^^stream(%i,%f)", chan, (double)IO_GetADC(chan-1)); + lua_settop(L, 0); + return 0; +} diff --git a/lib/l_ii_mod.c b/lib/l_ii_mod.c new file mode 100644 index 0000000..3f7e631 --- /dev/null +++ b/lib/l_ii_mod.c @@ -0,0 +1,288 @@ +#include "l_ii_mod.h" + +#include +#include + +// #include "l_ii_mod_gen.h" // include code-generated data structs +#include "../build/ii_mod_gen.h" // GENERATED BY BUILD PROCESS +#include "caw.h" +#include "ii.h" + +//////////////////////////////////////////////// +// global vars +// set as commands are executed +static uint8_t active_address = 0x0; +static uint8_t active_cmd = 0x0; +static ii_box_t* active_box = NULL; + + +//////////////////////////////////////////////// +// search functions +// peer into the generated structures + +static ii_box_t* find_mod_struct_by_address(uint8_t addr){ + // TODO searching manually for now + // in future i can add a static table of addresses for direct lookup + for(int i=0; iaddresses; + for(int a=0; aname, name) == 0){ + return ii_mods[i]; + } + } + return NULL; +} + +static const char* find_cmd_name(ii_box_t* box, uint8_t cmd){ + // this fn is used to create userspace callbacks (ie from getters) + // search backward as getters are 2nd half of command list + const ii_mod_cmds_t* cmds = box->commands; + for(int i=box->command_count-1; i>=0; i--){ + if(cmds[i].cmd == cmd){ + return cmds[i].name; + } + } + return NULL; +} + +static int query_to_cmd(ii_box_t* box, const char* str){ + // search backward as getters are 2nd half of command list + const ii_mod_cmds_t* cmds = box->commands; + for(int i=box->command_count-1; i>=0; i--){ + if(strcmp(cmds[i].name, str) == 0){ + return cmds[i].cmd; + } + } + return 0xFF; +} + +static int string_to_cmd(ii_box_t* box, const char* str){ + const ii_mod_cmds_t* cmds = box->commands; + for(int i=0; icommand_count; i++){ + if(strcmp(cmds[i].name, str) == 0){ + return cmds[i].cmd; + } + } + return 0xFF; +} + +static ii_box_t* find_mod_struct_from_table(lua_State* L, int self_stack_index){ + lua_getfield(L, self_stack_index, "_name"); // push the device name onto TOS + size_t len = 0; + const char* name = lua_tolstring(L, -1, &len); + // len now contains the length of the string. use for fast lookup + return find_mod_struct_by_name(name, len); +} + +static int find_addr_ix(ii_box_t* box, int addr_now){ + // returns the 1-based index into .addresses that is currently in .addr + uint8_t* addrs = box->addresses; + for(int i=0; i= II_MAX_ADDRESSES) addr_ix = 0; // protect against out of bounds lookup + + ii_box_t* box = find_mod_struct_from_table(L, 1); + + // now set the current address in thebox + int new_addr = box->addresses[addr_ix]; + if(new_addr){ // protect against 0 (ie. invalid addresses) + box->addr = new_addr; + } + + lua_settop(L, 1); // returns self which still sits at index 1 + return 1; +} + +///////////////////////////////////////////////////// +// generic fns for executing __index metamethod of the provided module + +static int __index_help( lua_State* L ){ + // NOTE: active_address must already be set + printf("i2c help %i\n", active_address); + Caw_stream_constchar( ii_list_cmds(active_address) ); + lua_settop(L, 0); + return 0; +} + +static int __index_event( lua_State* L ){ + // this is the generic event printer + + double data = luaL_checknumber(L, 2); + lua_getfield(L, 1, "name"); + const char* name = luaL_checkstring(L, -1); + lua_getfield(L, 1, "device"); + int device = luaL_checkinteger(L, -1); + lua_getfield(L, 1, "arg"); + double arg = luaL_checknumber(L, -1); + + Caw_printf( "^^ii.%s({name=[[%s]],device=%i,arg=%g},%g)" + , active_box->name + , name + , device + , arg + , data); + + lua_settop(L, 0); + return 0; +} + +static int __index_get( lua_State* L ){ + // lua: ii_lead with check on validity of cmd + const char* cmd_str = luaL_checkstring(L, 1); + int query_cmd = query_to_cmd(active_box, cmd_str); + if(query_cmd == 0xFF) + return luaL_error(L, "getter not found for '%s'", cmd_str); + // this should select a cmd struct so we can arity check + float data[4] = {0,0,0,0}; // always zero out data +// TODO lookup number of args & arity check +// then only copy that number (warn of over/underflow) + int nargs = lua_gettop(L) - 1; // reduce for 'string' + if(nargs > 4) nargs = 4; // limit to 4 ii arguments + for(int i=0; ierror on too few +// ->warn on too many + if(nargs > 4) nargs = 4; // limit to 4 ii arguments + for(int i=0; iaddr; // default to the actual set address + + // grab the command-string name + size_t len = 0; + const char* name = lua_tolstring(L, 2, &len); + + lua_settop(L, 0); // clear stack + + // search for the known strings + if(strcmp(name, "help") == 0){ + // we use the first address for help lookup + active_address = active_box->addresses[0]; + lua_pushcfunction(L, __index_help); + } else if(strcmp(name, "event") == 0){ + lua_pushcfunction(L, __index_event); + } else if(strcmp(name, "get") == 0){ + lua_pushcfunction(L, __index_get); + } else { // and now search for a command + active_cmd = string_to_cmd(active_box, name); + if(active_cmd == 0xFF){ + return luaL_error(L, "can't find: ii.%s.%s", active_box->name, name); + } + lua_pushcfunction(L, __index_command); + } + return 1; +} + +static int l_ii_cmd_from_ix( lua_State* L ){ + uint8_t addr = luaL_checkinteger(L, 1); + uint8_t cmd = luaL_checkinteger(L, 2); + lua_settop(L,0); + ii_box_t* box = find_mod_struct_by_address(addr); + if(box == NULL) + return luaL_error(L, "couldn't find address"); + lua_pushstring(L, box->name); + const char* cmd_name = find_cmd_name(box, cmd); + if(cmd_name == NULL) + return luaL_error(L, "couldn't find cmd"); + lua_pushstring(L, cmd_name); + lua_pushinteger(L, find_addr_ix(box, addr)); + return 3; +} + +static int l_ii_load_mods( lua_State* L ){ + // load each module into the ii table + // ii. = ii.newmod('') + + // TODO rather than push string, we can push the address of the table + // this optimizes away a string search in find_mod_struct_by_name() + + // @1 (TOS) is `ii` global table + for(int i=0; iname; + lua_getfield(L, 1, "newmod"); // pushes ii.newmod function onto TOS @2 + lua_pushstring(L, name); // @3 + lua_call(L, 1, 1); // @2 + lua_setfield(L, 1, name); // -> leaves only 'ii' at TOS @1 + // loop is stack neutral + } + lua_settop(L, 0); + return 0; +} + +// array of all the available functions +static const struct luaL_Reg lib_ii_mod[]= + { { "c_ii_setaddress" , l_ii_setaddress } + , { "c_ii_index" , l_ii_index } + , { "c_ii_cmd" , l_ii_cmd_from_ix } + , { "c_ii_load" , l_ii_load_mods } + , { NULL , NULL } + }; + +static void linkctolua( lua_State *L ) +{ + // Make C fns available to Lua + uint8_t fn = 0; + while( lib_ii_mod[fn].func != NULL ){ + lua_register( L, lib_ii_mod[fn].name, lib_ii_mod[fn].func ); + fn++; + } +} + +void l_ii_mod_preload(lua_State* L){ + // load C funcs into lua env + linkctolua(L); +} + diff --git a/lib/l_ii_mod.h b/lib/l_ii_mod.h new file mode 100644 index 0000000..c36c6a5 --- /dev/null +++ b/lib/l_ii_mod.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../submodules/lua/src/lua.h" // lua_State* +#include "../submodules/lua/src/lauxlib.h" +#include "../submodules/lua/src/lualib.h" + +typedef struct{ + uint8_t cmd; + const char* name; +} ii_mod_cmds_t; + +#define II_MAX_ADDRESSES 8 + +typedef struct{ + uint8_t addr; + const char* name; + uint8_t addresses[II_MAX_ADDRESSES]; // leave room for up to 8 so we have a static size + const ii_mod_cmds_t* commands; + int command_count; +} ii_box_t; + +void l_ii_mod_preload(lua_State* L); diff --git a/lib/lualink.c b/lib/lualink.c index 1909fad..3f0f960 100644 --- a/lib/lualink.c +++ b/lib/lualink.c @@ -20,7 +20,6 @@ #include "lib/metro.h" // metro_start() metro_stop() metro_set_time() #include "lib/clock.h" // clock_*() #include "lib/io.h" // IO_GetADC() -#include "../ll/random.h" // Random_Get() #include "../ll/adda.h" // CAL_*() #include "../ll/cal_ll.h" // CAL_LL_ActiveChannel() #include "../ll/system.h" // getUID_Word() @@ -513,8 +512,14 @@ static int _ii_list_commands( lua_State *L ) static int _ii_pullup( lua_State *L ) { - ii_set_pullups( luaL_checkinteger(L, 1) ); - lua_pop(L, 1); + int state = 0; + if(lua_isboolean(L, 1)){ + state = lua_toboolean(L, 1); + } else { + state = luaL_checkinteger(L, 1); + } + ii_set_pullups(!!state); // coerce to 0/1 + lua_settop(L, 0); return 0; } @@ -538,11 +543,14 @@ static int _ii_lead( lua_State *L ) static int _ii_lead_bytes( lua_State *L ) { int nargs = lua_gettop(L); - if( nargs != 3 ) return 0; + uint8_t rx_len = 0; // if no length provided, assume 0 + if(nargs < 2 || nargs > 3){ return 0; } + if(nargs == 3){ // explict length provided + rx_len = (uint8_t)luaL_checkinteger(L, 3); + } uint8_t address = luaL_checkinteger(L, 1); size_t len; uint8_t *data = (uint8_t *)luaL_checklstring(L, 2, &len); - uint8_t rx_len = (uint8_t)luaL_checkinteger(L, 3); if( ii_leader_enqueue_bytes( address , data , (uint8_t)len @@ -615,21 +623,6 @@ static int _metro_set_time( lua_State* L ) return 0; } -static int _random_float( lua_State* L ) -{ - lua_pushnumber( L, Random_Float() ); - return 1; -} - -static int _random_int( lua_State* L ) -{ - int r = Random_Int( luaL_checknumber(L, 1) - , luaL_checknumber(L, 2) ); - lua_pop(L, 2); - lua_pushinteger( L, r); - return 1; -} - static int _calibrate_source( lua_State* L ) { int chan = -1; @@ -786,8 +779,14 @@ static int _pub_view_out( lua_State* L ) // i2c debug control static int _i2c_set_timings( lua_State *L ) { - I2C_SetTimings( luaL_checkinteger(L, 1) ); - lua_pop( L, 1 ); + uint32_t state = 0; + switch(lua_type(L, 1)){ + case LUA_TSTRING: state = 2; break; // CLASSIC MODE + case LUA_TBOOLEAN: state = lua_toboolean(L, 1); break; + default: state = lua_tointeger(L, 1); break; + } + I2C_SetTimings(state); + lua_pop(L, 1); return 0; } @@ -811,9 +810,9 @@ static const struct luaL_Reg libCrow[]= , { "i2c_fastmode" , _i2c_set_timings } //, { "sys_cpu_load" , _sys_cpu } // io - , { "get_state" , _get_state } + // , { "get_state" , _get_state } , { "set_output_scale" , _set_scale } - , { "io_get_input" , _io_get_input } + // , { "io_get_input" , _io_get_input } , { "set_input_none" , _set_input_none } , { "set_input_stream" , _set_input_stream } , { "set_input_change" , _set_input_change } @@ -844,9 +843,6 @@ static const struct luaL_Reg libCrow[]= , { "metro_start" , _metro_start } , { "metro_stop" , _metro_stop } , { "metro_set_time" , _metro_set_time } - // random - , { "random_float" , _random_float } - , { "random_int" , _random_int } // calibration , { "calibrate_source" , _calibrate_source } , { "calibrate_get" , _calibrate_get } @@ -984,13 +980,16 @@ void L_queue_asl_done( int id ) }; event_post(&e); } + +// forward directly to output[e->index.i].done() void L_handle_asl_done( event_t* e ) { - lua_getglobal(L, "asl_done_handler"); + lua_getglobal(L, "output"); // @1 lua_pushinteger(L, e->index.i + 1); // 1-ix'd - if( Lua_call_usercode(L, 1, 0) != LUA_OK ){ - lua_pop( L, 1 ); - } + lua_gettable(L, 1); // @2 + lua_getfield(L, 2, "done"); + Lua_call_usercode(L, 0, 0); // lua_call with timeout hook + lua_settop(L, 0); } void L_queue_metro( int id, int state ) diff --git a/lib/lualink.h b/lib/lualink.h index 3365ff6..2eca95c 100644 --- a/lib/lualink.h +++ b/lib/lualink.h @@ -7,12 +7,6 @@ #include "../submodules/lua/src/lua.h" // lua_State* typedef void (*ErrorHandler_t)(char* error_message); -struct lua_lib_locator{ - const char* name; - const unsigned char* addr_of_luacode; - const bool stripped; - const unsigned int len; -}; extern volatile int CPU_count; // count from main.c diff --git a/lib/repl.c b/lib/repl.c index 948ce2a..e83af7e 100644 --- a/lib/repl.c +++ b/lib/repl.c @@ -211,7 +211,11 @@ static char* REPL_script_name_from_mem( char* dest, char* src, int max_len ) char* linebreak = strchr( src, '\n' ); int len = linebreak ? (int)(linebreak - src) : max_len; if( len >= max_len ){ len = max_len - 1; } +// disable this warning bc we take care to null terminate our string +// *dest is a static 64byte array, so there's always room for it +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy( dest, src, len ); +#pragma GCC diagnostic pop dest[len] = '\0'; return dest; } diff --git a/ll/i2c.c b/ll/i2c.c index acb9f65..b9994a8 100644 --- a/ll/i2c.c +++ b/ll/i2c.c @@ -33,17 +33,17 @@ typedef struct{ /////////////////////////////////////// // global vars & objects -I2C_HandleTypeDef i2c_handle; +static I2C_HandleTypeDef i2c_handle; -I2C_lead_callback_t lead_response; -I2C_follow_callback_t follow_action; -I2C_follow_callback_t follow_request; -I2C_error_callback_t error_action; +static I2C_lead_callback_t lead_response; +static I2C_follow_callback_t follow_action; +static I2C_follow_callback_t follow_request; +static I2C_error_callback_t error_action; -I2C_State_t buf; -uint8_t pullup_state = 0; +static I2C_State_t buf; +static uint8_t pullup_state = 1; -uint32_t i2c_timings = I2C_TIMING_STABLE; // default timings +static uint32_t i2c_timings = I2C_TIMING_STABLE; // default timings ////////////////////////////// @@ -88,10 +88,23 @@ void I2C_DeInit( void ) HAL_I2C_DeInit( &i2c_handle ); } +// 0: slow mode +// 1: fast mode +// 2: classic mode (use old timings from v3.1) +// anything higher than 16 is treated as a raw timing value for experimentation void I2C_SetTimings( uint32_t is_fast ) { I2C_DeInit(); - i2c_timings = is_fast ? I2C_TIMING_SPEED : I2C_TIMING_STABLE; // override timings + // update to custom speed, else fallback to stable timing + if(is_fast > 0xF){ + printf("setting i2c timing to 0x%x\n\r", (unsigned int)is_fast); + i2c_timings = is_fast; + } else { + switch(is_fast){ case 1: i2c_timings = I2C_TIMING_SPEED; break; + case 2: i2c_timings = I2C_TIMING_CLASSIC; break; + default: i2c_timings = I2C_TIMING_STABLE; break; } + } + // re-init the i2c driver in order to activate new timings. if( lead_response != NULL ){ I2C_Init( I2C_GetAddress() , lead_response @@ -136,7 +149,7 @@ void HAL_I2C_MspInit( I2C_HandleTypeDef* h ) gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN; gpio.Mode = GPIO_MODE_AF_OD; - gpio.Pull = GPIO_NOPULL; + gpio.Pull = (pullup_state) ? GPIO_PULLUP : GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_HIGH; gpio.Alternate = I2Cx_SCL_SDA_AF; HAL_GPIO_Init( I2Cx_SCL_GPIO_PORT, &gpio ); @@ -174,7 +187,7 @@ void I2C_SetPullups( uint8_t state ) gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN; gpio.Mode = GPIO_MODE_AF_OD; - gpio.Pull = (state) ? GPIO_PULLUP : GPIO_NOPULL; + gpio.Pull = (pullup_state) ? GPIO_PULLUP : GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_HIGH; gpio.Alternate = I2Cx_SCL_SDA_AF; @@ -443,14 +456,14 @@ void I2Cx_ER_IRQHandler( void ) // I2C_ITError void HAL_I2C_ErrorCallback( I2C_HandleTypeDef* h ) { - printf("I2C_ERROR_"); - switch( h->ErrorCode ){ - case HAL_I2C_ERROR_BERR: printf("BERR\n"); break; - case HAL_I2C_ERROR_OVR: printf("OVR\n"); break; - case HAL_I2C_ERROR_ARLO: printf("ARLO\n"); break; - case HAL_I2C_ERROR_AF: printf("AF\n"); break; - default: printf("unknown!\n"); break; - } + // printf("I2C_ERROR_"); + // switch( h->ErrorCode ){ + // case HAL_I2C_ERROR_BERR: printf("BERR\n"); break; + // case HAL_I2C_ERROR_OVR: printf("OVR\n"); break; + // case HAL_I2C_ERROR_ARLO: printf("ARLO\n"); break; + // case HAL_I2C_ERROR_AF: printf("AF\n"); break; + // default: printf("unknown!\n"); break; + // } if( h->ErrorCode == HAL_I2C_ERROR_AF ){ (*error_action)( 0 ); } else { diff --git a/ll/i2c.h b/ll/i2c.h index d890421..edceefd 100644 --- a/ll/i2c.h +++ b/ll/i2c.h @@ -10,8 +10,14 @@ // these are from reference manual (p1232) but they do not give correct values // I2C_TIMINGR register -#define I2C_TIMING_STABLE 0xD0421C0C // runs at 55kHz -#define I2C_TIMING_SPEED 0xA0420B07 // runs at 320kHz +// original v4.0.1 timings +// #define I2C_TIMING_STABLE 0xD0421C0C // runs at 55kHz +// #define I2C_TIMING_SPEED 0xA0420B07 // runs at 320kHz +// new v4.0.2 after much research of i2c & setting up a high-cap setup +#define I2C_TIMING_STABLE 0xA0411626 // 97~148kHz depending on bus capacitance +#define I2C_TIMING_SPEED 0x60440B13 // 165~350kHz depending on bus capacitance +#define I2C_TIMING_CLASSIC 0xB042080B // old timings for regression testing + // 157~320kHz for classic mode. #define I2Cx I2C1 #define I2Cx_CLK_ENABLE() __HAL_RCC_I2C1_CLK_ENABLE() diff --git a/ll/system.c b/ll/system.c index b02d1ea..fb1c621 100644 --- a/ll/system.c +++ b/ll/system.c @@ -83,7 +83,9 @@ static void MPU_Config(void) // Configure the MPU attributes as WT for SRAM mpu.Enable = MPU_REGION_ENABLE; - mpu.BaseAddress = 0x20020000; + // mpu.Enable = MPU_REGION_DISABLE; + // mpu.BaseAddress = 0x20020000; + mpu.BaseAddress = 0x20000000; mpu.Size = MPU_REGION_SIZE_256KB; mpu.AccessPermission = MPU_REGION_FULL_ACCESS; mpu.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; diff --git a/lua/crowlib.lua b/lua/crowlib.lua index b206bb9..078d7f4 100644 --- a/lua/crowlib.lua +++ b/lua/crowlib.lua @@ -5,196 +5,8 @@ print = function(...) local printResult = args[1] if arg_len > 1 then for i=2,arg_len do - printResult = printResult .. '\t' .. tostring(args[i]) + printResult = string.format('%s\t%s', printResult, tostring(args[i])) end end print_serial(tostring(printResult)) end - - ---- Crow standard library - -local C = {} - ---- Load all libraries -Input = dofile('lua/input.lua') -Output = dofile('lua/output.lua') -asl = dofile('lua/asl.lua') -asllib = dofile('lua/asllib.lua') -metro = dofile('lua/metro.lua') -ii = dofile('lua/ii.lua') -cal = dofile('lua/calibrate.lua') -public = dofile('lua/public.lua') -clock = dofile('lua/clock.lua') -sequins= dofile('lua/sequins.lua') -quote = dofile('lua/quote.lua') -timeline = dofile('lua/timeline.lua') -hotswap= dofile('lua/hotswap.lua') - - -function C.reset() - for n=1,2 do - input[n].mode = 'none' - input[n]:reset_events() - end - for n=1,4 do - output[n].slew = 0 - output[n].volts = 0 - output[n].scale('none') - output[n].done = function() end - output[n]:clock('none') - end - ii.reset_events(ii.self) - ii_follow_reset() -- resets forwarding to output libs - metro.free_all() - public.clear() - clock.cleanup() -end - ---- Communication functions --- these will be called from norns (or the REPL) --- they return values wrapped in strings that can be used in Lua directly --- via dostring - ---TODO tell should be in c-fns table, not C table? -function C.tell( event_name, ... ) - tell( event_name, ... ) -end - -function get_out( channel ) - C.tell( 'output', channel, get_state( channel )) -end -function get_cv( channel ) - C.tell( 'stream', channel, io_get_input( channel )) -end - - - ---- Input -input = {1,2} -for chan = 1, #input do - input[chan] = Input.new( chan ) -end - ---- Output -output = {1,2,3,4} -for chan = 1, #output do - output[chan] = Output.new( chan ) -end - - ---- asl -asl_done_handler = function(id) - output[id].done() -end - -function LL_get_state( id ) - return get_state(id) -end - - ---- ii --- pullups on by default -ii.pullup(true) - - ---- follower default actions -function ii_follow_reset() - ii.self.volts = function(chan,val) output[chan].volts = val end - ii.self.slew = function(chan,slew) output[chan].slew = slew end - ii.self.reset = function() C.reset() end - ii.self.pulse = function(chan,ms,volts,pol) output[chan](pulse(ms,volts,pol)) end - ii.self.ar = function(chan,atk,rel,volts) output[chan](ar(atk,rel,volts)) end - -- convert freq to seconds where freq==0 is 1Hz - ii.self.lfo = function(chan,freq,level,skew) output[chan](ramp(math.pow(2,-freq),skew,level)) end -end -ii_follow_reset() - - ---- Pseudo RNG --- use s prefix for seeded random -math.srandom = math.random -math.srandomseed = math.randomseed - - ---- True Random Number Generator --- redefine library function to use stm native rng -math.random = function(a,b) - if a == nil then return random_float() - elseif b == nil then return random_int(1,a) - else return random_int(a,b) - end -end - - ---- Syntax extensions -function closure_if_table( f ) - local _f = f - return function( ... ) - if ... == nil then - return _f() - elseif type( ... ) == 'table' then - local args = ... - debug_usart('table') - return function() return _f( table.unpack(args) ) end - else return _f( ... ) end - end -end --- these functions are overloaded with the table->closure functionality -wrapped_fns = { 'math.random' - , 'math.min' - , 'math.max' - } --- this hack is required to change the identifier (eg math.random) itself(?) -for _,fn in ipairs( wrapped_fns ) do - load( string.format('%s=closure_if_table(%s)',fn,fn))() - -- below is original version that didn't work. nb: wrapped_fns was fns not strs - -- fn = closure_if_table( fn ) -- this *doesn't* redirect the identifier -end - - ---- Delay execution of a function -function delay(action, time, repeats) - local r = repeats or 0 - return clock.run(function() - for i=1,1+r do - clock.sleep(time) - action(i) - end - end) -end - --- --- Just Intonation helpers --- -- convert a single fraction, or table of fractions to just intonation --- -- optional 'offset' is itself a just ratio --- -- justvolts converts to volts-per-octave --- -- just12 converts to 12TET representation (for *.scale libs) --- -- just12 will convert a fraction or table of fractions into 12tet 'semitones' --- function _justint(fn, f, off) --- off = off and fn(off) or 0 -- optional offset is a just ratio --- if type(f) == 'table' then --- local t = {} --- for k,v in ipairs(f) do --- t[k] = fn(v) + off --- end --- return t --- else -- assume number --- return fn(f) + off --- end --- end --- JIVOLT = 1 / math.log(2) --- JI12TET = 12 * JIVOLT --- function _jiv(f) return math.log(f) * JIVOLT end --- function _ji12(f) return math.log(f) * JI12TET end --- -- public functions --- function justvolts(f, off) return _justint(_jiv, f, off) end --- function just12(f, off) return _justint(_ji12, f, off) end --- function hztovolts(hz, ref) --- ref = ref or 261.63 -- optional. defaults to middle-C --- return justvolts(hz/ref) --- end - --- empty init function in case userscript doesn't define it -function init() end - -return C diff --git a/lua/ii.lua b/lua/ii.lua index deec1e0..19e4b0c 100644 --- a/lua/ii.lua +++ b/lua/ii.lua @@ -3,50 +3,62 @@ local ii = {} -ii.is = dofile('build/iihelp.lua') +---------------------------- +-- per-module setup +ii.new_mt = { + -- note: .event is handled in C unless defined in lua + -- if __index returns nil, then it calls C handler w self + -- no __newindex protection as we want it freely writable + __index = function(self,ix) + -- handle ii.mod[id] syntax for duplicate devices + if type(ix)=='number' then + return c_ii_setaddress(self, ix) + end + -- look up all other funcs + return c_ii_index(self, ix) + end +} -function ii.help() - ii_list_modules() +function ii.newmod(name) + -- TODO don't save string name, but pointer to C struct + -- speeds up ii_field_lookup + return setmetatable({_name = name}, ii.new_mt) end -function ii.m_help( address ) - ii_list_commands( address ) -end +-- loads all ii devices (generated from ii descriptors) +-- uses ii.newmod and ii.new_mt +-- eg: ii.jf, ii.ansible +c_ii_load(ii) -function ii.pullup( state ) - if state == true then state = 1 else state = 0 end - ii_pullup(state) -end - -function ii.fastmode( state ) - if state == true then state = 1 else state = 0 end - i2c_fastmode(state) -end +---------------------------- +-- basic ii functionality --- aliases to C functions +-- implemented in lualink.c +ii.help = ii_list_modules +ii.m_help = ii_list_commands +ii.pullup = ii_pullup +ii.fastmode = i2c_fastmode ii.set_address = ii_set_add ii.get_address = ii_get_add -ii.set = ii_lead +ii.raw = ii_lead_bytes -function ii.raw( address, bytes, rx_len ) - ii_lead_bytes( address, bytes, rx_len or 0 ) -end +-- to be deprecated +ii.set = ii_lead +-- to be deprecated function ii.get( address, cmd, ... ) if not cmd then print'param not found' else ii_lead( address, cmd, ... ) end end function ii_LeadRx_handler( addr, cmd, _arg, data ) - if ii.event_raw(addr, cmd, data, arg) then return end - local name, ix = ii.is.lookup(addr) - if name ~= nil then - local rx_event = { name = ii[name].e[cmd] - , device = ix or 1 - , arg = _arg - } - ii[name].event(rx_event, data) - end + if ii.event_raw(addr, cmd, data, _arg) then return end + local dev, name, addrix = c_ii_cmd(addr,cmd) + local rx_event = { name = name + , device = addrix + , arg = _arg + } + ii[dev].event(rx_event, data) end function ii.e( name, event, ... ) @@ -75,15 +87,21 @@ ii.self = , [8+128]='query3' } } + function ii.reset_events() - ii.self.call1 = function(a) ii.self.call{a} end - ii.self.call2 = function(a,a2) ii.self.call{a,a2} end - ii.self.call3 = function(a,a2,a3) ii.self.call{a,a2,a3} end - ii.self.call4 = function(a,a2,a3,a4) ii.self.call{a,a2,a3,a4} end - ii.self.query0 = function() return ii.self.query{} end - ii.self.query1 = function(a) return ii.self.query{a} end - ii.self.query2 = function(a,a2) return ii.self.query{a,a2} end - ii.self.query3 = function(a,a2,a3) return ii.self.query{a,a2,a3} end + -- the 8 variable-arity fns funnel through these shared fns + -- double-indirection seems unnecessary. not sure why? + ii.self._call = function(...) ii.self.call{...} end + ii.self._query = function(...) return ii.self.query{...} end + + ii.self.call1 = ii.self._call + ii.self.call2 = ii.self._call + ii.self.call3 = ii.self._call + ii.self.call4 = ii.self._call + ii.self.query0 = ii.self._query + ii.self.query1 = ii.self._query + ii.self.query2 = ii.self._query + ii.self.query3 = ii.self._query -- all the individual call/queries forward to these 2 user fns ii.self.call = function(args) print('call'..#args) end @@ -106,13 +124,11 @@ end --- METAMETHODS ii.__index = function( self, ix ) if ix == 'address' then return ii.get_address() end - local e = rawget(ii.is, ix) -- avoids openlib() in case of .help - if e ~= nil then return e - else print'not found. try ii.help()' end + print'not found. try ii.help()' end ii.__newindex = function( self, ix, v ) if ix == 'address' then ii.set_address(v) - else rawset(self, ix, v) end + else rawset(self, ix, v) end -- FIXME prob don't want this? end setmetatable(ii, ii) diff --git a/usbd/usbd_cdc_interface.c b/usbd/usbd_cdc_interface.c index 0ad52d9..f937ca7 100755 --- a/usbd/usbd_cdc_interface.c +++ b/usbd/usbd_cdc_interface.c @@ -229,14 +229,15 @@ static void USB_Timer_Callback( int count ) // on interrupt add to the queue static int8_t CDC_Itf_Receive( uint8_t* buf, uint32_t *len ) { - if( (UserRxDataLen + *len) >= APP_RX_DATA_SIZE ){ - *len = APP_RX_DATA_SIZE - UserRxDataLen; // stop buffer overflow + uint32_t length = *len; + if( (UserRxDataLen + length) >= APP_RX_DATA_SIZE ){ + length = APP_RX_DATA_SIZE - UserRxDataLen; // stop buffer overflow } memcpy( &UserRxBuffer[UserRxDataLen] , buf - , *len + , length ); - UserRxDataLen += *len; + UserRxDataLen += length; return USBD_OK; } diff --git a/util/ii_mod_gen.lua b/util/ii_mod_gen.lua new file mode 100644 index 0000000..8a0a2ab --- /dev/null +++ b/util/ii_mod_gen.lua @@ -0,0 +1,149 @@ +get_offset = 0x80 + +header = [[ +// THIS FILE IS AUTOGENERATED // +// DO NOT EDIT THIS MANUALLY // + +#pragma once +#include "../lib/l_ii_mod.h" + +]] + +--- comment to find modules +-- %s[1]: name of module +box_header = '//////////////////////////////\n// %s\n' + +--- struct of ii commands per module +-- %s[1]: name of module +-- %s[2]: comma-separated table of cmd,name pairs +command_list_fs = 'static const ii_mod_cmds_t ii_mod_%s_cmds[] = {\n%s\n};\n' +-- %i[1]: uint8_t of command +-- %s[2]: string name of command +command_fs = ' {.cmd = %i, .name = "%s"}' + +--- struct boxing a complete module +-- %s[1]: name of module +-- %i[2]: default address of module +-- %s[3]: name of module +-- %s[4]: comma-separated list of possible addresses +-- %s[5]: name of module +-- %i[6]: number of elements in cmd list +box_fs = 'static ii_box_t ii_box_%s = {\n' + .. ' .addr = %i,\n' + .. ' .name = "%s",\n' + .. ' .addresses = {%s},\n' + .. ' .commands = ii_mod_%s_cmds,\n' + .. ' .command_count = %i\n' + .. '};\n' + +function insert_commands(cmds_table, commands) + if(commands) then + for _,t in pairs(commands) do + table.insert(cmds_table, string.format(command_fs, t.cmd, t.name)) + end + end + return cmds_table +end +function insert_autogetters(cmds_table, commands) + if(commands) then + for _,t in pairs(commands) do + if t.get then + table.insert(cmds_table, string.format(command_fs, t.cmd + get_offset, t.name)) + end + end + end + return cmds_table +end + +function addresses_table(addrs) + if type(addrs) == 'number' then + addrs = {addrs} + end + return addrs[1], table.concat(addrs,',') +end + +function make_module(f) + local modheader = string.format(box_header, f.module_name) + + local cmds = {} + cmds = insert_commands({}, f.commands) + cmds = insert_autogetters(cmds, f.commands) + cmds = insert_commands(cmds, f.getters) + local cmdlist = string.format(command_list_fs, f.lua_name, table.concat(cmds, ',\n')) + + local def_addr, addrs = addresses_table(f.i2c_address) + local box = string.format(box_fs, f.lua_name, def_addr, f.lua_name, addrs, f.lua_name, #cmds) + + return modheader + .. cmdlist + .. box +end + +function make_all_modules(files) + -- just one file for now + local mod_t = {} + for _,f in ipairs(files) do + table.insert(mod_t, make_module(f)) + end + return table.concat(mod_t, '\n') +end + +------------------------------------------------------- +--- container for all modules for iterating across all + +-- comment to find modules +lookup_header = '//////////////////////////////\n// list of all module structs\n' + +-- %s[1]: comma-separated list of &ii_box refs +lookup_fs = 'static ii_box_t* ii_mods[] = {\n%s\n};\n' + +-- %i[1]: number of modules +lookup_len_fs = 'static int ii_mod_count = %i;\n' + +function module_lookup(files) + -- just one file for now + local mod_t = {} + for _,f in ipairs(files) do + table.insert(mod_t, string.format(' &ii_box_%s', f.lua_name)) + end + str = table.concat(mod_t, ',\n') + return lookup_header + .. string.format(lookup_fs, str) + .. string.format(lookup_len_fs, #files) +end + +function make_c(files) + return header + .. make_all_modules(files) + .. '\n' + .. module_lookup(files) +end + + +------------------------------------------------------- +--- process input directory & generate file +-- if no destination, then prints to console +-- writes to destination file otherwise + + +local in_file_dir = arg[1] +local out_file = arg[2] + +do + local dir = io.popen('/bin/ls ' .. in_file_dir) + local files = {} + for filename in dir:lines() do + table.insert(files, dofile('lua/ii/' .. filename)) + end + + if arg[2] then + local c = io.open( out_file, 'w' ) + c:write(make_c(files)) + c:close() + else -- debug mode: print to console + print(make_c(files)) + end +end + +-- example usage: +-- lua util/ii_mod_gen.lua lua/ii/ build/ii_mod_gen.h