mirror of
				https://github.com/DashyFox/StackSport.git
				synced 2025-11-04 04:42:47 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Sound.c
 | 
						|
 *
 | 
						|
 *  Created on: Oct 1, 2024
 | 
						|
 *      Author: DashyFox
 | 
						|
 */
 | 
						|
 | 
						|
#include "Sound.h"
 | 
						|
#include "stm32f103xb.h"
 | 
						|
#include "SimpleTimer.h"
 | 
						|
 | 
						|
static uint32_t bpm = 145;          // Default BPM
 | 
						|
static uint32_t note_end_time_ch3 = 0;  // End time for channel 3
 | 
						|
static uint32_t note_end_time_ch4 = 0;  // End time for channel 4
 | 
						|
static Note_t current_note_ch3;
 | 
						|
static Note_t current_note_ch4;
 | 
						|
 | 
						|
// Initialize the sound system
 | 
						|
void sound_init(void) {
 | 
						|
    // Enable timer clock
 | 
						|
    RCC->APB1ENR |= SOUND_TIMER_RCC;
 | 
						|
 | 
						|
    // Configure GPIO pins for timer output (assuming GPIOB pins for TIM4 CH3 and CH4)
 | 
						|
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
 | 
						|
 | 
						|
    if (SOUND_CHANNEL_1 == 1 || SOUND_CHANNEL_1 == 2) {
 | 
						|
        GPIOB->CRL &= ~(0xF << ((SOUND_CHANNEL_1 - 1) * 4));  // Clear
 | 
						|
        GPIOB->CRL |= (0xB << ((SOUND_CHANNEL_1 - 1) * 4));   // Alternate function push-pull
 | 
						|
    } else if (SOUND_CHANNEL_1 == 3 || SOUND_CHANNEL_1 == 4) {
 | 
						|
        GPIOB->CRH &= ~(0xF << ((SOUND_CHANNEL_1 - 8) * 4));  // Clear
 | 
						|
        GPIOB->CRH |= (0xB << ((SOUND_CHANNEL_1 - 8) * 4));   // Alternate function push-pull
 | 
						|
    }
 | 
						|
 | 
						|
    if (SOUND_CHANNEL_2 == 1 || SOUND_CHANNEL_2 == 2) {
 | 
						|
        GPIOB->CRL &= ~(0xF << ((SOUND_CHANNEL_2 - 1) * 4));  // Clear
 | 
						|
        GPIOB->CRL |= (0xB << ((SOUND_CHANNEL_2 - 1) * 4));   // Alternate function push-pull
 | 
						|
    } else if (SOUND_CHANNEL_2 == 3 || SOUND_CHANNEL_2 == 4) {
 | 
						|
        GPIOB->CRH &= ~(0xF << ((SOUND_CHANNEL_2 - 8) * 4));  // Clear
 | 
						|
        GPIOB->CRH |= (0xB << ((SOUND_CHANNEL_2 - 8) * 4));   // Alternate function push-pull
 | 
						|
    }
 | 
						|
 | 
						|
    // Initialize the timer for PWM output
 | 
						|
    SOUND_TIMER->PSC = 0; // Will be set in sound_play_note()
 | 
						|
    SOUND_TIMER->ARR = 0xFFFF; // Will be set in sound_play_note()
 | 
						|
 | 
						|
    // Configure PWM mode on both channels
 | 
						|
    if (SOUND_CHANNEL_1 == 3) {
 | 
						|
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC3M;
 | 
						|
        SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos);  // PWM mode 1 for channel 3
 | 
						|
        SOUND_TIMER->CCER |= TIM_CCER_CC3E; // Enable output on channel 3
 | 
						|
    } else if (SOUND_CHANNEL_1 == 4) {
 | 
						|
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC4M;
 | 
						|
        SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC4M_Pos);  // PWM mode 1 for channel 4
 | 
						|
        SOUND_TIMER->CCER |= TIM_CCER_CC4E; // Enable output on channel 4
 | 
						|
    }
 | 
						|
 | 
						|
    if (SOUND_CHANNEL_2 == 3) {
 | 
						|
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC3M;
 | 
						|
        SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos);  // PWM mode 1 for channel 3
 | 
						|
        SOUND_TIMER->CCER |= TIM_CCER_CC3E; // Enable output on channel 3
 | 
						|
    } else if (SOUND_CHANNEL_2 == 4) {
 | 
						|
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC4M;
 | 
						|
        SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC4M_Pos);  // PWM mode 1 for channel 4
 | 
						|
        SOUND_TIMER->CCER |= TIM_CCER_CC4E; // Enable output on channel 4
 | 
						|
    }
 | 
						|
 | 
						|
    // Generate an update event to reload the prescaler and ARR values
 | 
						|
    SOUND_TIMER->EGR = TIM_EGR_UG;
 | 
						|
 | 
						|
    // Enable the timer
 | 
						|
    SOUND_TIMER->CR1 |= TIM_CR1_CEN;
 | 
						|
}
 | 
						|
 | 
						|
// Set BPM
 | 
						|
void sound_set_bpm(uint32_t new_bpm) {
 | 
						|
    bpm = new_bpm;
 | 
						|
}
 | 
						|
 | 
						|
// Play a note on a specific channel
 | 
						|
void sound_play_note(Note_t note, uint8_t channel) {
 | 
						|
    if (channel == SOUND_CHANNEL_1) {
 | 
						|
        current_note_ch3 = note;
 | 
						|
        if (note.frequency == 0) {
 | 
						|
            // Pause on channel 3
 | 
						|
            SOUND_TIMER->CCER &= ~TIM_CCER_CC3E; // Disable output on channel 3
 | 
						|
            note_end_time_ch3 = millis() + note.duration;
 | 
						|
        } else {
 | 
						|
            // Configure frequency and duty cycle for channel 3
 | 
						|
            uint32_t timer_clock = SystemCoreClock / 2; // Assuming APB1 prescaler is 2
 | 
						|
            uint32_t prescaler = (timer_clock / (note.frequency * 65536)) + 1;
 | 
						|
            uint32_t arr = (timer_clock / (prescaler * note.frequency)) - 1;
 | 
						|
            uint32_t ccr = ((arr + 1) * note.duty_cycle) / 100 - 1;
 | 
						|
 | 
						|
            SOUND_TIMER->PSC = prescaler - 1;
 | 
						|
            SOUND_TIMER->ARR = arr;
 | 
						|
            SOUND_TIMER->CCR3 = ccr;
 | 
						|
 | 
						|
            // Configure PWM mode for channel 3
 | 
						|
            SOUND_TIMER->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC3PE);
 | 
						|
            SOUND_TIMER->CCMR2 |= (TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3PE); // PWM mode 1
 | 
						|
 | 
						|
            SOUND_TIMER->CCER |= TIM_CCER_CC3E; // Enable output on channel 3
 | 
						|
            SOUND_TIMER->EGR = TIM_EGR_UG;      // Update registers
 | 
						|
            SOUND_TIMER->CR1 |= TIM_CR1_CEN;    // Enable timer
 | 
						|
 | 
						|
            if (note.duration == 0) {
 | 
						|
                note_end_time_ch3 = 0; // Infinite duration
 | 
						|
            } else {
 | 
						|
                note_end_time_ch3 = millis() + note.duration;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else if (channel == SOUND_CHANNEL_2) {
 | 
						|
        current_note_ch4 = note;
 | 
						|
        if (note.frequency == 0) {
 | 
						|
            // Pause on channel 4
 | 
						|
            SOUND_TIMER->CCER &= ~TIM_CCER_CC4E; // Disable output on channel 4
 | 
						|
            note_end_time_ch4 = millis() + note.duration;
 | 
						|
        } else {
 | 
						|
            // Configure frequency and duty cycle for channel 4
 | 
						|
            uint32_t timer_clock = SystemCoreClock / 2; // Assuming APB1 prescaler is 2
 | 
						|
            uint32_t prescaler = (timer_clock / (note.frequency * 65536)) + 1;
 | 
						|
            uint32_t arr = (timer_clock / (prescaler * note.frequency)) - 1;
 | 
						|
            uint32_t ccr = ((arr + 1) * note.duty_cycle) / 100 - 1;
 | 
						|
 | 
						|
            SOUND_TIMER->PSC = prescaler - 1;
 | 
						|
            SOUND_TIMER->ARR = arr;
 | 
						|
            SOUND_TIMER->CCR4 = ccr;
 | 
						|
 | 
						|
            // Configure PWM mode for channel 4
 | 
						|
            SOUND_TIMER->CCMR2 &= ~(TIM_CCMR2_OC4M | TIM_CCMR2_OC4PE);
 | 
						|
            SOUND_TIMER->CCMR2 |= (TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE); // PWM mode 1
 | 
						|
 | 
						|
            SOUND_TIMER->CCER |= TIM_CCER_CC4E; // Enable output on channel 4
 | 
						|
            SOUND_TIMER->EGR = TIM_EGR_UG;      // Update registers
 | 
						|
            SOUND_TIMER->CR1 |= TIM_CR1_CEN;    // Enable timer
 | 
						|
 | 
						|
            if (note.duration == 0) {
 | 
						|
                note_end_time_ch4 = 0; // Infinite duration
 | 
						|
            } else {
 | 
						|
                note_end_time_ch4 = millis() + note.duration;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Function to be called periodically to handle note duration
 | 
						|
void sound_tick(void) {
 | 
						|
    if (note_end_time_ch3 != 0 && millis() >= note_end_time_ch3) {
 | 
						|
        // Stop the note on channel 3
 | 
						|
        SOUND_TIMER->CCER &= ~TIM_CCER_CC3E; // Disable output on channel 3
 | 
						|
        note_end_time_ch3 = 0;
 | 
						|
    }
 | 
						|
    if (note_end_time_ch4 != 0 && millis() >= note_end_time_ch4) {
 | 
						|
        // Stop the note on channel 4
 | 
						|
        SOUND_TIMER->CCER &= ~TIM_CCER_CC4E; // Disable output on channel 4
 | 
						|
        note_end_time_ch4 = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Create a Note_t from musical note and duration enums
 | 
						|
Note_t sound_create_note(MusicalNote_t note_enum, NoteDuration_t duration_enum, uint8_t duty_cycle) {
 | 
						|
    Note_t result_note;
 | 
						|
    result_note.frequency = note_enum;
 | 
						|
    if (duration_enum == 0) {
 | 
						|
        result_note.duration = 0; // Infinite duration
 | 
						|
    } else {
 | 
						|
        // Calculate duration in milliseconds
 | 
						|
        uint32_t whole_note_duration = (60000 / bpm) * 4; // Duration of whole note in ms
 | 
						|
        result_note.duration = whole_note_duration / duration_enum;
 | 
						|
    }
 | 
						|
    result_note.duty_cycle = duty_cycle;
 | 
						|
    return result_note;
 | 
						|
}
 |