The example code below may be compiled using the SDK v1.0.9 plus the Arduino extension as documented at e.g. Introduction | Milk-V. I’ve not got loading working over a serial port, but using scp works well: if the file is renamed to arduino.elf it is automatically picked up at the next reboot.
#include "mailbox.h" #define HOST_SERIAL Serial0 // These are from linux_5.10/drivers/soc/cvitek, https://milkv.io/docs/duo/getting-started/rtoscore etc. enum SYS_CMD_ID { CMD_TEST_A = 0x10, CMD_TEST_B, CMD_TEST_C, CMD_DUO_LED, SYS_CMD_INFO_LIMIT, }; enum DUO_LED_STATUS { DUO_LED_ON = 0x02, DUO_LED_OFF, DUO_LED_DONE, }; struct valid_t { unsigned char linux_valid; unsigned char rtos_valid; } __attribute__((packed)); typedef union resv_t { struct valid_t valid; unsigned short mstime; // 0 : noblock, -1 : block infinite } resv_t; typedef struct cmdqu_t cmdqu_t; /* cmdqu size should be 8 bytes because of mailbox buffer size */ struct cmdqu_t { unsigned char ip_id; unsigned char cmd_id : 7; unsigned char block : 1; union resv_t resv; unsigned int param_ptr; } __attribute__((packed)) __attribute__((aligned(0x8))); struct cmdqu_t cmd = {0}; bool dequeued = false; void print_hex2(byte x) { if (x < 0x10) HOST_SERIAL.print("0"); HOST_SERIAL.print(x, HEX); } // Function prototypes are from .arduino15/packages/sophgo/hardware/SG200X/0.2.3/cores/sg200x/include void receive_callback(MailboxMsg msg) { memcpy(&cmd, msg.data, sizeof(cmd)); *(msg.data) = 0; // Messages need to be cleared manually after receiving them dequeued = true; } void setup() { HOST_SERIAL.begin(115200); // Note: some boards do not support 9600 Baud etc. while (!HOST_SERIAL) ; // Wait for serial port to connect. Needed for Native USB only pinMode(LED_BUILTIN, OUTPUT); mailbox_init(false); mailbox_register(0, receive_callback); mailbox_enable_receive(0); } void loop() { HOST_SERIAL.println("Starting mailbox_target_demo..."); while (1) if (dequeued) { // A message was received by the ISR and copied into cmd. HOST_SERIAL.print("ip_id: 0x"); print_hex2(cmd.ip_id); HOST_SERIAL.println(); HOST_SERIAL.print("cmd_id: 0x"); print_hex2(cmd.cmd_id); HOST_SERIAL.println(); HOST_SERIAL.print("block: 0x"); print_hex2(cmd.block); HOST_SERIAL.println(); HOST_SERIAL.print("param: "); HOST_SERIAL.println(cmd.param_ptr); switch (cmd.cmd_id) { case CMD_DUO_LED: if (cmd.param_ptr == DUO_LED_ON) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); default: /* Code below isn't right. A response is needed here to prevent the sender * (i.e. master, running on the Linux core) objecting about a timeout after the * first message has been sent using RTOS_CMDQU_SEND_WAIT. I don't know whether * this code is actually wrong, or if it merely needs to be put "nearer" the * ISR i.e. in the callback... and in that case whether it should be before or * after the pointer is zeroed. * cmdqu_t rsp = {0}; MailboxMsg msg = {0}; memcpy(&rsp, &cmd, sizeof(rsp)); cmd.param_ptr = DUO_LED_DONE; msg.data = (uint32_t *)&cmd; mailbox_write(msg); */ ; } dequeued = false; } else delay(1); }
I believe it’s good enough to act as a foundation for other projects, for example the “small” processor looks very useful for applying PID to servo PWM control signals. As noted near the end however there’s a rough edge: I’ve not found how to send a response to the initial command so that there isn’t a timeout error. This isn’t a show-stopper, but if anybody else can make sense of the limited documentation and examples for this facility I’d be interested.
MarkMLl