Add initial CAN FD CDC composite firmware

- Custom board definition for STM32G0B1KBU6 (ews_board)
- Zephyr-based firmware with modular architecture
- USB composite device: gs_usb CAN FD + CDC ACM interfaces
- PFET control via CDC text commands (PA8, PB2)
- Status LED on PB4, CAN FD on PB0/PB1
- No external crystal - uses HSI with USB clock recovery
- Ready for build testing with: west build -b ews_board
This commit is contained in:
2025-12-08 11:27:15 +01:00
parent 69b8cb0b79
commit 3d328fb7a2
14 changed files with 652 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
/*
* CDC Handler Module
* Handles USB CDC ACM commands for PFET control
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>
#include <zephyr/logging/log.h>
#include <string.h>
#include "cdc_handler.h"
#include "pfet_control.h"
LOG_MODULE_REGISTER(cdc_handler, LOG_LEVEL_DBG);
#define CDC_DEVICE_NAME "CDC_ACM_0"
#define RX_BUF_SIZE 64
static const struct device *cdc_dev;
static char rx_buf[RX_BUF_SIZE];
static int rx_pos = 0;
static void process_command(char *cmd)
{
char response[128];
if (strncmp(cmd, "PFET1_ON", 8) == 0) {
pfet_set_state(1, true);
strcpy(response, "PFET1 ON\r\n");
} else if (strncmp(cmd, "PFET1_OFF", 9) == 0) {
pfet_set_state(1, false);
strcpy(response, "PFET1 OFF\r\n");
} else if (strncmp(cmd, "PFET2_ON", 8) == 0) {
pfet_set_state(2, true);
strcpy(response, "PFET2 ON\r\n");
} else if (strncmp(cmd, "PFET2_OFF", 9) == 0) {
pfet_set_state(2, false);
strcpy(response, "PFET2 OFF\r\n");
} else if (strncmp(cmd, "STATUS", 6) == 0) {
snprintf(response, sizeof(response),
"PFET1: %s, PFET2: %s\r\n",
pfet_get_state(1) ? "ON" : "OFF",
pfet_get_state(2) ? "ON" : "OFF");
} else {
strcpy(response, "ERROR: Unknown command\r\n");
}
/* Send response */
uart_poll_out_string(cdc_dev, response);
}
int cdc_handler_init(void)
{
cdc_dev = device_get_binding(CDC_DEVICE_NAME);
if (!cdc_dev) {
LOG_ERR("CDC ACM device not found");
return -ENODEV;
}
LOG_INF("CDC handler initialized");
return 0;
}
void cdc_handler_process(void)
{
char c;
/* Check for incoming characters */
while (uart_poll_in(cdc_dev, (unsigned char *)&c) == 0) {
if (c == '\n' || c == '\r') {
/* End of command */
if (rx_pos > 0) {
rx_buf[rx_pos] = '\0';
process_command(rx_buf);
rx_pos = 0;
}
} else if (rx_pos < (RX_BUF_SIZE - 1)) {
/* Add character to buffer */
rx_buf[rx_pos++] = c;
} else {
/* Buffer full, reset */
rx_pos = 0;
}
}
}

View File

@@ -0,0 +1,20 @@
/*
* CDC Handler Header
*/
#ifndef CDC_HANDLER_H
#define CDC_HANDLER_H
/**
* Initialize CDC handler
* @return 0 on success, negative error code on failure
*/
int cdc_handler_init(void);
/**
* Process incoming CDC commands
* Should be called regularly from main loop
*/
void cdc_handler_process(void);
#endif /* CDC_HANDLER_H */

View File

@@ -0,0 +1,62 @@
/*
* gs_usb CAN Interface
* Implements gs_usb protocol for CAN FD communication
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/can.h>
#include <zephyr/logging/log.h>
#include "gs_usb_can.h"
LOG_MODULE_REGISTER(gs_usb_can, LOG_LEVEL_DBG);
/* CAN device */
static const struct device *can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus));
int gs_usb_can_init(void)
{
int ret;
if (!device_is_ready(can_dev)) {
LOG_ERR("CAN device not ready");
return -ENODEV;
}
/* Configure CAN timing for 500kbps (adjust as needed) */
struct can_timing timing = {
.sjw = 1,
.prop_seg = 6,
.phase_seg1 = 7,
.phase_seg2 = 2,
.prescaler = 6
};
ret = can_set_timing(can_dev, &timing);
if (ret < 0) {
LOG_ERR("Failed to set CAN timing: %d", ret);
return ret;
}
/* Start CAN controller */
ret = can_start(can_dev);
if (ret < 0) {
LOG_ERR("Failed to start CAN: %d", ret);
return ret;
}
LOG_INF("gs_usb CAN interface initialized");
return 0;
}
int gs_usb_can_send_frame(const struct can_frame *frame)
{
return can_send(can_dev, frame, K_FOREVER, NULL, NULL);
}
/* TODO: Implement full gs_usb protocol */
/* This would require implementing the USB bulk endpoints and
gs_usb command/response protocol as defined in:
https://github.com/candle-usb/candleLight_fw
*/

View File

@@ -0,0 +1,23 @@
/*
* gs_usb CAN Interface Header
*/
#ifndef GS_USB_CAN_H
#define GS_USB_CAN_H
#include <zephyr/drivers/can.h>
/**
* Initialize gs_usb CAN interface
* @return 0 on success, negative error code on failure
*/
int gs_usb_can_init(void);
/**
* Send a CAN frame
* @param frame CAN frame to send
* @return 0 on success, negative error code on failure
*/
int gs_usb_can_send_frame(const struct can_frame *frame);
#endif /* GS_USB_CAN_H */

View File

@@ -0,0 +1,82 @@
/*
* EWS CAN FD CDC Composite Firmware
* Main application entry point
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/usb/usb_device.h>
#include <zephyr/logging/log.h>
#include "cdc_handler.h"
#include "pfet_control.h"
#include "gs_usb_can.h"
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
/* LED definitions - adjust to actual EWS board pins */
#define LED_STATUS_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led_status = GPIO_DT_SPEC_GET(LED_STATUS_NODE, gpios);
int main(void)
{
int ret;
LOG_INF("EWS CAN FD CDC Composite Firmware starting...");
/* Initialize status LED */
if (!gpio_is_ready_dt(&led_status)) {
LOG_ERR("Status LED device not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&led_status, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
LOG_ERR("Error configuring status LED: %d", ret);
return ret;
}
/* Initialize PFET control */
ret = pfet_control_init();
if (ret < 0) {
LOG_ERR("Failed to initialize PFET control: %d", ret);
return ret;
}
/* Initialize USB composite device */
ret = usb_enable(NULL);
if (ret != 0) {
LOG_ERR("Failed to enable USB: %d", ret);
return ret;
}
/* Initialize CDC handler */
ret = cdc_handler_init();
if (ret < 0) {
LOG_ERR("Failed to initialize CDC handler: %d", ret);
return ret;
}
/* Initialize gs_usb CAN interface */
ret = gs_usb_can_init();
if (ret < 0) {
LOG_ERR("Failed to initialize gs_usb CAN: %d", ret);
return ret;
}
LOG_INF("EWS firmware initialized successfully");
/* Main loop */
while (1) {
/* Toggle status LED to show activity */
gpio_pin_toggle_dt(&led_status);
/* Handle CDC commands */
cdc_handler_process();
k_msleep(500);
}
return 0;
}

View File

@@ -0,0 +1,78 @@
/*
* PFET Control Module
* Controls the two output PFETs on the EWS board
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
#include "pfet_control.h"
LOG_MODULE_REGISTER(pfet_control, LOG_LEVEL_DBG);
/* PFET control pin definitions */
static const struct gpio_dt_spec pfet1 = GPIO_DT_SPEC_GET(DT_ALIAS(pfet0), gpios);
static const struct gpio_dt_spec pfet2 = GPIO_DT_SPEC_GET(DT_ALIAS(pfet1), gpios);
static bool pfet1_state = false;
static bool pfet2_state = false;
int pfet_control_init(void)
{
int ret;
if (!gpio_is_ready_dt(&pfet1) || !gpio_is_ready_dt(&pfet2)) {
LOG_ERR("PFET GPIO devices not ready");
return -ENODEV;
}
/* Configure PFET pins as output, initially off */
ret = gpio_pin_configure_dt(&pfet1, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure PFET1 pin: %d", ret);
return ret;
}
ret = gpio_pin_configure_dt(&pfet2, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Failed to configure PFET2 pin: %d", ret);
return ret;
}
LOG_INF("PFET control initialized");
return 0;
}
int pfet_set_state(int pfet_num, bool state)
{
int ret;
if (pfet_num == 1) {
ret = gpio_pin_set_dt(&pfet1, state ? 1 : 0);
if (ret == 0) {
pfet1_state = state;
LOG_INF("PFET1 %s", state ? "ON" : "OFF");
}
} else if (pfet_num == 2) {
ret = gpio_pin_set_dt(&pfet2, state ? 1 : 0);
if (ret == 0) {
pfet2_state = state;
LOG_INF("PFET2 %s", state ? "ON" : "OFF");
}
} else {
return -EINVAL;
}
return ret;
}
bool pfet_get_state(int pfet_num)
{
if (pfet_num == 1) {
return pfet1_state;
} else if (pfet_num == 2) {
return pfet2_state;
}
return false;
}

View File

@@ -0,0 +1,31 @@
/*
* PFET Control Header
*/
#ifndef PFET_CONTROL_H
#define PFET_CONTROL_H
#include <stdbool.h>
/**
* Initialize PFET control
* @return 0 on success, negative error code on failure
*/
int pfet_control_init(void);
/**
* Set PFET state
* @param pfet_num PFET number (1 or 2)
* @param state true for ON, false for OFF
* @return 0 on success, negative error code on failure
*/
int pfet_set_state(int pfet_num, bool state);
/**
* Get current PFET state
* @param pfet_num PFET number (1 or 2)
* @return current state (true=ON, false=OFF)
*/
bool pfet_get_state(int pfet_num);
#endif /* PFET_CONTROL_H */