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,114 @@
|
||||
/**
|
||||
* @file adc_driver.h
|
||||
* @brief ADC driver interface
|
||||
*/
|
||||
|
||||
#ifndef ADC_DRIVER_H
|
||||
#define ADC_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* ADC Configuration Constants */
|
||||
#define ADC_MAX_INSTANCES 3
|
||||
#define ADC_MAX_CHANNELS 16
|
||||
#define ADC_MAX_SEQUENCES 8
|
||||
|
||||
/* ADC Resolution */
|
||||
typedef enum {
|
||||
ADC_RESOLUTION_6BIT = 6,
|
||||
ADC_RESOLUTION_8BIT = 8,
|
||||
ADC_RESOLUTION_10BIT = 10,
|
||||
ADC_RESOLUTION_12BIT = 12,
|
||||
ADC_RESOLUTION_14BIT = 14,
|
||||
ADC_RESOLUTION_16BIT = 16
|
||||
} AdcResolution_t;
|
||||
|
||||
/* ADC Conversion Modes */
|
||||
typedef enum {
|
||||
ADC_MODE_SINGLE = 0,
|
||||
ADC_MODE_CONTINUOUS = 1,
|
||||
ADC_MODE_SCAN = 2,
|
||||
ADC_MODE_DISCONTINUOUS = 3
|
||||
} AdcConversionMode_t;
|
||||
|
||||
/* ADC Trigger Sources */
|
||||
typedef enum {
|
||||
ADC_TRIGGER_SOFTWARE = 0,
|
||||
ADC_TRIGGER_TIMER = 1,
|
||||
ADC_TRIGGER_EXTERNAL = 2
|
||||
} AdcTriggerSource_t;
|
||||
|
||||
/* ADC Reference Voltage */
|
||||
typedef enum {
|
||||
ADC_REFERENCE_INTERNAL = 0,
|
||||
ADC_REFERENCE_EXTERNAL = 1,
|
||||
ADC_REFERENCE_VDD = 2
|
||||
} AdcReference_t;
|
||||
|
||||
/* ADC Sampling Time */
|
||||
typedef enum {
|
||||
ADC_SAMPLING_1_5_CYCLES = 0,
|
||||
ADC_SAMPLING_7_5_CYCLES = 1,
|
||||
ADC_SAMPLING_13_5_CYCLES = 2,
|
||||
ADC_SAMPLING_28_5_CYCLES = 3,
|
||||
ADC_SAMPLING_41_5_CYCLES = 4,
|
||||
ADC_SAMPLING_55_5_CYCLES = 5,
|
||||
ADC_SAMPLING_71_5_CYCLES = 6,
|
||||
ADC_SAMPLING_239_5_CYCLES = 7
|
||||
} AdcSamplingTime_t;
|
||||
|
||||
/* ADC Callbacks */
|
||||
typedef void (*AdcConversionCompleteCallback_t)(uint16_t* values, uint8_t count);
|
||||
|
||||
/* ADC Channel Configuration */
|
||||
typedef struct {
|
||||
uint8_t channel;
|
||||
AdcSamplingTime_t sampling_time;
|
||||
bool enable_watchdog;
|
||||
uint16_t watchdog_high_threshold;
|
||||
uint16_t watchdog_low_threshold;
|
||||
} AdcChannelConfig_t;
|
||||
|
||||
/* ADC Configuration */
|
||||
typedef struct {
|
||||
AdcResolution_t resolution;
|
||||
AdcConversionMode_t mode;
|
||||
AdcTriggerSource_t trigger_source;
|
||||
AdcReference_t reference;
|
||||
bool enable_dma;
|
||||
uint32_t conversion_frequency;
|
||||
AdcChannelConfig_t channels[ADC_MAX_CHANNELS];
|
||||
uint8_t channel_count;
|
||||
AdcConversionCompleteCallback_t conversion_complete_callback;
|
||||
} AdcConfig_t;
|
||||
|
||||
/* ADC Statistics */
|
||||
typedef struct {
|
||||
uint32_t conversions_completed;
|
||||
uint32_t conversions_failed;
|
||||
uint32_t watchdog_events;
|
||||
uint32_t overrun_errors;
|
||||
uint32_t dma_transfers;
|
||||
uint32_t average_conversion_time_us;
|
||||
} AdcStatistics_t;
|
||||
|
||||
/* ADC Driver Interface */
|
||||
KernelStatus_t adc_init(uint8_t instance, AdcConfig_t* config);
|
||||
KernelStatus_t adc_deinit(uint8_t instance);
|
||||
KernelStatus_t adc_start_conversion(uint8_t instance);
|
||||
KernelStatus_t adc_stop_conversion(uint8_t instance);
|
||||
KernelStatus_t adc_read_channel(uint8_t instance, uint8_t channel, uint16_t* value,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t adc_read_channels(uint8_t instance, uint16_t* values, uint8_t count,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t adc_start_dma(uint8_t instance, uint16_t* buffer, uint16_t length);
|
||||
KernelStatus_t adc_stop_dma(uint8_t instance);
|
||||
KernelStatus_t adc_calibrate(uint8_t instance);
|
||||
KernelStatus_t adc_get_statistics(uint8_t instance, AdcStatistics_t* stats);
|
||||
float adc_convert_to_voltage(uint16_t adc_value, AdcResolution_t resolution,
|
||||
float reference_voltage);
|
||||
void adc_process_interrupt(uint8_t instance);
|
||||
|
||||
#endif /* ADC_DRIVER_H */
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file can_driver.h
|
||||
* @brief CAN (Controller Area Network) driver interface
|
||||
* @note Supports classical CAN and CAN FD
|
||||
*/
|
||||
|
||||
#ifndef CAN_DRIVER_H
|
||||
#define CAN_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* CAN Configuration Constants */
|
||||
#define CAN_MAX_MESSAGE_LENGTH 8 /* Classical CAN */
|
||||
#define CAN_FD_MAX_MESSAGE_LENGTH 64 /* CAN FD */
|
||||
#define CAN_MAX_FILTERS 32
|
||||
#define CAN_MAX_TX_MAILBOXES 3
|
||||
#define CAN_MAX_RX_FIFO_DEPTH 16
|
||||
|
||||
/* CAN Baudrates */
|
||||
typedef enum {
|
||||
CAN_BAUD_125K = 125000,
|
||||
CAN_BAUD_250K = 250000,
|
||||
CAN_BAUD_500K = 500000,
|
||||
CAN_BAUD_1M = 1000000,
|
||||
CAN_FD_BAUD_2M = 2000000,
|
||||
CAN_FD_BAUD_5M = 5000000
|
||||
} CanBaudrate_t;
|
||||
|
||||
/* CAN Message Types */
|
||||
typedef enum {
|
||||
CAN_FRAME_CLASSIC = 0,
|
||||
CAN_FRAME_FD = 1,
|
||||
CAN_FRAME_FD_BRS = 2 /* FD with Bit Rate Switch */
|
||||
} CanFrameType_t;
|
||||
|
||||
/* CAN Frame Format */
|
||||
typedef enum {
|
||||
CAN_FORMAT_STANDARD = 0, /* 11-bit identifier */
|
||||
CAN_FORMAT_EXTENDED = 1 /* 29-bit identifier */
|
||||
} CanFrameFormat_t;
|
||||
|
||||
/* CAN Message ID */
|
||||
typedef struct {
|
||||
uint32_t id : 29;
|
||||
CanFrameFormat_t format : 1;
|
||||
bool is_remote : 1;
|
||||
bool is_extended : 1;
|
||||
} CanMessageId_t;
|
||||
|
||||
/* CAN Message Structure */
|
||||
typedef struct {
|
||||
CanMessageId_t id;
|
||||
CanFrameType_t type;
|
||||
uint8_t data[CAN_FD_MAX_MESSAGE_LENGTH];
|
||||
uint8_t length;
|
||||
uint32_t timestamp;
|
||||
bool is_rx;
|
||||
} CanMessage_t;
|
||||
|
||||
/* CAN Filter Configuration */
|
||||
typedef struct {
|
||||
uint32_t filter_id;
|
||||
uint32_t filter_mask;
|
||||
CanFrameFormat_t format;
|
||||
bool enable;
|
||||
} CanFilterConfig_t;
|
||||
|
||||
/* CAN Statistics */
|
||||
typedef struct {
|
||||
uint32_t tx_messages;
|
||||
uint32_t rx_messages;
|
||||
uint32_t tx_errors;
|
||||
uint32_t rx_errors;
|
||||
uint32_t bus_off_count;
|
||||
uint32_t error_warning_count;
|
||||
uint32_t error_passive_count;
|
||||
uint32_t tx_overflow;
|
||||
uint32_t rx_overflow;
|
||||
} CanStatistics_t;
|
||||
|
||||
/* CAN Callbacks */
|
||||
typedef void (*CanRxCallback_t)(const CanMessage_t* message);
|
||||
typedef void (*CanTxCallback_t)(uint32_t mailbox, bool success);
|
||||
typedef void (*CanErrorCallback_t)(uint32_t error_code);
|
||||
|
||||
/* CAN Configuration Structure */
|
||||
typedef struct {
|
||||
CanBaudrate_t nominal_baudrate;
|
||||
CanBaudrate_t data_baudrate; /* For CAN FD */
|
||||
CanFrameType_t frame_type;
|
||||
bool enable_fd;
|
||||
bool enable_automatic_retransmission;
|
||||
CanFilterConfig_t filters[CAN_MAX_FILTERS];
|
||||
uint8_t filter_count;
|
||||
CanRxCallback_t rx_callback;
|
||||
CanTxCallback_t tx_callback;
|
||||
CanErrorCallback_t error_callback;
|
||||
} CanConfig_t;
|
||||
|
||||
/* CAN Driver Interface */
|
||||
KernelStatus_t can_init(CanConfig_t* config);
|
||||
KernelStatus_t can_deinit(void);
|
||||
KernelStatus_t can_send_message(const CanMessage_t* message, uint32_t timeout_ms);
|
||||
KernelStatus_t can_receive_message(CanMessage_t* message, uint32_t timeout_ms);
|
||||
KernelStatus_t can_configure_filter(const CanFilterConfig_t* filter);
|
||||
KernelStatus_t can_set_baudrate(CanBaudrate_t baudrate);
|
||||
KernelStatus_t can_get_statistics(CanStatistics_t* stats);
|
||||
KernelStatus_t can_clear_statistics(void);
|
||||
bool can_is_bus_off(void);
|
||||
KernelStatus_t can_recover_bus_off(void);
|
||||
void can_process_interrupt(void);
|
||||
|
||||
/* CAN FD Specific Functions */
|
||||
KernelStatus_t can_fd_set_data_baudrate(CanBaudrate_t baudrate);
|
||||
KernelStatus_t can_fd_set_transmit_delay(uint16_t delay_us);
|
||||
bool can_fd_is_enabled(void);
|
||||
|
||||
#endif /* CAN_DRIVER_H */
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @file gpio_driver.h
|
||||
* @brief GPIO driver interface
|
||||
*/
|
||||
|
||||
#ifndef GPIO_DRIVER_H
|
||||
#define GPIO_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* GPIO Configuration Constants */
|
||||
#define GPIO_MAX_PORTS 8
|
||||
#define GPIO_MAX_PINS_PER_PORT 16
|
||||
#define GPIO_MAX_INTERRUPTS 32
|
||||
|
||||
/* GPIO Modes */
|
||||
typedef enum {
|
||||
GPIO_MODE_INPUT = 0,
|
||||
GPIO_MODE_OUTPUT = 1,
|
||||
GPIO_MODE_ALTERNATE = 2,
|
||||
GPIO_MODE_ANALOG = 3
|
||||
} GpioMode_t;
|
||||
|
||||
/* GPIO Output Types */
|
||||
typedef enum {
|
||||
GPIO_OUTPUT_PUSH_PULL = 0,
|
||||
GPIO_OUTPUT_OPEN_DRAIN = 1
|
||||
} GpioOutputType_t;
|
||||
|
||||
/* GPIO Pull Configuration */
|
||||
typedef enum {
|
||||
GPIO_PULL_NONE = 0,
|
||||
GPIO_PULL_UP = 1,
|
||||
GPIO_PULL_DOWN = 2
|
||||
} GpioPull_t;
|
||||
|
||||
/* GPIO Speed */
|
||||
typedef enum {
|
||||
GPIO_SPEED_LOW = 0,
|
||||
GPIO_SPEED_MEDIUM = 1,
|
||||
GPIO_SPEED_HIGH = 2,
|
||||
GPIO_SPEED_VERY_HIGH = 3
|
||||
} GpioSpeed_t;
|
||||
|
||||
/* GPIO Alternate Functions */
|
||||
typedef enum {
|
||||
GPIO_AF0 = 0,
|
||||
GPIO_AF1 = 1,
|
||||
GPIO_AF2 = 2,
|
||||
GPIO_AF3 = 3,
|
||||
GPIO_AF4 = 4,
|
||||
GPIO_AF5 = 5,
|
||||
GPIO_AF6 = 6,
|
||||
GPIO_AF7 = 7,
|
||||
GPIO_AF8 = 8,
|
||||
GPIO_AF9 = 9,
|
||||
GPIO_AF10 = 10,
|
||||
GPIO_AF11 = 11,
|
||||
GPIO_AF12 = 12,
|
||||
GPIO_AF13 = 13,
|
||||
GPIO_AF14 = 14,
|
||||
GPIO_AF15 = 15
|
||||
} GpioAlternateFunction_t;
|
||||
|
||||
/* GPIO Interrupt Triggers */
|
||||
typedef enum {
|
||||
GPIO_INTERRUPT_NONE = 0,
|
||||
GPIO_INTERRUPT_RISING = 1,
|
||||
GPIO_INTERRUPT_FALLING = 2,
|
||||
GPIO_INTERRUPT_BOTH = 3
|
||||
} GpioInterruptTrigger_t;
|
||||
|
||||
/* GPIO Pin Configuration */
|
||||
typedef struct {
|
||||
uint8_t port;
|
||||
uint8_t pin;
|
||||
GpioMode_t mode;
|
||||
GpioOutputType_t output_type;
|
||||
GpioPull_t pull;
|
||||
GpioSpeed_t speed;
|
||||
GpioAlternateFunction_t alternate_function;
|
||||
} GpioPinConfig_t;
|
||||
|
||||
/* GPIO Interrupt Configuration */
|
||||
typedef struct {
|
||||
uint8_t port;
|
||||
uint8_t pin;
|
||||
GpioInterruptTrigger_t trigger;
|
||||
void (*callback)(uint8_t port, uint8_t pin);
|
||||
} GpioInterruptConfig_t;
|
||||
|
||||
/* GPIO Driver Interface */
|
||||
KernelStatus_t gpio_init(const GpioPinConfig_t* config);
|
||||
KernelStatus_t gpio_deinit(uint8_t port, uint8_t pin);
|
||||
KernelStatus_t gpio_set_mode(uint8_t port, uint8_t pin, GpioMode_t mode);
|
||||
KernelStatus_t gpio_set_output_type(uint8_t port, uint8_t pin, GpioOutputType_t type);
|
||||
KernelStatus_t gpio_set_pull(uint8_t port, uint8_t pin, GpioPull_t pull);
|
||||
KernelStatus_t gpio_set_speed(uint8_t port, uint8_t pin, GpioSpeed_t speed);
|
||||
KernelStatus_t gpio_set_alternate_function(uint8_t port, uint8_t pin,
|
||||
GpioAlternateFunction_t af);
|
||||
KernelStatus_t gpio_write(uint8_t port, uint8_t pin, bool value);
|
||||
KernelStatus_t gpio_write_port(uint8_t port, uint16_t value);
|
||||
bool gpio_read(uint8_t port, uint8_t pin);
|
||||
uint16_t gpio_read_port(uint8_t port);
|
||||
KernelStatus_t gpio_toggle(uint8_t port, uint8_t pin);
|
||||
KernelStatus_t gpio_configure_interrupt(const GpioInterruptConfig_t* config);
|
||||
KernelStatus_t gpio_enable_interrupt(uint8_t port, uint8_t pin);
|
||||
KernelStatus_t gpio_disable_interrupt(uint8_t port, uint8_t pin);
|
||||
void gpio_process_interrupt(uint8_t port);
|
||||
|
||||
#endif /* GPIO_DRIVER_H */
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @file i2c_driver.h
|
||||
* @brief I2C driver interface
|
||||
*/
|
||||
|
||||
#ifndef I2C_DRIVER_H
|
||||
#define I2C_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* I2C Configuration Constants */
|
||||
#define I2C_MAX_INSTANCES 4
|
||||
#define I2C_MAX_BUFFER_SIZE 1024
|
||||
#define I2C_MAX_TRANSFER_SIZE 255
|
||||
|
||||
/* I2C Speeds */
|
||||
typedef enum {
|
||||
I2C_SPEED_STANDARD = 100000, /* 100 kHz */
|
||||
I2C_SPEED_FAST = 400000, /* 400 kHz */
|
||||
I2C_SPEED_FAST_PLUS = 1000000, /* 1 MHz */
|
||||
I2C_SPEED_HIGH = 3400000 /* 3.4 MHz */
|
||||
} I2cSpeed_t;
|
||||
|
||||
/* I2C Addressing Mode */
|
||||
typedef enum {
|
||||
I2C_ADDRESSING_7BIT = 0,
|
||||
I2C_ADDRESSING_10BIT = 1
|
||||
} I2cAddressingMode_t;
|
||||
|
||||
/* I2C Transfer Direction */
|
||||
typedef enum {
|
||||
I2C_DIRECTION_WRITE = 0,
|
||||
I2C_DIRECTION_READ = 1
|
||||
} I2cDirection_t;
|
||||
|
||||
/* I2C Transfer Status */
|
||||
typedef enum {
|
||||
I2C_TRANSFER_COMPLETE = 0,
|
||||
I2C_TRANSFER_ERROR = 1,
|
||||
I2C_TRANSFER_NACK = 2,
|
||||
I2C_TRANSFER_TIMEOUT = 3,
|
||||
I2C_TRANSFER_ARBITRATION_LOST = 4,
|
||||
I2C_TRANSFER_BUS_ERROR = 5
|
||||
} I2cTransferStatus_t;
|
||||
|
||||
/* I2C Callbacks */
|
||||
typedef void (*I2cTransferCompleteCallback_t)(I2cTransferStatus_t status, void* user_data);
|
||||
|
||||
/* I2C Configuration */
|
||||
typedef struct {
|
||||
I2cSpeed_t speed;
|
||||
I2cAddressingMode_t addressing_mode;
|
||||
uint16_t own_address;
|
||||
bool enable_general_call;
|
||||
bool enable_clock_stretching;
|
||||
bool use_dma;
|
||||
I2cTransferCompleteCallback_t transfer_complete_callback;
|
||||
} I2cConfig_t;
|
||||
|
||||
/* I2C Message */
|
||||
typedef struct {
|
||||
uint16_t slave_address;
|
||||
I2cDirection_t direction;
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
bool generate_stop;
|
||||
bool generate_restart;
|
||||
} I2cMessage_t;
|
||||
|
||||
/* I2C Statistics */
|
||||
typedef struct {
|
||||
uint32_t transfers_completed;
|
||||
uint32_t transfers_failed;
|
||||
uint32_t nack_errors;
|
||||
uint32_t arbitration_lost;
|
||||
uint32_t bus_errors;
|
||||
uint32_t timeout_errors;
|
||||
uint32_t bytes_transferred;
|
||||
} I2cStatistics_t;
|
||||
|
||||
/* I2C Driver Interface */
|
||||
KernelStatus_t i2c_init(uint8_t instance, I2cConfig_t* config);
|
||||
KernelStatus_t i2c_deinit(uint8_t instance);
|
||||
KernelStatus_t i2c_transfer(uint8_t instance, const I2cMessage_t* message,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t i2c_transfer_async(uint8_t instance, const I2cMessage_t* message);
|
||||
KernelStatus_t i2c_write(uint8_t instance, uint16_t slave_address,
|
||||
const uint8_t* data, uint16_t length, uint32_t timeout_ms);
|
||||
KernelStatus_t i2c_read(uint8_t instance, uint16_t slave_address,
|
||||
uint8_t* data, uint16_t length, uint32_t timeout_ms);
|
||||
KernelStatus_t i2c_write_read(uint8_t instance, uint16_t slave_address,
|
||||
const uint8_t* tx_data, uint16_t tx_length,
|
||||
uint8_t* rx_data, uint16_t rx_length,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t i2c_get_statistics(uint8_t instance, I2cStatistics_t* stats);
|
||||
bool i2c_is_device_ready(uint8_t instance, uint16_t slave_address);
|
||||
void i2c_process_interrupt(uint8_t instance);
|
||||
|
||||
#endif /* I2C_DRIVER_H */
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file pwm_driver.h
|
||||
* @brief PWM driver interface
|
||||
*/
|
||||
|
||||
#ifndef PWM_DRIVER_H
|
||||
#define PWM_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* PWM Configuration Constants */
|
||||
#define PWM_MAX_INSTANCES 8
|
||||
#define PWM_MAX_CHANNELS 4
|
||||
#define PWM_MAX_DUTY_CYCLE 10000 /* 100.00% in 0.01% steps */
|
||||
|
||||
/* PWM Alignment Modes */
|
||||
typedef enum {
|
||||
PWM_ALIGNMENT_EDGE = 0,
|
||||
PWM_ALIGNMENT_CENTER = 1
|
||||
} PwmAlignment_t;
|
||||
|
||||
/* PWM Polarity */
|
||||
typedef enum {
|
||||
PWM_POLARITY_ACTIVE_HIGH = 0,
|
||||
PWM_POLARITY_ACTIVE_LOW = 1
|
||||
} PwmPolarity_t;
|
||||
|
||||
/* PWM Modes */
|
||||
typedef enum {
|
||||
PWM_MODE_NORMAL = 0,
|
||||
PWM_MODE_COMPLEMENTARY = 1,
|
||||
PWM_MODE_COMBINED = 2
|
||||
} PwmMode_t;
|
||||
|
||||
/* PWM Dead Time */
|
||||
typedef struct {
|
||||
uint16_t rising_edge_delay_ns;
|
||||
uint16_t falling_edge_delay_ns;
|
||||
} PwmDeadTime_t;
|
||||
|
||||
/* PWM Fault Actions */
|
||||
typedef enum {
|
||||
PWM_FAULT_DISABLE = 0,
|
||||
PWM_FAULT_ENABLE = 1,
|
||||
PWM_FAULT_HIGH_Z = 2
|
||||
} PwmFaultAction_t;
|
||||
|
||||
/* PWM Callbacks */
|
||||
typedef void (*PwmPeriodElapsedCallback_t)(uint8_t instance);
|
||||
typedef void (*PwmFaultCallback_t)(uint8_t instance, uint32_t fault_flags);
|
||||
|
||||
/* PWM Channel Configuration */
|
||||
typedef struct {
|
||||
uint8_t channel;
|
||||
uint32_t duty_cycle; /* 0 to PWM_MAX_DUTY_CYCLE */
|
||||
PwmPolarity_t polarity;
|
||||
PwmMode_t mode;
|
||||
PwmDeadTime_t dead_time;
|
||||
} PwmChannelConfig_t;
|
||||
|
||||
/* PWM Configuration */
|
||||
typedef struct {
|
||||
uint32_t frequency_hz;
|
||||
PwmAlignment_t alignment;
|
||||
uint32_t period_ticks;
|
||||
uint8_t prescaler;
|
||||
PwmChannelConfig_t channels[PWM_MAX_CHANNELS];
|
||||
uint8_t channel_count;
|
||||
bool enable_fault_protection;
|
||||
PwmFaultAction_t fault_action;
|
||||
PwmPeriodElapsedCallback_t period_elapsed_callback;
|
||||
PwmFaultCallback_t fault_callback;
|
||||
} PwmConfig_t;
|
||||
|
||||
/* PWM Statistics */
|
||||
typedef struct {
|
||||
uint32_t period_elapsed_count;
|
||||
uint32_t fault_events;
|
||||
uint32_t duty_cycle_updates;
|
||||
uint32_t overcurrent_events;
|
||||
uint32_t overvoltage_events;
|
||||
} PwmStatistics_t;
|
||||
|
||||
/* PWM Driver Interface */
|
||||
KernelStatus_t pwm_init(uint8_t instance, PwmConfig_t* config);
|
||||
KernelStatus_t pwm_deinit(uint8_t instance);
|
||||
KernelStatus_t pwm_start(uint8_t instance);
|
||||
KernelStatus_t pwm_stop(uint8_t instance);
|
||||
KernelStatus_t pwm_set_duty_cycle(uint8_t instance, uint8_t channel, uint32_t duty_cycle);
|
||||
KernelStatus_t pwm_set_frequency(uint8_t instance, uint32_t frequency_hz);
|
||||
KernelStatus_t pwm_set_period(uint8_t instance, uint32_t period_ticks);
|
||||
KernelStatus_t pwm_set_dead_time(uint8_t instance, uint8_t channel,
|
||||
const PwmDeadTime_t* dead_time);
|
||||
KernelStatus_t pwm_enable_channel(uint8_t instance, uint8_t channel);
|
||||
KernelStatus_t pwm_disable_channel(uint8_t instance, uint8_t channel);
|
||||
KernelStatus_t pwm_configure_fault(uint8_t instance, PwmFaultAction_t action);
|
||||
KernelStatus_t pwm_clear_fault(uint8_t instance);
|
||||
KernelStatus_t pwm_get_statistics(uint8_t instance, PwmStatistics_t* stats);
|
||||
uint32_t pwm_get_duty_cycle(uint8_t instance, uint8_t channel);
|
||||
uint32_t pwm_get_frequency(uint8_t instance);
|
||||
void pwm_process_interrupt(uint8_t instance);
|
||||
|
||||
#endif /* PWM_DRIVER_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @file spi_driver.h
|
||||
* @brief SPI driver interface
|
||||
*/
|
||||
|
||||
#ifndef SPI_DRIVER_H
|
||||
#define SPI_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* SPI Configuration Constants */
|
||||
#define SPI_MAX_INSTANCES 4
|
||||
#define SPI_MAX_BUFFER_SIZE 4096
|
||||
#define SPI_MAX_TRANSFER_SIZE 65535
|
||||
|
||||
/* SPI Modes */
|
||||
typedef enum {
|
||||
SPI_MODE_0 = 0, /* CPOL=0, CPHA=0 */
|
||||
SPI_MODE_1 = 1, /* CPOL=0, CPHA=1 */
|
||||
SPI_MODE_2 = 2, /* CPOL=1, CPHA=0 */
|
||||
SPI_MODE_3 = 3 /* CPOL=1, CPHA=1 */
|
||||
} SpiMode_t;
|
||||
|
||||
/* SPI Clock Speeds */
|
||||
typedef enum {
|
||||
SPI_CLOCK_1MHZ = 1000000,
|
||||
SPI_CLOCK_2MHZ = 2000000,
|
||||
SPI_CLOCK_4MHZ = 4000000,
|
||||
SPI_CLOCK_8MHZ = 8000000,
|
||||
SPI_CLOCK_16MHZ = 16000000,
|
||||
SPI_CLOCK_32MHZ = 32000000
|
||||
} SpiClockSpeed_t;
|
||||
|
||||
/* SPI Data Order */
|
||||
typedef enum {
|
||||
SPI_DATA_ORDER_MSB_FIRST = 0,
|
||||
SPI_DATA_ORDER_LSB_FIRST = 1
|
||||
} SpiDataOrder_t;
|
||||
|
||||
/* SPI Chip Select */
|
||||
typedef enum {
|
||||
SPI_CS_ACTIVE_LOW = 0,
|
||||
SPI_CS_ACTIVE_HIGH = 1
|
||||
} SpiChipSelectPolarity_t;
|
||||
|
||||
/* SPI Transfer Status */
|
||||
typedef enum {
|
||||
SPI_TRANSFER_COMPLETE = 0,
|
||||
SPI_TRANSFER_ERROR = 1,
|
||||
SPI_TRANSFER_TIMEOUT = 2,
|
||||
SPI_TRANSFER_DMA_COMPLETE = 3
|
||||
} SpiTransferStatus_t;
|
||||
|
||||
/* SPI Callbacks */
|
||||
typedef void (*SpiTransferCompleteCallback_t)(SpiTransferStatus_t status, void* user_data);
|
||||
|
||||
/* SPI Configuration */
|
||||
typedef struct {
|
||||
SpiMode_t mode;
|
||||
SpiClockSpeed_t clock_speed;
|
||||
SpiDataOrder_t data_order;
|
||||
uint8_t data_size; /* 8 or 16 bits */
|
||||
bool use_dma;
|
||||
bool enable_hardware_cs;
|
||||
SpiChipSelectPolarity_t cs_polarity;
|
||||
uint8_t cs_port;
|
||||
uint8_t cs_pin;
|
||||
SpiTransferCompleteCallback_t transfer_complete_callback;
|
||||
} SpiConfig_t;
|
||||
|
||||
/* SPI Statistics */
|
||||
typedef struct {
|
||||
uint32_t transfers_completed;
|
||||
uint32_t transfers_failed;
|
||||
uint32_t bytes_transferred;
|
||||
uint32_t dma_transfers;
|
||||
uint32_t overrun_errors;
|
||||
uint32_t underrun_errors;
|
||||
uint32_t timeout_errors;
|
||||
} SpiStatistics_t;
|
||||
|
||||
/* SPI Transaction */
|
||||
typedef struct {
|
||||
const uint8_t* tx_data;
|
||||
uint8_t* rx_data;
|
||||
uint16_t length;
|
||||
bool keep_cs_active;
|
||||
void* user_data;
|
||||
} SpiTransaction_t;
|
||||
|
||||
/* SPI Driver Interface */
|
||||
KernelStatus_t spi_init(uint8_t instance, SpiConfig_t* config);
|
||||
KernelStatus_t spi_deinit(uint8_t instance);
|
||||
KernelStatus_t spi_transfer(uint8_t instance, const SpiTransaction_t* transaction,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t spi_transfer_async(uint8_t instance, const SpiTransaction_t* transaction);
|
||||
KernelStatus_t spi_read(uint8_t instance, uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t spi_write(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t spi_read_write(uint8_t instance, const uint8_t* tx_data,
|
||||
uint8_t* rx_data, uint16_t length, uint32_t timeout_ms);
|
||||
KernelStatus_t spi_get_statistics(uint8_t instance, SpiStatistics_t* stats);
|
||||
void spi_set_chip_select(uint8_t instance, bool active);
|
||||
void spi_process_interrupt(uint8_t instance);
|
||||
|
||||
#endif /* SPI_DRIVER_H */
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @file uart_driver.h
|
||||
* @brief UART driver interface
|
||||
*/
|
||||
|
||||
#ifndef UART_DRIVER_H
|
||||
#define UART_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
/* UART Configuration Constants */
|
||||
#define UART_MAX_INSTANCES 6
|
||||
#define UART_MAX_BUFFER_SIZE 2048
|
||||
#define UART_DEFAULT_BAUDRATE 115200
|
||||
|
||||
/* UART Baudrates */
|
||||
typedef enum {
|
||||
UART_BAUD_9600 = 9600,
|
||||
UART_BAUD_19200 = 19200,
|
||||
UART_BAUD_38400 = 38400,
|
||||
UART_BAUD_57600 = 57600,
|
||||
UART_BAUD_115200 = 115200,
|
||||
UART_BAUD_230400 = 230400,
|
||||
UART_BAUD_460800 = 460800,
|
||||
UART_BAUD_921600 = 921600
|
||||
} UartBaudrate_t;
|
||||
|
||||
/* UART Data Bits */
|
||||
typedef enum {
|
||||
UART_DATA_BITS_5 = 5,
|
||||
UART_DATA_BITS_6 = 6,
|
||||
UART_DATA_BITS_7 = 7,
|
||||
UART_DATA_BITS_8 = 8,
|
||||
UART_DATA_BITS_9 = 9
|
||||
} UartDataBits_t;
|
||||
|
||||
/* UART Stop Bits */
|
||||
typedef enum {
|
||||
UART_STOP_BITS_1 = 0,
|
||||
UART_STOP_BITS_1_5 = 1,
|
||||
UART_STOP_BITS_2 = 2
|
||||
} UartStopBits_t;
|
||||
|
||||
/* UART Parity */
|
||||
typedef enum {
|
||||
UART_PARITY_NONE = 0,
|
||||
UART_PARITY_EVEN = 1,
|
||||
UART_PARITY_ODD = 2
|
||||
} UartParity_t;
|
||||
|
||||
/* UART Flow Control */
|
||||
typedef enum {
|
||||
UART_FLOW_CONTROL_NONE = 0,
|
||||
UART_FLOW_CONTROL_RTS_CTS = 1,
|
||||
UART_FLOW_CONTROL_XON_XOFF = 2
|
||||
} UartFlowControl_t;
|
||||
|
||||
/* UART Callbacks */
|
||||
typedef void (*UartRxCallback_t)(uint8_t* data, uint16_t length);
|
||||
typedef void (*UartTxCallback_t)(void);
|
||||
typedef void (*UartErrorCallback_t)(uint32_t error);
|
||||
|
||||
/* UART Configuration */
|
||||
typedef struct {
|
||||
UartBaudrate_t baudrate;
|
||||
UartDataBits_t data_bits;
|
||||
UartStopBits_t stop_bits;
|
||||
UartParity_t parity;
|
||||
UartFlowControl_t flow_control;
|
||||
bool enable_rx;
|
||||
bool enable_tx;
|
||||
bool use_dma;
|
||||
UartRxCallback_t rx_callback;
|
||||
UartTxCallback_t tx_callback;
|
||||
UartErrorCallback_t error_callback;
|
||||
} UartConfig_t;
|
||||
|
||||
/* UART Statistics */
|
||||
typedef struct {
|
||||
uint32_t tx_bytes;
|
||||
uint32_t rx_bytes;
|
||||
uint32_t tx_errors;
|
||||
uint32_t rx_errors;
|
||||
uint32_t parity_errors;
|
||||
uint32_t framing_errors;
|
||||
uint32_t overrun_errors;
|
||||
uint32_t dma_transfers;
|
||||
} UartStatistics_t;
|
||||
|
||||
/* UART Driver Interface */
|
||||
KernelStatus_t uart_init(uint8_t instance, UartConfig_t* config);
|
||||
KernelStatus_t uart_deinit(uint8_t instance);
|
||||
KernelStatus_t uart_send(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t uart_receive(uint8_t instance, uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms);
|
||||
KernelStatus_t uart_send_async(uint8_t instance, const uint8_t* data, uint16_t length);
|
||||
KernelStatus_t uart_receive_async(uint8_t instance, uint8_t* data, uint16_t length);
|
||||
KernelStatus_t uart_flush(uint8_t instance);
|
||||
KernelStatus_t uart_get_statistics(uint8_t instance, UartStatistics_t* stats);
|
||||
uint16_t uart_get_rx_count(uint8_t instance);
|
||||
uint16_t uart_get_tx_count(uint8_t instance);
|
||||
void uart_process_interrupt(uint8_t instance);
|
||||
|
||||
#endif /* UART_DRIVER_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @file tc3xx_config.h
|
||||
* @brief Infineon TriCore TC3xx specific configuration
|
||||
*/
|
||||
|
||||
#ifndef TC3XX_CONFIG_H
|
||||
#define TC3XX_CONFIG_H
|
||||
|
||||
/* MCU Specific Definitions */
|
||||
#define TC397
|
||||
#define CPU_FREQUENCY 300000000U /* 300 MHz */
|
||||
#define PERIPHERAL_FREQUENCY 150000000U /* 150 MHz */
|
||||
#define STM_FREQUENCY 100000000U /* 100 MHz */
|
||||
|
||||
/* Peripheral Base Addresses */
|
||||
#define CAN0_BASE 0xF0200000U
|
||||
#define CAN1_BASE 0xF0210000U
|
||||
#define CAN2_BASE 0xF0220000U
|
||||
#define CAN3_BASE 0xF0230000U
|
||||
|
||||
#define ASCLIN0_BASE 0xF0000000U
|
||||
#define ASCLIN1_BASE 0xF0000100U
|
||||
#define ASCLIN2_BASE 0xF0000200U
|
||||
#define ASCLIN3_BASE 0xF0000300U
|
||||
|
||||
#define QSPI0_BASE 0xF0001000U
|
||||
#define QSPI1_BASE 0xF0001100U
|
||||
#define QSPI2_BASE 0xF0001200U
|
||||
#define QSPI3_BASE 0xF0001300U
|
||||
|
||||
#define I2C0_BASE 0xF0002000U
|
||||
#define I2C1_BASE 0xF0002100U
|
||||
|
||||
#define VADC0_BASE 0xF0020000U
|
||||
#define VADC1_BASE 0xF0020100U
|
||||
|
||||
#define GTM_BASE 0xF0100000U
|
||||
#define GPT12_BASE 0xF0003000U
|
||||
|
||||
/* Interrupt Priorities */
|
||||
#define CAN0_IRQ_PRIORITY 5
|
||||
#define CAN1_IRQ_PRIORITY 5
|
||||
#define ASCLIN0_IRQ_PRIORITY 6
|
||||
#define QSPI0_IRQ_PRIORITY 7
|
||||
#define I2C0_IRQ_PRIORITY 7
|
||||
#define VADC0_IRQ_PRIORITY 8
|
||||
#define GTM_IRQ_PRIORITY 8
|
||||
|
||||
/* DMA Configuration */
|
||||
#define DMA_CHANNEL_COUNT 128
|
||||
#define DMA_PRIORITY_HIGH 0
|
||||
#define DMA_PRIORITY_MEDIUM 1
|
||||
#define DMA_PRIORITY_LOW 2
|
||||
|
||||
/* Safety Features */
|
||||
#define ENABLE_SAFETY_WATCHDOG 1
|
||||
#define WATCHDOG_TIMEOUT_MS 100
|
||||
#define ENABLE_ECC 1
|
||||
#define ENABLE_MEMORY_PROTECTION 1
|
||||
|
||||
/* Memory Configuration */
|
||||
#define FLASH_SIZE 0x800000U /* 8 MB */
|
||||
#define RAM_SIZE 0x280000U /* 2.5 MB */
|
||||
#define DSPR_SIZE 0x20000U /* 128 KB per CPU */
|
||||
|
||||
/* CAN FD Configuration */
|
||||
#define CAN_FD_ENABLED 1
|
||||
#define CAN_FD_MAX_PAYLOAD 64
|
||||
#define CAN_TX_FIFO_SIZE 32
|
||||
#define CAN_RX_FIFO_SIZE 64
|
||||
|
||||
#endif /* TC3XX_CONFIG_H */
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @file tc3xx_hal.c
|
||||
* @brief Infineon TriCore TC3xx Hardware Abstraction Layer
|
||||
*/
|
||||
|
||||
#include "tc3xx_config.h"
|
||||
#include "can_driver.h"
|
||||
#include "uart_driver.h"
|
||||
#include "spi_driver.h"
|
||||
#include "i2c_driver.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "adc_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "Ifx_Types.h"
|
||||
#include "IfxCan_Can.h"
|
||||
#include "IfxAsclin_Asc.h"
|
||||
#include "IfxQspi_SpiMaster.h"
|
||||
#include "IfxI2c_I2c.h"
|
||||
#include "IfxPort.h"
|
||||
#include "IfxVadc_Adc.h"
|
||||
#include "IfxGtm_Tom_PwmHl.h"
|
||||
|
||||
/* CAN HAL Implementation */
|
||||
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||
/* Create CAN module configuration */
|
||||
IfxCan_Can_Config canConfig;
|
||||
IfxCan_Can_initModuleConfig(&canConfig, &MODULE_CAN0);
|
||||
|
||||
/* Configure node */
|
||||
canConfig.nodeConfig[0].baudRate.baudrate = baudrate;
|
||||
canConfig.nodeConfig[0].frame.type = IfxCan_FrameType_receive;
|
||||
|
||||
if (enable_fd) {
|
||||
canConfig.nodeConfig[0].frame.mode = IfxCan_FrameMode_fd;
|
||||
}
|
||||
|
||||
/* Initialize CAN module */
|
||||
IfxCan_Can_initModule(&g_canDriver, &canConfig);
|
||||
|
||||
/* Configure TX FIFO */
|
||||
IfxCan_Can_initTxFifo(&g_canDriver, &g_canTxFifo);
|
||||
|
||||
/* Configure RX FIFO */
|
||||
IfxCan_Can_initRxFifo(&g_canDriver, &g_canRxFifo);
|
||||
|
||||
/* Enable interrupts */
|
||||
IfxCan_Can_enableInterrupt(&g_canDriver, IfxCan_Interrupt_messageStoredToDedicatedRxFifo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||
/* Create TX message */
|
||||
IfxCan_Can_Message txMessage;
|
||||
|
||||
/* Set message ID */
|
||||
txMessage.messageId = message->id.id;
|
||||
txMessage.extendedFrame = message->id.is_extended;
|
||||
|
||||
/* Set data */
|
||||
txMessage.dataLengthCode = message->length;
|
||||
for (int i = 0; i < message->length; i++) {
|
||||
txMessage.data[i] = message->data[i];
|
||||
}
|
||||
|
||||
/* Send message */
|
||||
if (IfxCan_Can_sendMessage(&g_canDriver, &txMessage, &txMessage.messageId)
|
||||
!= IfxCan_Status_ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*mailbox = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_receive_message(CanMessage_t* message) {
|
||||
/* Create RX message */
|
||||
IfxCan_Can_Message rxMessage;
|
||||
|
||||
/* Receive message */
|
||||
if (IfxCan_Can_readMessage(&g_canDriver, &rxMessage, &rxMessage.messageId)
|
||||
!= IfxCan_Status_ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy message data */
|
||||
message->id.id = rxMessage.messageId;
|
||||
message->id.is_extended = rxMessage.extendedFrame;
|
||||
message->length = rxMessage.dataLengthCode;
|
||||
|
||||
for (int i = 0; i < message->length; i++) {
|
||||
message->data[i] = rxMessage.data[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPIO HAL Implementation */
|
||||
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||
/* Configure port pin */
|
||||
IfxPort_setPinMode(&MODULE_P00, pin, IfxPort_Mode_outputPushPullGeneral);
|
||||
}
|
||||
|
||||
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||
if (value) {
|
||||
IfxPort_setPinHigh(&MODULE_P00, pin);
|
||||
} else {
|
||||
IfxPort_setPinLow(&MODULE_P00, pin);
|
||||
}
|
||||
}
|
||||
|
||||
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||
return IfxPort_getPinState(&MODULE_P00, pin);
|
||||
}
|
||||
|
||||
/* UART HAL Implementation */
|
||||
int hal_uart_init(uint8_t instance, UartConfig_t* config) {
|
||||
/* Create ASCLIN configuration */
|
||||
IfxAsclin_Asc_Config ascConfig;
|
||||
IfxAsclin_Asc_initModuleConfig(&ascConfig, &MODULE_ASCLIN0);
|
||||
|
||||
/* Configure baudrate */
|
||||
ascConfig.baudrate.baudrate = config->baudrate;
|
||||
|
||||
/* Configure pins */
|
||||
ascConfig.pins = &g_ascPins;
|
||||
|
||||
/* Initialize module */
|
||||
IfxAsclin_Asc_initModule(&g_ascDriver, &ascConfig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @file s32k14x_config.h
|
||||
* @brief NXP S32K14x specific configuration
|
||||
*/
|
||||
|
||||
#ifndef S32K14X_CONFIG_H
|
||||
#define S32K14X_CONFIG_H
|
||||
|
||||
/* MCU Specific Definitions */
|
||||
#define S32K144
|
||||
#define CPU_FREQUENCY 160000000U /* 160 MHz */
|
||||
#define BUS_FREQUENCY 40000000U /* 40 MHz */
|
||||
#define SLOW_FREQUENCY 10000000U /* 10 MHz */
|
||||
|
||||
/* Peripheral Base Addresses */
|
||||
#define GPIOA_BASE 0x400FF000U
|
||||
#define GPIOB_BASE 0x400FF040U
|
||||
#define GPIOC_BASE 0x400FF080U
|
||||
#define GPIOD_BASE 0x400FF0C0U
|
||||
#define GPIOE_BASE 0x400FF100U
|
||||
|
||||
#define LPUART0_BASE 0x4006A000U
|
||||
#define LPUART1_BASE 0x4006B000U
|
||||
#define LPUART2_BASE 0x4006C000U
|
||||
|
||||
#define LPSPI0_BASE 0x4002C000U
|
||||
#define LPSPI1_BASE 0x4002D000U
|
||||
#define LPSPI2_BASE 0x4002E000U
|
||||
|
||||
#define LPI2C0_BASE 0x40066000U
|
||||
#define LPI2C1_BASE 0x40067000U
|
||||
|
||||
#define ADC0_BASE 0x4003B000U
|
||||
#define ADC1_BASE 0x4003C000U
|
||||
|
||||
#define FTM0_BASE 0x40038000U
|
||||
#define FTM1_BASE 0x40039000U
|
||||
#define FTM2_BASE 0x4003A000U
|
||||
#define FTM3_BASE 0x40026000U
|
||||
#define FTM4_BASE 0x40027000U
|
||||
|
||||
#define FLEXCAN0_BASE 0x40024000U
|
||||
#define FLEXCAN1_BASE 0x40025000U
|
||||
#define FLEXCAN2_BASE 0x4002B000U
|
||||
|
||||
/* Clock Configuration */
|
||||
#define SOSC_FREQUENCY 8000000U /* System oscillator */
|
||||
#define SPLL_FREQUENCY 160000000U /* System PLL */
|
||||
#define FIRC_FREQUENCY 48000000U /* Fast IRC */
|
||||
#define SIRC_FREQUENCY 8000000U /* Slow IRC */
|
||||
|
||||
/* Peripheral Clock Configuration */
|
||||
#define FLEXCAN0_CLOCK 40000000U /* 40 MHz */
|
||||
#define FLEXCAN1_CLOCK 40000000U
|
||||
#define LPUART0_CLOCK 40000000U
|
||||
#define LPSPI0_CLOCK 40000000U
|
||||
#define LPI2C0_CLOCK 40000000U
|
||||
#define ADC0_CLOCK 40000000U
|
||||
#define FTM0_CLOCK 40000000U
|
||||
|
||||
/* NVIC Priority Configuration */
|
||||
#define FLEXCAN0_IRQ_PRIORITY 5
|
||||
#define FLEXCAN1_IRQ_PRIORITY 5
|
||||
#define LPUART0_IRQ_PRIORITY 6
|
||||
#define LPUART1_IRQ_PRIORITY 6
|
||||
#define LPSPI0_IRQ_PRIORITY 7
|
||||
#define LPI2C0_IRQ_PRIORITY 7
|
||||
#define ADC0_IRQ_PRIORITY 8
|
||||
#define FTM0_IRQ_PRIORITY 8
|
||||
|
||||
/* DMA Configuration */
|
||||
#define DMA_CHANNEL_COUNT 16
|
||||
#define DMA_MUX_CHANNEL_COUNT 16
|
||||
|
||||
/* Safety Features */
|
||||
#define ENABLE_CLOCK_MONITORING 1
|
||||
#define ENABLE_MEMORY_PROTECTION 1
|
||||
#define WATCHDOG_TIMEOUT_MS 100
|
||||
|
||||
/* Memory Configuration */
|
||||
#define FLASH_SIZE 0x100000U /* 1 MB */
|
||||
#define RAM_SIZE 0x20000U /* 128 KB */
|
||||
#define EEPROM_SIZE 0x1000U /* 4 KB */
|
||||
|
||||
/* CAN FD Configuration */
|
||||
#define CAN_FD_ENABLED 1
|
||||
#define CAN_FD_MAX_PAYLOAD 64
|
||||
|
||||
#endif /* S32K14X_CONFIG_H */
|
||||
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* @file s32k14x_hal.c
|
||||
* @brief NXP S32K14x Hardware Abstraction Layer
|
||||
*/
|
||||
|
||||
#include "s32k14x_config.h"
|
||||
#include "can_driver.h"
|
||||
#include "uart_driver.h"
|
||||
#include "spi_driver.h"
|
||||
#include "i2c_driver.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "adc_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "S32K144.h"
|
||||
|
||||
/* CAN HAL Implementation */
|
||||
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||
/* Enable FLEXCAN0 clock */
|
||||
PCC->PCCn[PCC_FlexCAN0_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||
|
||||
/* Configure CAN pins */
|
||||
// PTE4 - CAN0_RX, PTE5 - CAN0_TX
|
||||
PCC->PCCn[PCC_PORTE_INDEX] |= PCC_PCCn_CGC_MASK;
|
||||
PORTE->PCR[4] = PORT_PCR_MUX(5); /* CAN0_RX */
|
||||
PORTE->PCR[5] = PORT_PCR_MUX(5); /* CAN0_TX */
|
||||
|
||||
/* Reset FLEXCAN */
|
||||
CAN0->MCR |= CAN_MCR_SOFTRST_MASK;
|
||||
while (CAN0->MCR & CAN_MCR_SOFTRST_MASK);
|
||||
|
||||
/* Configure for CAN FD if enabled */
|
||||
if (enable_fd) {
|
||||
CAN0->MCR |= CAN_MCR_FDEN_MASK; /* Enable FD */
|
||||
}
|
||||
|
||||
/* Set baudrate */
|
||||
uint32_t prescaler = BUS_FREQUENCY / (baudrate * 10); /* 10 time quanta */
|
||||
CAN0->CTRL1 = CAN_CTRL1_PRESDIV(prescaler - 1) |
|
||||
CAN_CTRL1_PSEG1(3) |
|
||||
CAN_CTRL1_PSEG2(2) |
|
||||
CAN_CTRL1_PROPSEG(4);
|
||||
|
||||
/* Configure message buffers */
|
||||
CAN0->RXMGMASK = 0x1FFFFFFF; /* Accept all IDs */
|
||||
CAN0->RX14MASK = 0x1FFFFFFF;
|
||||
CAN0->RX15MASK = 0x1FFFFFFF;
|
||||
|
||||
/* Enable interrupts */
|
||||
CAN0->IMASK1 |= CAN_IMASK1_BUF31TO0M_MASK;
|
||||
CAN0->MCR |= CAN_MCR_IRMQ_MASK; /* Individual RX masking */
|
||||
|
||||
/* Normal mode */
|
||||
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
|
||||
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||
/* Find free message buffer */
|
||||
*mailbox = 0;
|
||||
while (*mailbox < 32) {
|
||||
if ((CAN0->IFLAG1 & (1 << *mailbox)) != 0) {
|
||||
break;
|
||||
}
|
||||
(*mailbox)++;
|
||||
}
|
||||
|
||||
if (*mailbox >= 32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Configure message buffer */
|
||||
CAN0->RAMn[*mailbox * 4 + 1] = (message->id.id << 18) |
|
||||
(message->id.is_extended ? 1 << 29 : 0) |
|
||||
(message->length << 16);
|
||||
|
||||
/* Copy data */
|
||||
for (int i = 0; i < message->length; i += 4) {
|
||||
uint32_t data = 0;
|
||||
for (int j = 0; j < 4 && (i + j) < message->length; j++) {
|
||||
data |= (message->data[i + j] << (j * 8));
|
||||
}
|
||||
CAN0->RAMn[*mailbox * 4 + 2 + (i / 4)] = data;
|
||||
}
|
||||
|
||||
/* Enable transmission */
|
||||
CAN0->RAMn[*mailbox * 4] = CAN_WORD0_IDE_MASK |
|
||||
CAN_WORD0_SRR_MASK |
|
||||
CAN_WORD0_ESI_MASK |
|
||||
CAN_WORD0_CODE(0xC); /* TX data */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_receive_message(CanMessage_t* message) {
|
||||
/* Check for received messages */
|
||||
uint32_t iflag = CAN0->IFLAG1;
|
||||
if (iflag == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find received message buffer */
|
||||
uint32_t mailbox = 0;
|
||||
while (mailbox < 32) {
|
||||
if (iflag & (1 << mailbox)) {
|
||||
break;
|
||||
}
|
||||
mailbox++;
|
||||
}
|
||||
|
||||
if (mailbox >= 32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read message */
|
||||
uint32_t word0 = CAN0->RAMn[mailbox * 4];
|
||||
uint32_t word1 = CAN0->RAMn[mailbox * 4 + 1];
|
||||
|
||||
/* Check if RX buffer */
|
||||
if ((word0 & CAN_WORD0_CODE_MASK) != CAN_WORD0_CODE(0x4)) {
|
||||
CAN0->IFLAG1 = (1 << mailbox); /* Clear flag */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get ID */
|
||||
message->id.is_extended = (word0 & CAN_WORD0_IDE_MASK) != 0;
|
||||
if (message->id.is_extended) {
|
||||
message->id.id = (word0 & CAN_WORD0_ID_MASK) >> 0;
|
||||
} else {
|
||||
message->id.id = (word0 & CAN_WORD0_ID_MASK) >> 18;
|
||||
}
|
||||
|
||||
/* Get data length */
|
||||
message->length = (word1 & CAN_WORD1_DLC_MASK) >> 16;
|
||||
|
||||
/* Get data */
|
||||
for (int i = 0; i < message->length; i += 4) {
|
||||
uint32_t data = CAN0->RAMn[mailbox * 4 + 2 + (i / 4)];
|
||||
for (int j = 0; j < 4 && (i + j) < message->length; j++) {
|
||||
message->data[i + j] = (data >> (j * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear flag */
|
||||
CAN0->IFLAG1 = (1 << mailbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPIO HAL Implementation */
|
||||
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||
PORT_Type* port_config = get_port_config(port);
|
||||
|
||||
if (gpio_port == NULL || port_config == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable clock */
|
||||
PCC->PCCn[PCC_PORTA_INDEX + port] |= PCC_PCCn_CGC_MASK;
|
||||
PCC->PCCn[PCC_GPIOA_INDEX + port] |= PCC_PCCn_CGC_MASK;
|
||||
|
||||
/* Configure pin mux */
|
||||
switch (mode) {
|
||||
case GPIO_MODE_INPUT:
|
||||
port_config->PCR[pin] = PORT_PCR_MUX(1);
|
||||
gpio_port->PDDR &= ~(1 << pin);
|
||||
break;
|
||||
case GPIO_MODE_OUTPUT:
|
||||
port_config->PCR[pin] = PORT_PCR_MUX(1);
|
||||
gpio_port->PDDR |= (1 << pin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||
|
||||
if (gpio_port == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
gpio_port->PSOR = (1 << pin);
|
||||
} else {
|
||||
gpio_port->PCOR = (1 << pin);
|
||||
}
|
||||
}
|
||||
|
||||
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||
GPIO_Type* gpio_port = get_gpio_port(port);
|
||||
|
||||
if (gpio_port == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (gpio_port->PDIR & (1 << pin)) != 0;
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
static GPIO_Type* get_gpio_port(uint8_t port) {
|
||||
switch (port) {
|
||||
case 0: return PTA;
|
||||
case 1: return PTB;
|
||||
case 2: return PTC;
|
||||
case 3: return PTD;
|
||||
case 4: return PTE;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PORT_Type* get_port_config(uint8_t port) {
|
||||
switch (port) {
|
||||
case 0: return PORTA;
|
||||
case 1: return PORTB;
|
||||
case 2: return PORTC;
|
||||
case 3: return PORTD;
|
||||
case 4: return PORTE;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @file stm32f4xx_config.h
|
||||
* @brief STM32F4 specific configuration
|
||||
*/
|
||||
|
||||
#ifndef STM32F4XX_CONFIG_H
|
||||
#define STM32F4XX_CONFIG_H
|
||||
|
||||
/* MCU Specific Definitions */
|
||||
#define STM32F407xx
|
||||
#define HSE_VALUE 8000000U /* External crystal */
|
||||
#define HSI_VALUE 16000000U /* Internal oscillator */
|
||||
#define LSE_VALUE 32768U /* Low speed external */
|
||||
#define LSI_VALUE 32000U /* Low speed internal */
|
||||
|
||||
/* Clock Configuration */
|
||||
#define SYSTEM_CLOCK 168000000U /* 168 MHz */
|
||||
#define AHB_CLOCK 168000000U
|
||||
#define APB1_CLOCK 42000000U /* 42 MHz */
|
||||
#define APB2_CLOCK 84000000U /* 84 MHz */
|
||||
|
||||
/* Peripheral Base Addresses */
|
||||
#define GPIOA_BASE 0x40020000U
|
||||
#define GPIOB_BASE 0x40020400U
|
||||
#define GPIOC_BASE 0x40020800U
|
||||
#define GPIOD_BASE 0x40020C00U
|
||||
#define GPIOE_BASE 0x40021000U
|
||||
#define GPIOF_BASE 0x40021400U
|
||||
#define GPIOG_BASE 0x40021800U
|
||||
#define GPIOH_BASE 0x40021C00U
|
||||
|
||||
#define USART1_BASE 0x40011000U
|
||||
#define USART2_BASE 0x40004400U
|
||||
#define USART3_BASE 0x40004800U
|
||||
#define UART4_BASE 0x40004C00U
|
||||
#define UART5_BASE 0x40005000U
|
||||
#define USART6_BASE 0x40011400U
|
||||
|
||||
#define SPI1_BASE 0x40013000U
|
||||
#define SPI2_BASE 0x40003800U
|
||||
#define SPI3_BASE 0x40003C00U
|
||||
|
||||
#define I2C1_BASE 0x40005400U
|
||||
#define I2C2_BASE 0x40005800U
|
||||
#define I2C3_BASE 0x40005C00U
|
||||
|
||||
#define ADC1_BASE 0x40012000U
|
||||
#define ADC2_BASE 0x40012100U
|
||||
#define ADC3_BASE 0x40012200U
|
||||
|
||||
#define TIM1_BASE 0x40010000U
|
||||
#define TIM2_BASE 0x40000000U
|
||||
#define TIM3_BASE 0x40000400U
|
||||
#define TIM4_BASE 0x40000800U
|
||||
#define TIM5_BASE 0x40000C00U
|
||||
#define TIM8_BASE 0x40010400U
|
||||
#define TIM9_BASE 0x40014000U
|
||||
#define TIM10_BASE 0x40014400U
|
||||
#define TIM11_BASE 0x40014800U
|
||||
#define TIM12_BASE 0x40001800U
|
||||
#define TIM13_BASE 0x40001C00U
|
||||
#define TIM14_BASE 0x40002000U
|
||||
|
||||
#define CAN1_BASE 0x40006400U
|
||||
#define CAN2_BASE 0x40006800U
|
||||
|
||||
/* NVIC Priority Configuration */
|
||||
#define CAN1_IRQ_PRIORITY 5
|
||||
#define CAN2_IRQ_PRIORITY 5
|
||||
#define USART1_IRQ_PRIORITY 6
|
||||
#define USART2_IRQ_PRIORITY 6
|
||||
#define USART3_IRQ_PRIORITY 6
|
||||
#define SPI1_IRQ_PRIORITY 7
|
||||
#define SPI2_IRQ_PRIORITY 7
|
||||
#define I2C1_IRQ_PRIORITY 7
|
||||
#define I2C2_IRQ_PRIORITY 7
|
||||
#define ADC_IRQ_PRIORITY 8
|
||||
#define TIM_IRQ_PRIORITY 8
|
||||
|
||||
/* DMA Configuration */
|
||||
#define DMA1_STREAM0_CHANNEL 0
|
||||
#define DMA1_STREAM1_CHANNEL 1
|
||||
#define DMA1_STREAM2_CHANNEL 2
|
||||
#define DMA1_STREAM3_CHANNEL 3
|
||||
#define DMA1_STREAM4_CHANNEL 4
|
||||
#define DMA1_STREAM5_CHANNEL 5
|
||||
#define DMA1_STREAM6_CHANNEL 6
|
||||
#define DMA1_STREAM7_CHANNEL 7
|
||||
|
||||
#define DMA2_STREAM0_CHANNEL 0
|
||||
#define DMA2_STREAM1_CHANNEL 1
|
||||
#define DMA2_STREAM2_CHANNEL 2
|
||||
#define DMA2_STREAM3_CHANNEL 3
|
||||
#define DMA2_STREAM4_CHANNEL 4
|
||||
#define DMA2_STREAM5_CHANNEL 5
|
||||
#define DMA2_STREAM6_CHANNEL 6
|
||||
#define DMA2_STREAM7_CHANNEL 7
|
||||
|
||||
/* GPIO Alternate Function Mapping */
|
||||
#define GPIO_AF_UART1_TX 7
|
||||
#define GPIO_AF_UART1_RX 7
|
||||
#define GPIO_AF_UART2_TX 7
|
||||
#define GPIO_AF_UART2_RX 7
|
||||
#define GPIO_AF_SPI1_SCK 5
|
||||
#define GPIO_AF_SPI1_MOSI 5
|
||||
#define GPIO_AF_SPI1_MISO 5
|
||||
#define GPIO_AF_I2C1_SCL 4
|
||||
#define GPIO_AF_I2C1_SDA 4
|
||||
#define GPIO_AF_CAN1_TX 9
|
||||
#define GPIO_AF_CAN1_RX 9
|
||||
#define GPIO_AF_TIM1_CH1 1
|
||||
#define GPIO_AF_TIM1_CH2 1
|
||||
#define GPIO_AF_TIM1_CH3 1
|
||||
#define GPIO_AF_TIM1_CH4 1
|
||||
|
||||
/* Memory Configuration */
|
||||
#define FLASH_SIZE 0x100000U /* 1 MB */
|
||||
#define RAM_SIZE 0x20000U /* 128 KB */
|
||||
#define CCM_RAM_SIZE 0x10000U /* 64 KB */
|
||||
|
||||
/* Safety Features */
|
||||
#define ENABLE_CLOCK_SECURITY_SYSTEM 1
|
||||
#define ENABLE_BROWN_OUT_RESET 1
|
||||
#define BROWNOUT_THRESHOLD 0x08 /* 2.7V */
|
||||
|
||||
#endif /* STM32F4XX_CONFIG_H */
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* @file stm32f4xx_hal.c
|
||||
* @brief STM32F4 Hardware Abstraction Layer
|
||||
*/
|
||||
|
||||
#include "stm32f4xx_config.h"
|
||||
#include "can_driver.h"
|
||||
#include "uart_driver.h"
|
||||
#include "spi_driver.h"
|
||||
#include "i2c_driver.h"
|
||||
#include "gpio_driver.h"
|
||||
#include "adc_driver.h"
|
||||
#include "pwm_driver.h"
|
||||
#include "stm32f4xx.h"
|
||||
|
||||
/* CAN HAL Implementation */
|
||||
int hal_can_init(uint32_t baudrate, uint8_t frame_type, bool enable_fd) {
|
||||
/* Enable CAN clock */
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
|
||||
|
||||
/* Configure CAN GPIO */
|
||||
// PB8 - CAN1_RX, PB9 - CAN1_TX
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
|
||||
|
||||
GPIOB->MODER |= (GPIO_MODER_MODER8_1 | GPIO_MODER_MODER9_1);
|
||||
GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_8 | GPIO_OTYPER_OT_9);
|
||||
GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9);
|
||||
GPIOB->AFR[1] |= (9 << 0) | (9 << 4); /* AF9 for CAN */
|
||||
|
||||
/* Reset CAN */
|
||||
CAN1->MCR |= CAN_MCR_RESET;
|
||||
CAN1->MCR &= ~CAN_MCR_RESET;
|
||||
|
||||
/* Exit sleep mode */
|
||||
CAN1->MCR &= ~CAN_MCR_SLEEP;
|
||||
|
||||
/* Set baudrate */
|
||||
uint32_t prescaler = 0;
|
||||
uint32_t time_quantum = 0;
|
||||
|
||||
switch (baudrate) {
|
||||
case 125000:
|
||||
prescaler = 21;
|
||||
time_quantum = 16;
|
||||
break;
|
||||
case 250000:
|
||||
prescaler = 11;
|
||||
time_quantum = 15;
|
||||
break;
|
||||
case 500000:
|
||||
prescaler = 5;
|
||||
time_quantum = 16;
|
||||
break;
|
||||
case 1000000:
|
||||
prescaler = 3;
|
||||
time_quantum = 14;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
CAN1->BTR = ((prescaler - 1) << 20) |
|
||||
((time_quantum - 1) << 16) |
|
||||
(3 << 20) | /* SJW = 4 */
|
||||
(7 << 16); /* BS1 = 8 */
|
||||
|
||||
/* Configure filters */
|
||||
CAN1->FMR |= CAN_FMR_FINIT;
|
||||
CAN1->FM1R &= ~CAN_FM1R_FBM0; /* Mask mode for filter 0 */
|
||||
CAN1->FS1R |= CAN_FS1R_FSC0; /* 32-bit scale */
|
||||
CAN1->FFA1R &= ~CAN_FFA1R_FFA0; /* FIFO 0 */
|
||||
CAN1->FMR &= ~CAN_FMR_FINIT;
|
||||
|
||||
/* Enable interrupts */
|
||||
CAN1->IER |= CAN_IER_FMPIE0 | /* FIFO 0 message pending */
|
||||
CAN_IER_TMEIE | /* Transmit mailbox empty */
|
||||
CAN_IER_BOFIE | /* Bus-off */
|
||||
CAN_IER_ERRIE; /* Error */
|
||||
|
||||
/* Normal mode */
|
||||
CAN1->MCR &= ~CAN_MCR_SLEEP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_send_message(const CanMessage_t* message, uint32_t* mailbox) {
|
||||
/* Check for free mailbox */
|
||||
if ((CAN1->TSR & CAN_TSR_TME0) != 0) {
|
||||
*mailbox = 0;
|
||||
} else if ((CAN1->TSR & CAN_TSR_TME1) != 0) {
|
||||
*mailbox = 1;
|
||||
} else if ((CAN1->TSR & CAN_TSR_TME2) != 0) {
|
||||
*mailbox = 2;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Configure mailbox */
|
||||
CAN_TxMailBox_TypeDef* tx_mailbox = &CAN1->sTxMailBox[*mailbox];
|
||||
|
||||
/* Set ID */
|
||||
if (message->id.is_extended) {
|
||||
tx_mailbox->TIR = (message->id.id << 3) | CAN_TI0R_IDE;
|
||||
} else {
|
||||
tx_mailbox->TIR = (message->id.id << 21);
|
||||
}
|
||||
|
||||
/* Set data length and data */
|
||||
tx_mailbox->TDTR = message->length;
|
||||
|
||||
/* Copy data */
|
||||
uint32_t data[2] = {0, 0};
|
||||
for (int i = 0; i < message->length; i++) {
|
||||
if (i < 4) {
|
||||
data[0] |= (message->data[i] << (i * 8));
|
||||
} else {
|
||||
data[1] |= (message->data[i] << ((i - 4) * 8));
|
||||
}
|
||||
}
|
||||
|
||||
tx_mailbox->TDLR = data[0];
|
||||
tx_mailbox->TDHR = data[1];
|
||||
|
||||
/* Request transmission */
|
||||
tx_mailbox->TIR |= CAN_TI0R_TXRQ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hal_can_receive_message(CanMessage_t* message) {
|
||||
/* Check if message available */
|
||||
if ((CAN1->RF0R & CAN_RF0R_FMP0) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get message */
|
||||
CAN_FIFOMailBox_TypeDef* rx_mailbox = &CAN1->sFIFOMailBox[0];
|
||||
|
||||
/* Get ID */
|
||||
if (rx_mailbox->RIR & CAN_RI0R_IDE) {
|
||||
message->id.is_extended = true;
|
||||
message->id.id = (rx_mailbox->RIR >> 3) & 0x1FFFFFFF;
|
||||
} else {
|
||||
message->id.is_extended = false;
|
||||
message->id.id = (rx_mailbox->RIR >> 21) & 0x7FF;
|
||||
}
|
||||
|
||||
/* Get data length */
|
||||
message->length = rx_mailbox->RDTR & CAN_RDT0R_DLC;
|
||||
|
||||
/* Get data */
|
||||
uint32_t data_low = rx_mailbox->RDLR;
|
||||
uint32_t data_high = rx_mailbox->RDHR;
|
||||
|
||||
for (int i = 0; i < message->length; i++) {
|
||||
if (i < 4) {
|
||||
message->data[i] = (data_low >> (i * 8)) & 0xFF;
|
||||
} else {
|
||||
message->data[i] = (data_high >> ((i - 4) * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* Release FIFO */
|
||||
CAN1->RF0R |= CAN_RF0R_RFOM0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPIO HAL Implementation */
|
||||
void hal_gpio_init(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||
|
||||
if (gpio_port == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable GPIO clock */
|
||||
RCC->AHB1ENR |= (1 << port);
|
||||
|
||||
/* Configure mode */
|
||||
uint32_t moder_value = 0;
|
||||
switch (mode) {
|
||||
case GPIO_MODE_INPUT:
|
||||
moder_value = 0x00;
|
||||
break;
|
||||
case GPIO_MODE_OUTPUT:
|
||||
moder_value = 0x01;
|
||||
break;
|
||||
case GPIO_MODE_ALTERNATE:
|
||||
moder_value = 0x02;
|
||||
break;
|
||||
case GPIO_MODE_ANALOG:
|
||||
moder_value = 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
gpio_port->MODER &= ~(0x03 << (pin * 2));
|
||||
gpio_port->MODER |= (moder_value << (pin * 2));
|
||||
}
|
||||
|
||||
void hal_gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||
|
||||
if (gpio_port == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
gpio_port->BSRR = (1 << pin);
|
||||
} else {
|
||||
gpio_port->BSRR = (1 << (pin + 16));
|
||||
}
|
||||
}
|
||||
|
||||
bool hal_gpio_read(uint8_t port, uint8_t pin) {
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port(port);
|
||||
|
||||
if (gpio_port == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (gpio_port->IDR & (1 << pin)) != 0;
|
||||
}
|
||||
|
||||
/* UART HAL Implementation */
|
||||
int hal_uart_init(uint8_t instance, UartConfig_t* config) {
|
||||
USART_TypeDef* uart = get_uart_instance(instance);
|
||||
|
||||
if (uart == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Enable clock */
|
||||
if (instance == 0) {
|
||||
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
||||
} else {
|
||||
RCC->APB1ENR |= (RCC_APB1ENR_USART2EN << (instance - 1));
|
||||
}
|
||||
|
||||
/* Configure baudrate */
|
||||
uint32_t clock = (instance == 0 || instance == 5) ? APB2_CLOCK : APB1_CLOCK;
|
||||
uart->BRR = clock / config->baudrate;
|
||||
|
||||
/* Configure control registers */
|
||||
uart->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
|
||||
|
||||
if (config->data_bits == UART_DATA_BITS_9) {
|
||||
uart->CR1 |= USART_CR1_M;
|
||||
}
|
||||
|
||||
uart->CR2 = 0;
|
||||
if (config->stop_bits == UART_STOP_BITS_2) {
|
||||
uart->CR2 |= USART_CR2_STOP_1;
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
if (config->enable_rx) {
|
||||
uart->CR1 |= USART_CR1_RXNEIE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
static GPIO_TypeDef* get_gpio_port(uint8_t port) {
|
||||
switch (port) {
|
||||
case 0: return GPIOA;
|
||||
case 1: return GPIOB;
|
||||
case 2: return GPIOC;
|
||||
case 3: return GPIOD;
|
||||
case 4: return GPIOE;
|
||||
case 5: return GPIOF;
|
||||
case 6: return GPIOG;
|
||||
case 7: return GPIOH;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static USART_TypeDef* get_uart_instance(uint8_t instance) {
|
||||
switch (instance) {
|
||||
case 0: return USART1;
|
||||
case 1: return USART2;
|
||||
case 2: return USART3;
|
||||
case 3: return UART4;
|
||||
case 4: return UART5;
|
||||
case 5: return USART6;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* @file adc_driver.c
|
||||
* @brief ADC driver implementation
|
||||
*/
|
||||
|
||||
#include "adc_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ADC Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
AdcConfig_t config;
|
||||
AdcStatistics_t statistics;
|
||||
uint16_t conversion_buffer[ADC_MAX_CHANNELS];
|
||||
uint8_t current_channel;
|
||||
bool conversion_active;
|
||||
bool dma_active;
|
||||
uint16_t* dma_buffer;
|
||||
uint16_t dma_length;
|
||||
uint16_t dma_index;
|
||||
Semaphore_t conversion_semaphore;
|
||||
Mutex_t conversion_mutex;
|
||||
} AdcDriverState_t;
|
||||
|
||||
static AdcDriverState_t adc_drivers[ADC_MAX_INSTANCES];
|
||||
|
||||
/* Initialize ADC Driver */
|
||||
KernelStatus_t adc_init(uint8_t instance, AdcConfig_t* config) {
|
||||
if (instance >= ADC_MAX_INSTANCES || config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&driver->config, config, sizeof(AdcConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&driver->statistics, 0, sizeof(AdcStatistics_t));
|
||||
driver->current_channel = 0;
|
||||
driver->conversion_active = false;
|
||||
driver->dma_active = false;
|
||||
driver->dma_buffer = NULL;
|
||||
driver->dma_length = 0;
|
||||
driver->dma_index = 0;
|
||||
|
||||
/* Create synchronization primitives */
|
||||
semaphore_create(&driver->conversion_semaphore, SEMAPHORE_BINARY, 0, 1);
|
||||
mutex_create(&driver->conversion_mutex, false);
|
||||
|
||||
/* Initialize ADC hardware */
|
||||
if (hal_adc_init(instance, config) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
hal_adc_enable_interrupts(instance);
|
||||
|
||||
driver->initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start ADC Conversion */
|
||||
KernelStatus_t adc_start_conversion(uint8_t instance) {
|
||||
if (instance >= ADC_MAX_INSTANCES) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (driver->conversion_active) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Reset conversion state */
|
||||
driver->current_channel = 0;
|
||||
driver->conversion_active = true;
|
||||
|
||||
/* Start conversion */
|
||||
hal_adc_start_conversion(instance);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Stop ADC Conversion */
|
||||
KernelStatus_t adc_stop_conversion(uint8_t instance) {
|
||||
if (instance >= ADC_MAX_INSTANCES) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
hal_adc_stop_conversion(instance);
|
||||
driver->conversion_active = false;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Read ADC Channel */
|
||||
KernelStatus_t adc_read_channel(uint8_t instance, uint8_t channel, uint16_t* value,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= ADC_MAX_INSTANCES || value == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Lock conversion mutex */
|
||||
if (mutex_lock(&driver->conversion_mutex, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Configure single channel conversion */
|
||||
hal_adc_configure_channel(instance, channel);
|
||||
|
||||
/* Start conversion */
|
||||
hal_adc_start_single_conversion(instance, channel);
|
||||
|
||||
/* Wait for conversion complete */
|
||||
if (semaphore_take(&driver->conversion_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
mutex_unlock(&driver->conversion_mutex);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Read value */
|
||||
*value = hal_adc_read_value(instance, channel);
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.conversions_completed++;
|
||||
|
||||
/* Unlock */
|
||||
mutex_unlock(&driver->conversion_mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Read Multiple ADC Channels */
|
||||
KernelStatus_t adc_read_channels(uint8_t instance, uint16_t* values, uint8_t count,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= ADC_MAX_INSTANCES || values == NULL || count == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Start conversion */
|
||||
if (adc_start_conversion(instance) != KERNEL_OK) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Wait for all channels */
|
||||
if (semaphore_take(&driver->conversion_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Copy values */
|
||||
memcpy(values, driver->conversion_buffer, count * sizeof(uint16_t));
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start DMA Conversion */
|
||||
KernelStatus_t adc_start_dma(uint8_t instance, uint16_t* buffer, uint16_t length) {
|
||||
if (instance >= ADC_MAX_INSTANCES || buffer == NULL || length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (driver->dma_active) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Configure DMA */
|
||||
driver->dma_buffer = buffer;
|
||||
driver->dma_length = length;
|
||||
driver->dma_index = 0;
|
||||
driver->dma_active = true;
|
||||
|
||||
/* Start DMA conversion */
|
||||
hal_adc_start_dma(instance, buffer, length);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* ADC Interrupt Handler */
|
||||
void adc_process_interrupt(uint8_t instance) {
|
||||
if (instance >= ADC_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
AdcDriverState_t* driver = &adc_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t interrupt_status = hal_adc_get_interrupt_status(instance);
|
||||
|
||||
/* Handle conversion complete */
|
||||
if (interrupt_status & ADC_INTERRUPT_CONVERSION_COMPLETE) {
|
||||
if (driver->conversion_active) {
|
||||
/* Store converted value */
|
||||
uint16_t value = hal_adc_read_current_value(instance);
|
||||
|
||||
if (driver->current_channel < ADC_MAX_CHANNELS) {
|
||||
driver->conversion_buffer[driver->current_channel] = value;
|
||||
driver->current_channel++;
|
||||
}
|
||||
|
||||
/* Check if all channels converted */
|
||||
if (driver->current_channel >= driver->config.channel_count) {
|
||||
driver->conversion_active = false;
|
||||
|
||||
/* Call callback */
|
||||
if (driver->config.conversion_complete_callback != NULL) {
|
||||
driver->config.conversion_complete_callback(
|
||||
driver->conversion_buffer,
|
||||
driver->config.channel_count);
|
||||
}
|
||||
|
||||
/* Signal conversion complete */
|
||||
semaphore_give(&driver->conversion_semaphore);
|
||||
} else {
|
||||
/* Start next channel conversion */
|
||||
hal_adc_start_channel_conversion(instance,
|
||||
driver->config.channels[driver->current_channel].channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle DMA complete */
|
||||
if (interrupt_status & ADC_INTERRUPT_DMA_COMPLETE) {
|
||||
driver->dma_active = false;
|
||||
driver->statistics.dma_transfers++;
|
||||
}
|
||||
|
||||
/* Handle watchdog */
|
||||
if (interrupt_status & ADC_INTERRUPT_WATCHDOG) {
|
||||
driver->statistics.watchdog_events++;
|
||||
}
|
||||
|
||||
/* Handle errors */
|
||||
if (interrupt_status & ADC_INTERRUPT_ERROR) {
|
||||
driver->statistics.conversions_failed++;
|
||||
driver->statistics.overrun_errors++;
|
||||
}
|
||||
|
||||
/* Clear interrupt flags */
|
||||
hal_adc_clear_interrupts(instance, interrupt_status);
|
||||
}
|
||||
|
||||
/* Convert ADC Value to Voltage */
|
||||
float adc_convert_to_voltage(uint16_t adc_value, AdcResolution_t resolution,
|
||||
float reference_voltage) {
|
||||
uint32_t max_value = (1 << resolution) - 1;
|
||||
return ((float)adc_value / (float)max_value) * reference_voltage;
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* @file can_driver.c
|
||||
* @brief CAN driver implementation
|
||||
*/
|
||||
|
||||
#include "can_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* CAN Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
CanConfig_t config;
|
||||
CanStatistics_t statistics;
|
||||
CanMessage_t rx_fifo[CAN_MAX_RX_FIFO_DEPTH];
|
||||
uint8_t rx_head;
|
||||
uint8_t rx_tail;
|
||||
uint8_t rx_count;
|
||||
Semaphore_t rx_semaphore;
|
||||
Semaphore_t tx_semaphore;
|
||||
Mutex_t tx_mutex;
|
||||
bool bus_off;
|
||||
} CanDriverState_t;
|
||||
|
||||
static CanDriverState_t can_driver;
|
||||
|
||||
/* Initialize CAN Driver */
|
||||
KernelStatus_t can_init(CanConfig_t* config) {
|
||||
if (config == NULL || can_driver.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&can_driver.config, config, sizeof(CanConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&can_driver.statistics, 0, sizeof(CanStatistics_t));
|
||||
can_driver.rx_head = 0;
|
||||
can_driver.rx_tail = 0;
|
||||
can_driver.rx_count = 0;
|
||||
can_driver.bus_off = false;
|
||||
|
||||
/* Create synchronization primitives */
|
||||
semaphore_create(&can_driver.rx_semaphore, SEMAPHORE_COUNTING, 0,
|
||||
CAN_MAX_RX_FIFO_DEPTH);
|
||||
semaphore_create(&can_driver.tx_semaphore, SEMAPHORE_COUNTING,
|
||||
CAN_MAX_TX_MAILBOXES, CAN_MAX_TX_MAILBOXES);
|
||||
mutex_create(&can_driver.tx_mutex, false);
|
||||
|
||||
/* Configure CAN hardware */
|
||||
if (hal_can_init(&config->nominal_baudrate, config->frame_type,
|
||||
config->enable_fd) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Configure filters */
|
||||
for (uint8_t i = 0; i < config->filter_count; i++) {
|
||||
if (config->filters[i].enable) {
|
||||
hal_can_configure_filter(&config->filters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable CAN interrupts */
|
||||
hal_can_enable_interrupts();
|
||||
|
||||
can_driver.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Send CAN Message */
|
||||
KernelStatus_t can_send_message(const CanMessage_t* message, uint32_t timeout_ms) {
|
||||
if (!can_driver.initialized || message == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (can_driver.bus_off) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Take TX semaphore with timeout */
|
||||
if (semaphore_take(&can_driver.tx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
can_driver.statistics.tx_overflow++;
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Lock TX mutex */
|
||||
if (mutex_lock(&can_driver.tx_mutex, timeout_ms) != KERNEL_OK) {
|
||||
semaphore_give(&can_driver.tx_semaphore);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Send message via hardware */
|
||||
uint32_t mailbox;
|
||||
if (hal_can_send_message(message, &mailbox) != 0) {
|
||||
mutex_unlock(&can_driver.tx_mutex);
|
||||
semaphore_give(&can_driver.tx_semaphore);
|
||||
can_driver.statistics.tx_errors++;
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
can_driver.statistics.tx_messages++;
|
||||
|
||||
/* Unlock TX mutex */
|
||||
mutex_unlock(&can_driver.tx_mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Receive CAN Message */
|
||||
KernelStatus_t can_receive_message(CanMessage_t* message, uint32_t timeout_ms) {
|
||||
if (!can_driver.initialized || message == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Wait for message */
|
||||
if (semaphore_take(&can_driver.rx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Get message from FIFO */
|
||||
critical_section_enter();
|
||||
|
||||
memcpy(message, &can_driver.rx_fifo[can_driver.rx_head], sizeof(CanMessage_t));
|
||||
can_driver.rx_head = (can_driver.rx_head + 1) % CAN_MAX_RX_FIFO_DEPTH;
|
||||
can_driver.rx_count--;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
can_driver.statistics.rx_messages++;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* CAN Interrupt Handler */
|
||||
void can_process_interrupt(void) {
|
||||
uint32_t interrupt_status = hal_can_get_interrupt_status();
|
||||
|
||||
/* Check for received messages */
|
||||
if (interrupt_status & CAN_INTERRUPT_RX) {
|
||||
CanMessage_t message;
|
||||
|
||||
while (hal_can_receive_message(&message) == 0) {
|
||||
/* Add to RX FIFO */
|
||||
critical_section_enter();
|
||||
|
||||
if (can_driver.rx_count < CAN_MAX_RX_FIFO_DEPTH) {
|
||||
memcpy(&can_driver.rx_fifo[can_driver.rx_tail], &message,
|
||||
sizeof(CanMessage_t));
|
||||
can_driver.rx_tail = (can_driver.rx_tail + 1) % CAN_MAX_RX_FIFO_DEPTH;
|
||||
can_driver.rx_count++;
|
||||
|
||||
/* Signal message available */
|
||||
semaphore_give(&can_driver.rx_semaphore);
|
||||
|
||||
/* Call callback if registered */
|
||||
if (can_driver.config.rx_callback != NULL) {
|
||||
can_driver.config.rx_callback(&message);
|
||||
}
|
||||
} else {
|
||||
can_driver.statistics.rx_overflow++;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for transmit complete */
|
||||
if (interrupt_status & CAN_INTERRUPT_TX) {
|
||||
uint32_t mailbox = hal_can_get_tx_mailbox();
|
||||
|
||||
/* Release TX semaphore */
|
||||
semaphore_give(&can_driver.tx_semaphore);
|
||||
|
||||
/* Call callback if registered */
|
||||
if (can_driver.config.tx_callback != NULL) {
|
||||
can_driver.config.tx_callback(mailbox, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for errors */
|
||||
if (interrupt_status & CAN_INTERRUPT_ERROR) {
|
||||
uint32_t error_code = hal_can_get_error_status();
|
||||
|
||||
can_driver.statistics.rx_errors++;
|
||||
can_driver.statistics.tx_errors++;
|
||||
|
||||
/* Check for bus-off */
|
||||
if (error_code & CAN_ERROR_BUS_OFF) {
|
||||
can_driver.bus_off = true;
|
||||
can_driver.statistics.bus_off_count++;
|
||||
}
|
||||
|
||||
/* Call error callback */
|
||||
if (can_driver.config.error_callback != NULL) {
|
||||
can_driver.config.error_callback(error_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Recover from Bus-Off */
|
||||
KernelStatus_t can_recover_bus_off(void) {
|
||||
if (!can_driver.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Reset CAN controller */
|
||||
hal_can_reset();
|
||||
|
||||
/* Reconfigure */
|
||||
hal_can_init(&can_driver.config.nominal_baudrate,
|
||||
can_driver.config.frame_type, can_driver.config.enable_fd);
|
||||
|
||||
/* Clear bus-off flag */
|
||||
can_driver.bus_off = false;
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get CAN Statistics */
|
||||
KernelStatus_t can_get_statistics(CanStatistics_t* stats) {
|
||||
if (stats == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
memcpy(stats, &can_driver.statistics, sizeof(CanStatistics_t));
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @file gpio_driver.c
|
||||
* @brief GPIO driver implementation
|
||||
*/
|
||||
|
||||
#include "gpio_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* GPIO Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
GpioPinConfig_t pins[GPIO_MAX_PORTS][GPIO_MAX_PINS_PER_PORT];
|
||||
GpioInterruptConfig_t interrupts[GPIO_MAX_INTERRUPTS];
|
||||
uint8_t interrupt_count;
|
||||
Mutex_t mutex;
|
||||
} GpioDriverState_t;
|
||||
|
||||
static GpioDriverState_t gpio_driver;
|
||||
|
||||
/* Initialize GPIO Driver */
|
||||
KernelStatus_t gpio_init(const GpioPinConfig_t* config) {
|
||||
if (config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!gpio_driver.initialized) {
|
||||
memset(&gpio_driver, 0, sizeof(GpioDriverState_t));
|
||||
mutex_create(&gpio_driver.mutex, false);
|
||||
gpio_driver.initialized = true;
|
||||
}
|
||||
|
||||
/* Validate parameters */
|
||||
if (config->port >= GPIO_MAX_PORTS || config->pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Store pin configuration */
|
||||
gpio_driver.pins[config->port][config->pin] = *config;
|
||||
|
||||
/* Configure GPIO hardware */
|
||||
hal_gpio_init(config->port, config->pin, config->mode);
|
||||
|
||||
/* Configure output type */
|
||||
if (config->mode == GPIO_MODE_OUTPUT || config->mode == GPIO_MODE_ALTERNATE) {
|
||||
hal_gpio_set_output_type(config->port, config->pin, config->output_type);
|
||||
}
|
||||
|
||||
/* Configure pull */
|
||||
hal_gpio_set_pull(config->port, config->pin, config->pull);
|
||||
|
||||
/* Configure speed */
|
||||
hal_gpio_set_speed(config->port, config->pin, config->speed);
|
||||
|
||||
/* Configure alternate function */
|
||||
if (config->mode == GPIO_MODE_ALTERNATE) {
|
||||
hal_gpio_set_alternate_function(config->port, config->pin,
|
||||
config->alternate_function);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set GPIO Mode */
|
||||
KernelStatus_t gpio_set_mode(uint8_t port, uint8_t pin, GpioMode_t mode) {
|
||||
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gpio_driver.pins[port][pin].mode = mode;
|
||||
hal_gpio_init(port, pin, mode);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Write GPIO Pin */
|
||||
KernelStatus_t gpio_write(uint8_t port, uint8_t pin, bool value) {
|
||||
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (gpio_driver.pins[port][pin].mode != GPIO_MODE_OUTPUT) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
hal_gpio_write(port, pin, value);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Write GPIO Port */
|
||||
KernelStatus_t gpio_write_port(uint8_t port, uint16_t value) {
|
||||
if (port >= GPIO_MAX_PORTS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
hal_gpio_write_port(port, value);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Read GPIO Pin */
|
||||
bool gpio_read(uint8_t port, uint8_t pin) {
|
||||
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hal_gpio_read(port, pin);
|
||||
}
|
||||
|
||||
/* Read GPIO Port */
|
||||
uint16_t gpio_read_port(uint8_t port) {
|
||||
if (port >= GPIO_MAX_PORTS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hal_gpio_read_port(port);
|
||||
}
|
||||
|
||||
/* Toggle GPIO Pin */
|
||||
KernelStatus_t gpio_toggle(uint8_t port, uint8_t pin) {
|
||||
if (port >= GPIO_MAX_PORTS || pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
hal_gpio_toggle(port, pin);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Configure GPIO Interrupt */
|
||||
KernelStatus_t gpio_configure_interrupt(const GpioInterruptConfig_t* config) {
|
||||
if (config == NULL || config->callback == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (config->port >= GPIO_MAX_PORTS || config->pin >= GPIO_MAX_PINS_PER_PORT) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (gpio_driver.interrupt_count >= GPIO_MAX_INTERRUPTS) {
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* Store interrupt configuration */
|
||||
gpio_driver.interrupts[gpio_driver.interrupt_count] = *config;
|
||||
gpio_driver.interrupt_count++;
|
||||
|
||||
/* Configure hardware interrupt */
|
||||
hal_gpio_configure_interrupt(config->port, config->pin, config->trigger);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* GPIO Interrupt Handler */
|
||||
void gpio_process_interrupt(uint8_t port) {
|
||||
if (port >= GPIO_MAX_PORTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get interrupt status */
|
||||
uint16_t interrupt_status = hal_gpio_get_interrupt_status(port);
|
||||
|
||||
/* Process each interrupt */
|
||||
for (uint8_t pin = 0; pin < GPIO_MAX_PINS_PER_PORT; pin++) {
|
||||
if (interrupt_status & (1 << pin)) {
|
||||
/* Find matching interrupt configuration */
|
||||
for (uint8_t i = 0; i < gpio_driver.interrupt_count; i++) {
|
||||
if (gpio_driver.interrupts[i].port == port &&
|
||||
gpio_driver.interrupts[i].pin == pin) {
|
||||
/* Call callback */
|
||||
gpio_driver.interrupts[i].callback(port, pin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt flag */
|
||||
hal_gpio_clear_interrupt(port, pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* @file i2c_driver.c
|
||||
* @brief I2C driver implementation
|
||||
*/
|
||||
|
||||
#include "i2c_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* I2C Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
I2cConfig_t config;
|
||||
I2cStatistics_t statistics;
|
||||
I2cMessage_t current_message;
|
||||
uint16_t transfer_index;
|
||||
bool transfer_active;
|
||||
Semaphore_t transfer_semaphore;
|
||||
Mutex_t transfer_mutex;
|
||||
void* user_data;
|
||||
} I2cDriverState_t;
|
||||
|
||||
static I2cDriverState_t i2c_drivers[I2C_MAX_INSTANCES];
|
||||
|
||||
/* Initialize I2C Driver */
|
||||
KernelStatus_t i2c_init(uint8_t instance, I2cConfig_t* config) {
|
||||
if (instance >= I2C_MAX_INSTANCES || config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||
|
||||
if (driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&driver->config, config, sizeof(I2cConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&driver->statistics, 0, sizeof(I2cStatistics_t));
|
||||
driver->transfer_index = 0;
|
||||
driver->transfer_active = false;
|
||||
|
||||
/* Create synchronization primitives */
|
||||
semaphore_create(&driver->transfer_semaphore, SEMAPHORE_BINARY, 1, 1);
|
||||
mutex_create(&driver->transfer_mutex, false);
|
||||
|
||||
/* Initialize I2C hardware */
|
||||
if (hal_i2c_init(instance, config) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
hal_i2c_enable_interrupts(instance);
|
||||
|
||||
driver->initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* I2C Transfer */
|
||||
KernelStatus_t i2c_transfer(uint8_t instance, const I2cMessage_t* message,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= I2C_MAX_INSTANCES || message == NULL ||
|
||||
message->data == NULL || message->length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (driver->transfer_active) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Take transfer semaphore */
|
||||
if (semaphore_take(&driver->transfer_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Lock transfer mutex */
|
||||
if (mutex_lock(&driver->transfer_mutex, timeout_ms) != KERNEL_OK) {
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Set up transfer */
|
||||
memcpy(&driver->current_message, message, sizeof(I2cMessage_t));
|
||||
driver->transfer_index = 0;
|
||||
driver->transfer_active = true;
|
||||
|
||||
/* Start transfer */
|
||||
if (message->direction == I2C_DIRECTION_WRITE) {
|
||||
hal_i2c_start_write(instance, message->slave_address);
|
||||
} else {
|
||||
hal_i2c_start_read(instance, message->slave_address);
|
||||
}
|
||||
|
||||
/* Wait for completion */
|
||||
if (timeout_ms > 0) {
|
||||
TickType_t start_tick = kernel_get_tick_count();
|
||||
|
||||
while (driver->transfer_active) {
|
||||
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||
/* Timeout - abort transfer */
|
||||
hal_i2c_abort_transfer(instance);
|
||||
driver->transfer_active = false;
|
||||
|
||||
mutex_unlock(&driver->transfer_mutex);
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
driver->statistics.timeout_errors++;
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
kernel_delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.transfers_completed++;
|
||||
driver->statistics.bytes_transferred += message->length;
|
||||
|
||||
/* Unlock */
|
||||
mutex_unlock(&driver->transfer_mutex);
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* I2C Write */
|
||||
KernelStatus_t i2c_write(uint8_t instance, uint16_t slave_address,
|
||||
const uint8_t* data, uint16_t length, uint32_t timeout_ms) {
|
||||
I2cMessage_t message = {
|
||||
.slave_address = slave_address,
|
||||
.direction = I2C_DIRECTION_WRITE,
|
||||
.data = (uint8_t*)data,
|
||||
.length = length,
|
||||
.generate_stop = true,
|
||||
.generate_restart = false
|
||||
};
|
||||
|
||||
return i2c_transfer(instance, &message, timeout_ms);
|
||||
}
|
||||
|
||||
/* I2C Read */
|
||||
KernelStatus_t i2c_read(uint8_t instance, uint16_t slave_address,
|
||||
uint8_t* data, uint16_t length, uint32_t timeout_ms) {
|
||||
I2cMessage_t message = {
|
||||
.slave_address = slave_address,
|
||||
.direction = I2C_DIRECTION_READ,
|
||||
.data = data,
|
||||
.length = length,
|
||||
.generate_stop = true,
|
||||
.generate_restart = false
|
||||
};
|
||||
|
||||
return i2c_transfer(instance, &message, timeout_ms);
|
||||
}
|
||||
|
||||
/* I2C Write/Read */
|
||||
KernelStatus_t i2c_write_read(uint8_t instance, uint16_t slave_address,
|
||||
const uint8_t* tx_data, uint16_t tx_length,
|
||||
uint8_t* rx_data, uint16_t rx_length,
|
||||
uint32_t timeout_ms) {
|
||||
/* First write */
|
||||
I2cMessage_t write_message = {
|
||||
.slave_address = slave_address,
|
||||
.direction = I2C_DIRECTION_WRITE,
|
||||
.data = (uint8_t*)tx_data,
|
||||
.length = tx_length,
|
||||
.generate_stop = false,
|
||||
.generate_restart = true
|
||||
};
|
||||
|
||||
KernelStatus_t status = i2c_transfer(instance, &write_message, timeout_ms);
|
||||
if (status != KERNEL_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Then read */
|
||||
I2cMessage_t read_message = {
|
||||
.slave_address = slave_address,
|
||||
.direction = I2C_DIRECTION_READ,
|
||||
.data = rx_data,
|
||||
.length = rx_length,
|
||||
.generate_stop = true,
|
||||
.generate_restart = false
|
||||
};
|
||||
|
||||
return i2c_transfer(instance, &read_message, timeout_ms);
|
||||
}
|
||||
|
||||
/* Check if Device is Ready */
|
||||
bool i2c_is_device_ready(uint8_t instance, uint16_t slave_address) {
|
||||
if (instance >= I2C_MAX_INSTANCES) {
|
||||
return false;
|
||||
}
|
||||
|
||||
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hal_i2c_check_device(instance, slave_address);
|
||||
}
|
||||
|
||||
/* I2C Interrupt Handler */
|
||||
void i2c_process_interrupt(uint8_t instance) {
|
||||
if (instance >= I2C_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
I2cDriverState_t* driver = &i2c_drivers[instance];
|
||||
|
||||
if (!driver->initialized || !driver->transfer_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t interrupt_status = hal_i2c_get_interrupt_status(instance);
|
||||
|
||||
/* Handle address sent */
|
||||
if (interrupt_status & I2C_INTERRUPT_ADDRESS_SENT) {
|
||||
if (driver->current_message.direction == I2C_DIRECTION_READ) {
|
||||
hal_i2c_prepare_receive(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle data transfer */
|
||||
if (interrupt_status & I2C_INTERRUPT_DATA) {
|
||||
if (driver->transfer_index < driver->current_message.length) {
|
||||
if (driver->current_message.direction == I2C_DIRECTION_WRITE) {
|
||||
/* Transmit next byte */
|
||||
uint8_t byte = driver->current_message.data[driver->transfer_index];
|
||||
hal_i2c_transmit_byte(instance, byte);
|
||||
} else {
|
||||
/* Receive next byte */
|
||||
driver->current_message.data[driver->transfer_index] =
|
||||
hal_i2c_receive_byte(instance);
|
||||
}
|
||||
|
||||
driver->transfer_index++;
|
||||
|
||||
/* Check if last byte */
|
||||
if (driver->transfer_index == driver->current_message.length) {
|
||||
if (driver->current_message.generate_stop) {
|
||||
hal_i2c_generate_stop(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle NACK */
|
||||
if (interrupt_status & I2C_INTERRUPT_NACK) {
|
||||
driver->transfer_active = false;
|
||||
driver->statistics.nack_errors++;
|
||||
|
||||
if (driver->config.transfer_complete_callback != NULL) {
|
||||
driver->config.transfer_complete_callback(I2C_TRANSFER_NACK,
|
||||
driver->user_data);
|
||||
}
|
||||
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
}
|
||||
|
||||
/* Handle transfer complete */
|
||||
if (interrupt_status & I2C_INTERRUPT_COMPLETE) {
|
||||
driver->transfer_active = false;
|
||||
|
||||
if (driver->config.transfer_complete_callback != NULL) {
|
||||
driver->config.transfer_complete_callback(I2C_TRANSFER_COMPLETE,
|
||||
driver->user_data);
|
||||
}
|
||||
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
}
|
||||
|
||||
/* Handle errors */
|
||||
if (interrupt_status & I2C_INTERRUPT_ERROR) {
|
||||
driver->transfer_active = false;
|
||||
driver->statistics.transfers_failed++;
|
||||
|
||||
uint32_t error = hal_i2c_get_error(instance);
|
||||
|
||||
if (error & I2C_ERROR_ARBITRATION_LOST) {
|
||||
driver->statistics.arbitration_lost++;
|
||||
}
|
||||
if (error & I2C_ERROR_BUS) {
|
||||
driver->statistics.bus_errors++;
|
||||
}
|
||||
|
||||
if (driver->config.transfer_complete_callback != NULL) {
|
||||
driver->config.transfer_complete_callback(I2C_TRANSFER_ERROR,
|
||||
driver->user_data);
|
||||
}
|
||||
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* @file pwm_driver.c
|
||||
* @brief PWM driver implementation
|
||||
*/
|
||||
|
||||
#include "pwm_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* PWM Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
PwmConfig_t config;
|
||||
PwmStatistics_t statistics;
|
||||
uint32_t current_duty_cycles[PWM_MAX_CHANNELS];
|
||||
bool channel_enabled[PWM_MAX_CHANNELS];
|
||||
uint32_t fault_flags;
|
||||
Mutex_t mutex;
|
||||
} PwmDriverState_t;
|
||||
|
||||
static PwmDriverState_t pwm_drivers[PWM_MAX_INSTANCES];
|
||||
|
||||
/* Initialize PWM Driver */
|
||||
KernelStatus_t pwm_init(uint8_t instance, PwmConfig_t* config) {
|
||||
if (instance >= PWM_MAX_INSTANCES || config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&driver->config, config, sizeof(PwmConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&driver->statistics, 0, sizeof(PwmStatistics_t));
|
||||
driver->fault_flags = 0;
|
||||
|
||||
/* Initialize channel states */
|
||||
for (uint8_t i = 0; i < PWM_MAX_CHANNELS; i++) {
|
||||
driver->current_duty_cycles[i] = 0;
|
||||
driver->channel_enabled[i] = false;
|
||||
}
|
||||
|
||||
/* Create mutex */
|
||||
mutex_create(&driver->mutex, false);
|
||||
|
||||
/* Initialize PWM hardware */
|
||||
if (hal_pwm_init(instance, config) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Configure channels */
|
||||
for (uint8_t i = 0; i < config->channel_count; i++) {
|
||||
driver->current_duty_cycles[config->channels[i].channel] =
|
||||
config->channels[i].duty_cycle;
|
||||
driver->channel_enabled[config->channels[i].channel] = true;
|
||||
|
||||
hal_pwm_configure_channel(instance, &config->channels[i]);
|
||||
}
|
||||
|
||||
/* Configure fault protection */
|
||||
if (config->enable_fault_protection) {
|
||||
hal_pwm_configure_fault(instance, config->fault_action);
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
hal_pwm_enable_interrupts(instance);
|
||||
|
||||
driver->initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start PWM */
|
||||
KernelStatus_t pwm_start(uint8_t instance) {
|
||||
if (instance >= PWM_MAX_INSTANCES) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
hal_pwm_start(instance);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Stop PWM */
|
||||
KernelStatus_t pwm_stop(uint8_t instance) {
|
||||
if (instance >= PWM_MAX_INSTANCES) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
hal_pwm_stop(instance);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set PWM Duty Cycle */
|
||||
KernelStatus_t pwm_set_duty_cycle(uint8_t instance, uint8_t channel,
|
||||
uint32_t duty_cycle) {
|
||||
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (duty_cycle > PWM_MAX_DUTY_CYCLE) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Lock mutex */
|
||||
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Update duty cycle */
|
||||
driver->current_duty_cycles[channel] = duty_cycle;
|
||||
driver->statistics.duty_cycle_updates++;
|
||||
|
||||
/* Update hardware */
|
||||
hal_pwm_set_duty_cycle(instance, channel, duty_cycle);
|
||||
|
||||
/* Unlock mutex */
|
||||
mutex_unlock(&driver->mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set PWM Frequency */
|
||||
KernelStatus_t pwm_set_frequency(uint8_t instance, uint32_t frequency_hz) {
|
||||
if (instance >= PWM_MAX_INSTANCES || frequency_hz == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Lock mutex */
|
||||
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Update frequency */
|
||||
driver->config.frequency_hz = frequency_hz;
|
||||
|
||||
/* Update hardware */
|
||||
hal_pwm_set_frequency(instance, frequency_hz);
|
||||
|
||||
/* Unlock mutex */
|
||||
mutex_unlock(&driver->mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set PWM Dead Time */
|
||||
KernelStatus_t pwm_set_dead_time(uint8_t instance, uint8_t channel,
|
||||
const PwmDeadTime_t* dead_time) {
|
||||
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS ||
|
||||
dead_time == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Lock mutex */
|
||||
if (mutex_lock(&driver->mutex, 100) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Update dead time */
|
||||
driver->config.channels[channel].dead_time = *dead_time;
|
||||
|
||||
/* Update hardware */
|
||||
hal_pwm_set_dead_time(instance, channel, dead_time);
|
||||
|
||||
/* Unlock mutex */
|
||||
mutex_unlock(&driver->mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Enable PWM Channel */
|
||||
KernelStatus_t pwm_enable_channel(uint8_t instance, uint8_t channel) {
|
||||
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
driver->channel_enabled[channel] = true;
|
||||
hal_pwm_enable_channel(instance, channel);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Disable PWM Channel */
|
||||
KernelStatus_t pwm_disable_channel(uint8_t instance, uint8_t channel) {
|
||||
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
driver->channel_enabled[channel] = false;
|
||||
hal_pwm_disable_channel(instance, channel);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Clear PWM Fault */
|
||||
KernelStatus_t pwm_clear_fault(uint8_t instance) {
|
||||
if (instance >= PWM_MAX_INSTANCES) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
driver->fault_flags = 0;
|
||||
hal_pwm_clear_fault(instance);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* PWM Interrupt Handler */
|
||||
void pwm_process_interrupt(uint8_t instance) {
|
||||
if (instance >= PWM_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
PwmDriverState_t* driver = &pwm_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t interrupt_status = hal_pwm_get_interrupt_status(instance);
|
||||
|
||||
/* Handle period elapsed */
|
||||
if (interrupt_status & PWM_INTERRUPT_PERIOD_ELAPSED) {
|
||||
driver->statistics.period_elapsed_count++;
|
||||
|
||||
/* Call callback */
|
||||
if (driver->config.period_elapsed_callback != NULL) {
|
||||
driver->config.period_elapsed_callback(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle fault */
|
||||
if (interrupt_status & PWM_INTERRUPT_FAULT) {
|
||||
driver->statistics.fault_events++;
|
||||
driver->fault_flags = hal_pwm_get_fault_flags(instance);
|
||||
|
||||
/* Apply fault action */
|
||||
hal_pwm_apply_fault_action(instance, driver->config.fault_action);
|
||||
|
||||
/* Call callback */
|
||||
if (driver->config.fault_callback != NULL) {
|
||||
driver->config.fault_callback(instance, driver->fault_flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt flags */
|
||||
hal_pwm_clear_interrupts(instance, interrupt_status);
|
||||
}
|
||||
|
||||
/* Get PWM Statistics */
|
||||
KernelStatus_t pwm_get_statistics(uint8_t instance, PwmStatistics_t* stats) {
|
||||
if (instance >= PWM_MAX_INSTANCES || stats == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(stats, &pwm_drivers[instance].statistics, sizeof(PwmStatistics_t));
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get PWM Duty Cycle */
|
||||
uint32_t pwm_get_duty_cycle(uint8_t instance, uint8_t channel) {
|
||||
if (instance >= PWM_MAX_INSTANCES || channel >= PWM_MAX_CHANNELS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pwm_drivers[instance].current_duty_cycles[channel];
|
||||
}
|
||||
|
||||
/* Get PWM Frequency */
|
||||
uint32_t pwm_get_frequency(uint8_t instance) {
|
||||
if (instance >= PWM_MAX_INSTANCES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pwm_drivers[instance].config.frequency_hz;
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* @file spi_driver.c
|
||||
* @brief SPI driver implementation
|
||||
*/
|
||||
|
||||
#include "spi_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* SPI Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
SpiConfig_t config;
|
||||
SpiStatistics_t statistics;
|
||||
uint8_t tx_buffer[SPI_MAX_BUFFER_SIZE];
|
||||
uint8_t rx_buffer[SPI_MAX_BUFFER_SIZE];
|
||||
uint16_t transfer_length;
|
||||
uint16_t transfer_index;
|
||||
bool transfer_active;
|
||||
bool use_dma;
|
||||
Semaphore_t transfer_semaphore;
|
||||
Mutex_t transfer_mutex;
|
||||
void* user_data;
|
||||
} SpiDriverState_t;
|
||||
|
||||
static SpiDriverState_t spi_drivers[SPI_MAX_INSTANCES];
|
||||
|
||||
/* Initialize SPI Driver */
|
||||
KernelStatus_t spi_init(uint8_t instance, SpiConfig_t* config) {
|
||||
if (instance >= SPI_MAX_INSTANCES || config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||
|
||||
if (driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&driver->config, config, sizeof(SpiConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&driver->statistics, 0, sizeof(SpiStatistics_t));
|
||||
driver->transfer_length = 0;
|
||||
driver->transfer_index = 0;
|
||||
driver->transfer_active = false;
|
||||
driver->use_dma = config->use_dma;
|
||||
|
||||
/* Create synchronization primitives */
|
||||
semaphore_create(&driver->transfer_semaphore, SEMAPHORE_BINARY, 1, 1);
|
||||
mutex_create(&driver->transfer_mutex, false);
|
||||
|
||||
/* Initialize SPI hardware */
|
||||
if (hal_spi_init(instance, config) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Enable interrupts if not using DMA */
|
||||
if (!driver->use_dma) {
|
||||
hal_spi_enable_interrupts(instance);
|
||||
}
|
||||
|
||||
driver->initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* SPI Transfer */
|
||||
KernelStatus_t spi_transfer(uint8_t instance, const SpiTransaction_t* transaction,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= SPI_MAX_INSTANCES || transaction == NULL ||
|
||||
transaction->length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Check if transfer is already active */
|
||||
if (driver->transfer_active) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Take transfer semaphore */
|
||||
if (semaphore_take(&driver->transfer_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Lock transfer mutex */
|
||||
if (mutex_lock(&driver->transfer_mutex, timeout_ms) != KERNEL_OK) {
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Set up transfer */
|
||||
driver->transfer_length = transaction->length;
|
||||
driver->transfer_index = 0;
|
||||
driver->transfer_active = true;
|
||||
driver->user_data = transaction->user_data;
|
||||
|
||||
/* Copy TX data if provided */
|
||||
if (transaction->tx_data != NULL) {
|
||||
memcpy(driver->tx_buffer, transaction->tx_data, transaction->length);
|
||||
} else {
|
||||
memset(driver->tx_buffer, 0xFF, transaction->length); /* Dummy data */
|
||||
}
|
||||
|
||||
/* Clear RX buffer */
|
||||
memset(driver->rx_buffer, 0, transaction->length);
|
||||
|
||||
/* Select chip if hardware CS is not used */
|
||||
if (!driver->config.enable_hardware_cs) {
|
||||
spi_set_chip_select(instance, true);
|
||||
}
|
||||
|
||||
/* Start transfer */
|
||||
if (driver->use_dma) {
|
||||
hal_spi_start_dma_transfer(instance, driver->tx_buffer, driver->rx_buffer,
|
||||
transaction->length);
|
||||
} else {
|
||||
hal_spi_start_transfer(instance);
|
||||
}
|
||||
|
||||
/* Wait for completion */
|
||||
if (timeout_ms > 0) {
|
||||
TickType_t start_tick = kernel_get_tick_count();
|
||||
|
||||
while (driver->transfer_active) {
|
||||
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||
/* Timeout - abort transfer */
|
||||
hal_spi_abort_transfer(instance);
|
||||
driver->transfer_active = false;
|
||||
|
||||
if (!driver->config.enable_hardware_cs) {
|
||||
spi_set_chip_select(instance, false);
|
||||
}
|
||||
|
||||
mutex_unlock(&driver->transfer_mutex);
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
driver->statistics.timeout_errors++;
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
kernel_delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy RX data if requested */
|
||||
if (transaction->rx_data != NULL) {
|
||||
memcpy(transaction->rx_data, driver->rx_buffer, transaction->length);
|
||||
}
|
||||
|
||||
/* Deselect chip */
|
||||
if (!driver->config.enable_hardware_cs) {
|
||||
spi_set_chip_select(instance, false);
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.transfers_completed++;
|
||||
driver->statistics.bytes_transferred += transaction->length;
|
||||
|
||||
/* Unlock */
|
||||
mutex_unlock(&driver->transfer_mutex);
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* SPI Transfer Asynchronously */
|
||||
KernelStatus_t spi_transfer_async(uint8_t instance, const SpiTransaction_t* transaction) {
|
||||
if (instance >= SPI_MAX_INSTANCES || transaction == NULL ||
|
||||
transaction->length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (driver->transfer_active) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Set up transfer */
|
||||
driver->transfer_length = transaction->length;
|
||||
driver->transfer_index = 0;
|
||||
driver->transfer_active = true;
|
||||
driver->user_data = transaction->user_data;
|
||||
|
||||
/* Copy TX data */
|
||||
if (transaction->tx_data != NULL) {
|
||||
memcpy(driver->tx_buffer, transaction->tx_data, transaction->length);
|
||||
} else {
|
||||
memset(driver->tx_buffer, 0xFF, transaction->length);
|
||||
}
|
||||
|
||||
/* Select chip */
|
||||
if (!driver->config.enable_hardware_cs) {
|
||||
spi_set_chip_select(instance, true);
|
||||
}
|
||||
|
||||
/* Start transfer */
|
||||
if (driver->use_dma) {
|
||||
hal_spi_start_dma_transfer(instance, driver->tx_buffer, driver->rx_buffer,
|
||||
transaction->length);
|
||||
} else {
|
||||
hal_spi_start_transfer(instance);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* SPI Read */
|
||||
KernelStatus_t spi_read(uint8_t instance, uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms) {
|
||||
SpiTransaction_t transaction = {
|
||||
.tx_data = NULL,
|
||||
.rx_data = data,
|
||||
.length = length,
|
||||
.keep_cs_active = false,
|
||||
.user_data = NULL
|
||||
};
|
||||
|
||||
return spi_transfer(instance, &transaction, timeout_ms);
|
||||
}
|
||||
|
||||
/* SPI Write */
|
||||
KernelStatus_t spi_write(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms) {
|
||||
SpiTransaction_t transaction = {
|
||||
.tx_data = data,
|
||||
.rx_data = NULL,
|
||||
.length = length,
|
||||
.keep_cs_active = false,
|
||||
.user_data = NULL
|
||||
};
|
||||
|
||||
return spi_transfer(instance, &transaction, timeout_ms);
|
||||
}
|
||||
|
||||
/* SPI Read/Write */
|
||||
KernelStatus_t spi_read_write(uint8_t instance, const uint8_t* tx_data,
|
||||
uint8_t* rx_data, uint16_t length, uint32_t timeout_ms) {
|
||||
SpiTransaction_t transaction = {
|
||||
.tx_data = tx_data,
|
||||
.rx_data = rx_data,
|
||||
.length = length,
|
||||
.keep_cs_active = false,
|
||||
.user_data = NULL
|
||||
};
|
||||
|
||||
return spi_transfer(instance, &transaction, timeout_ms);
|
||||
}
|
||||
|
||||
/* SPI Interrupt Handler */
|
||||
void spi_process_interrupt(uint8_t instance) {
|
||||
if (instance >= SPI_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||
|
||||
if (!driver->initialized || !driver->transfer_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if more data to transfer */
|
||||
if (driver->transfer_index < driver->transfer_length) {
|
||||
/* Transmit next byte */
|
||||
uint8_t tx_byte = driver->tx_buffer[driver->transfer_index];
|
||||
uint8_t rx_byte = hal_spi_transfer_byte(instance, tx_byte);
|
||||
|
||||
/* Store received byte */
|
||||
if (driver->transfer_index < SPI_MAX_BUFFER_SIZE) {
|
||||
driver->rx_buffer[driver->transfer_index] = rx_byte;
|
||||
}
|
||||
|
||||
driver->transfer_index++;
|
||||
} else {
|
||||
/* Transfer complete */
|
||||
driver->transfer_active = false;
|
||||
|
||||
/* Deselect chip */
|
||||
if (!driver->config.enable_hardware_cs) {
|
||||
spi_set_chip_select(instance, false);
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.transfers_completed++;
|
||||
driver->statistics.bytes_transferred += driver->transfer_length;
|
||||
|
||||
/* Call callback */
|
||||
if (driver->config.transfer_complete_callback != NULL) {
|
||||
driver->config.transfer_complete_callback(SPI_TRANSFER_COMPLETE,
|
||||
driver->user_data);
|
||||
}
|
||||
|
||||
/* Signal completion */
|
||||
semaphore_give(&driver->transfer_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set Chip Select */
|
||||
void spi_set_chip_select(uint8_t instance, bool active) {
|
||||
if (instance >= SPI_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpiDriverState_t* driver = &spi_drivers[instance];
|
||||
|
||||
bool cs_level = active ?
|
||||
(driver->config.cs_polarity == SPI_CS_ACTIVE_LOW ? false : true) :
|
||||
(driver->config.cs_polarity == SPI_CS_ACTIVE_LOW ? true : false);
|
||||
|
||||
gpio_write(driver->config.cs_port, driver->config.cs_pin, cs_level);
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/**
|
||||
* @file uart_driver.c
|
||||
* @brief UART driver implementation
|
||||
*/
|
||||
|
||||
#include "uart_driver.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* UART Driver State */
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
UartConfig_t config;
|
||||
UartStatistics_t statistics;
|
||||
uint8_t tx_buffer[UART_MAX_BUFFER_SIZE];
|
||||
uint8_t rx_buffer[UART_MAX_BUFFER_SIZE];
|
||||
uint16_t tx_head;
|
||||
uint16_t tx_tail;
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
uint16_t tx_count;
|
||||
uint16_t rx_count;
|
||||
Semaphore_t tx_semaphore;
|
||||
Semaphore_t rx_semaphore;
|
||||
Mutex_t tx_mutex;
|
||||
Mutex_t rx_mutex;
|
||||
} UartDriverState_t;
|
||||
|
||||
static UartDriverState_t uart_drivers[UART_MAX_INSTANCES];
|
||||
|
||||
/* Initialize UART Driver */
|
||||
KernelStatus_t uart_init(uint8_t instance, UartConfig_t* config) {
|
||||
if (instance >= UART_MAX_INSTANCES || config == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
UartDriverState_t* driver = &uart_drivers[instance];
|
||||
|
||||
if (driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&driver->config, config, sizeof(UartConfig_t));
|
||||
|
||||
/* Initialize state */
|
||||
memset(&driver->statistics, 0, sizeof(UartStatistics_t));
|
||||
driver->tx_head = 0;
|
||||
driver->tx_tail = 0;
|
||||
driver->rx_head = 0;
|
||||
driver->rx_tail = 0;
|
||||
driver->tx_count = 0;
|
||||
driver->rx_count = 0;
|
||||
|
||||
/* Create synchronization primitives */
|
||||
semaphore_create(&driver->tx_semaphore, SEMAPHORE_COUNTING,
|
||||
UART_MAX_BUFFER_SIZE, UART_MAX_BUFFER_SIZE);
|
||||
semaphore_create(&driver->rx_semaphore, SEMAPHORE_COUNTING, 0,
|
||||
UART_MAX_BUFFER_SIZE);
|
||||
mutex_create(&driver->tx_mutex, false);
|
||||
mutex_create(&driver->rx_mutex, false);
|
||||
|
||||
/* Initialize UART hardware */
|
||||
if (hal_uart_init(instance, config) != 0) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Enable interrupts */
|
||||
hal_uart_enable_interrupts(instance, true, true);
|
||||
|
||||
driver->initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Send Data via UART */
|
||||
KernelStatus_t uart_send(uint8_t instance, const uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
UartDriverState_t* driver = &uart_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Lock TX mutex */
|
||||
if (mutex_lock(&driver->tx_mutex, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Check if async transfer is in progress */
|
||||
if (driver->tx_count > 0) {
|
||||
mutex_unlock(&driver->tx_mutex);
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Copy data to TX buffer */
|
||||
uint16_t copy_length = (length < UART_MAX_BUFFER_SIZE) ? length : UART_MAX_BUFFER_SIZE;
|
||||
|
||||
critical_section_enter();
|
||||
memcpy(driver->tx_buffer, data, copy_length);
|
||||
driver->tx_head = 0;
|
||||
driver->tx_tail = copy_length;
|
||||
driver->tx_count = copy_length;
|
||||
critical_section_exit();
|
||||
|
||||
/* Start transmission */
|
||||
hal_uart_start_tx(instance);
|
||||
|
||||
/* Wait for completion if synchronous */
|
||||
if (timeout_ms > 0) {
|
||||
TickType_t start_tick = kernel_get_tick_count();
|
||||
|
||||
while (driver->tx_count > 0) {
|
||||
if ((kernel_get_tick_count() - start_tick) > timeout_ms) {
|
||||
mutex_unlock(&driver->tx_mutex);
|
||||
driver->statistics.tx_errors++;
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
kernel_delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.tx_bytes += copy_length;
|
||||
|
||||
/* Unlock TX mutex */
|
||||
mutex_unlock(&driver->tx_mutex);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Receive Data via UART */
|
||||
KernelStatus_t uart_receive(uint8_t instance, uint8_t* data, uint16_t length,
|
||||
uint32_t timeout_ms) {
|
||||
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
UartDriverState_t* driver = &uart_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Wait for data */
|
||||
if (semaphore_take(&driver->rx_semaphore, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Lock RX mutex */
|
||||
if (mutex_lock(&driver->rx_mutex, timeout_ms) != KERNEL_OK) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Copy data from RX buffer */
|
||||
critical_section_enter();
|
||||
|
||||
uint16_t copy_length = 0;
|
||||
while (copy_length < length && driver->rx_count > 0) {
|
||||
data[copy_length] = driver->rx_buffer[driver->rx_head];
|
||||
driver->rx_head = (driver->rx_head + 1) % UART_MAX_BUFFER_SIZE;
|
||||
driver->rx_count--;
|
||||
copy_length++;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Update statistics */
|
||||
driver->statistics.rx_bytes += copy_length;
|
||||
|
||||
/* Unlock RX mutex */
|
||||
mutex_unlock(&driver->rx_mutex);
|
||||
|
||||
return (copy_length == length) ? KERNEL_OK : KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Send Data Asynchronously */
|
||||
KernelStatus_t uart_send_async(uint8_t instance, const uint8_t* data, uint16_t length) {
|
||||
if (instance >= UART_MAX_INSTANCES || data == NULL || length == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
UartDriverState_t* driver = &uart_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
if (driver->tx_count > 0) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Copy data to TX buffer */
|
||||
uint16_t copy_length = (length < UART_MAX_BUFFER_SIZE) ? length : UART_MAX_BUFFER_SIZE;
|
||||
|
||||
critical_section_enter();
|
||||
memcpy(driver->tx_buffer, data, copy_length);
|
||||
driver->tx_head = 0;
|
||||
driver->tx_tail = copy_length;
|
||||
driver->tx_count = copy_length;
|
||||
critical_section_exit();
|
||||
|
||||
/* Start transmission */
|
||||
hal_uart_start_tx(instance);
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* UART Interrupt Handler */
|
||||
void uart_process_interrupt(uint8_t instance) {
|
||||
if (instance >= UART_MAX_INSTANCES) {
|
||||
return;
|
||||
}
|
||||
|
||||
UartDriverState_t* driver = &uart_drivers[instance];
|
||||
|
||||
if (!driver->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t interrupt_status = hal_uart_get_interrupt_status(instance);
|
||||
|
||||
/* Handle TX complete */
|
||||
if (interrupt_status & UART_INTERRUPT_TX_COMPLETE) {
|
||||
if (driver->tx_count > 0) {
|
||||
/* Get next byte to transmit */
|
||||
uint8_t byte = driver->tx_buffer[driver->tx_head];
|
||||
driver->tx_head = (driver->tx_head + 1) % UART_MAX_BUFFER_SIZE;
|
||||
driver->tx_count--;
|
||||
|
||||
/* Transmit byte */
|
||||
hal_uart_transmit_byte(instance, byte);
|
||||
|
||||
if (driver->tx_count == 0) {
|
||||
/* All data transmitted */
|
||||
if (driver->config.tx_callback != NULL) {
|
||||
driver->config.tx_callback();
|
||||
}
|
||||
hal_uart_disable_tx_interrupt(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle RX ready */
|
||||
if (interrupt_status & UART_INTERRUPT_RX_READY) {
|
||||
while (hal_uart_is_rx_ready(instance)) {
|
||||
uint8_t byte = hal_uart_receive_byte(instance);
|
||||
|
||||
/* Add to RX buffer */
|
||||
critical_section_enter();
|
||||
|
||||
if (driver->rx_count < UART_MAX_BUFFER_SIZE) {
|
||||
driver->rx_buffer[driver->rx_tail] = byte;
|
||||
driver->rx_tail = (driver->rx_tail + 1) % UART_MAX_BUFFER_SIZE;
|
||||
driver->rx_count++;
|
||||
|
||||
/* Signal data available */
|
||||
semaphore_give(&driver->rx_semaphore);
|
||||
|
||||
/* Call callback if registered */
|
||||
if (driver->config.rx_callback != NULL) {
|
||||
driver->config.rx_callback(&byte, 1);
|
||||
}
|
||||
} else {
|
||||
driver->statistics.rx_errors++;
|
||||
driver->statistics.overrun_errors++;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle errors */
|
||||
if (interrupt_status & UART_INTERRUPT_ERROR) {
|
||||
uint32_t error = hal_uart_get_error_status(instance);
|
||||
|
||||
if (error & UART_ERROR_PARITY) {
|
||||
driver->statistics.parity_errors++;
|
||||
}
|
||||
if (error & UART_ERROR_FRAMING) {
|
||||
driver->statistics.framing_errors++;
|
||||
}
|
||||
if (error & UART_ERROR_OVERRUN) {
|
||||
driver->statistics.overrun_errors++;
|
||||
}
|
||||
|
||||
if (driver->config.error_callback != NULL) {
|
||||
driver->config.error_callback(error);
|
||||
}
|
||||
|
||||
/* Clear error flags */
|
||||
hal_uart_clear_errors(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get UART Statistics */
|
||||
KernelStatus_t uart_get_statistics(uint8_t instance, UartStatistics_t* stats) {
|
||||
if (instance >= UART_MAX_INSTANCES || stats == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(stats, &uart_drivers[instance].statistics, sizeof(UartStatistics_t));
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get RX Count */
|
||||
uint16_t uart_get_rx_count(uint8_t instance) {
|
||||
if (instance >= UART_MAX_INSTANCES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return uart_drivers[instance].rx_count;
|
||||
}
|
||||
|
||||
/* Get TX Count */
|
||||
uint16_t uart_get_tx_count(uint8_t instance) {
|
||||
if (instance >= UART_MAX_INSTANCES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return uart_drivers[instance].tx_count;
|
||||
}
|
||||
Reference in New Issue
Block a user