Add full automotive RTOS project

Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers,
middleware (CAN stack, diagnostics, safety), applications, board
support, build/test tooling, and documentation.
This commit is contained in:
root
2026-08-23 03:35:29 -04:00
parent f113bf0a05
commit ca13734bf0
151 changed files with 23945 additions and 0 deletions
+319
View File
@@ -0,0 +1,319 @@
/**
* @file display_task.c
* @brief Dashboard display task
*/
#include "kernel.h"
#include "can_driver.h"
#include "spi_driver.h"
#include "gpio_driver.h"
#include "engine_control.h"
#include "brake_control.h"
#include <string.h>
#include <stdio.h>
/* Display State */
typedef struct {
bool initialized;
uint16_t display_buffer[1024]; /* Display frame buffer */
uint8_t current_screen;
bool backlight_on;
uint8_t backlight_intensity;
Mutex_t mutex;
} DisplayState_t;
static DisplayState_t display;
/* Dashboard Data */
typedef struct {
uint16_t speed;
uint16_t rpm;
int16_t coolant_temp;
uint16_t fuel_level;
uint16_t odometer;
uint16_t trip_meter;
bool turn_left;
bool turn_right;
bool high_beam;
bool check_engine;
bool abs_warning;
bool oil_pressure_warning;
bool battery_warning;
} DashboardData_t;
static DashboardData_t dashboard_data;
/* Initialize Display */
KernelStatus_t display_init(void) {
if (display.initialized) {
return KERNEL_ERROR;
}
memset(&display, 0, sizeof(DisplayState_t));
display.current_screen = 0;
display.backlight_on = true;
display.backlight_intensity = 100;
mutex_create(&display.mutex, false);
/* Initialize SPI for display */
spi_init(0, &(SpiConfig_t){
.mode = SPI_MODE_0,
.clock_speed = SPI_CLOCK_8MHZ,
.data_order = SPI_DATA_ORDER_MSB_FIRST,
.data_size = 8,
.use_dma = true,
.enable_hardware_cs = false,
.cs_polarity = SPI_CS_ACTIVE_LOW,
.cs_port = 0,
.cs_pin = 15
});
display.initialized = true;
return KERNEL_OK;
}
/* Dashboard Display Task */
void display_task(void* parameters) {
(void)parameters;
while (1) {
/* Update dashboard data */
update_dashboard_data();
/* Render display */
render_display();
/* Update display */
update_display_hardware();
/* 50ms refresh rate */
kernel_delay(50);
}
}
/* Update Dashboard Data */
static void update_dashboard_data(void) {
/* Get data from CAN bus */
CanMessage_t message;
while (can_receive_message(&message, 0) == KERNEL_OK) {
switch (message.id.id) {
case 0x300: /* Engine data */
dashboard_data.rpm = (message.data[0] << 8) | message.data[1];
dashboard_data.coolant_temp = (int16_t)((message.data[2] << 8) |
message.data[3]);
dashboard_data.check_engine = message.data[4] & 0x01;
dashboard_data.oil_pressure_warning = message.data[4] & 0x02;
break;
case 0x301: /* Vehicle speed */
dashboard_data.speed = (message.data[0] << 8) | message.data[1];
dashboard_data.odometer = (message.data[2] << 16) |
(message.data[3] << 8) |
message.data[4];
break;
case 0x302: /* Fuel level */
dashboard_data.fuel_level = message.data[0];
dashboard_data.battery_warning = message.data[1] & 0x01;
break;
case 0x303: /* Turn signals */
dashboard_data.turn_left = message.data[0] & 0x01;
dashboard_data.turn_right = message.data[0] & 0x02;
dashboard_data.high_beam = message.data[0] & 0x04;
break;
case 0x304: /* ABS status */
dashboard_data.abs_warning = message.data[0] & 0x01;
break;
}
}
}
/* Render Display */
static void render_display(void) {
mutex_lock(&display.mutex, 100);
/* Clear display buffer */
memset(display.display_buffer, 0, sizeof(display.display_buffer));
/* Draw speedometer */
draw_speedometer();
/* Draw tachometer */
draw_tachometer();
/* Draw fuel gauge */
draw_fuel_gauge();
/* Draw temperature gauge */
draw_temperature_gauge();
/* Draw warning indicators */
draw_warning_indicators();
/* Draw odometer */
draw_odometer();
mutex_unlock(&display.mutex);
}
/* Draw Speedometer */
static void draw_speedometer(void) {
/* Draw circular gauge */
int center_x = 100;
int center_y = 100;
int radius = 80;
/* Draw arc */
for (int angle = 0; angle < 270; angle++) {
int x = center_x + (int)(radius * cos(angle * M_PI / 180));
int y = center_y + (int)(radius * sin(angle * M_PI / 180));
if (x >= 0 && x < 240 && y >= 0 && y < 320) {
display.display_buffer[y * 240 + x] = 0xFFFF; /* White */
}
}
/* Draw speed needle */
float speed_angle = (dashboard_data.speed * 270.0f) / 240.0f; /* 240 km/h max */
int needle_x = center_x + (int)((radius - 10) * cos(speed_angle * M_PI / 180));
int needle_y = center_y + (int)((radius - 10) * sin(speed_angle * M_PI / 180));
/* Draw line from center to needle tip */
draw_line(center_x, center_y, needle_x, needle_y, 0xF800); /* Red */
/* Draw speed text */
char speed_text[10];
snprintf(speed_text, sizeof(speed_text), "%d km/h", dashboard_data.speed);
draw_text(60, 200, speed_text, 0xFFFF);
}
/* Draw Tachometer */
static void draw_tachometer(void) {
int center_x = 300;
int center_y = 100;
int radius = 60;
/* Draw arc */
for (int angle = 0; angle < 270; angle++) {
int x = center_x + (int)(radius * cos(angle * M_PI / 180));
int y = center_y + (int)(radius * sin(angle * M_PI / 180));
if (x >= 0 && x < 480 && y >= 0 && y < 320) {
display.display_buffer[y * 480 + x] = 0xFFFF;
}
}
/* Draw RPM needle */
float rpm_angle = (dashboard_data.rpm * 270.0f) / 8000.0f; /* 8000 RPM max */
int needle_x = center_x + (int)((radius - 10) * cos(rpm_angle * M_PI / 180));
int needle_y = center_y + (int)((radius - 10) * sin(rpm_angle * M_PI / 180));
draw_line(center_x, center_y, needle_x, needle_y, 0x07E0); /* Green */
/* Draw RPM text */
char rpm_text[10];
snprintf(rpm_text, sizeof(rpm_text), "%d RPM", dashboard_data.rpm);
draw_text(260, 200, rpm_text, 0xFFFF);
}
/* Draw Warning Indicators */
static void draw_warning_indicators(void) {
/* Check engine light */
if (dashboard_data.check_engine) {
draw_text(20, 280, "CHECK ENGINE", 0xF800); /* Red */
}
/* ABS warning */
if (dashboard_data.abs_warning) {
draw_text(20, 300, "ABS", 0xF800);
}
/* Oil pressure warning */
if (dashboard_data.oil_pressure_warning) {
draw_text(100, 300, "OIL", 0xF800);
}
/* Battery warning */
if (dashboard_data.battery_warning) {
draw_text(150, 300, "BAT", 0xF800);
}
/* Turn signals */
if (dashboard_data.turn_left) {
draw_text(400, 280, "<--", 0x07E0); /* Green */
}
if (dashboard_data.turn_right) {
draw_text(440, 280, "-->", 0x07E0);
}
/* High beam */
if (dashboard_data.high_beam) {
draw_text(400, 300, "HIGH", 0x001F); /* Blue */
}
}
/* Draw Line */
static void draw_line(int x1, int y1, int x2, int y2, uint16_t color) {
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while (1) {
if (x1 >= 0 && x1 < 480 && y1 >= 0 && y1 < 320) {
display.display_buffer[y1 * 480 + x1] = color;
}
if (x1 == x2 && y1 == y2) {
break;
}
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
}
}
/* Draw Text */
static void draw_text(int x, int y, const char* text, uint16_t color) {
/* Simple 8x8 font rendering */
while (*text) {
char c = *text++;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (font_bitmap[(uint8_t)c][i] & (1 << j)) {
int px = x + j;
int py = y + i;
if (px >= 0 && px < 480 && py >= 0 && py < 320) {
display.display_buffer[py * 480 + px] = color;
}
}
}
}
x += 8;
}
}
/* Update Display Hardware */
static void update_display_hardware(void) {
mutex_lock(&display.mutex, 100);
/* Send frame buffer to display via SPI */
spi_write(0, (uint8_t*)display.display_buffer, sizeof(display.display_buffer), 100);
mutex_unlock(&display.mutex);
}
+200
View File
@@ -0,0 +1,200 @@
/**
* @file gauge_control.c
* @brief Analog gauge control for dashboard
*/
#include "kernel.h"
#include "pwm_driver.h"
#include "adc_driver.h"
#include "can_driver.h"
#include <string.h>
#include <math.h>
/* Gauge Control State */
typedef struct {
bool initialized;
uint16_t speed_gauge_position;
uint16_t rpm_gauge_position;
uint16_t fuel_gauge_position;
uint16_t temp_gauge_position;
uint16_t target_positions[4];
Mutex_t mutex;
} GaugeControlState_t;
static GaugeControlState_t gauge_control;
/* Initialize Gauge Control */
KernelStatus_t gauge_control_init(void) {
if (gauge_control.initialized) {
return KERNEL_ERROR;
}
memset(&gauge_control, 0, sizeof(GaugeControlState_t));
/* Initialize PWM for gauges */
pwm_init(3, &(PwmConfig_t){
.frequency_hz = 100, /* 100 Hz for smooth gauge movement */
.channel_count = 4,
.channels = {
{.channel = 0, .duty_cycle = 0}, /* Speedometer */
{.channel = 1, .duty_cycle = 0}, /* Tachometer */
{.channel = 2, .duty_cycle = 0}, /* Fuel gauge */
{.channel = 3, .duty_cycle = 0} /* Temperature gauge */
}
});
mutex_create(&gauge_control.mutex, false);
gauge_control.initialized = true;
return KERNEL_OK;
}
/* Gauge Control Task */
void gauge_control_task(void* parameters) {
(void)parameters;
while (1) {
/* Read CAN messages for gauge data */
CanMessage_t message;
while (can_receive_message(&message, 0) == KERNEL_OK) {
process_gauge_message(&message);
}
/* Smooth gauge movement */
smooth_gauge_movement();
/* 10ms update rate */
kernel_delay(10);
}
}
/* Process Gauge Message */
static void process_gauge_message(const CanMessage_t* message) {
mutex_lock(&gauge_control.mutex, 100);
switch (message->id.id) {
case 0x300: /* Engine data */
/* RPM: 0-8000 RPM maps to 0-10000 duty cycle */
gauge_control.target_positions[1] =
((message->data[0] << 8) | message->data[1]) * 10000 / 8000;
/* Temperature: -40 to 120°C maps to 0-10000 */
int16_t temp = (message->data[2] << 8) | message->data[3];
gauge_control.target_positions[3] =
(temp + 40) * 10000 / 160;
break;
case 0x301: /* Vehicle speed */
/* Speed: 0-240 km/h maps to 0-10000 */
gauge_control.target_positions[0] =
((message->data[0] << 8) | message->data[1]) * 10000 / 240;
break;
case 0x302: /* Fuel level */
/* Fuel: 0-100% maps to 0-10000 */
gauge_control.target_positions[2] = message->data[0] * 100;
break;
}
mutex_unlock(&gauge_control.mutex);
}
/* Smooth Gauge Movement */
static void smooth_gauge_movement(void) {
mutex_lock(&gauge_control.mutex, 100);
/* Smooth movement for each gauge */
for (int i = 0; i < 4; i++) {
uint16_t current = 0;
uint16_t target = gauge_control.target_positions[i];
/* Get current position */
switch (i) {
case 0:
current = gauge_control.speed_gauge_position;
break;
case 1:
current = gauge_control.rpm_gauge_position;
break;
case 2:
current = gauge_control.fuel_gauge_position;
break;
case 3:
current = gauge_control.temp_gauge_position;
break;
}
/* Calculate new position with smoothing */
int32_t delta = target - current;
uint16_t new_position = current + (delta / 10); /* 10% movement per update */
/* Update position */
switch (i) {
case 0:
gauge_control.speed_gauge_position = new_position;
break;
case 1:
gauge_control.rpm_gauge_position = new_position;
break;
case 2:
gauge_control.fuel_gauge_position = new_position;
break;
case 3:
gauge_control.temp_gauge_position = new_position;
break;
}
/* Update PWM output */
pwm_set_duty_cycle(3, i, new_position);
}
mutex_unlock(&gauge_control.mutex);
}
/* Calibrate Gauges */
KernelStatus_t gauge_calibrate(uint8_t gauge, uint16_t min_position,
uint16_t max_position) {
if (!gauge_control.initialized || gauge >= 4) {
return KERNEL_ERROR;
}
mutex_lock(&gauge_control.mutex, 100);
/* Set gauge to minimum position */
pwm_set_duty_cycle(3, gauge, min_position);
kernel_delay(1000); /* Wait 1 second */
/* Set gauge to maximum position */
pwm_set_duty_cycle(3, gauge, max_position);
kernel_delay(1000); /* Wait 1 second */
/* Return to zero */
pwm_set_duty_cycle(3, gauge, min_position);
mutex_unlock(&gauge_control.mutex);
return KERNEL_OK;
}
/* Self-Test Gauges */
KernelStatus_t gauge_self_test(void) {
if (!gauge_control.initialized) {
return KERNEL_ERROR;
}
/* Perform gauge sweep */
for (uint16_t position = 0; position <= 10000; position += 100) {
for (int i = 0; i < 4; i++) {
pwm_set_duty_cycle(3, i, position);
}
kernel_delay(10);
}
/* Return to zero */
for (int i = 0; i < 4; i++) {
pwm_set_duty_cycle(3, i, 0);
}
return KERNEL_OK;
}