You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
9.1 KiB

#include "common.h"
#ifdef USE_LUA_ST_UART
#include "lua_base.h"
#include "bsp_uart.h"
#include "ymodem.h"
#ifdef USE_LWIP
#include "rd_udp.h" /* UDP 链路句柄 (上位机 provision 经 telnet 下发 c=UDP(...)) */
#endif
#ifdef PRINT_ID
#ifdef USE_USB
#include "bsp_usb.h"
#endif
#endif
/* 句柄类型 (主仓库 lacmd.c 同款语义) */
#define COM_TYPE_SERIAL 0
#define COM_TYPE_UDP 1
// 串口对象结构体
typedef struct
{
TComCtrl *m_ptComCtrl;
int m_iType; /* COM_TYPE_* */
int m_iOwned; /* 1=自建句柄 GC 关闭 (仅 UDP); 0=借用, 维持 robot 现状不关 */
} stm32_com_t;
TComCtrl *g_ptCom1;
TComCtrl *g_ptCom0;
int COM1_Send(char *_pBuffer, uint32_t _iSize)
{
TUartUserData *ptUartUserData = (TUartUserData *)g_ptCom1->m_pUserData;
HAL_GPIO_WritePin(RS485_2_DIR_GPIO_Port, RS485_2_DIR_Pin,GPIO_PIN_SET);
int iRet = HAL_UART_Transmit(ptUartUserData->m_uart, (uint8_t *)_pBuffer, _iSize, 100);
HAL_GPIO_WritePin(RS485_2_DIR_GPIO_Port, RS485_2_DIR_Pin,GPIO_PIN_RESET);
if (iRet != HAL_OK) return RD_FAILURE;
return (int)_iSize;
}
int COM0_Send(char *_pBuffer, uint32_t _iSize)
{
TUartUserData *ptUartUserData = (TUartUserData *)g_ptCom0->m_pUserData;
return HAL_UART_Transmit(ptUartUserData->m_uart, (uint8_t *)_pBuffer, _iSize, 100);
}
// 构造函数: st.COM(1, 115200)
static int l_com_new(lua_State *L)
{
int id = luaL_checkinteger(L, 1);
int baudrate = luaL_optinteger(L, 2, 115200); // 默认波特率9600
// 创建 userdata
stm32_com_t *u = (stm32_com_t*)lua_newuserdata(L, sizeof(stm32_com_t));
u->m_iType = COM_TYPE_SERIAL;
u->m_iOwned = 0; /* 串口句柄维持 robot 现状: 不随 GC 关闭 */
if (id == 1 || id == 2)
{
TUartUserData *ptUartUserData = UART_userdata_init(LUACOM_ID, baudrate, 4096);
if (NULL == ptUartUserData)
{
lua_print("com is already used\n");
u->m_ptComCtrl = g_ptCom1;
luaL_getmetatable(L, "stm32.COMMT");
lua_setmetatable(L, -2);
return 1;
}
g_ptCom1 = rd_ComCreate(NULL, NULL, COM1_Send, ptUartUserData->m_buf_size, ptUartUserData);
UART_IT_init(g_ptCom1);
u->m_ptComCtrl = g_ptCom1;
}
else if (id == 0)
{
#ifdef PRINT_ID
#ifdef USE_USB
g_ptCom0 = bsp_usb_init();
#endif
#else
TUartUserData *ptUartUserData = UART_userdata_init(0, baudrate, 4096);
if (NULL == ptUartUserData)
{
lua_print("com is already used\n");
}
g_ptCom0 = rd_ComCreate(NULL, NULL, COM0_Send, ptUartUserData->m_buf_size, ptUartUserData);
UART_IT_init(g_ptCom0);
#endif
u->m_ptComCtrl = g_ptCom0;
}
// 设置 Metatable
luaL_getmetatable(L, "stm32.COMMT");
lua_setmetatable(L, -2);
return 1;
}
// 方法: COM:write(str)
static int l_com_write(lua_State *L)
{
stm32_com_t *u = (stm32_com_t*)luaL_checkudata(L, 1, "stm32.COMMT");
size_t len;
const char *str = luaL_checklstring(L, 2, &len); // 支持二进制数据
rd_ComSend(u->m_ptComCtrl, (char *)str, len);
lua_pushinteger(L, len); // 返回发送的字节数
return 1;
}
// 方法: COM:read(mode)
static int l_com_read(lua_State *L)
{
int iRet = 1;
char ch = 0;
stm32_com_t *u = (stm32_com_t*)luaL_checkudata(L, 1, "stm32.COMMT");
int mode = luaL_optinteger(L, 2, 0); // 默认不使用16进制
char *buf = (char*)RD_MALLOC(CONFIG_UART_BUFFER_SIZE);
int iLen = 0;
if (!buf) return luaL_error(L, "Memory alloc failed");
while(iRet)
{
__disable_irq();
iRet = rd_ComRead(u->m_ptComCtrl, &ch, 1);
__enable_irq();
if (iRet > 0)
{
if (3 == mode)
{
lua_print("%02X ", ch);
}
else
{
lua_print("%c", ch);
}
if (iLen >= CONFIG_UART_BUFFER_SIZE) break;
buf[iLen++] = ch;
}
}
if (iLen > 0)
{
lua_pushlstring(L, buf, iLen);
}
else
{
lua_pushstring(L, "");
}
RD_FREE(buf);
return 1;
}
// 方法: COM:ry(filepath) — YMODEM 接收文件
static int l_com_ry(lua_State *L)
{
stm32_com_t *u = (stm32_com_t*)luaL_checkudata(L, 1, "stm32.COMMT");
const char *path = luaL_checkstring(L, 2);
lua_print("ry: receiving '%s' ...\n", path);
int res = rym_download_file(u->m_ptComCtrl, path);
lua_pushinteger(L, res);
return 1;
}
// 方法: COM:sy(filepath) — YMODEM 发送文件
static int l_com_sy(lua_State *L)
{
stm32_com_t *u = (stm32_com_t*)luaL_checkudata(L, 1, "stm32.COMMT");
const char *path = luaL_checkstring(L, 2);
lua_print("sy: sending '%s' ...\n", path);
int res = rym_upload_file(u->m_ptComCtrl, path);
lua_pushinteger(L, res);
return 1;
}
// 构造函数: CON() — 控制台串口句柄 (借用语义)
// MCU(USE_PRINT) 上 lua 终端自身的收发(printf/fgetc)都走 g_ptUartCtrl,
// CON() 把它包成 COMMT 句柄, 便可在控制台 REPL 直接对控制台串口 ymodem:
// c:ry('.') -- 接收上位机发来的文件到当前目录
// c:sy('f.bin') -- 把本地文件发给上位机
// 借用句柄 GC 不关闭 (m_iOwned=0), 控制台生命周期归 Myprint;
// 无控制台句柄的平台(如 Linux 桌面构建)返回 nil
static int l_con_new(lua_State *L)
{
#if defined(USE_PRINT)
extern TComCtrl *Myprint_GetCtrl(void);
TComCtrl *ptCtrl = Myprint_GetCtrl();
if (NULL == ptCtrl)
{
lua_print("CON: console port not ready\n");
lua_pushnil(L);
return 1;
}
stm32_com_t *u = (stm32_com_t *)lua_newuserdata(L, sizeof(stm32_com_t));
u->m_iType = COM_TYPE_SERIAL;
u->m_iOwned = 0;
u->m_ptComCtrl = ptCtrl;
luaL_getmetatable(L, "stm32.COMMT");
lua_setmetatable(L, -2);
return 1;
#else
lua_pushnil(L);
return 1;
#endif
}
#ifdef USE_LWIP
// __gc: 仅自建 UDP 句柄关闭 (上位机 provision 行 c=nil collectgarbage()
// 重绑旧句柄、telnet 断线重连 lua_init 重建 Lua 态时, 靠这里释放旧 UDP
// 链路/8007 端口, 否则二次 Rd_UdpCreate bind 失败);
// 串口/CON 借用句柄维持 robot 现状 (不关, 生命周期归宿主)
static int l_com_gc(lua_State *L)
{
stm32_com_t *u = (stm32_com_t*)luaL_testudata(L, 1, "stm32.COMMT");
if (u != NULL && u->m_iOwned && u->m_iType == COM_TYPE_UDP && u->m_ptComCtrl != NULL)
{
Rd_UdpClose(u->m_ptComCtrl);
u->m_ptComCtrl = NULL;
}
return 0;
}
// 构造函数: UDP(localport, "remoteip" [,remoteport [,txsrcport]]) — UDP 链路句柄
// 上位机连接后自动下发 c=UDP(8007,"<本端IP>",9007) 建 ymodem 文件链路,
// 之后 c:ry('.')/c:sy('f') 与串口句柄用法完全一致 (rym_* 按 TComCtrl 分派)
static int l_udp_new(lua_State *L)
{
if (lua_gettop(L) == 0)
{
lua_print("Usage: UDP(localport, \"remoteip\" [,remoteport [,txsrcport]])\n");
lua_print(" UDP(8007, \"192.168.1.100\", 9007) -- 固定直连: bind 8007 rx, 发往 9007\n");
return 0;
}
int localport = (int)luaL_checkinteger(L, 1);
const char *ip = luaL_checkstring(L, 2);
int remoteport = (int)luaL_optinteger(L, 3, 0);
int txsrcport = (int)luaL_optinteger(L, 4, 0);
TRdUdpCfg stUdpCfg = {
.m_psRemoteIp = ip,
.m_iRemotePort = remoteport,
.m_iLocalPort = localport,
.m_iTxSrcPort = txsrcport,
.m_uiBufSize = 4096,
.m_psName = "lua",
.m_pfCheck = NULL, /* 数据报全透传: ymodem 原始字节语义 */
.m_pfDecode = NULL,
};
TComCtrl *ptComCtrl = Rd_UdpCreate(&stUdpCfg);
if (ptComCtrl == NULL)
{
return luaL_error(L, "UDP(%d, %s, %d) create failed", localport, ip, remoteport);
}
stm32_com_t *u = (stm32_com_t*)lua_newuserdata(L, sizeof(stm32_com_t));
u->m_ptComCtrl = ptComCtrl;
u->m_iType = COM_TYPE_UDP;
u->m_iOwned = 1; // 自建句柄: GC 时关闭 (重连 provision 幂等的关键)
luaL_getmetatable(L, "stm32.COMMT");
lua_setmetatable(L, -2);
lua_print("UDP bind %d txsrc %d -> %s:%d\n", localport, txsrcport, ip, remoteport);
return 1;
}
#endif /* USE_LWIP */
// 模块注册
int luaopen_stm32_com(lua_State *L)
{
// 1. 注册 Metatable
if (luaL_newmetatable(L, "stm32.COMMT"))
{
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
const luaL_Reg uart_methods[] =
{
{"write", l_com_write},
{"read", l_com_read},
{"ry", l_com_ry},
{"sy", l_com_sy},
#ifdef USE_LWIP
{"__gc", l_com_gc},
#endif
{NULL, NULL}
};
const luaL_Reg *l;
for (l = uart_methods; l->name != NULL; l++)
{
lua_pushcfunction(L, l->func);
lua_setfield(L, -2, l->name);
}
}
lua_pop(L, 1);
// 2. 创建模块表
lua_newtable(L);
// 3. 注册构造函数
lua_pushcfunction(L, l_com_new);
lua_setfield(L, -2, "new");
lua_pushcfunction(L, l_con_new);
lua_setfield(L, -2, "console");
#ifdef USE_LWIP
lua_pushcfunction(L, l_udp_new);
lua_setfield(L, -2, "udp");
#endif
return 1;
}
#endif