Added thread safety to USB TX, servo control thread, primitive ADC thread, python test codes

This commit is contained in:
Your Name
2026-09-22 13:01:31 +03:00
parent 1d1416d5d8
commit dd7e6d9456
16 changed files with 944 additions and 40 deletions
+120
View File
@@ -0,0 +1,120 @@
#include "adc.h"
#include <zephyr/logging/log.h>
#include <zephyr/drivers/adc.h>
LOG_MODULE_REGISTER(adc, LOG_LEVEL_INF);
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
static const struct adc_dt_spec adc_mux_spec = ADC_DT_SPEC_GET_BY_IDX(ZEPHYR_USER_NODE, 0);
static const struct adc_dt_spec adc_ch16_spec = ADC_DT_SPEC_GET_BY_IDX(ZEPHYR_USER_NODE, 1);
static const struct adc_dt_spec adc_ch17_spec = ADC_DT_SPEC_GET_BY_IDX(ZEPHYR_USER_NODE, 2);
int16_t adc_buffer;
struct adc_sequence sequence = {
.buffer = &adc_buffer,
.buffer_size = sizeof(adc_buffer),
};
int adc_init_all(void) {
int ret;
// ret = mux_init();
// if (ret != 0) {
// LOG_ERR("Failed to setup multiplexer: %d", ret);
// return ret;
// }
if (!adc_is_ready_dt(&adc_mux_spec)) {
LOG_ERR("ADC mux channel not ready");
return -1;
}
if (!adc_is_ready_dt(&adc_ch16_spec)) {
LOG_ERR("ADC channel 17 not ready");
return -1;
}
if (!adc_is_ready_dt(&adc_ch17_spec)) {
LOG_ERR("ADC channel 18 not ready");
return -1;
}
ret = adc_channel_setup_dt(&adc_mux_spec);
if (ret != 0) {
LOG_ERR("Failed to setup ADC mux: %d", ret);
return ret;
}
ret = adc_channel_setup_dt(&adc_ch16_spec);
if (ret != 0) {
LOG_ERR("Failed to setup ADC channel 17: %d", ret);
return ret;
}
ret = adc_channel_setup_dt(&adc_ch17_spec);
if (ret != 0) {
LOG_ERR("Failed to setup ADC channel 18: %d", ret);
return ret;
}
adc_sequence_init_dt(&adc_mux_spec, &sequence);
adc_sequence_init_dt(&adc_ch16_spec, &sequence);
adc_sequence_init_dt(&adc_ch17_spec, &sequence);
return 0;
}
int adc_read_id(int id) {
int ret;
int val_mv = 0;
adc_buffer = 0;
if (id < MUX_CHANNELS) {
// Read muxed channels
(void)adc_sequence_init_dt(&adc_mux_spec, &sequence);
// Set mux TODO:
// ret = mux_select_channel(id);
// if (ret < 0) {
// LOG_ERR("Could not set multiplexer (%d)\n", ret);
// return 0;
// }
ret = adc_read_dt(&adc_mux_spec, &sequence);
if (ret < 0) {
LOG_ERR("Could not read (%d)\n", ret);
return 0;
}
val_mv = (int)adc_buffer;
// ret = adc_raw_to_millivolts_dt(&adc_mux_spec, &val_mv);
}
else if (id == MUX_CHANNELS) {
// Read unmuxed channel 17
(void)adc_sequence_init_dt(&adc_ch16_spec, &sequence);
ret = adc_read_dt(&adc_ch16_spec, &sequence);
if (ret < 0) {
LOG_ERR("Could not read (%d)\n", ret);
return 0;
}
val_mv = (int)adc_buffer;
// ret = adc_raw_to_millivolts_dt(&adc_ch16_spec, &val_mv);
}
else if (id == (MUX_CHANNELS + 1)) {
// Read unmuxed channel 18
(void)adc_sequence_init_dt(&adc_ch17_spec, &sequence);
ret = adc_read_dt(&adc_ch17_spec, &sequence);
if (ret < 0) {
LOG_ERR("Could not read (%d)\n", ret);
return 0;
}
val_mv = (int)adc_buffer;
// ret = adc_raw_to_millivolts_dt(&adc_ch17_spec, &val_mv);
}
return val_mv;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef ADC_H
#define ADC_H
#define MUX_CHANNELS 16
#define NUM_ADC_CHANNELS 18
int adc_init_all();
int adc_read_id(int id);
#endif // ADC_H
+51
View File
@@ -1,5 +1,9 @@
#include "command_handler.h"
#include "command_message.h"
#include "led.h"
#include "servo.h"
#include "adc.h"
#include "usb.h"
#include <zephyr/logging/log.h>
@@ -37,6 +41,53 @@ int command_handler(struct command_message_t *msg) {
break;
}
case SERVO_SET: {
// Set SERVO
int servo = msg->data[0];
float angle;
memcpy(&angle, &msg->data[1], sizeof(float));
servo_set_angle(servo, angle);
break;
}
case SERVO_SET_ALL: {
// Set ALL SERVO
float angles[NUM_SERVO_CHANNELS];
for (int i = 0; i < NUM_SERVO_CHANNELS; i++) {
int start = i*4;
memcpy(&angles[i], &msg->data[start], sizeof(float));
}
servo_set_all_angles(angles);
break;
}
case ADC_READ: {
int channel = msg->data[0];
int value = adc_read_id(channel);
struct command_message_t *reply = usb_get_next_tx_buf();
command_create_message(reply, sizeof(value), ADC_READ, (uint8_t *)&value);
usb_send_command(reply);
break;
}
case ADC_READ_ALL: {
int values[NUM_ADC_CHANNELS];
for (int i = 0; i < NUM_ADC_CHANNELS; i++) {
values[i] = adc_read_id(i);
}
struct command_message_t *reply = usb_get_next_tx_buf();
command_create_message(reply, sizeof(values), ADC_READ_ALL, (uint8_t *)values);
usb_send_command(reply);
break;
}
default: {
LOG_WRN("Unknown command received: %d", msg->command);
return -EINVAL;
+1 -1
View File
@@ -11,7 +11,7 @@ void command_message_init(struct command_message_t *msg) {
msg->id = COMMAND_ID;
}
void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[160]) {
void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[]) {
// Ensure length doesn't exceed available space
if (length > sizeof(msg->data) - 1) {
return;
+9 -1
View File
@@ -8,12 +8,20 @@
#define COMMAND_PREFIX 0x69
#define COMMAND_ID 0x00
#define COMMAND_DATA_SIZE 160
#define COMMAND_HEADER_SIZE 5
typedef enum {
COMMAND_ACK,
COMMAND_NACK,
LED_TOGGLE,
LED_SET,
SERVO_SET,
SERVO_SET_ALL,
ADC_READ,
ADC_READ_ALL,
// Keep last
NUM_COMMANDS,
} commands_e;
struct command_message_t {
@@ -40,7 +48,7 @@ void command_message_init(struct command_message_t *msg);
* @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]);
void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[]);
/**
* @brief Calculate CRC for command message
+18
View File
@@ -1,5 +1,7 @@
#include "led.h"
#include "usb.h"
#include "servo.h"
#include "adc.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
@@ -8,17 +10,33 @@ LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
int main(void) {
int ret;
// USB init
ret = usb_init();
if (ret != 0) {
LOG_ERR("Failed to enable USB");
return 0;
}
// LED init
ret = led_init();
if (ret != 0) {
LOG_ERR("Failed to enable LED");
return 0;
}
// ADC init before SERVO
ret = adc_init_all();
if (ret != 0) {
LOG_ERR("Failed to enable ADC");
return 0;
}
// SERVO init
ret = servo_init();
if (ret != 0) {
LOG_ERR("Failed to enable SERVO");
return 0;
}
return 0;
}
+113 -2
View File
@@ -2,10 +2,14 @@
#include <zephyr/logging/log.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/kernel.h>
#include <math.h>
LOG_MODULE_REGISTER(servo, LOG_LEVEL_INF);
// ----------- SERVOS -----------
// SERVOS
static const struct pwm_dt_spec servo_pwm_specs[NUM_SERVO_CHANNELS] = {
PWM_DT_SPEC_GET(DT_ALIAS(servo1)),
PWM_DT_SPEC_GET(DT_ALIAS(servo2)),
@@ -26,12 +30,119 @@ static const struct pwm_dt_spec servo_pwm_specs[NUM_SERVO_CHANNELS] = {
PWM_DT_SPEC_GET(DT_ALIAS(servo17)),
PWM_DT_SPEC_GET(DT_ALIAS(servo18)),
};
static struct servo_t servos[NUM_SERVO_CHANNELS];
// THREAD
static struct k_thread servo_thread_data;
static k_tid_t servo_thread_id = NULL;
#define SERVO_THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(servo_thread_stack, SERVO_THREAD_STACK_SIZE);
struct k_sem servo_semaphore;
static int pin_init(const ) {
static void servo_thread(void *p1, void *p2, void *p3) {
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
LOG_INF("Servo control thread started");
while (1) {
k_sem_take(&servo_semaphore, K_FOREVER);
// TODO: acceleration?
for (int i = 0; i < NUM_SERVO_CHANNELS; i++) {
servo_set(i, servos[i].set_position);
servos[i].current_position = servos[i].set_position;
}
}
}
int servo_init() {
int ret;
k_sem_init(&servo_semaphore, 0, 1);
memset(servos, 0, sizeof(servos));
for (int ch = 0; ch < NUM_SERVO_CHANNELS; ch++) {
if (!pwm_is_ready_dt(&servo_pwm_specs[ch])) {
LOG_ERR("PWM device for servo %d is not ready", ch + 1);
return -ENODEV;
}
}
for (int ch = 0; ch < NUM_SERVO_CHANNELS; ch++) {
ret = pwm_set_pulse_dt(&servo_pwm_specs[ch], PWM_USEC(0));
if (ret != 0) {
LOG_ERR("Failed to initialize PWM for servo %d: %d", ch + 1, ret);
return ret;
}
}
servo_thread_id = k_thread_create(
&servo_thread_data,
servo_thread_stack,
K_THREAD_STACK_SIZEOF(servo_thread_stack),
servo_thread,
NULL, NULL, NULL,
5,
0,
K_NO_WAIT
);
return 0;
}
void servo_set_angle(int channel, float angle) {
servos[channel].set_position = angle;
LOG_DBG("Settings servo %d to %f", channel, (double)angle);
k_sem_give(&servo_semaphore);
}
void servo_set_all_angles(float angles[NUM_SERVO_CHANNELS]) {
for (int i = 0; i < NUM_SERVO_CHANNELS; i++) {
servos[i].set_position = angles[i];
LOG_DBG("Settings servo %d to %f", i, (double)angles[i]);
}
k_sem_give(&servo_semaphore);
}
static uint32_t servo_angle_to_pulse_us(float angle) {
// Check limits
if (angle > SERVO_MAX_ANGLE) {
return SERVO_MAX_PULSE;
} else if (angle < SERVO_MIN_ANGLE) {
return SERVO_MIN_PULSE;
}
// Angle to pulse
float pulse_offset_us = angle * SERVO_ANGLE_PULSE_MULTIPLIER;
uint32_t pulse_us = (uint32_t)(SERVO_CENTER_PULSE + pulse_offset_us);
if (pulse_us < SERVO_MIN_PULSE) {
return SERVO_MIN_PULSE;
} else if (pulse_us > SERVO_MAX_PULSE) {
return SERVO_MAX_PULSE;
}
return pulse_us;
}
int servo_set(int channel, float angle) {
if (channel >= NUM_SERVO_CHANNELS || channel < 0) {
LOG_ERR("Servo channel exceeds NUM_SERVO_CHANNELS");
return -EINVAL;
}
float effective_angle = angle;
uint32_t pulse_us = servo_angle_to_pulse_us(effective_angle);
int ret = pwm_set_pulse_dt(&servo_pwm_specs[channel], PWM_USEC(pulse_us));
if (ret != 0) {
LOG_ERR("Failed to set PWM pulse for servo %d: %d", channel + 1, ret);
return ret;
}
return 0;
}
+15 -1
View File
@@ -2,12 +2,26 @@
#define SERVO_H
#define NUM_SERVO_CHANNELS 18
#define SERVO_CENTER_PULSE 1500
#define SERVO_MIN_PULSE 750
#define SERVO_MAX_PULSE 2500
#define SERVO_HALF_PULSE_RANGE 875
#define SERVO_MAX_ANGLE 2.3561944902f
#define SERVO_MIN_ANGLE -2.3561944902f
#define SERVO_ANGLE_PULSE_MULTIPLIER 371.3615338798826f
struct servo_t {
float current_position;
float set_position;
};
int servo_init();
void servo_set_angle(int channel, float angle);
void servo_set_all_angles(float angles[NUM_SERVO_CHANNELS]);
int servo_set(int channel, float angle);
#endif // SERVO_H
+88 -32
View File
@@ -17,11 +17,11 @@ LOG_MODULE_REGISTER(usb, LOG_LEVEL_INF);
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 THREAD
static struct k_thread usb_rx_thread_data;
static k_tid_t usb_rx_thread_id = NULL;
#define USB_RX_THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(usb_rx_thread_stack, USB_RX_THREAD_STACK_SIZE);
// RX BUFFER
#define RING_BUF_SIZE 255
@@ -29,10 +29,21 @@ static uint8_t ring_buffer[RING_BUF_SIZE];
static struct ring_buf ringbuf;
struct k_sem rx_semaphore;
// TX THREAD
static struct k_thread usb_tx_thread_data;
static k_tid_t usb_tx_thread_id = NULL;
#define USB_TX_THREAD_STACK_SIZE 512
K_THREAD_STACK_DEFINE(usb_tx_thread_stack, USB_TX_THREAD_STACK_SIZE);
// TX BUFFER (add ack and nack at the and as static)
#define TX_BUFFER_SIZE 10
struct command_message_t usb_tx_buffer[TX_BUFFER_SIZE + 2];
struct command_message_t *usb_tx_buf_ptr;
char usb_tx_ptr_msgq_buffer[(TX_BUFFER_SIZE + 2) * sizeof(struct command_message_t *)];
struct k_msgq usb_tx_ptr_msgq;
// ACK / NACK messages
#define RETURN_ACK true
struct command_message_t ack_msg;
struct command_message_t nack_msg;
static void interrupt_handler(const struct device *dev, void *user_data) {
@@ -73,7 +84,7 @@ static void interrupt_handler(const struct device *dev, void *user_data) {
}
}
static void usb_thread(void *p1, void *p2, void *p3) {
static void usb_rx_thread(void *p1, void *p2, void *p3) {
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
@@ -109,7 +120,7 @@ static void usb_thread(void *p1, void *p2, void *p3) {
if (calculated_crc != msg.crc) {
if (RETURN_ACK) {
// Send NACK
usb_send_command(&nack_msg);
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
}
continue;
}
@@ -118,13 +129,13 @@ static void usb_thread(void *p1, void *p2, void *p3) {
if (ret == 0) {
if (RETURN_ACK) {
// Send ACK
usb_send_command(&ack_msg);
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE]);
}
}
else {
if (RETURN_ACK) {
// Send NACK
usb_send_command(&nack_msg);
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
}
}
@@ -162,12 +173,36 @@ static void usb_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *ms
}
}
static void usb_tx_thread(void *p1, void *p2, void *p3) {
struct command_message_t *data;
while (1) {
k_msgq_get(&usb_tx_ptr_msgq, &data, K_FOREVER);
if (!device_is_ready(uart_dev)) {
// FIXME: Don't drop packages
continue;
}
// Message size: prefix + length + id + command + crc + data
size_t msg_size = COMMAND_HEADER_SIZE + data->length;
uint8_t *msg_bytes = (uint8_t *)data;
/* 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]);
}
}
}
int usb_init() {
ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer);
k_sem_init(&rx_semaphore, 0, 1);
usb_tx_buf_ptr = usb_tx_buffer;
k_msgq_init(&usb_tx_ptr_msgq, usb_tx_ptr_msgq_buffer, sizeof(struct command_message_t *), TX_BUFFER_SIZE);
command_create_ack(&ack_msg);
command_create_nack(&nack_msg);
command_create_ack(&usb_tx_buffer[TX_BUFFER_SIZE]);
command_create_nack(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
int ret;
@@ -194,38 +229,59 @@ int usb_init() {
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,
// RX THREAD
usb_rx_thread_id = k_thread_create(
&usb_rx_thread_data,
usb_rx_thread_stack,
K_THREAD_STACK_SIZEOF(usb_rx_thread_stack),
usb_rx_thread,
NULL, NULL, NULL,
5,
0,
K_NO_WAIT
);
if (usb_thread_id == NULL) {
LOG_ERR("Failed to create USB thread");
if (usb_rx_thread_id == NULL) {
LOG_ERR("Failed to create USB RX thread");
return -ENOMEM;
}
// TX THREAD
usb_tx_thread_id = k_thread_create(
&usb_tx_thread_data,
usb_tx_thread_stack,
K_THREAD_STACK_SIZEOF(usb_tx_thread_stack),
usb_tx_thread,
NULL, NULL, NULL,
5,
0,
K_NO_WAIT
);
if (usb_tx_thread_id == NULL) {
LOG_ERR("Failed to create USB TX thread");
return -ENOMEM;
}
return ret;
}
struct command_message_t* usb_get_next_tx_buf() {
struct command_message_t *buf = usb_tx_buf_ptr;
// Increment the buffer pointer
usb_tx_buf_ptr++;
if (usb_tx_buf_ptr > &usb_tx_buffer[TX_BUFFER_SIZE-1]) {
usb_tx_buf_ptr = usb_tx_buffer;
}
return buf;
}
int usb_send_command(struct command_message_t *msg) {
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]);
}
k_msgq_put(&usb_tx_ptr_msgq, &msg, K_NO_WAIT);
return 0;
}
+1
View File
@@ -6,6 +6,7 @@
int usb_init();
struct command_message_t* usb_get_next_tx_buf();
int usb_send_command(struct command_message_t *msg);