mirror of
				https://github.com/DashyFox/StackSport.git
				synced 2025-11-04 12:52:48 +00:00 
			
		
		
		
	music
This commit is contained in:
		
							
								
								
									
										144
									
								
								Core/Inc/Sound.h
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								Core/Inc/Sound.h
									
									
									
									
									
								
							@ -13,8 +13,6 @@
 | 
				
			|||||||
// Define default timer and channels
 | 
					// Define default timer and channels
 | 
				
			||||||
#define SOUND_TIMER        TIM4
 | 
					#define SOUND_TIMER        TIM4
 | 
				
			||||||
#define SOUND_TIMER_RCC    RCC_APB1ENR_TIM4EN
 | 
					#define SOUND_TIMER_RCC    RCC_APB1ENR_TIM4EN
 | 
				
			||||||
#define SOUND_CHANNEL_1    3  // TIM4 Channel 3
 | 
					 | 
				
			||||||
#define SOUND_CHANNEL_2    4  // TIM4 Channel 4
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Define the Note structure
 | 
					// Define the Note structure
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
@ -25,34 +23,142 @@ typedef struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Enum for musical notes
 | 
					// Enum for musical notes
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    NOTE_C4 = 261,
 | 
					    C0 = 16,
 | 
				
			||||||
    NOTE_D4 = 293,
 | 
					    C0_sharp = 17,
 | 
				
			||||||
    NOTE_E4 = 329,
 | 
					    D0 = 18,
 | 
				
			||||||
    NOTE_F4 = 349,
 | 
					    D0_sharp = 19,
 | 
				
			||||||
    NOTE_G4 = 392,
 | 
					    E0 = 21,
 | 
				
			||||||
    NOTE_A4 = 440,
 | 
					    F0 = 22,
 | 
				
			||||||
    NOTE_B4 = 493,
 | 
					    F0_sharp = 23,
 | 
				
			||||||
    NOTE_C5 = 523,
 | 
					    G0 = 25,
 | 
				
			||||||
    // Add more notes as needed
 | 
					    G0_sharp = 26,
 | 
				
			||||||
    NOTE_REST = 0
 | 
					    A0 = 28,
 | 
				
			||||||
 | 
					    A0_sharp = 29,
 | 
				
			||||||
 | 
					    B0 = 31,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C1 = 33,
 | 
				
			||||||
 | 
					    C1_sharp = 35,
 | 
				
			||||||
 | 
					    D1 = 37,
 | 
				
			||||||
 | 
					    D1_sharp = 39,
 | 
				
			||||||
 | 
					    E1 = 41,
 | 
				
			||||||
 | 
					    F1 = 44,
 | 
				
			||||||
 | 
					    F1_sharp = 46,
 | 
				
			||||||
 | 
					    G1 = 49,
 | 
				
			||||||
 | 
					    G1_sharp = 52,
 | 
				
			||||||
 | 
					    A1 = 55,
 | 
				
			||||||
 | 
					    A1_sharp = 58,
 | 
				
			||||||
 | 
					    B1 = 62,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C2 = 65,
 | 
				
			||||||
 | 
					    C2_sharp = 69,
 | 
				
			||||||
 | 
					    D2 = 73,
 | 
				
			||||||
 | 
					    D2_sharp = 78,
 | 
				
			||||||
 | 
					    E2 = 82,
 | 
				
			||||||
 | 
					    F2 = 87,
 | 
				
			||||||
 | 
					    F2_sharp = 92,
 | 
				
			||||||
 | 
					    G2 = 98,
 | 
				
			||||||
 | 
					    G2_sharp = 104,
 | 
				
			||||||
 | 
					    A2 = 110,
 | 
				
			||||||
 | 
					    A2_sharp = 117,
 | 
				
			||||||
 | 
					    B2 = 123,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C3 = 130,
 | 
				
			||||||
 | 
					    C3_sharp = 138,
 | 
				
			||||||
 | 
					    D3 = 146,
 | 
				
			||||||
 | 
					    D3_sharp = 155,
 | 
				
			||||||
 | 
					    E3 = 164,
 | 
				
			||||||
 | 
					    F3 = 174,
 | 
				
			||||||
 | 
					    F3_sharp = 185,
 | 
				
			||||||
 | 
					    G3 = 196,
 | 
				
			||||||
 | 
					    G3_sharp = 207,
 | 
				
			||||||
 | 
					    A3 = 220,
 | 
				
			||||||
 | 
					    A3_sharp = 233,
 | 
				
			||||||
 | 
					    B3 = 246,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C4 = 261,
 | 
				
			||||||
 | 
					    C4_sharp = 277,
 | 
				
			||||||
 | 
					    D4 = 293,
 | 
				
			||||||
 | 
					    D4_sharp = 311,
 | 
				
			||||||
 | 
					    E4 = 329,
 | 
				
			||||||
 | 
					    F4 = 349,
 | 
				
			||||||
 | 
					    F4_sharp = 369,
 | 
				
			||||||
 | 
					    G4 = 392,
 | 
				
			||||||
 | 
					    G4_sharp = 415,
 | 
				
			||||||
 | 
					    A4 = 440,
 | 
				
			||||||
 | 
					    A4_sharp = 466,
 | 
				
			||||||
 | 
					    B4 = 493,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C5 = 523,
 | 
				
			||||||
 | 
					    C5_sharp = 554,
 | 
				
			||||||
 | 
					    D5 = 587,
 | 
				
			||||||
 | 
					    D5_sharp = 622,
 | 
				
			||||||
 | 
					    E5 = 659,
 | 
				
			||||||
 | 
					    F5 = 698,
 | 
				
			||||||
 | 
					    F5_sharp = 740,
 | 
				
			||||||
 | 
					    G5 = 784,
 | 
				
			||||||
 | 
					    G5_sharp = 830,
 | 
				
			||||||
 | 
					    A5 = 880,
 | 
				
			||||||
 | 
					    A5_sharp = 932,
 | 
				
			||||||
 | 
					    B5 = 987,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C6 = 1046,
 | 
				
			||||||
 | 
					    C6_sharp = 1108,
 | 
				
			||||||
 | 
					    D6 = 1174,
 | 
				
			||||||
 | 
					    D6_sharp = 1244,
 | 
				
			||||||
 | 
					    E6 = 1318,
 | 
				
			||||||
 | 
					    F6 = 1396,
 | 
				
			||||||
 | 
					    F6_sharp = 1480,
 | 
				
			||||||
 | 
					    G6 = 1568,
 | 
				
			||||||
 | 
					    G6_sharp = 1661,
 | 
				
			||||||
 | 
					    A6 = 1760,
 | 
				
			||||||
 | 
					    A6_sharp = 1864,
 | 
				
			||||||
 | 
					    B6 = 1975,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C7 = 2093,
 | 
				
			||||||
 | 
					    C7_sharp = 2217,
 | 
				
			||||||
 | 
					    D7 = 2349,
 | 
				
			||||||
 | 
					    D7_sharp = 2489,
 | 
				
			||||||
 | 
					    E7 = 2637,
 | 
				
			||||||
 | 
					    F7 = 2793,
 | 
				
			||||||
 | 
					    F7_sharp = 2960,
 | 
				
			||||||
 | 
					    G7 = 3136,
 | 
				
			||||||
 | 
					    G7_sharp = 3322,
 | 
				
			||||||
 | 
					    A7 = 3520,
 | 
				
			||||||
 | 
					    A7_sharp = 3729,
 | 
				
			||||||
 | 
					    B7 = 3951,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    C8 = 4186,
 | 
				
			||||||
 | 
					    C8_sharp = 4435,
 | 
				
			||||||
 | 
					    D8 = 4699,
 | 
				
			||||||
 | 
					    D8_sharp = 4978
 | 
				
			||||||
} MusicalNote_t;
 | 
					} MusicalNote_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Enum for note durations (in fractions of a whole note)
 | 
					// Enum for note durations (in fractions of a whole note)
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    DURATION_WHOLE = 1,
 | 
					    T0 = 0,  // Infinite
 | 
				
			||||||
    DURATION_HALF = 2,
 | 
					    T1 = 1,  // Whole note
 | 
				
			||||||
    DURATION_QUARTER = 4,
 | 
					    T2 = 2,  // Half note
 | 
				
			||||||
    DURATION_EIGHTH = 8,
 | 
					    T4 = 4,  // Quarter note
 | 
				
			||||||
    DURATION_SIXTEENTH = 16
 | 
					    T8 = 8,  // Eighth note
 | 
				
			||||||
    // Add more durations as needed
 | 
					    T16 = 16 // Sixteenth note
 | 
				
			||||||
} NoteDuration_t;
 | 
					} NoteDuration_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Structure for melody playback
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    Note_t* notes;         // Pointer to an array of notes
 | 
				
			||||||
 | 
					    uint32_t size;         // Number of notes in the melody
 | 
				
			||||||
 | 
					    uint32_t current_note; // Index of the current note
 | 
				
			||||||
 | 
					    uint32_t repeat_count; // Total repeat count
 | 
				
			||||||
 | 
					    uint32_t repeat_left;  // Repeats left
 | 
				
			||||||
 | 
					} Melody_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Function declarations
 | 
					// Function declarations
 | 
				
			||||||
void sound_init(void);
 | 
					void sound_init(void);
 | 
				
			||||||
void sound_set_bpm(uint32_t bpm);
 | 
					void sound_set_bpm(uint32_t bpm);
 | 
				
			||||||
void sound_play_note(Note_t note, uint8_t channel);
 | 
					void sound_play_note(Note_t note, uint8_t channel);
 | 
				
			||||||
 | 
					void sound_play_melody(Note_t* melody, uint32_t size, uint8_t channel, uint32_t repeat_count);
 | 
				
			||||||
void sound_tick(void);
 | 
					void sound_tick(void);
 | 
				
			||||||
Note_t sound_create_note(MusicalNote_t note, NoteDuration_t duration, uint8_t duty_cycle);
 | 
					Note_t sound_create_note(MusicalNote_t note, NoteDuration_t duration, uint8_t duty_cycle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* INC_SOUND_H_ */
 | 
					#endif /* INC_SOUND_H_ */
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										225
									
								
								Core/Src/Sound.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								Core/Src/Sound.c
									
									
									
									
									
								
							@ -9,11 +9,13 @@
 | 
				
			|||||||
#include "stm32f103xb.h"
 | 
					#include "stm32f103xb.h"
 | 
				
			||||||
#include "SimpleTimer.h"
 | 
					#include "SimpleTimer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint32_t bpm = 145;          // Default BPM
 | 
					static uint32_t bpm = 120;          // Default BPM
 | 
				
			||||||
static uint32_t note_end_time_ch3 = 0;  // End time for channel 3
 | 
					static uint32_t note_end_time_ch[4] = {0, 0, 0, 0}; // End times for each channel (1-4)
 | 
				
			||||||
static uint32_t note_end_time_ch4 = 0;  // End time for channel 4
 | 
					static Melody_t melodies[4];        // Array of melodies for each channel
 | 
				
			||||||
static Note_t current_note_ch3;
 | 
					
 | 
				
			||||||
static Note_t current_note_ch4;
 | 
					// Initialize the sound system
 | 
				
			||||||
 | 
					#define SOUND_TIMER        TIM4
 | 
				
			||||||
 | 
					#define SOUND_TIMER_RCC    RCC_APB1ENR_TIM4EN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Initialize the sound system
 | 
					// Initialize the sound system
 | 
				
			||||||
void sound_init(void) {
 | 
					void sound_init(void) {
 | 
				
			||||||
@ -23,54 +25,29 @@ void sound_init(void) {
 | 
				
			|||||||
    // Configure GPIO pins for timer output (assuming GPIOB pins for TIM4 CH3 and CH4)
 | 
					    // Configure GPIO pins for timer output (assuming GPIOB pins for TIM4 CH3 and CH4)
 | 
				
			||||||
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
 | 
					    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (SOUND_CHANNEL_1 == 1 || SOUND_CHANNEL_1 == 2) {
 | 
					    // Configure only channels 3 and 4
 | 
				
			||||||
        GPIOB->CRL &= ~(0xF << ((SOUND_CHANNEL_1 - 1) * 4));  // Clear
 | 
					    // Channel 3
 | 
				
			||||||
        GPIOB->CRL |= (0xB << ((SOUND_CHANNEL_1 - 1) * 4));   // Alternate function push-pull
 | 
					    GPIOB->CRH &= ~(0xF << (3 * 4));  // Clear configuration for PB10 (TIM4_CH3)
 | 
				
			||||||
    } else if (SOUND_CHANNEL_1 == 3 || SOUND_CHANNEL_1 == 4) {
 | 
					    GPIOB->CRH |= (0xB << (3 * 4));   // Set to alternate function push-pull
 | 
				
			||||||
        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) {
 | 
					    // Channel 4
 | 
				
			||||||
        GPIOB->CRL &= ~(0xF << ((SOUND_CHANNEL_2 - 1) * 4));  // Clear
 | 
					    GPIOB->CRH &= ~(0xF << (4 * 4));  // Clear configuration for PB11 (TIM4_CH4)
 | 
				
			||||||
        GPIOB->CRL |= (0xB << ((SOUND_CHANNEL_2 - 1) * 4));   // Alternate function push-pull
 | 
					    GPIOB->CRH |= (0xB << (4 * 4));   // Set to 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
 | 
					    // Initialize the timer for PWM output
 | 
				
			||||||
    SOUND_TIMER->PSC = 0; // Will be set in sound_play_note()
 | 
					    SOUND_TIMER->PSC = 0;
 | 
				
			||||||
    SOUND_TIMER->ARR = 0xFFFF; // Will be set in sound_play_note()
 | 
					    SOUND_TIMER->ARR = 0xFFFF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Configure PWM mode on both channels
 | 
					    // Configure only channels 3 and 4 for PWM output
 | 
				
			||||||
    if (SOUND_CHANNEL_1 == 3) {
 | 
					    SOUND_TIMER->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC4M); // Clear channel 3 and 4 mode
 | 
				
			||||||
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC3M;
 | 
					    SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos);           // Set channel 3 to PWM mode 1
 | 
				
			||||||
        SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos);  // PWM mode 1 for channel 3
 | 
					    SOUND_TIMER->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC4E);     // Enable output for channels 3 and 4
 | 
				
			||||||
        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->EGR = TIM_EGR_UG; // Update the timer
 | 
				
			||||||
        SOUND_TIMER->CCMR2 &= ~TIM_CCMR2_OC3M;
 | 
					    SOUND_TIMER->CR1 |= TIM_CR1_CEN; // Enable the timer
 | 
				
			||||||
        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
 | 
					// Set BPM
 | 
				
			||||||
void sound_set_bpm(uint32_t new_bpm) {
 | 
					void sound_set_bpm(uint32_t new_bpm) {
 | 
				
			||||||
    bpm = new_bpm;
 | 
					    bpm = new_bpm;
 | 
				
			||||||
@ -78,96 +55,88 @@ void sound_set_bpm(uint32_t new_bpm) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Play a note on a specific channel
 | 
					// Play a note on a specific channel
 | 
				
			||||||
void sound_play_note(Note_t note, uint8_t channel) {
 | 
					void sound_play_note(Note_t note, uint8_t channel) {
 | 
				
			||||||
    if (channel == SOUND_CHANNEL_1) {
 | 
					    if (channel < 1 || channel > 4) return;
 | 
				
			||||||
        current_note_ch3 = note;
 | 
					
 | 
				
			||||||
        if (note.frequency == 0) {
 | 
					    uint32_t *ccr = &SOUND_TIMER->CCR1 + (channel - 1); // Select correct CCR for the channel
 | 
				
			||||||
            // Pause on channel 3
 | 
					    uint32_t *ccmr = (channel <= 2) ? &SOUND_TIMER->CCMR1 : &SOUND_TIMER->CCMR2;
 | 
				
			||||||
            SOUND_TIMER->CCER &= ~TIM_CCER_CC3E; // Disable output on channel 3
 | 
					    uint32_t *ccer = &SOUND_TIMER->CCER;
 | 
				
			||||||
            note_end_time_ch3 = millis() + note.duration;
 | 
					    uint32_t enable_cc = TIM_CCER_CC1E << ((channel - 1) * 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (note.frequency == 0) {
 | 
				
			||||||
 | 
					        *ccer &= ~enable_cc;
 | 
				
			||||||
 | 
					        note_end_time_ch[channel - 1] = millis() + note.duration;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        uint32_t timer_clock = SystemCoreClock / 2;
 | 
				
			||||||
 | 
					        uint32_t prescaler = (timer_clock / (note.frequency * 65536)) + 1;
 | 
				
			||||||
 | 
					        uint32_t arr = (timer_clock / (prescaler * note.frequency)) - 1;
 | 
				
			||||||
 | 
					        uint32_t ccr_val = ((arr + 1) * note.duty_cycle) / 100 - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SOUND_TIMER->PSC = prescaler - 1;
 | 
				
			||||||
 | 
					        SOUND_TIMER->ARR = arr;
 | 
				
			||||||
 | 
					        *ccr = ccr_val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *ccmr &= ~(TIM_CCMR1_OC1M << ((channel - 1) * 8));
 | 
				
			||||||
 | 
					        *ccmr |= (6 << TIM_CCMR1_OC1M_Pos) << ((channel - 1) * 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *ccer |= enable_cc;
 | 
				
			||||||
 | 
					        SOUND_TIMER->EGR = TIM_EGR_UG;
 | 
				
			||||||
 | 
					        SOUND_TIMER->CR1 |= TIM_CR1_CEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        note_end_time_ch[channel - 1] = (note.duration == 0) ? 0 : millis() + note.duration;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Play a melody on a specific channel with repeat count
 | 
				
			||||||
 | 
					void sound_play_melody(Note_t* melody, uint32_t size, uint8_t channel, uint32_t repeat_count) {
 | 
				
			||||||
 | 
					    if (channel < 1 || channel > 4 || melody == NULL || size == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uint8_t ch_idx = channel - 1;
 | 
				
			||||||
 | 
					    melodies[ch_idx].notes = melody;
 | 
				
			||||||
 | 
					    melodies[ch_idx].size = size;
 | 
				
			||||||
 | 
					    melodies[ch_idx].current_note = 0;
 | 
				
			||||||
 | 
					    melodies[ch_idx].repeat_count = repeat_count;
 | 
				
			||||||
 | 
					    melodies[ch_idx].repeat_left = repeat_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sound_play_note(melody[0], channel);  // Play the first note
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Function to play the next note in the melody for a specific channel
 | 
				
			||||||
 | 
					void play_next_note(uint8_t channel) {
 | 
				
			||||||
 | 
					    uint8_t ch_idx = channel - 1;
 | 
				
			||||||
 | 
					    Melody_t* melody = &melodies[ch_idx];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (melody->current_note >= melody->size) {
 | 
				
			||||||
 | 
					        // If end of melody, check repeat
 | 
				
			||||||
 | 
					        if (melody->repeat_left > 0) {
 | 
				
			||||||
 | 
					            melody->repeat_left--;
 | 
				
			||||||
 | 
					            melody->current_note = 0;  // Restart melody
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Configure frequency and duty cycle for channel 3
 | 
					            // Melody finished, no more repeats
 | 
				
			||||||
            uint32_t timer_clock = SystemCoreClock / 2; // Assuming APB1 prescaler is 2
 | 
					            melody->notes = NULL;
 | 
				
			||||||
            uint32_t prescaler = (timer_clock / (note.frequency * 65536)) + 1;
 | 
					            return;
 | 
				
			||||||
            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;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sound_play_note(melody->notes[melody->current_note], channel);
 | 
				
			||||||
 | 
					    melody->current_note++;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Function to be called periodically to handle note duration
 | 
					// Function to be called periodically to handle note duration
 | 
				
			||||||
void sound_tick(void) {
 | 
					void sound_tick(void) {
 | 
				
			||||||
    if (note_end_time_ch3 != 0 && millis() >= note_end_time_ch3) {
 | 
					    for (uint8_t ch = 0; ch < 4; ++ch) {
 | 
				
			||||||
        // Stop the note on channel 3
 | 
					        if (note_end_time_ch[ch] != 0 && millis() >= note_end_time_ch[ch]) {
 | 
				
			||||||
        SOUND_TIMER->CCER &= ~TIM_CCER_CC3E; // Disable output on channel 3
 | 
					            SOUND_TIMER->CCER &= ~(TIM_CCER_CC1E << (ch * 4));  // Disable output
 | 
				
			||||||
        note_end_time_ch3 = 0;
 | 
					            note_end_time_ch[ch] = 0;
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
    if (note_end_time_ch4 != 0 && millis() >= note_end_time_ch4) {
 | 
					            if (melodies[ch].notes != NULL) {
 | 
				
			||||||
        // Stop the note on channel 4
 | 
					                play_next_note(ch + 1);  // Play next note for channel
 | 
				
			||||||
        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
 | 
					// Create a note
 | 
				
			||||||
Note_t sound_create_note(MusicalNote_t note_enum, NoteDuration_t duration_enum, uint8_t duty_cycle) {
 | 
					Note_t sound_create_note(MusicalNote_t note, NoteDuration_t duration, uint8_t duty_cycle) {
 | 
				
			||||||
    Note_t result_note;
 | 
					    uint32_t note_duration = (duration == T0) ? 0 : (60000 * 4 / (bpm * duration));
 | 
				
			||||||
    result_note.frequency = note_enum;
 | 
					    return (Note_t){ .frequency = note, .duration = note_duration, .duty_cycle = duty_cycle };
 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										111
									
								
								Core/Src/main.c
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								Core/Src/main.c
									
									
									
									
									
								
							@ -208,6 +208,113 @@ int main(void)
 | 
				
			|||||||
	            return "UNKNOWN";
 | 
						            return "UNKNOWN";
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sound_set_bpm(147);
 | 
				
			||||||
 | 
					    Note_t melody[] = {
 | 
				
			||||||
 | 
					        sound_create_note(C0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C0_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D0_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F0_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G0_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A0_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B0, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C1_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D1_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F1_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G1_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A1_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B1, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C2_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D2_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F2_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G2_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A2_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B2, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C3_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D3_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F3_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G3_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A3_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B3, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C4_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D4_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F4_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G4_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A4_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B4, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C5_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D5_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F5_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G5_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A5_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B5, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C6_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D6_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F6_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G6_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A6_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B6, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C7_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D7_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(E7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(F7_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(G7_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(A7_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(B7, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C8, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(C8_sharp, T16, 50),
 | 
				
			||||||
 | 
					        sound_create_note(D8, T16, 50)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sound_play_melody(melody, sizeof(melody)/sizeof(melody), 3, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		IR_CMD_Handler();
 | 
							IR_CMD_Handler();
 | 
				
			||||||
@ -240,10 +347,6 @@ int main(void)
 | 
				
			|||||||
		    CDC_Transmit_FS((uint8_t*)buffer, strlen(buffer));
 | 
							    CDC_Transmit_FS((uint8_t*)buffer, strlen(buffer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		    Note_t note = sound_create_note(NOTE_C4, DURATION_EIGHTH, 50);
 | 
					 | 
				
			||||||
		    sound_play_note(note, SOUND_CHANNEL_1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* USER CODE END WHILE */
 | 
					    /* USER CODE END WHILE */
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user