Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FW-319: PWM x86 implementation #17

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions libraries/ms-common/inc/pwm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#pragma once

/************************************************************************************************
* @file pwm.h
*
* @brief PWM Library Header file
*
* @date 2025-01-15
* @author Midnight Sun Team #24 - MSXVI
************************************************************************************************/

/* Standard library Headers */
#include <stdbool.h>
#include <stdint.h>

/* Inter-component Headers */

/* Intra-component Headers */
#include "status.h"

typedef enum {
PWM_TIMER_1 = 0, // advanced control
PWM_TIMER_2, // general purpose
PWM_TIMER_3, // general purpose
PWM_TIMER_6, // basic
PWM_TIMER_7, // basic
PWM_TIMER_15, // general purpose
PWM_TIMER_16, // general purpose
NUM_PWM_TIMERS,
} PwmTimer;

typedef enum {
PWM_CHANNEL_1 = 0,
PWM_CHANNEL_2,
PWM_CHANNEL_3,
PWM_CHANNEL_4,
PWM_CHANNEL_5,
PWM_CHANNEL_6,
NUM_PWM_CHANNELS // max # of pwm channels
} PwmChannel;

// Initializes the PWM for a set timer with a specific frequency (Hz)
#define pwm_init_hz(timer, frequency) pwm_init((timer), 1000000 / (frequency))

/**
* @brief Initializes a specified PWM timer with a period
* @param timer PWM timer to configure
* @param period_ms Duration of one cycle in microseconds
* @return STATUS_CODE_OK if PWM timer initialization succeeded
* STATUS_CODE_INVALID_ARGS if one of the parameters are incorrect
*/
StatusCode pwm_init(PwmTimer timer, uint16_t period_us);

/**
* @brief Gets the current period of a specified PWM timer in microseconds
* @param timer PWM timer to check
* @return STATUS_CODE_OK if period retrieval succeeded
* STATUS_CODE_INVALID_ARGS if one of the parameters are incorrect
*/
uint16_t pwm_get_period(PwmTimer timer);

/**
* @brief Sets the pulse width of a specified channel of a PWM timer; High-res control
* @param timer PWM timer to configure
* @param pulse_width_us Pulse width in microseconds
* @param channel Channel of the PWM timer to configure
* @param n_channel_en Whether n channel is enabled or not
* @return STATUS_CODE_OK if PWM duty cycle configuration succeeded
* STATUS_CODE_UNINITIALIZED if PWM timer was not initialized
* STATUS_CODE_INVALID_ARGS if one of the parameters are incorrect
*/
StatusCode pwm_set_pulse(PwmTimer timer, uint16_t pulse_width_us, PwmChannel channel, bool n_channel_en);

/**
* @brief Sets the duty cycle of a specificed channel for a PWM timer
* @param timer PWM timer to configure
* @param dc Duty cycle in units of 1%
* @param channel Channel of the PWM timer to configure
* @param n_channel_en Whether n channel is enabled or not
* @return STATUS_CODE_OK if PWM duty cycle configuration succeeded
* STATUS_CODE_INVALID_ARGS if one of the parameters are incorrect
*/
StatusCode pwm_set_dc(PwmTimer timer, uint16_t dc, PwmChannel channel, bool n_channel_en);

/**
* @brief Gets the duty cycle of a specified channel for a PWM timer
* @param timer PWM timer to configure
* @param channel Channel of the PWM timer to configure
* @return STATUS_CODE_OK if duty cycle retrieval succeeded
* STATUS_CODE_INVALID_ARGS if one of the parameters are incorrect
*/
uint16_t pwm_get_dc(PwmTimer timer, PwmChannel channel);
114 changes: 114 additions & 0 deletions libraries/ms-common/src/x86/pwm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/************************************************************************************************
* @file pwm.c
*
* @brief PWM Library Source code
*
* @date 2025-01-13
* @author Midnight Sun Team #24 - MSXVI
************************************************************************************************/

/* Standard library Headers */
#include <stdint.h>

/* Inter-component Headers */

/* Intra-component Headers */
#include "pwm.h"
#include "status.h"

/**
* @brief PWM Timer details
* @details Stores period and dc while tracking enabled (n) channels
*/
typedef struct PwmInfo {
uint16_t period;
bool initialized;
uint16_t duty_cycles[NUM_PWM_CHANNELS];
bool channel_en[NUM_PWM_CHANNELS];
bool n_channel_en[NUM_PWM_CHANNELS];
} PwmInfo;

/** @brief Number of channels associated to each PWM timer*/
static const uint8_t num_channels[NUM_PWM_TIMERS] = {
6, // TIM1 - 6 channels
4, // TIM2 - 4 channels
4, // TIM3 - 4 channels
0, // TIM6 - 0 channels
0, // TIM7 - 0 channels
2, // TIM15 - 2 channels
1 // TIM16 - 1 channel
};

PwmInfo pwm[NUM_PWM_TIMERS];

StatusCode pwm_init(PwmTimer timer, uint16_t period_us) {
if (timer >= NUM_PWM_TIMERS) {
LOG_DEBUG("Invalid timer id");
return STATUS_CODE_INVALID_ARGS;
} else if (period_us == 0) {
LOG_DEBUG("Period must be greater than 0");
return STATUS_CODE_INVALID_ARGS;
}

pwm[timer].period = period_us;
pwm[timer].initialized = true;

return STATUS_CODE_OK;
}

uint16_t pwm_get_period(PwmTimer timer) {
if (timer >= NUM_PWM_TIMERS) {
LOG_DEBUG("Invalid timer id");
return STATUS_CODE_INVALID_ARGS;
}

return pwm[timer].period;
}

StatusCode pwm_set_pulse(PwmTimer timer, uint16_t pulse_width_us, PwmChannel channel, bool n_channel_en) {
if (timer >= NUM_PWM_TIMERS) {
LOG_DEBUG("Invalid timer id");
return STATUS_CODE_INVALID_ARGS;
} else if (pwm[timer].period == 0) {
LOG_DEBUG("Pwm must be initialized");
return STATUS_CODE_UNINITIALIZED;
} else if (pulse_width_us > pwm[timer].period) {
LOG_DEBUG("Pulse width must be leq period");
return STATUS_CODE_INVALID_ARGS;
}

(pwm[timer]).duty_cycles[channel] = (pulse_width_us * 100) / pwm[timer].period;

return STATUS_CODE_OK;
}

StatusCode pwm_set_dc(PwmTimer timer, uint16_t dc, PwmChannel channel, bool n_channel_en) {
if (timer >= NUM_PWM_TIMERS) {
LOG_DEBUG("Invalid timer id");
return STATUS_CODE_INVALID_ARGS;
} else if (channel > num_channels[timer]) {
LOG_DEBUG("Invalid channel number");
return STATUS_CODE_INVALID_ARGS;
} else if (dc > 100) {
LOG_DEBUG("Duty Cycle must be leq 100 percent");
return STATUS_CODE_INVALID_ARGS;
}

(pwm[timer]).duty_cycles[channel] = dc;
(pwm[timer]).channel_en[channel] = true;
(pwm[timer]).n_channel_en[channel] = true;

return STATUS_CODE_OK;
}

uint16_t pwm_get_dc(PwmTimer timer, PwmChannel channel) {
if (timer >= NUM_PWM_TIMERS) {
LOG_DEBUG("Invalid timer id");
return STATUS_CODE_INVALID_ARGS;
} else if (channel > num_channels[timer]) {
LOG_DEBUG("Invalid channel number");
return STATUS_CODE_INVALID_ARGS;
}

return (pwm[timer]).duty_cycles[channel];
}
Loading