Skip to content
Merged
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
90 changes: 74 additions & 16 deletions include/pwm.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* @file
* @brief PIC18 PWM (CCP) driver
*
* This module provides PWM functionality using the PIC18 Capture/Compare/PWM (CCP) modules.
* It supports up to 4 CCP modules (CCP1-CCP4) and uses Timer2 as the timebase for PWM
* generation.
*/

#ifndef ROCKETLIB_PWM_H
#define ROCKETLIB_PWM_H

Expand All @@ -9,46 +18,95 @@
#error "C++ is not supported"
#endif

// Structure to hold the configuration details for a PWM pin
/**
* @brief Structure to hold the configuration details for a PWM pin
*/
typedef struct {
volatile uint8_t *tris_reg; // Pointer to TRIS register
volatile uint8_t *pps_reg; // Pointer to PPS register
uint8_t pin; // Pin number (0-7)
} pwm_pin_config_t;

// Macro to concatenate tokens for register naming
// This macro concatenates three tokens together, used for constructing register names dynamically
/**
* @brief Macro to concatenate two tokens for register naming
*
* This macro concatenates two tokens together, used for constructing register names dynamically
*/
#define CONCAT2(a, b) a##b

/**
* @brief Macro to concatenate tokens for register naming
*
* This macro concatenates three tokens together, used for constructing register names dynamically
*/
#define CONCAT(a, b, c) a##b##c

// Macro to get the CCPR Low register based on module number
// This macro forms the register name for the low byte of the Compare/Capture/PWM register
/**
* @brief Macro to get the CCPR Low register based on module number
*
* This macro forms the register name for the low byte of the Compare/Capture/PWM register
*
* @param module CCP module number (1-4)
*/
#define CCPR_L(module) CONCAT(CCPR, module, L)

// Macro to get the CCPR High register based on module number
// This macro forms the register name for the high byte of the Compare/Capture/PWM register
/**
* @brief Macro to get the CCPR High register based on module number
*
* This macro forms the register name for the high byte of the Compare/Capture/PWM register
*
* @param module CCP module number (1-4)
*/
#define CCPR_H(module) CONCAT(CCPR, module, H)

// Macro to get the CCPxCON register based on module number
// This macro forms the register name for the control register of the specified CCP module
/**
* @brief Macro to get the CCPxCON register based on module number
*
* This macro forms the register name for the control register of the specified CCP module.
*
* @param module CCP module number (1-4)
*/
#define CCP_CON(module) CONCAT(CCP, module, CON)

// Function prototypes
w_status_t pwm_init(uint8_t ccp_module, pwm_pin_config_t pin_config,
uint16_t pwm_period); // Initializes the PWM for a specific CCP module
/**
* @brief Initializes the PWM for a specific CCP module
*
* This function configures a CCP module for PWM operation. It sets up the pin configuration,
* enables PWM mode, and configures Timer2 as the timebase. The PWM period is set based on
* the Timer2 period register (PR2).
*
* @param ccp_module CCP module number (1-4)
* @param pin_config Pin configuration structure containing TRIS register pointer, PPS register
* pointer, and pin number
* @param pwm_period PWM period value (0-255) loaded into PR2 register
* @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if module number is out of range
*/
w_status_t pwm_init(uint8_t ccp_module, pwm_pin_config_t pin_config, uint16_t pwm_period);

/**
* Example usage:
* @brief Example usage:
*
* @code
* pwm_pin_config_t config;
* config.tris_reg = &TRISA; // Direct register pointer
* config.pps_reg = &RA0PPS; // Direct register pointer for PPS
* config.pin = 0; // Pin number (0-7)
*
* pwm_init(1, config, 255); // Initialize PWM on CCP1 with period 255
* @endcode
*/

w_status_t
pwm_update_duty_cycle(uint8_t ccp_module,
uint16_t duty_cycle); // Updates the duty cycle of the specified CCP module
/**
* @brief Updates the duty cycle of the specified CCP module
*
* This function updates the PWM duty cycle for a given CCP module. The duty cycle is a 10-bit
* value (0-1023) where 0 represents 0% duty cycle and 1023 represents 100% duty cycle.
*
* @param ccp_module CCP module number (1-4)
* @param duty_cycle Duty cycle value (0-1023) for 10-bit PWM resolution
* @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if module number is out of
* range or duty cycle exceeds 1023
*/
w_status_t pwm_update_duty_cycle(uint8_t ccp_module, uint16_t duty_cycle);

#endif /* ROCKETLIB_PWM_H */
38 changes: 35 additions & 3 deletions pic18f26k83/pwm.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
#include "pwm.h"
#include <xc.h>

// Helper function to configure PPS registers using direct register access
/**
* @brief Helper function to configure PPS registers using direct register access
*
* This function configures the Peripheral Pin Select (PPS) registers to map a CCP module
* to a specific pin. It sets the pin as an output and assigns the CCP module to the PPS register.
*
* @param ccp_module CCP module number (1-4)
* @param pin_config Pin configuration structure containing TRIS register pointer, PPS register
* pointer, and pin number
* @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if module number is out of range
*/
static w_status_t configure_pps(uint8_t ccp_module, pwm_pin_config_t pin_config) {
// Ensure the CCP module number is within valid range (1-4)
if (ccp_module < 1 || ccp_module > 4) {
Expand All @@ -17,7 +27,19 @@ static w_status_t configure_pps(uint8_t ccp_module, pwm_pin_config_t pin_config)
return W_SUCCESS; // Return success status after configuring PPS
}

// Initialize PWM for a specific CCP module
/**
* @brief Initializes the PWM for a specific CCP module
*
* This function configures a CCP module for PWM operation. It sets up the pin configuration,
* enables PWM mode, and configures Timer2 as the timebase. The PWM period is set based on
* the Timer2 period register (PR2).
*
* @param ccp_module CCP module number (1-4)
* @param pin_config Pin configuration structure containing TRIS register pointer, PPS register
* pointer, and pin number
* @param pwm_period PWM period value (0-255) loaded into PR2 register
* @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if module number is out of range
*/
w_status_t pwm_init(uint8_t ccp_module, pwm_pin_config_t pin_config, uint16_t pwm_period) {
// Configure PPS registers to map CCP module to the selected pin
w_status_t status = configure_pps(ccp_module, pin_config);
Expand Down Expand Up @@ -60,7 +82,17 @@ w_status_t pwm_init(uint8_t ccp_module, pwm_pin_config_t pin_config, uint16_t pw
return W_SUCCESS; // Return success status after PWM initialization
}

// Update the duty cycle of a specific CCP module
/**
* @brief Updates the duty cycle of the specified CCP module
*
* This function updates the PWM duty cycle for a given CCP module. The duty cycle is a 10-bit
* value (0-1023) where 0 represents 0% duty cycle and 1023 represents 100% duty cycle.
*
* @param ccp_module CCP module number (1-4)
* @param duty_cycle Duty cycle value (0-1023) for 10-bit PWM resolution
* @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if module number is out of
* range or duty cycle exceeds 1023
*/
w_status_t pwm_update_duty_cycle(uint8_t ccp_module, uint16_t duty_cycle) {
// Validate CCP module and duty cycle range
if (ccp_module < 1 || ccp_module > 4 || duty_cycle > 1023) {
Expand Down