This commit is contained in:
Your Name
2026-07-27 15:09:32 +03:00
commit fa89c89fbe
21 changed files with 1043 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
#include "command_handler.h"
#include "led.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(command_handler, LOG_LEVEL_INF);
int command_handler(struct command_message_t *msg) {
if (msg == NULL) {
LOG_ERR("Received NULL message pointer");
return -EINVAL;
}
LOG_DBG("Processing command: %d, length: %d", msg->command, msg->length);
switch (msg->command) {
case COMMAND_ERROR: {
LOG_WRN("Received COMMAND_ERROR");
break;
}
case LED: {
// Toggle LED with the LED command
led_toggle();
break;
}
default: {
LOG_WRN("Unknown command received: %d", msg->command);
return -EINVAL;
}
}
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef COMMAND_HANDLER_H
#define COMMAND_HANDLER_H
#include "command_message.h"
/**
* @brief Process received command message
*
* @param msg Command message to process
* @return 0 on success, negative errno on failure
*/
int command_handler(struct command_message_t *msg);
#endif // COMMAND_HANDLER_H
+73
View File
@@ -0,0 +1,73 @@
#include "command_message.h"
#include <string.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(command_message, LOG_LEVEL_INF);
void command_message_init(struct command_message_t *msg) {
memset(msg, 0, sizeof(struct command_message_t));
msg->prefix = COMMAND_PREFIX;
msg->id = COMMAND_ID;
}
void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[160]) {
// Ensure length doesn't exceed available space
if (length > sizeof(msg->data) - 1) {
return;
}
command_message_init(msg);
msg->length = length;
msg->command = command;
// Copy the data
if (data != NULL) {
for (int i = 0; i < msg->length; i++) {
msg->data[i] = data[i];
}
}
msg->crc = command_calculate_crc(msg);
}
uint8_t command_calculate_crc(struct command_message_t *msg) {
uint32_t sum = 0;
uint8_t crc = 0;
uint8_t *byte_ptr = (uint8_t *)msg;
for (int i = 0; i < (sizeof(struct command_message_t) - sizeof(msg->data) + msg->length) - 1; i++) {
sum += byte_ptr[i];
}
crc = 0x100 - (sum & 0xff);
return crc;
}
void command_create_ack(struct command_message_t *msg) {
command_create_message(msg, 0, COMMAND_ACK, NULL);
}
void command_create_nack(struct command_message_t *msg) {
command_create_message(msg, 0, COMMAND_NACK, NULL);
}
void command_log(struct command_message_t *msg) {
if (msg->length > sizeof(msg->data) - 1) {
LOG_ERR("Message length too long: %d", msg->length);
return;
}
LOG_INF("Prefix: %d\n\r", msg->prefix);
LOG_INF("Length: %d\n\r", msg->length);
LOG_INF("COMMAND_ID: %d\n\r", msg->id);
LOG_INF("Command: %d\n\r", msg->command);
LOG_INF("Data:\n\r");
for (int i = 0; i < msg->length; i++) {
LOG_INF("%d", msg->data[i]);
}
LOG_INF("CRC: %d\n\r", msg->crc);
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef COMMAND_MESSAGE_H
#define COMMAND_MESSAGE_H
#include <stdint.h>
#define COMMAND_PREFIX 0x69
#define COMMAND_ID 0x00
typedef enum {
COMMAND_ERROR,
COMMAND_ACK,
COMMAND_NACK,
LED,
} commands_e;
struct command_message_t {
uint8_t prefix;
uint8_t length;
uint8_t id;
uint8_t command;
uint8_t crc;
uint8_t data[160];
} __attribute__((packed));
/**
* @brief Initialize command message to default state
*
* @param msg Message to initialize
*/
void command_message_init(struct command_message_t *msg);
/**
* @brief Create command message with data and CRC
*
* @param msg Message to populate
* @param length Data length in bytes
* @param command Command type
* @param data Data payload
*/
void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[160]);
/**
* @brief Calculate CRC for command message
*
* @param msg Message to calculate CRC for
* @return CRC value
*/
uint8_t command_calculate_crc(struct command_message_t *msg);
/**
* @brief Create ACK command message
*
* @param msg Message to populate
*/
void command_create_ack(struct command_message_t *msg);
/**
* @brief Create NACK command message
*
* @param msg Message to populate
*/
void command_create_nack(struct command_message_t *msg);
/**
* @brief Print the command with LOG
*
* @param msg Message to calculate CRC for
*/
void command_log(struct command_message_t *msg);
#endif // COMMAND_MESSAGE_H
+51
View File
@@ -0,0 +1,51 @@
#include "led.h"
#include <zephyr/logging/log.h>
#include <zephyr/drivers/gpio.h>
LOG_MODULE_REGISTER(led, LOG_LEVEL_INF);
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led0_gpio = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static int led0_state = 0;
int led_init() {
int ret;
if (!device_is_ready(led0_gpio.port)) {
LOG_ERR("LED0 GPIO device not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&led0_gpio, GPIO_OUTPUT_INACTIVE);
if (ret != 0) {
LOG_ERR("Failed to configure LED0 GPIO: %d", ret);
return ret;
}
// Turn it off
ret = gpio_pin_set_dt(&led0_gpio, 0);
if (ret != 0) {
LOG_ERR("Failed to initialize LED");
return ret;
}
LOG_INF("LED driver initialized (LED0: GPIO %d)", led0_gpio.pin);
return 0;
}
int led_set(int state) {
led0_state = state ? 1 : 0;
int ret = gpio_pin_set_dt(&led0_gpio, led0_state);
if (ret != 0) {
LOG_ERR("Failed to set LED0: %d", ret);
}
return ret;
}
int led_toggle() {
return led_set(!led0_state);
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef LED_H
#define LED_H
int led_init();
int led_set(int state);
int led_toggle();
#endif // LED_H
+24
View File
@@ -0,0 +1,24 @@
#include "led.h"
#include "usb.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
int main(void) {
int ret;
ret = usb_init();
if (ret != 0) {
LOG_ERR("Failed to enable USB");
return 0;
}
ret = led_init();
if (ret != 0) {
LOG_ERR("Failed to enable LED");
return 0;
}
return 0;
}
+193
View File
@@ -0,0 +1,193 @@
#include "usb.h"
#include "usb_conf.h"
#include "command_handler.h"
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
LOG_MODULE_REGISTER(usb, LOG_LEVEL_DBG);
// DEVICE
const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
static struct usbd_context *usb_context;
// THREAD
static struct k_thread usb_thread_data;
static k_tid_t usb_thread_id = NULL;
#define USB_THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(usb_thread_stack, USB_THREAD_STACK_SIZE);
// RX BUFFER
#define RX_BUF_SIZE 4
struct rx_buffer_t {
int current_read;
int current_write;
struct k_sem semaphore;
struct command_message_t buffer[RX_BUF_SIZE];
};
static struct rx_buffer_t rx_buf;
static void interrupt_handler(const struct device *dev, void *user_data) {
ARG_UNUSED(user_data);
while (true) {
uart_irq_update(dev);
if (uart_irq_is_pending(dev) <= 0) {
break;
}
if (uart_irq_rx_ready(dev)) {
size_t len = sizeof(struct command_message_t);
int recv_len = uart_fifo_read(dev, (uint8_t *)&rx_buf.buffer[rx_buf.current_write], len);
if (recv_len < 0) {
LOG_ERR("Failed to read UART FIFO");
recv_len = 0;
};
rx_buf.current_write += 1;
if (rx_buf.current_write >= RX_BUF_SIZE) {
rx_buf.current_write = 0;
rx_buf.current_read = (RX_BUF_SIZE - 1);
}
else {
rx_buf.current_read = (rx_buf.current_write - 1);
}
// Give semaphore for usb read thread
k_sem_give(&rx_buf.semaphore);
}
}
}
static void usb_thread(void *p1, void *p2, void *p3) {
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
LOG_INF("USB command processing thread started");
while (1) {
// Wait forever for the rx semaphore
k_sem_take(&rx_buf.semaphore, K_FOREVER);
int read_index = rx_buf.current_read;
// Check the prefix
if (rx_buf.buffer[read_index].prefix != COMMAND_PREFIX) {
LOG_ERR("COMMAND_PREFIX does not match: %d", rx_buf.buffer[read_index].prefix);
// Send NACK
struct command_message_t nack_buf;
command_create_nack(&nack_buf);
usb_send_command(&nack_buf);
continue;
}
// Check the CRC
uint8_t calculated_crc = command_calculate_crc(&rx_buf.buffer[read_index]);
if (calculated_crc != rx_buf.buffer[read_index].crc) {
LOG_ERR("CRC does not match:");
LOG_ERR("Calculated CRC: %d", calculated_crc);
LOG_ERR("Received CRC: %d", rx_buf.buffer[read_index].prefix);
// Send NACK
struct command_message_t nack_buf;
command_create_nack(&nack_buf);
usb_send_command(&nack_buf);
continue;
}
int ret = command_handler(&rx_buf.buffer[read_index]);
if (ret == 0) {
// Send ACK
struct command_message_t ack_buf;
command_create_ack(&ack_buf);
usb_send_command(&ack_buf);
}
else {
// Send NACK
struct command_message_t nack_buf;
command_create_nack(&nack_buf);
usb_send_command(&nack_buf);
}
}
LOG_INF("USB command processing thread exiting");
}
int usb_init() {
rx_buf.current_read = 0;
rx_buf.current_write = 0;
k_sem_init(&rx_buf.semaphore, 1, 1);
memset(rx_buf.buffer, 0, sizeof(rx_buf.buffer));
int ret;
if (!device_is_ready(uart_dev)) {
LOG_ERR("CDC ACM device not ready");
return -ENODEV;
}
usb_context = usb_device_init(NULL);
if (usb_context == NULL) {
LOG_ERR("Failed to initialize USB device");
return -ENODEV;
}
if (!usbd_can_detect_vbus(usb_context)) {
ret = usbd_enable(usb_context);
if (ret) {
LOG_ERR("Failed to enable device support");
return ret;
}
}
k_msleep(100);
uart_irq_callback_set(uart_dev, interrupt_handler);
uart_irq_rx_enable(uart_dev);
usb_thread_id = k_thread_create(
&usb_thread_data,
usb_thread_stack,
K_THREAD_STACK_SIZEOF(usb_thread_stack),
usb_thread,
NULL, NULL, NULL,
5,
0,
K_NO_WAIT
);
if (usb_thread_id == NULL) {
LOG_ERR("Failed to create USB thread");
return -ENOMEM;
}
return ret;
}
int usb_send_command(struct command_message_t *msg) {
int ret = 0;
if (!device_is_ready(uart_dev)) {
return -ENODEV;
}
// Message size: prefix + length + id + command + crc + data
size_t msg_size = 5 + msg->length;
uint8_t *msg_bytes = (uint8_t *)msg;
/* uart_poll_out blocks until sent, ensuring data integrity */
for (size_t i = 0; i < msg_size; i++) {
uart_poll_out(uart_dev, msg_bytes[i]);
}
return ret;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef USB_H
#define USB_H
#include "command_message.h"
int usb_init();
int usb_send_command(struct command_message_t *msg);
#endif // USB_H
+184
View File
@@ -0,0 +1,184 @@
#include "usb_conf.h"
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/usb/bos.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usb_conf, LOG_LEVEL_DBG);
/* By default, do not register the USB DFU class DFU mode instance. */
static const char *const blocklist[] = {
"dfu_dfu",
NULL,
};
/*
* Instantiate a context named my_usb_context using the default USB device
* controller, the Zephyr project vendor ID, and the sample product ID.
* Zephyr project vendor ID must not be used outside of Zephyr samples.
*/
USBD_DEVICE_DEFINE(my_usb_context,
DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
CONFIG_USBD_VID, CONFIG_USBD_PID);
USBD_DESC_LANG_DEFINE(my_usb_lang);
USBD_DESC_MANUFACTURER_DEFINE(my_usb_mfr, CONFIG_USBD_MANUFACTURER);
USBD_DESC_PRODUCT_DEFINE(my_usb_product, CONFIG_USBD_PRODUCT);
IF_ENABLED(CONFIG_HWINFO, (USBD_DESC_SERIAL_NUMBER_DEFINE(my_usb_serial)));
USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration");
USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration");
static const uint8_t attributes = (IS_ENABLED(CONFIG_USBD_SELF_POWERED) ?
USB_SCD_SELF_POWERED : 0) |
(IS_ENABLED(CONFIG_USBD_REMOTE_WAKEUP) ?
USB_SCD_REMOTE_WAKEUP : 0);
/* Full speed configuration */
USBD_CONFIGURATION_DEFINE(sample_fs_config,
attributes,
CONFIG_USBD_MAX_POWER, &fs_cfg_desc);
/* High speed configuration */
USBD_CONFIGURATION_DEFINE(sample_hs_config,
attributes,
CONFIG_USBD_MAX_POWER, &hs_cfg_desc);
#if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC
/*
* This does not yet provide valuable information, but rather serves as an
* example, and will be improved in the future.
*/
static const struct usb_bos_capability_lpm bos_cap_lpm = {
.bLength = sizeof(struct usb_bos_capability_lpm),
.bDescriptorType = USB_DESC_DEVICE_CAPABILITY,
.bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION,
.bmAttributes = 0UL,
};
USBD_DESC_BOS_DEFINE(my_usb_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm);
#endif
static void usb_fix_code_triple(struct usbd_context *uds_ctx, const enum usbd_speed speed) {
/* Always use class code information from Interface Descriptors */
if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) ||
IS_ENABLED(CONFIG_USBD_MIDI2_CLASS) ||
IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS) ||
IS_ENABLED(CONFIG_USBD_VIDEO_CLASS)) {
/*
* Class with multiple interfaces have an Interface
* Association Descriptor available, use an appropriate triple
* to indicate it.
*/
usbd_device_set_code_triple(uds_ctx, speed,
USB_BCC_MISCELLANEOUS, 0x02, 0x01);
} else {
usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
}
}
struct usbd_context *usb_device_setup(usbd_msg_cb_t msg_cb) {
int err;
err = usbd_add_descriptor(&my_usb_context, &my_usb_lang);
if (err) {
LOG_ERR("Failed to initialize language descriptor (%d)", err);
return NULL;
}
err = usbd_add_descriptor(&my_usb_context, &my_usb_mfr);
if (err) {
LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
return NULL;
}
err = usbd_add_descriptor(&my_usb_context, &my_usb_product);
if (err) {
LOG_ERR("Failed to initialize product descriptor (%d)", err);
return NULL;
}
IF_ENABLED(CONFIG_HWINFO, (
err = usbd_add_descriptor(&my_usb_context, &my_usb_serial);
))
if (err) {
LOG_ERR("Failed to initialize SN descriptor (%d)", err);
return NULL;
}
if (USBD_SUPPORTS_HIGH_SPEED &&
usbd_caps_speed(&my_usb_context) == USBD_SPEED_HS) {
err = usbd_add_configuration(&my_usb_context, USBD_SPEED_HS,
&sample_hs_config);
if (err) {
LOG_ERR("Failed to add High-Speed configuration");
return NULL;
}
err = usbd_register_all_classes(&my_usb_context, USBD_SPEED_HS, 1,
blocklist);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
}
usb_fix_code_triple(&my_usb_context, USBD_SPEED_HS);
}
err = usbd_add_configuration(&my_usb_context, USBD_SPEED_FS,
&sample_fs_config);
if (err) {
LOG_ERR("Failed to add Full-Speed configuration");
return NULL;
}
err = usbd_register_all_classes(&my_usb_context, USBD_SPEED_FS, 1, blocklist);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
}
usb_fix_code_triple(&my_usb_context, USBD_SPEED_FS);
usbd_self_powered(&my_usb_context, attributes & USB_SCD_SELF_POWERED);
if (msg_cb != NULL) {
err = usbd_msg_register_cb(&my_usb_context, msg_cb);
if (err) {
LOG_ERR("Failed to register message callback");
return NULL;
}
}
#if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC
(void)usbd_device_set_bcd_usb(&my_usb_context, USBD_SPEED_FS, 0x0201);
(void)usbd_device_set_bcd_usb(&my_usb_context, USBD_SPEED_HS, 0x0201);
err = usbd_add_descriptor(&my_usb_context, &my_usb_usbext);
if (err) {
LOG_ERR("Failed to add USB 2.0 Extension Descriptor");
return NULL;
}
#endif
return &my_usb_context;
}
struct usbd_context *usb_device_init(usbd_msg_cb_t msg_cb) {
int err;
if (usb_device_setup(msg_cb) == NULL) {
return NULL;
}
err = usbd_init(&my_usb_context);
if (err) {
LOG_ERR("Failed to initialize device support");
return NULL;
}
return &my_usb_context;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef USB_CONF_H
#define USB_CONF_H
#include <zephyr/usb/usbd.h>
struct usbd_context *usb_device_setup(usbd_msg_cb_t msg_cb);
struct usbd_context *usb_device_init(usbd_msg_cb_t msg_cb);
#endif // USB_CONF_H
+21
View File
@@ -0,0 +1,21 @@
#include "zbus_channels.h"
#include "command_message.h"
ZBUS_CHAN_DEFINE(
in_command_chan,
struct command_message_t,
NULL,
NULL,
ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(0)
);
ZBUS_CHAN_DEFINE(
out_command_chan,
struct command_message_t,
NULL,
NULL,
ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(0)
);
+15
View File
@@ -0,0 +1,15 @@
#ifndef ZBUS_CHANNELS_H
#define ZBUS_CHANNELS_H
#include <zephyr/kernel.h>
#include <zephyr/zbus/zbus.h>
#include "command_message.h"
ZBUS_CHAN_DECLARE(in_command_chan);
ZBUS_CHAN_DECLARE(out_command_chan);
#endif // ZBUS_CHANNELS_H