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:
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file body_control_task.c
|
||||
* @brief Body control module main task
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "can_driver.h"
|
||||
#include "door_control.h"
|
||||
#include "lighting_control.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Body Control State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
uint8_t door_status[4]; /* 0=closed, 1=open, 2=locked, 3=unlocked */
|
||||
uint8_t light_status[8]; /* 0=off, 1=on, 2=auto */
|
||||
bool alarm_active;
|
||||
uint16_t interior_temp;
|
||||
Mutex_t data_mutex;
|
||||
} BodyControlState_t;
|
||||
|
||||
static BodyControlState_t body_control;
|
||||
|
||||
/* Initialize Body Control */
|
||||
KernelStatus_t body_control_init(void) {
|
||||
if (body_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&body_control, 0, sizeof(BodyControlState_t));
|
||||
|
||||
/* Initialize door status */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
body_control.door_status[i] = 2; /* Locked */
|
||||
}
|
||||
|
||||
/* Initialize light status */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
body_control.light_status[i] = 2; /* Auto */
|
||||
}
|
||||
|
||||
mutex_create(&body_control.data_mutex, false);
|
||||
|
||||
body_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Body Control Task */
|
||||
void body_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Process body control functions */
|
||||
process_door_control();
|
||||
process_lighting_control();
|
||||
process_can_messages();
|
||||
process_alarm_system();
|
||||
|
||||
/* 50ms period */
|
||||
kernel_delay(50);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process Door Control */
|
||||
static void process_door_control(void) {
|
||||
/* Check door switches */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool door_open = gpio_read(0, i);
|
||||
bool door_locked = gpio_read(1, i);
|
||||
|
||||
mutex_lock(&body_control.data_mutex, 100);
|
||||
|
||||
if (door_open) {
|
||||
body_control.door_status[i] = 1; /* Open */
|
||||
} else if (door_locked) {
|
||||
body_control.door_status[i] = 2; /* Locked */
|
||||
} else {
|
||||
body_control.door_status[i] = 3; /* Unlocked */
|
||||
}
|
||||
|
||||
mutex_unlock(&body_control.data_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process Lighting Control */
|
||||
static void process_lighting_control(void) {
|
||||
/* Read light switch position */
|
||||
bool headlights_on = gpio_read(2, 0);
|
||||
bool auto_mode = gpio_read(2, 1);
|
||||
|
||||
/* Read ambient light sensor */
|
||||
uint16_t ambient_light = adc_read_single(3);
|
||||
|
||||
mutex_lock(&body_control.data_mutex, 100);
|
||||
|
||||
if (auto_mode) {
|
||||
/* Automatic headlights */
|
||||
if (ambient_light < 100) {
|
||||
body_control.light_status[0] = 1; /* Headlights on */
|
||||
} else {
|
||||
body_control.light_status[0] = 0; /* Headlights off */
|
||||
}
|
||||
} else {
|
||||
/* Manual control */
|
||||
body_control.light_status[0] = headlights_on ? 1 : 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&body_control.data_mutex);
|
||||
}
|
||||
|
||||
/* Process CAN Messages */
|
||||
static void process_can_messages(void) {
|
||||
CanMessage_t message;
|
||||
|
||||
/* Check for body control messages */
|
||||
if (can_receive_message(&message, 0) == KERNEL_OK) {
|
||||
/* Process message based on ID */
|
||||
switch (message.id.id) {
|
||||
case 0x100: /* Door control command */
|
||||
handle_door_command(&message);
|
||||
break;
|
||||
|
||||
case 0x101: /* Lighting command */
|
||||
handle_lighting_command(&message);
|
||||
break;
|
||||
|
||||
case 0x102: /* Alarm command */
|
||||
handle_alarm_command(&message);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle Door Command */
|
||||
static void handle_door_command(const CanMessage_t* message) {
|
||||
uint8_t door = message->data[0];
|
||||
uint8_t action = message->data[1];
|
||||
|
||||
if (door < 4) {
|
||||
switch (action) {
|
||||
case 0: /* Lock */
|
||||
gpio_write(1, door, true);
|
||||
break;
|
||||
case 1: /* Unlock */
|
||||
gpio_write(1, door, false);
|
||||
break;
|
||||
case 2: /* Open */
|
||||
gpio_write(3, door, true);
|
||||
break;
|
||||
case 3: /* Close */
|
||||
gpio_write(3, door, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Process Alarm System */
|
||||
static void process_alarm_system(void) {
|
||||
/* Check if alarm is armed */
|
||||
static bool alarm_armed = true;
|
||||
|
||||
if (alarm_armed && !body_control.alarm_active) {
|
||||
/* Check for intrusion */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (body_control.door_status[i] == 1) {
|
||||
/* Door opened while armed */
|
||||
body_control.alarm_active = true;
|
||||
|
||||
/* Send alarm message */
|
||||
CanMessage_t alarm_msg;
|
||||
alarm_msg.id.id = 0x200;
|
||||
alarm_msg.length = 2;
|
||||
alarm_msg.data[0] = i; /* Door number */
|
||||
alarm_msg.data[1] = 1; /* Alarm active */
|
||||
|
||||
can_send_message(&alarm_msg, 100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @file door_control.c
|
||||
* @brief Door control module
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "can_driver.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Door Control State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
uint8_t door_position[4]; /* 0=closed, 1=opening, 2=open, 3=closing */
|
||||
uint8_t door_lock[4]; /* 0=unlocked, 1=locked */
|
||||
uint32_t door_timer[4];
|
||||
bool window_position[4]; /* 0=down, 1=up */
|
||||
Mutex_t mutex;
|
||||
} DoorControlState_t;
|
||||
|
||||
static DoorControlState_t door_control;
|
||||
|
||||
/* Initialize Door Control */
|
||||
KernelStatus_t door_control_init(void) {
|
||||
if (door_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&door_control, 0, sizeof(DoorControlState_t));
|
||||
|
||||
/* Initialize door positions */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
door_control.door_position[i] = 0; /* Closed */
|
||||
door_control.door_lock[i] = 1; /* Locked */
|
||||
door_control.window_position[i] = 0; /* Down */
|
||||
}
|
||||
|
||||
mutex_create(&door_control.mutex, false);
|
||||
|
||||
door_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Open Door */
|
||||
KernelStatus_t door_open(uint8_t door) {
|
||||
if (!door_control.initialized || door >= 4) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
/* Check if door is locked */
|
||||
if (door_control.door_lock[door] == 1) {
|
||||
mutex_unlock(&door_control.mutex);
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Start opening */
|
||||
door_control.door_position[door] = 1; /* Opening */
|
||||
door_control.door_timer[door] = kernel_get_tick_count();
|
||||
|
||||
/* Activate door motor */
|
||||
gpio_write(3, door, true);
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Close Door */
|
||||
KernelStatus_t door_close(uint8_t door) {
|
||||
if (!door_control.initialized || door >= 4) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
/* Start closing */
|
||||
door_control.door_position[door] = 3; /* Closing */
|
||||
door_control.door_timer[door] = kernel_get_tick_count();
|
||||
|
||||
/* Reverse door motor */
|
||||
gpio_write(3, door, false);
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Lock Door */
|
||||
KernelStatus_t door_lock(uint8_t door) {
|
||||
if (!door_control.initialized || door >= 4) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
door_control.door_lock[door] = 1;
|
||||
gpio_write(1, door, true); /* Lock */
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Unlock Door */
|
||||
KernelStatus_t door_unlock(uint8_t door) {
|
||||
if (!door_control.initialized || door >= 4) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
door_control.door_lock[door] = 0;
|
||||
gpio_write(1, door, false); /* Unlock */
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Control Window */
|
||||
KernelStatus_t window_control(uint8_t door, bool up) {
|
||||
if (!door_control.initialized || door >= 4) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
door_control.window_position[door] = up ? 1 : 0;
|
||||
gpio_write(4, door, up); /* Window motor */
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Door Control Task */
|
||||
void door_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Process door operations */
|
||||
mutex_lock(&door_control.mutex, 100);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
/* Check if door is moving */
|
||||
if (door_control.door_position[i] == 1) {
|
||||
/* Check if opening complete */
|
||||
if ((kernel_get_tick_count() - door_control.door_timer[i]) > 3000) {
|
||||
door_control.door_position[i] = 2; /* Open */
|
||||
gpio_write(3, i, false); /* Stop motor */
|
||||
}
|
||||
} else if (door_control.door_position[i] == 3) {
|
||||
/* Check if closing complete */
|
||||
if ((kernel_get_tick_count() - door_control.door_timer[i]) > 3000) {
|
||||
door_control.door_position[i] = 0; /* Closed */
|
||||
gpio_write(3, i, false); /* Stop motor */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&door_control.mutex);
|
||||
|
||||
/* 100ms period */
|
||||
kernel_delay(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* @file lighting_control.c
|
||||
* @brief Lighting control module
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "can_driver.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Lighting Control State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
uint8_t light_state[10]; /* 0=off, 1=on, 2=auto */
|
||||
uint16_t light_intensity[10]; /* 0-10000 (100.00%) */
|
||||
uint8_t ambient_light_level;
|
||||
bool daytime_running_active;
|
||||
Mutex_t mutex;
|
||||
} LightingControlState_t;
|
||||
|
||||
/* Light Types */
|
||||
typedef enum {
|
||||
LIGHT_HEADLIGHT_LOW = 0,
|
||||
LIGHT_HEADLIGHT_HIGH = 1,
|
||||
LIGHT_FOG_FRONT = 2,
|
||||
LIGHT_FOG_REAR = 3,
|
||||
LIGHT_TURN_LEFT = 4,
|
||||
LIGHT_TURN_RIGHT = 5,
|
||||
LIGHT_BRAKE = 6,
|
||||
LIGHT_REVERSE = 7,
|
||||
LIGHT_INTERIOR = 8,
|
||||
LIGHT_DAYTIME_RUNNING = 9
|
||||
} LightType_t;
|
||||
|
||||
static LightingControlState_t lighting_control;
|
||||
|
||||
/* Initialize Lighting Control */
|
||||
KernelStatus_t lighting_control_init(void) {
|
||||
if (lighting_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&lighting_control, 0, sizeof(LightingControlState_t));
|
||||
|
||||
/* Initialize lights */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lighting_control.light_state[i] = 0; /* Off */
|
||||
lighting_control.light_intensity[i] = 0;
|
||||
}
|
||||
|
||||
lighting_control.daytime_running_active = true;
|
||||
|
||||
mutex_create(&lighting_control.mutex, false);
|
||||
|
||||
lighting_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set Light State */
|
||||
KernelStatus_t light_set_state(LightType_t light, uint8_t state) {
|
||||
if (!lighting_control.initialized || light >= 10) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&lighting_control.mutex, 100);
|
||||
|
||||
lighting_control.light_state[light] = state;
|
||||
|
||||
/* Update GPIO */
|
||||
switch (light) {
|
||||
case LIGHT_HEADLIGHT_LOW:
|
||||
gpio_write(5, 0, state == 1);
|
||||
break;
|
||||
case LIGHT_HEADLIGHT_HIGH:
|
||||
gpio_write(5, 1, state == 1);
|
||||
break;
|
||||
case LIGHT_FOG_FRONT:
|
||||
gpio_write(5, 2, state == 1);
|
||||
break;
|
||||
case LIGHT_FOG_REAR:
|
||||
gpio_write(5, 3, state == 1);
|
||||
break;
|
||||
case LIGHT_TURN_LEFT:
|
||||
gpio_write(5, 4, state == 1);
|
||||
break;
|
||||
case LIGHT_TURN_RIGHT:
|
||||
gpio_write(5, 5, state == 1);
|
||||
break;
|
||||
case LIGHT_BRAKE:
|
||||
gpio_write(5, 6, state == 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&lighting_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set Light Intensity */
|
||||
KernelStatus_t light_set_intensity(LightType_t light, uint16_t intensity) {
|
||||
if (!lighting_control.initialized || light >= 10 || intensity > 10000) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&lighting_control.mutex, 100);
|
||||
|
||||
lighting_control.light_intensity[light] = intensity;
|
||||
|
||||
/* Update PWM for dimmable lights */
|
||||
switch (light) {
|
||||
case LIGHT_INTERIOR:
|
||||
pwm_set_duty_cycle(2, 0, intensity);
|
||||
break;
|
||||
case LIGHT_DAYTIME_RUNNING:
|
||||
pwm_set_duty_cycle(2, 1, intensity);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&lighting_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Turn Signal Control */
|
||||
KernelStatus_t light_turn_signal(LightType_t direction, uint8_t flashes) {
|
||||
if (!lighting_control.initialized ||
|
||||
(direction != LIGHT_TURN_LEFT && direction != LIGHT_TURN_RIGHT)) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Flash turn signal */
|
||||
for (uint8_t i = 0; i < flashes; i++) {
|
||||
light_set_state(direction, 1);
|
||||
kernel_delay(500);
|
||||
light_set_state(direction, 0);
|
||||
kernel_delay(500);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Lighting Control Task */
|
||||
void lighting_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Read ambient light sensor */
|
||||
uint16_t ambient = adc_read_single(4);
|
||||
|
||||
mutex_lock(&lighting_control.mutex, 100);
|
||||
|
||||
/* Update ambient light level */
|
||||
lighting_control.ambient_light_level = ambient / 41; /* 0-100 */
|
||||
|
||||
/* Automatic headlight control */
|
||||
if (lighting_control.light_state[LIGHT_HEADLIGHT_LOW] == 2) {
|
||||
/* Auto mode */
|
||||
if (lighting_control.ambient_light_level < 30) {
|
||||
gpio_write(5, 0, true); /* Turn on headlights */
|
||||
lighting_control.light_state[LIGHT_HEADLIGHT_LOW] = 1;
|
||||
} else {
|
||||
gpio_write(5, 0, false); /* Turn off headlights */
|
||||
lighting_control.light_state[LIGHT_HEADLIGHT_LOW] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Daytime running lights */
|
||||
if (lighting_control.daytime_running_active) {
|
||||
pwm_set_duty_cycle(2, 1, 5000); /* 50% intensity */
|
||||
}
|
||||
|
||||
mutex_unlock(&lighting_control.mutex);
|
||||
|
||||
/* 100ms period */
|
||||
kernel_delay(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @file brake_control.h
|
||||
* @brief Brake control module interface
|
||||
*/
|
||||
|
||||
#ifndef BRAKE_CONTROL_H
|
||||
#define BRAKE_CONTROL_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Brake Control Parameters */
|
||||
#define BRAKE_CONTROL_PERIOD_MS 5
|
||||
#define ABS_CONTROL_PERIOD_MS 1
|
||||
#define WHEEL_SPEED_SENSOR_COUNT 4
|
||||
#define MAX_BRAKE_PRESSURE 20000 /* kPa */
|
||||
#define ABS_SLIP_THRESHOLD 0.2f
|
||||
#define ABS_DECELERATION_THRESHOLD -10.0f /* m/s² */
|
||||
|
||||
/* Brake States */
|
||||
typedef enum {
|
||||
BRAKE_STATE_IDLE = 0,
|
||||
BRAKE_STATE_NORMAL_BRAKING = 1,
|
||||
BRAKE_STATE_ABS_ACTIVE = 2,
|
||||
BRAKE_STATE_EMERGENCY = 3,
|
||||
BRAKE_STATE_FAULT = 4
|
||||
} BrakeState_t;
|
||||
|
||||
/* Wheel Speed Data */
|
||||
typedef struct {
|
||||
float wheel_speed[WHEEL_SPEED_SENSOR_COUNT]; /* km/h */
|
||||
float wheel_acceleration[WHEEL_SPEED_SENSOR_COUNT]; /* m/s² */
|
||||
uint16_t wheel_sensor_raw[WHEEL_SPEED_SENSOR_COUNT];
|
||||
bool sensor_fault[WHEEL_SPEED_SENSOR_COUNT];
|
||||
} WheelSpeedData_t;
|
||||
|
||||
/* Brake System Data */
|
||||
typedef struct {
|
||||
uint16_t brake_pedal_position;
|
||||
uint16_t brake_pressure;
|
||||
float vehicle_speed;
|
||||
float vehicle_deceleration;
|
||||
bool abs_active;
|
||||
bool brake_light;
|
||||
uint8_t abs_fault_code;
|
||||
} BrakeSystemData_t;
|
||||
|
||||
/* Brake Control Functions */
|
||||
KernelStatus_t brake_control_init(void);
|
||||
KernelStatus_t brake_control_start(void);
|
||||
KernelStatus_t brake_control_stop(void);
|
||||
KernelStatus_t brake_control_get_data(BrakeSystemData_t* data);
|
||||
KernelStatus_t brake_control_get_wheel_data(WheelSpeedData_t* data);
|
||||
BrakeState_t brake_control_get_state(void);
|
||||
void brake_control_task(void* parameters);
|
||||
void abs_control_task(void* parameters);
|
||||
|
||||
#endif /* BRAKE_CONTROL_H */
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @file abs_control.c
|
||||
* @brief Anti-lock Braking System control
|
||||
*/
|
||||
|
||||
#include "brake_control.h"
|
||||
#include "gpio_driver.h"
|
||||
#include <math.h>
|
||||
|
||||
/* ABS Control State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
bool abs_active;
|
||||
float reference_speed;
|
||||
float slip[WHEEL_SPEED_SENSOR_COUNT];
|
||||
uint8_t control_phase; /* 0=increase, 1=hold, 2=decrease */
|
||||
uint32_t phase_timer;
|
||||
Mutex_t mutex;
|
||||
} AbsControlState_t;
|
||||
|
||||
static AbsControlState_t abs_control;
|
||||
|
||||
/* Initialize ABS */
|
||||
KernelStatus_t abs_control_init(void) {
|
||||
if (abs_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
abs_control.abs_active = false;
|
||||
abs_control.reference_speed = 0;
|
||||
abs_control.control_phase = 0;
|
||||
abs_control.phase_timer = 0;
|
||||
|
||||
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||
abs_control.slip[i] = 0;
|
||||
}
|
||||
|
||||
mutex_create(&abs_control.mutex, false);
|
||||
|
||||
abs_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* ABS Control Task */
|
||||
void abs_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Wait for next ABS control period */
|
||||
kernel_delay(ABS_CONTROL_PERIOD_MS);
|
||||
|
||||
WheelSpeedData_t wheels;
|
||||
brake_control_get_wheel_data(&wheels);
|
||||
|
||||
/* Check if ABS should be active */
|
||||
if (brake_control_get_state() == BRAKE_STATE_NORMAL_BRAKING &&
|
||||
check_abs_activation(&wheels)) {
|
||||
|
||||
abs_control.abs_active = true;
|
||||
control_abs(&wheels);
|
||||
} else {
|
||||
abs_control.abs_active = false;
|
||||
abs_control.control_phase = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check ABS Activation Conditions */
|
||||
static bool check_abs_activation(const WheelSpeedData_t* wheels) {
|
||||
/* Calculate reference speed (maximum wheel speed) */
|
||||
float max_speed = 0;
|
||||
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||
if (wheels->wheel_speed[i] > max_speed) {
|
||||
max_speed = wheels->wheel_speed[i];
|
||||
}
|
||||
}
|
||||
|
||||
abs_control.reference_speed = max_speed;
|
||||
|
||||
/* Calculate slip for each wheel */
|
||||
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||
if (max_speed > 5.0f) { /* Only calculate above 5 km/h */
|
||||
abs_control.slip[i] = (max_speed - wheels->wheel_speed[i]) / max_speed;
|
||||
} else {
|
||||
abs_control.slip[i] = 0;
|
||||
}
|
||||
|
||||
/* Check if slip exceeds threshold */
|
||||
if (abs_control.slip[i] > ABS_SLIP_THRESHOLD) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for excessive deceleration */
|
||||
if (wheels->wheel_acceleration[i] < ABS_DECELERATION_THRESHOLD) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ABS Control Algorithm */
|
||||
static void control_abs(const WheelSpeedData_t* wheels) {
|
||||
mutex_lock(&abs_control.mutex, 100);
|
||||
|
||||
/* Simple ABS control algorithm */
|
||||
switch (abs_control.control_phase) {
|
||||
case 0: /* Increase pressure */
|
||||
if (abs_control.slip[0] > ABS_SLIP_THRESHOLD) {
|
||||
abs_control.control_phase = 2; /* Switch to decrease */
|
||||
abs_control.phase_timer = kernel_get_tick_count();
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: /* Hold pressure */
|
||||
if ((kernel_get_tick_count() - abs_control.phase_timer) > 10) {
|
||||
abs_control.control_phase = 0; /* Switch to increase */
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* Decrease pressure */
|
||||
if (abs_control.slip[0] < ABS_SLIP_THRESHOLD * 0.5f) {
|
||||
abs_control.control_phase = 1; /* Switch to hold */
|
||||
abs_control.phase_timer = kernel_get_tick_count();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&abs_control.mutex);
|
||||
|
||||
/* Control brake pressure valves */
|
||||
control_pressure_valves();
|
||||
}
|
||||
|
||||
/* Control Pressure Valves */
|
||||
static void control_pressure_valves(void) {
|
||||
switch (abs_control.control_phase) {
|
||||
case 0: /* Increase pressure */
|
||||
gpio_write(0, 0, false); /* Inlet valve open */
|
||||
gpio_write(0, 1, false); /* Outlet valve closed */
|
||||
break;
|
||||
|
||||
case 1: /* Hold pressure */
|
||||
gpio_write(0, 0, true); /* Inlet valve closed */
|
||||
gpio_write(0, 1, false); /* Outlet valve closed */
|
||||
break;
|
||||
|
||||
case 2: /* Decrease pressure */
|
||||
gpio_write(0, 0, true); /* Inlet valve closed */
|
||||
gpio_write(0, 1, true); /* Outlet valve open */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get ABS Status */
|
||||
bool abs_control_is_active(void) {
|
||||
return abs_control.abs_active;
|
||||
}
|
||||
|
||||
/* Get Slip Values */
|
||||
KernelStatus_t abs_control_get_slip(float* slip, uint8_t* count) {
|
||||
if (slip == NULL || count == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&abs_control.mutex, 100);
|
||||
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||
slip[i] = abs_control.slip[i];
|
||||
}
|
||||
*count = WHEEL_SPEED_SENSOR_COUNT;
|
||||
mutex_unlock(&abs_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @file brake_task.c
|
||||
* @brief Main brake control task
|
||||
*/
|
||||
|
||||
#include "brake_control.h"
|
||||
#include "adc_driver.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "dtc_manager.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Brake Control State */
|
||||
typedef struct {
|
||||
BrakeState_t state;
|
||||
BrakeSystemData_t data;
|
||||
WheelSpeedData_t wheels;
|
||||
uint16_t target_pressure;
|
||||
uint16_t actual_pressure;
|
||||
Mutex_t data_mutex;
|
||||
bool initialized;
|
||||
} BrakeControlState_t;
|
||||
|
||||
static BrakeControlState_t brake_control;
|
||||
|
||||
/* Initialize Brake Control */
|
||||
KernelStatus_t brake_control_init(void) {
|
||||
if (brake_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memset(&brake_control, 0, sizeof(BrakeControlState_t));
|
||||
brake_control.state = BRAKE_STATE_IDLE;
|
||||
|
||||
mutex_create(&brake_control.data_mutex, false);
|
||||
|
||||
/* Initialize ADC for brake sensors */
|
||||
adc_init(1, &(AdcConfig_t){
|
||||
.resolution = ADC_RESOLUTION_12BIT,
|
||||
.mode = ADC_MODE_CONTINUOUS,
|
||||
.channel_count = 2,
|
||||
.channels = {
|
||||
{.channel = 0}, /* Brake pedal position */
|
||||
{.channel = 1} /* Brake pressure */
|
||||
}
|
||||
});
|
||||
|
||||
/* Initialize PWM for brake pressure control */
|
||||
pwm_init(1, &(PwmConfig_t){
|
||||
.frequency_hz = 2000,
|
||||
.channel_count = 1,
|
||||
.channels = {
|
||||
{.channel = 0, .duty_cycle = 0} /* Brake pressure valve */
|
||||
}
|
||||
});
|
||||
|
||||
brake_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Brake Control Task */
|
||||
void brake_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Wait for next brake control period */
|
||||
kernel_delay(BRAKE_CONTROL_PERIOD_MS);
|
||||
|
||||
/* Read brake sensors */
|
||||
read_brake_sensors();
|
||||
|
||||
/* Process brake logic */
|
||||
process_brake_logic();
|
||||
|
||||
/* Check for faults */
|
||||
check_brake_faults();
|
||||
}
|
||||
}
|
||||
|
||||
/* Read Brake Sensors */
|
||||
static void read_brake_sensors(void) {
|
||||
uint16_t brake_pedal = 0;
|
||||
uint16_t brake_pressure = 0;
|
||||
|
||||
adc_read_channel(1, 0, &brake_pedal, 10);
|
||||
adc_read_channel(1, 1, &brake_pressure, 10);
|
||||
|
||||
mutex_lock(&brake_control.data_mutex, 100);
|
||||
brake_control.data.brake_pedal_position = brake_pedal;
|
||||
brake_control.data.brake_pressure = brake_pressure;
|
||||
mutex_unlock(&brake_control.data_mutex);
|
||||
}
|
||||
|
||||
/* Process Brake Logic */
|
||||
static void process_brake_logic(void) {
|
||||
mutex_lock(&brake_control.data_mutex, 100);
|
||||
|
||||
/* Check brake pedal position */
|
||||
if (brake_control.data.brake_pedal_position > 100) {
|
||||
brake_control.state = BRAKE_STATE_NORMAL_BRAKING;
|
||||
brake_control.data.brake_light = true;
|
||||
|
||||
/* Calculate target pressure based on pedal position */
|
||||
brake_control.target_pressure =
|
||||
(brake_control.data.brake_pedal_position - 100) * 200;
|
||||
|
||||
/* Limit pressure */
|
||||
if (brake_control.target_pressure > MAX_BRAKE_PRESSURE) {
|
||||
brake_control.target_pressure = MAX_BRAKE_PRESSURE;
|
||||
}
|
||||
} else {
|
||||
brake_control.state = BRAKE_STATE_IDLE;
|
||||
brake_control.data.brake_light = false;
|
||||
brake_control.target_pressure = 0;
|
||||
}
|
||||
|
||||
/* Check for emergency braking */
|
||||
if (brake_control.data.brake_pedal_position > 900 &&
|
||||
brake_control.data.vehicle_deceleration < -8.0f) {
|
||||
brake_control.state = BRAKE_STATE_EMERGENCY;
|
||||
|
||||
/* Maximum braking force */
|
||||
brake_control.target_pressure = MAX_BRAKE_PRESSURE;
|
||||
}
|
||||
|
||||
mutex_unlock(&brake_control.data_mutex);
|
||||
|
||||
/* Update brake pressure control */
|
||||
update_brake_pressure();
|
||||
}
|
||||
|
||||
/* Update Brake Pressure */
|
||||
static void update_brake_pressure(void) {
|
||||
/* Simple PID pressure control */
|
||||
int32_t pressure_error = brake_control.target_pressure -
|
||||
brake_control.actual_pressure;
|
||||
|
||||
/* Calculate valve duty cycle */
|
||||
uint16_t valve_duty = 0;
|
||||
|
||||
if (pressure_error > 100) {
|
||||
valve_duty = 10000; /* Full open */
|
||||
} else if (pressure_error > 0) {
|
||||
valve_duty = (uint16_t)((pressure_error * 10000) / 100);
|
||||
} else {
|
||||
valve_duty = 0; /* Closed */
|
||||
}
|
||||
|
||||
/* Update PWM */
|
||||
pwm_set_duty_cycle(1, 0, valve_duty);
|
||||
|
||||
/* Update actual pressure */
|
||||
brake_control.actual_pressure += pressure_error / 10;
|
||||
}
|
||||
|
||||
/* Check Brake Faults */
|
||||
static void check_brake_faults(void) {
|
||||
/* Check wheel speed sensors */
|
||||
for (int i = 0; i < WHEEL_SPEED_SENSOR_COUNT; i++) {
|
||||
if (brake_control.wheels.sensor_fault[i]) {
|
||||
brake_control.state = BRAKE_STATE_FAULT;
|
||||
brake_control.data.abs_fault_code = i + 1;
|
||||
|
||||
/* Add DTC */
|
||||
DtcCode_t dtc_code = {
|
||||
.high_byte = 0x01, /* Chassis */
|
||||
.middle_byte = 0x00,
|
||||
.low_byte = i + 1
|
||||
};
|
||||
dtc_manager_add_dtc(&dtc_code, 4); /* High severity */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file engine_control.h
|
||||
* @brief Engine control module interface
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_CONTROL_H
|
||||
#define ENGINE_CONTROL_H
|
||||
|
||||
#include "kernel.h"
|
||||
#include "engine_parameters.h"
|
||||
|
||||
/* Engine Control Functions */
|
||||
KernelStatus_t engine_control_init(void);
|
||||
KernelStatus_t engine_control_start(void);
|
||||
KernelStatus_t engine_control_stop(void);
|
||||
KernelStatus_t engine_control_get_sensor_data(EngineSensorData_t* data);
|
||||
KernelStatus_t engine_control_get_actuator_data(EngineActuatorData_t* data);
|
||||
KernelStatus_t engine_control_set_actuator_data(const EngineActuatorData_t* data);
|
||||
EngineState_t engine_control_get_state(void);
|
||||
KernelStatus_t engine_control_get_faults(EngineFaultCode_t* faults,
|
||||
uint8_t* count);
|
||||
KernelStatus_t engine_control_clear_faults(void);
|
||||
void engine_control_task(void* parameters);
|
||||
void fuel_injection_task(void* parameters);
|
||||
void ignition_control_task(void* parameters);
|
||||
void sensor_reading_task(void* parameters);
|
||||
|
||||
#endif /* ENGINE_CONTROL_H */
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @file engine_parameters.h
|
||||
* @brief Engine control parameters and calibration data
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_PARAMETERS_H
|
||||
#define ENGINE_PARAMETERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Engine Operating Parameters */
|
||||
#define ENGINE_MAX_RPM 6500
|
||||
#define ENGINE_IDLE_RPM 800
|
||||
#define ENGINE_REDLINE_RPM 6000
|
||||
#define ENGINE_MAX_TORQUE_RPM 4000
|
||||
#define ENGINE_MAX_POWER_RPM 5500
|
||||
|
||||
/* Temperature Limits */
|
||||
#define ENGINE_MAX_COOLANT_TEMP 120 /* °C */
|
||||
#define ENGINE_MIN_COOLANT_TEMP -40 /* °C */
|
||||
#define ENGINE_OPTIMAL_TEMP 90 /* °C */
|
||||
#define ENGINE_MAX_OIL_TEMP 150 /* °C */
|
||||
#define ENGINE_MAX_INTAKE_TEMP 80 /* °C */
|
||||
|
||||
/* Pressure Limits */
|
||||
#define ENGINE_MAX_MANIFOLD_PRESSURE 250 /* kPa */
|
||||
#define ENGINE_MIN_OIL_PRESSURE 100 /* kPa */
|
||||
#define ENGINE_MAX_FUEL_PRESSURE 500 /* kPa */
|
||||
|
||||
/* Fuel System Parameters */
|
||||
#define FUEL_STOICHIOMETRIC_RATIO 14.7f
|
||||
#define FUEL_MAX_INJECTION_TIME 20.0f /* ms */
|
||||
#define FUEL_MIN_INJECTION_TIME 0.5f /* ms */
|
||||
#define FUEL_INJECTOR_FLOW_RATE 250.0f /* cc/min */
|
||||
|
||||
/* Ignition Parameters */
|
||||
#define IGNITION_MAX_ADVANCE 45.0f /* degrees BTDC */
|
||||
#define IGNITION_MIN_ADVANCE -10.0f /* degrees ATDC */
|
||||
#define IGNITION_BASE_ADVANCE 10.0f /* degrees BTDC */
|
||||
#define IGNITION_DWELL_TIME 3.5f /* ms */
|
||||
|
||||
/* Control Loop Parameters */
|
||||
#define ENGINE_CONTROL_PERIOD_MS 1 /* 1ms control loop */
|
||||
#define FUEL_CONTROL_PERIOD_MS 10 /* 10ms fuel update */
|
||||
#define IGNITION_CONTROL_PERIOD_MS 5 /* 5ms ignition update */
|
||||
#define SENSOR_READ_PERIOD_MS 2 /* 2ms sensor reading */
|
||||
|
||||
/* PID Controller Gains */
|
||||
typedef struct {
|
||||
float kp;
|
||||
float ki;
|
||||
float kd;
|
||||
float integral_limit;
|
||||
float output_limit;
|
||||
} PidGains_t;
|
||||
|
||||
/* Fuel Control PID */
|
||||
static const PidGains_t fuel_pid_gains = {
|
||||
.kp = 0.5f,
|
||||
.ki = 0.1f,
|
||||
.kd = 0.05f,
|
||||
.integral_limit = 100.0f,
|
||||
.output_limit = 100.0f
|
||||
};
|
||||
|
||||
/* Idle Control PID */
|
||||
static const PidGains_t idle_pid_gains = {
|
||||
.kp = 0.8f,
|
||||
.ki = 0.2f,
|
||||
.kd = 0.1f,
|
||||
.integral_limit = 50.0f,
|
||||
.output_limit = 100.0f
|
||||
};
|
||||
|
||||
/* Boost Control PID */
|
||||
static const PidGains_t boost_pid_gains = {
|
||||
.kp = 0.3f,
|
||||
.ki = 0.05f,
|
||||
.kd = 0.02f,
|
||||
.integral_limit = 200.0f,
|
||||
.output_limit = 250.0f
|
||||
};
|
||||
|
||||
/* Engine State Enumeration */
|
||||
typedef enum {
|
||||
ENGINE_STATE_OFF = 0,
|
||||
ENGINE_STATE_CRANKING = 1,
|
||||
ENGINE_STATE_RUNNING = 2,
|
||||
ENGINE_STATE_IDLE = 3,
|
||||
ENGINE_STATE_ACCELERATING = 4,
|
||||
ENGINE_STATE_DECELERATING = 5,
|
||||
ENGINE_STATE_FAULT = 6,
|
||||
ENGINE_STATE_LIMP_HOME = 7
|
||||
} EngineState_t;
|
||||
|
||||
/* Engine Sensor Data */
|
||||
typedef struct {
|
||||
uint16_t rpm;
|
||||
uint16_t vehicle_speed;
|
||||
int16_t coolant_temp;
|
||||
int16_t intake_air_temp;
|
||||
int16_t oil_temp;
|
||||
uint16_t manifold_pressure;
|
||||
uint16_t oil_pressure;
|
||||
uint16_t fuel_pressure;
|
||||
uint16_t throttle_position;
|
||||
uint16_t accelerator_pedal;
|
||||
float mass_air_flow;
|
||||
float lambda;
|
||||
float battery_voltage;
|
||||
} EngineSensorData_t;
|
||||
|
||||
/* Engine Actuator Data */
|
||||
typedef struct {
|
||||
uint16_t injector_pulse_width;
|
||||
float ignition_advance;
|
||||
uint16_t idle_air_control;
|
||||
uint16_t boost_control;
|
||||
uint16_t fuel_pump_duty;
|
||||
uint16_t cooling_fan_duty;
|
||||
} EngineActuatorData_t;
|
||||
|
||||
/* Engine Fault Codes */
|
||||
typedef enum {
|
||||
ENGINE_FAULT_NONE = 0,
|
||||
ENGINE_FAULT_COOLANT_TEMP_SENSOR = 1,
|
||||
ENGINE_FAULT_INTAKE_TEMP_SENSOR = 2,
|
||||
ENGINE_FAULT_MANIFOLD_PRESSURE_SENSOR = 3,
|
||||
ENGINE_FAULT_MAF_SENSOR = 4,
|
||||
ENGINE_FAULT_OXYGEN_SENSOR = 5,
|
||||
ENGINE_FAULT_KNOCK_SENSOR = 6,
|
||||
ENGINE_FAULT_CRANKSHAFT_SENSOR = 7,
|
||||
ENGINE_FAULT_CAMSHAFT_SENSOR = 8,
|
||||
ENGINE_FAULT_INJECTOR_1 = 9,
|
||||
ENGINE_FAULT_INJECTOR_2 = 10,
|
||||
ENGINE_FAULT_INJECTOR_3 = 11,
|
||||
ENGINE_FAULT_INJECTOR_4 = 12,
|
||||
ENGINE_FAULT_IGNITION_COIL = 13,
|
||||
ENGINE_FAULT_FUEL_PUMP = 14,
|
||||
ENGINE_FAULT_OVERHEAT = 15,
|
||||
ENGINE_FAULT_LOW_OIL_PRESSURE = 16
|
||||
} EngineFaultCode_t;
|
||||
|
||||
/* Lookup Tables */
|
||||
typedef struct {
|
||||
const uint16_t* rpm_points;
|
||||
const uint16_t* load_points;
|
||||
const uint16_t* values;
|
||||
uint8_t rpm_count;
|
||||
uint8_t load_count;
|
||||
} LookupTable2D_t;
|
||||
|
||||
/* Fuel Map (injection time in microseconds) */
|
||||
static const uint16_t fuel_map_rpm[] = {0, 500, 1000, 2000, 3000, 4000, 5000, 6000};
|
||||
static const uint16_t fuel_map_load[] = {0, 20, 40, 60, 80, 100};
|
||||
static const uint16_t fuel_map_values[][8] = {
|
||||
{0, 1000, 800, 700, 650, 600, 550, 500},
|
||||
{1000, 1500, 1300, 1200, 1100, 1000, 950, 900},
|
||||
{2000, 2500, 2200, 2000, 1800, 1700, 1600, 1500},
|
||||
{3000, 3500, 3200, 3000, 2800, 2600, 2400, 2200},
|
||||
{4000, 4500, 4200, 4000, 3800, 3600, 3400, 3200},
|
||||
{5000, 5500, 5200, 5000, 4800, 4600, 4400, 4200}
|
||||
};
|
||||
|
||||
/* Ignition Advance Map (degrees BTDC) */
|
||||
static const uint16_t ignition_map_rpm[] = {0, 500, 1000, 2000, 3000, 4000, 5000, 6000};
|
||||
static const uint16_t ignition_map_load[] = {0, 20, 40, 60, 80, 100};
|
||||
static const int16_t ignition_map_values[][8] = {
|
||||
{10, 12, 15, 18, 20, 22, 25, 28},
|
||||
{10, 12, 15, 18, 20, 22, 25, 28},
|
||||
{8, 10, 13, 16, 18, 20, 23, 26},
|
||||
{6, 8, 11, 14, 16, 18, 21, 24},
|
||||
{4, 6, 9, 12, 14, 16, 19, 22},
|
||||
{2, 4, 7, 10, 12, 14, 17, 20}
|
||||
};
|
||||
|
||||
#endif /* ENGINE_PARAMETERS_H */
|
||||
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* @file engine_control_task.c
|
||||
* @brief Main engine control task
|
||||
*/
|
||||
|
||||
#include "engine_control.h"
|
||||
#include "fuel_injection.h"
|
||||
#include "ignition_control.h"
|
||||
#include "adc_driver.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "dtc_manager.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Engine Control State */
|
||||
typedef struct {
|
||||
EngineState_t state;
|
||||
EngineSensorData_t sensors;
|
||||
EngineActuatorData_t actuators;
|
||||
EngineFaultCode_t faults[20];
|
||||
uint8_t fault_count;
|
||||
uint32_t engine_run_time;
|
||||
Mutex_t data_mutex;
|
||||
bool initialized;
|
||||
} EngineControlState_t;
|
||||
|
||||
static EngineControlState_t engine_control;
|
||||
|
||||
/* PID Controller Structure */
|
||||
typedef struct {
|
||||
PidGains_t gains;
|
||||
float integral;
|
||||
float previous_error;
|
||||
float output;
|
||||
} PidController_t;
|
||||
|
||||
static PidController_t fuel_pid;
|
||||
static PidController_t idle_pid;
|
||||
static PidController_t boost_pid;
|
||||
|
||||
/* Initialize Engine Control */
|
||||
KernelStatus_t engine_control_init(void) {
|
||||
if (engine_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Initialize state */
|
||||
memset(&engine_control, 0, sizeof(EngineControlState_t));
|
||||
engine_control.state = ENGINE_STATE_OFF;
|
||||
engine_control.fault_count = 0;
|
||||
|
||||
/* Initialize PID controllers */
|
||||
fuel_pid.gains = fuel_pid_gains;
|
||||
fuel_pid.integral = 0;
|
||||
fuel_pid.previous_error = 0;
|
||||
fuel_pid.output = 0;
|
||||
|
||||
idle_pid.gains = idle_pid_gains;
|
||||
idle_pid.integral = 0;
|
||||
idle_pid.previous_error = 0;
|
||||
idle_pid.output = 0;
|
||||
|
||||
boost_pid.gains = boost_pid_gains;
|
||||
boost_pid.integral = 0;
|
||||
boost_pid.previous_error = 0;
|
||||
boost_pid.output = 0;
|
||||
|
||||
/* Create mutex */
|
||||
mutex_create(&engine_control.data_mutex, false);
|
||||
|
||||
/* Initialize sensors */
|
||||
adc_init(0, &(AdcConfig_t){
|
||||
.resolution = ADC_RESOLUTION_12BIT,
|
||||
.mode = ADC_MODE_SCAN,
|
||||
.trigger_source = ADC_TRIGGER_TIMER,
|
||||
.reference = ADC_REFERENCE_VDD,
|
||||
.channel_count = 8,
|
||||
.channels = {
|
||||
{.channel = 0, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Coolant temp */
|
||||
{.channel = 1, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Intake temp */
|
||||
{.channel = 2, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Manifold pressure */
|
||||
{.channel = 3, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Throttle position */
|
||||
{.channel = 4, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Accelerator pedal */
|
||||
{.channel = 5, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Oil pressure */
|
||||
{.channel = 6, .sampling_time = ADC_SAMPLING_28_5_CYCLES}, /* Fuel pressure */
|
||||
{.channel = 7, .sampling_time = ADC_SAMPLING_28_5_CYCLES} /* Battery voltage */
|
||||
}
|
||||
});
|
||||
|
||||
/* Initialize actuators */
|
||||
pwm_init(0, &(PwmConfig_t){
|
||||
.frequency_hz = 1000,
|
||||
.alignment = PWM_ALIGNMENT_EDGE,
|
||||
.channel_count = 4,
|
||||
.channels = {
|
||||
{.channel = 0, .duty_cycle = 0}, /* Injector 1 */
|
||||
{.channel = 1, .duty_cycle = 0}, /* Injector 2 */
|
||||
{.channel = 2, .duty_cycle = 0}, /* Idle air control */
|
||||
{.channel = 3, .duty_cycle = 0} /* Boost control */
|
||||
}
|
||||
});
|
||||
|
||||
engine_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start Engine */
|
||||
KernelStatus_t engine_control_start(void) {
|
||||
if (!engine_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
engine_control.state = ENGINE_STATE_CRANKING;
|
||||
|
||||
/* Start PWM outputs */
|
||||
pwm_start(0);
|
||||
|
||||
/* Start ADC conversions */
|
||||
adc_start_conversion(0);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Stop Engine */
|
||||
KernelStatus_t engine_control_stop(void) {
|
||||
if (!engine_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Stop actuators */
|
||||
pwm_stop(0);
|
||||
|
||||
/* Stop ADC */
|
||||
adc_stop_conversion(0);
|
||||
|
||||
engine_control.state = ENGINE_STATE_OFF;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Main Engine Control Task */
|
||||
void engine_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
TickType_t last_wake_time = kernel_get_tick_count();
|
||||
|
||||
while (1) {
|
||||
/* Wait for next control period */
|
||||
kernel_delay(ENGINE_CONTROL_PERIOD_MS);
|
||||
|
||||
switch (engine_control.state) {
|
||||
case ENGINE_STATE_CRANKING:
|
||||
/* Check RPM for engine start */
|
||||
if (engine_control.sensors.rpm > 400) {
|
||||
engine_control.state = ENGINE_STATE_RUNNING;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_RUNNING:
|
||||
/* Check for idle condition */
|
||||
if (engine_control.sensors.rpm < ENGINE_IDLE_RPM + 50 &&
|
||||
engine_control.sensors.accelerator_pedal < 5) {
|
||||
engine_control.state = ENGINE_STATE_IDLE;
|
||||
}
|
||||
|
||||
/* Check for acceleration */
|
||||
if (engine_control.sensors.accelerator_pedal > 80) {
|
||||
engine_control.state = ENGINE_STATE_ACCELERATING;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_IDLE:
|
||||
/* Idle speed control */
|
||||
idle_pid_control();
|
||||
|
||||
/* Check if leaving idle */
|
||||
if (engine_control.sensors.accelerator_pedal > 5) {
|
||||
engine_control.state = ENGINE_STATE_RUNNING;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_ACCELERATING:
|
||||
/* Acceleration enrichment */
|
||||
engine_control.actuators.injector_pulse_width *= 1.2f;
|
||||
|
||||
/* Check if still accelerating */
|
||||
if (engine_control.sensors.accelerator_pedal < 80) {
|
||||
engine_control.state = ENGINE_STATE_RUNNING;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_DECELERATING:
|
||||
/* Deceleration fuel cutoff */
|
||||
if (engine_control.sensors.rpm > 1500 &&
|
||||
engine_control.sensors.accelerator_pedal < 2) {
|
||||
engine_control.actuators.injector_pulse_width = 0;
|
||||
}
|
||||
|
||||
/* Check if still decelerating */
|
||||
if (engine_control.sensors.accelerator_pedal > 2) {
|
||||
engine_control.state = ENGINE_STATE_RUNNING;
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_FAULT:
|
||||
/* Handle faults */
|
||||
handle_engine_faults();
|
||||
break;
|
||||
|
||||
case ENGINE_STATE_LIMP_HOME:
|
||||
/* Limp home mode */
|
||||
engine_control.actuators.injector_pulse_width =
|
||||
fuel_map_values[0][3]; /* Fixed fuel */
|
||||
engine_control.actuators.ignition_advance =
|
||||
ignition_map_values[0][3]; /* Fixed timing */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update engine run time */
|
||||
engine_control.engine_run_time += ENGINE_CONTROL_PERIOD_MS;
|
||||
|
||||
/* Check for faults */
|
||||
check_engine_faults();
|
||||
|
||||
last_wake_time = kernel_get_tick_count();
|
||||
}
|
||||
}
|
||||
|
||||
/* Idle Speed Control */
|
||||
static void idle_pid_control(void) {
|
||||
int32_t rpm_error = ENGINE_IDLE_RPM - engine_control.sensors.rpm;
|
||||
|
||||
/* PID calculation */
|
||||
idle_pid.integral += rpm_error * (IDLE_CONTROL_PERIOD_MS / 1000.0f);
|
||||
|
||||
/* Limit integral */
|
||||
if (idle_pid.integral > idle_pid.gains.integral_limit) {
|
||||
idle_pid.integral = idle_pid.gains.integral_limit;
|
||||
} else if (idle_pid.integral < -idle_pid.gains.integral_limit) {
|
||||
idle_pid.integral = -idle_pid.gains.integral_limit;
|
||||
}
|
||||
|
||||
float derivative = (rpm_error - idle_pid.previous_error) /
|
||||
(IDLE_CONTROL_PERIOD_MS / 1000.0f);
|
||||
|
||||
idle_pid.output = idle_pid.gains.kp * rpm_error +
|
||||
idle_pid.gains.ki * idle_pid.integral +
|
||||
idle_pid.gains.kd * derivative;
|
||||
|
||||
/* Limit output */
|
||||
if (idle_pid.output > idle_pid.gains.output_limit) {
|
||||
idle_pid.output = idle_pid.gains.output_limit;
|
||||
} else if (idle_pid.output < 0) {
|
||||
idle_pid.output = 0;
|
||||
}
|
||||
|
||||
/* Update actuator */
|
||||
engine_control.actuators.idle_air_control = (uint16_t)idle_pid.output;
|
||||
|
||||
idle_pid.previous_error = rpm_error;
|
||||
}
|
||||
|
||||
/* Check Engine Faults */
|
||||
static void check_engine_faults(void) {
|
||||
/* Check coolant temperature */
|
||||
if (engine_control.sensors.coolant_temp > ENGINE_MAX_COOLANT_TEMP) {
|
||||
add_engine_fault(ENGINE_FAULT_OVERHEAT);
|
||||
engine_control.state = ENGINE_STATE_FAULT;
|
||||
}
|
||||
|
||||
/* Check oil pressure */
|
||||
if (engine_control.sensors.oil_pressure < ENGINE_MIN_OIL_PRESSURE &&
|
||||
engine_control.sensors.rpm > 1000) {
|
||||
add_engine_fault(ENGINE_FAULT_LOW_OIL_PRESSURE);
|
||||
engine_control.state = ENGINE_STATE_FAULT;
|
||||
}
|
||||
|
||||
/* Check battery voltage */
|
||||
if (engine_control.sensors.battery_voltage < 9.0f ||
|
||||
engine_control.sensors.battery_voltage > 16.0f) {
|
||||
add_engine_fault(ENGINE_FAULT_NONE); // Just log the condition
|
||||
}
|
||||
}
|
||||
|
||||
/* Add Engine Fault */
|
||||
static void add_engine_fault(EngineFaultCode_t fault) {
|
||||
if (engine_control.fault_count < 20) {
|
||||
/* Check if fault already exists */
|
||||
for (uint8_t i = 0; i < engine_control.fault_count; i++) {
|
||||
if (engine_control.faults[i] == fault) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
engine_control.faults[engine_control.fault_count] = fault;
|
||||
engine_control.fault_count++;
|
||||
|
||||
/* Add DTC */
|
||||
DtcCode_t dtc_code = {
|
||||
.high_byte = 0x00, /* Powertrain */
|
||||
.middle_byte = 0x00,
|
||||
.low_byte = fault
|
||||
};
|
||||
dtc_manager_add_dtc(&dtc_code, 3); /* Medium severity */
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle Engine Faults */
|
||||
static void handle_engine_faults(void) {
|
||||
for (uint8_t i = 0; i < engine_control.fault_count; i++) {
|
||||
switch (engine_control.faults[i]) {
|
||||
case ENGINE_FAULT_OVERHEAT:
|
||||
/* Enable cooling fan */
|
||||
engine_control.actuators.cooling_fan_duty = 100;
|
||||
|
||||
/* Reduce power */
|
||||
engine_control.actuators.injector_pulse_width *= 0.5f;
|
||||
break;
|
||||
|
||||
case ENGINE_FAULT_LOW_OIL_PRESSURE:
|
||||
/* Immediate engine shutdown */
|
||||
engine_control_stop();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Enter limp home mode */
|
||||
engine_control.state = ENGINE_STATE_LIMP_HOME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get Engine State */
|
||||
EngineState_t engine_control_get_state(void) {
|
||||
return engine_control.state;
|
||||
}
|
||||
|
||||
/* Get Sensor Data */
|
||||
KernelStatus_t engine_control_get_sensor_data(EngineSensorData_t* data) {
|
||||
if (data == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&engine_control.data_mutex, 100);
|
||||
memcpy(data, &engine_control.sensors, sizeof(EngineSensorData_t));
|
||||
mutex_unlock(&engine_control.data_mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Actuator Data */
|
||||
KernelStatus_t engine_control_get_actuator_data(EngineActuatorData_t* data) {
|
||||
if (data == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&engine_control.data_mutex, 100);
|
||||
memcpy(data, &engine_control.actuators, sizeof(EngineActuatorData_t));
|
||||
mutex_unlock(&engine_control.data_mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @file fuel_injection.c
|
||||
* @brief Fuel injection control
|
||||
*/
|
||||
|
||||
#include "engine_control.h"
|
||||
#include "fuel_injection.h"
|
||||
#include "adc_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Fuel Injection State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
float fuel_pressure;
|
||||
float injection_time;
|
||||
uint16_t injector_duty;
|
||||
uint8_t injection_mode; /* 0=sequential, 1=batch, 2=simultaneous */
|
||||
Mutex_t mutex;
|
||||
} FuelInjectionState_t;
|
||||
|
||||
static FuelInjectionState_t fuel_injection;
|
||||
|
||||
/* Initialize Fuel Injection */
|
||||
KernelStatus_t fuel_injection_init(void) {
|
||||
if (fuel_injection.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
fuel_injection.fuel_pressure = 0;
|
||||
fuel_injection.injection_time = 0;
|
||||
fuel_injection.injector_duty = 0;
|
||||
fuel_injection.injection_mode = 0; /* Sequential */
|
||||
|
||||
mutex_create(&fuel_injection.mutex, false);
|
||||
|
||||
fuel_injection.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Fuel Injection Control Task */
|
||||
void fuel_injection_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Wait for next fuel update period */
|
||||
kernel_delay(FUEL_CONTROL_PERIOD_MS);
|
||||
|
||||
EngineSensorData_t sensors;
|
||||
engine_control_get_sensor_data(&sensors);
|
||||
|
||||
/* Calculate required fuel */
|
||||
float required_fuel = calculate_fuel_requirement(&sensors);
|
||||
|
||||
/* Apply corrections */
|
||||
required_fuel = apply_fuel_corrections(required_fuel, &sensors);
|
||||
|
||||
/* Convert to injector pulse width */
|
||||
uint16_t pulse_width = (uint16_t)(required_fuel * 1000); /* Convert to microseconds */
|
||||
|
||||
/* Limit pulse width */
|
||||
if (pulse_width > (uint16_t)(FUEL_MAX_INJECTION_TIME * 1000)) {
|
||||
pulse_width = (uint16_t)(FUEL_MAX_INJECTION_TIME * 1000);
|
||||
}
|
||||
|
||||
/* Update injector PWM */
|
||||
mutex_lock(&fuel_injection.mutex, 100);
|
||||
fuel_injection.injection_time = required_fuel;
|
||||
fuel_injection.injector_duty = calculate_injector_duty(pulse_width,
|
||||
sensors.rpm);
|
||||
mutex_unlock(&fuel_injection.mutex);
|
||||
|
||||
/* Update PWM output */
|
||||
pwm_set_duty_cycle(0, 0, fuel_injection.injector_duty); /* Injector 1 */
|
||||
pwm_set_duty_cycle(0, 1, fuel_injection.injector_duty); /* Injector 2 */
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate Fuel Requirement */
|
||||
static float calculate_fuel_requirement(const EngineSensorData_t* sensors) {
|
||||
/* Basic fuel calculation using speed-density method */
|
||||
float air_mass = 0;
|
||||
|
||||
if (sensors->mass_air_flow > 0) {
|
||||
/* Use MAF sensor if available */
|
||||
air_mass = sensors->mass_air_flow / (sensors->rpm / 2);
|
||||
} else {
|
||||
/* Speed-density calculation */
|
||||
float air_density = 1.225f; /* kg/m³ at sea level */
|
||||
float engine_displacement = 2.0f; /* 2.0L engine */
|
||||
float volumetric_efficiency = 0.85f;
|
||||
|
||||
air_mass = air_density * engine_displacement *
|
||||
volumetric_efficiency * sensors->manifold_pressure / 101.3f;
|
||||
}
|
||||
|
||||
/* Calculate fuel mass for stoichiometric mixture */
|
||||
float fuel_mass = air_mass / FUEL_STOICHIOMETRIC_RATIO;
|
||||
|
||||
/* Convert to injection time */
|
||||
float injection_time = (fuel_mass * 1000000) / FUEL_INJECTOR_FLOW_RATE;
|
||||
|
||||
return injection_time;
|
||||
}
|
||||
|
||||
/* Apply Fuel Corrections */
|
||||
static float apply_fuel_corrections(float base_fuel, const EngineSensorData_t* sensors) {
|
||||
float corrected_fuel = base_fuel;
|
||||
|
||||
/* Lambda correction */
|
||||
if (sensors->lambda > 0) {
|
||||
float lambda_error = 1.0f - sensors->lambda;
|
||||
corrected_fuel *= (1.0f + lambda_error * 0.5f);
|
||||
}
|
||||
|
||||
/* Coolant temperature correction */
|
||||
if (sensors->coolant_temp < 70) {
|
||||
float enrichment = (70 - sensors->coolant_temp) * 0.01f;
|
||||
corrected_fuel *= (1.0f + enrichment);
|
||||
}
|
||||
|
||||
/* Acceleration enrichment */
|
||||
if (sensors->accelerator_pedal > 80) {
|
||||
corrected_fuel *= 1.2f;
|
||||
}
|
||||
|
||||
/* Battery voltage correction */
|
||||
if (sensors->battery_voltage < 12.0f) {
|
||||
corrected_fuel *= (12.0f / sensors->battery_voltage);
|
||||
}
|
||||
|
||||
return corrected_fuel;
|
||||
}
|
||||
|
||||
/* Calculate Injector Duty Cycle */
|
||||
static uint16_t calculate_injector_duty(uint16_t pulse_width_us, uint16_t rpm) {
|
||||
/* Calculate period in microseconds */
|
||||
uint32_t period_us = (60000000UL) / rpm; /* 2 revolutions per cycle */
|
||||
|
||||
/* Calculate duty cycle */
|
||||
uint32_t duty = (pulse_width_us * PWM_MAX_DUTY_CYCLE) / period_us;
|
||||
|
||||
if (duty > PWM_MAX_DUTY_CYCLE) {
|
||||
duty = PWM_MAX_DUTY_CYCLE;
|
||||
}
|
||||
|
||||
return (uint16_t)duty;
|
||||
}
|
||||
|
||||
/* Get Fuel Injection Data */
|
||||
KernelStatus_t fuel_injection_get_data(float* pressure, float* injection_time,
|
||||
uint16_t* duty_cycle) {
|
||||
if (pressure == NULL || injection_time == NULL || duty_cycle == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&fuel_injection.mutex, 100);
|
||||
*pressure = fuel_injection.fuel_pressure;
|
||||
*injection_time = fuel_injection.injection_time;
|
||||
*duty_cycle = fuel_injection.injector_duty;
|
||||
mutex_unlock(&fuel_injection.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file ignition_control.c
|
||||
* @brief Ignition timing control
|
||||
*/
|
||||
|
||||
#include "engine_control.h"
|
||||
#include "ignition_control.h"
|
||||
#include "gpio_driver.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Ignition Control State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
float ignition_advance;
|
||||
float dwell_time;
|
||||
uint8_t ignition_mode;
|
||||
bool knock_detected;
|
||||
uint32_t knock_count;
|
||||
Mutex_t mutex;
|
||||
} IgnitionControlState_t;
|
||||
|
||||
static IgnitionControlState_t ignition_control;
|
||||
|
||||
/* Initialize Ignition Control */
|
||||
KernelStatus_t ignition_control_init(void) {
|
||||
if (ignition_control.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
ignition_control.ignition_advance = IGNITION_BASE_ADVANCE;
|
||||
ignition_control.dwell_time = IGNITION_DWELL_TIME;
|
||||
ignition_control.ignition_mode = 0;
|
||||
ignition_control.knock_detected = false;
|
||||
ignition_control.knock_count = 0;
|
||||
|
||||
mutex_create(&ignition_control.mutex, false);
|
||||
|
||||
ignition_control.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Ignition Control Task */
|
||||
void ignition_control_task(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Wait for next ignition update period */
|
||||
kernel_delay(IGNITION_CONTROL_PERIOD_MS);
|
||||
|
||||
EngineSensorData_t sensors;
|
||||
engine_control_get_sensor_data(&sensors);
|
||||
|
||||
/* Calculate ignition advance */
|
||||
float advance = calculate_ignition_advance(&sensors);
|
||||
|
||||
/* Apply knock correction */
|
||||
if (ignition_control.knock_detected) {
|
||||
advance -= 5.0f; /* Retard timing */
|
||||
ignition_control.knock_detected = false;
|
||||
ignition_control.knock_count++;
|
||||
}
|
||||
|
||||
/* Limit advance */
|
||||
if (advance > IGNITION_MAX_ADVANCE) {
|
||||
advance = IGNITION_MAX_ADVANCE;
|
||||
} else if (advance < IGNITION_MIN_ADVANCE) {
|
||||
advance = IGNITION_MIN_ADVANCE;
|
||||
}
|
||||
|
||||
/* Update ignition control */
|
||||
mutex_lock(&ignition_control.mutex, 100);
|
||||
ignition_control.ignition_advance = advance;
|
||||
mutex_unlock(&ignition_control.mutex);
|
||||
|
||||
/* Update actuator */
|
||||
EngineActuatorData_t actuators;
|
||||
engine_control_get_actuator_data(&actuators);
|
||||
actuators.ignition_advance = advance;
|
||||
engine_control_set_actuator_data(&actuators);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate Ignition Advance */
|
||||
static float calculate_ignition_advance(const EngineSensorData_t* sensors) {
|
||||
float advance = IGNITION_BASE_ADVANCE;
|
||||
|
||||
/* RPM correction */
|
||||
if (sensors->rpm < 1000) {
|
||||
advance += 5.0f; /* More advance at low RPM */
|
||||
} else if (sensors->rpm > 5000) {
|
||||
advance -= 3.0f; /* Less advance at high RPM */
|
||||
}
|
||||
|
||||
/* Load correction */
|
||||
if (sensors->manifold_pressure < 40) {
|
||||
advance += 2.0f; /* More advance at light load */
|
||||
} else if (sensors->manifold_pressure > 80) {
|
||||
advance -= 4.0f; /* Less advance at high load */
|
||||
}
|
||||
|
||||
/* Temperature correction */
|
||||
if (sensors->coolant_temp < 0) {
|
||||
advance += 3.0f; /* More advance when cold */
|
||||
} else if (sensors->coolant_temp > 100) {
|
||||
advance -= 5.0f; /* Less advance when hot */
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
/* Detect Knock */
|
||||
void ignition_control_detect_knock(void) {
|
||||
/* Read knock sensor */
|
||||
uint16_t knock_signal = adc_read_single(7);
|
||||
|
||||
/* Check for knock */
|
||||
if (knock_signal > 200) {
|
||||
ignition_control.knock_detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get Ignition Data */
|
||||
KernelStatus_t ignition_control_get_data(float* advance, float* dwell) {
|
||||
if (advance == NULL || dwell == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
mutex_lock(&ignition_control.mutex, 100);
|
||||
*advance = ignition_control.ignition_advance;
|
||||
*dwell = ignition_control.dwell_time;
|
||||
mutex_unlock(&ignition_control.mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user