From 0e845d9ebd86636dd36f95b7109949788a8be48b Mon Sep 17 00:00:00 2001 From: DashyFox Date: Wed, 2 Oct 2024 22:59:51 +0300 Subject: [PATCH] music --- Core/Inc/Sound.h | 144 ++++++++++++++++++++++++++---- Core/Src/Sound.c | 225 ++++++++++++++++++++--------------------------- Core/Src/main.c | 111 ++++++++++++++++++++++- 3 files changed, 329 insertions(+), 151 deletions(-) diff --git a/Core/Inc/Sound.h b/Core/Inc/Sound.h index 45a067f..63452a6 100644 --- a/Core/Inc/Sound.h +++ b/Core/Inc/Sound.h @@ -13,8 +13,6 @@ // Define default timer and channels #define SOUND_TIMER TIM4 #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 typedef struct { @@ -25,34 +23,142 @@ typedef struct { // Enum for musical notes typedef enum { - NOTE_C4 = 261, - NOTE_D4 = 293, - NOTE_E4 = 329, - NOTE_F4 = 349, - NOTE_G4 = 392, - NOTE_A4 = 440, - NOTE_B4 = 493, - NOTE_C5 = 523, - // Add more notes as needed - NOTE_REST = 0 + C0 = 16, + C0_sharp = 17, + D0 = 18, + D0_sharp = 19, + E0 = 21, + F0 = 22, + F0_sharp = 23, + G0 = 25, + G0_sharp = 26, + 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; + // Enum for note durations (in fractions of a whole note) typedef enum { - DURATION_WHOLE = 1, - DURATION_HALF = 2, - DURATION_QUARTER = 4, - DURATION_EIGHTH = 8, - DURATION_SIXTEENTH = 16 - // Add more durations as needed + T0 = 0, // Infinite + T1 = 1, // Whole note + T2 = 2, // Half note + T4 = 4, // Quarter note + T8 = 8, // Eighth note + T16 = 16 // Sixteenth note } 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 void sound_init(void); void sound_set_bpm(uint32_t bpm); 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); Note_t sound_create_note(MusicalNote_t note, NoteDuration_t duration, uint8_t duty_cycle); - #endif /* INC_SOUND_H_ */ diff --git a/Core/Src/Sound.c b/Core/Src/Sound.c index 375a629..17747f0 100644 --- a/Core/Src/Sound.c +++ b/Core/Src/Sound.c @@ -9,11 +9,13 @@ #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; +static uint32_t bpm = 120; // Default BPM +static uint32_t note_end_time_ch[4] = {0, 0, 0, 0}; // End times for each channel (1-4) +static Melody_t melodies[4]; // Array of melodies for each channel + +// Initialize the sound system +#define SOUND_TIMER TIM4 +#define SOUND_TIMER_RCC RCC_APB1ENR_TIM4EN // Initialize the sound system 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) 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 - } + // Configure only channels 3 and 4 + // Channel 3 + GPIOB->CRH &= ~(0xF << (3 * 4)); // Clear configuration for PB10 (TIM4_CH3) + GPIOB->CRH |= (0xB << (3 * 4)); // Set to 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 - } + // Channel 4 + GPIOB->CRH &= ~(0xF << (4 * 4)); // Clear configuration for PB11 (TIM4_CH4) + GPIOB->CRH |= (0xB << (4 * 4)); // Set to 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() + SOUND_TIMER->PSC = 0; + SOUND_TIMER->ARR = 0xFFFF; - // 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 - } + // Configure only channels 3 and 4 for PWM output + SOUND_TIMER->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC4M); // Clear channel 3 and 4 mode + SOUND_TIMER->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos); // Set channel 3 to PWM mode 1 + SOUND_TIMER->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC4E); // Enable output for channels 3 and 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; + SOUND_TIMER->EGR = TIM_EGR_UG; // Update the timer + SOUND_TIMER->CR1 |= TIM_CR1_CEN; // Enable the timer } + // Set BPM void sound_set_bpm(uint32_t new_bpm) { bpm = new_bpm; @@ -78,96 +55,88 @@ void sound_set_bpm(uint32_t 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; + if (channel < 1 || channel > 4) return; + + uint32_t *ccr = &SOUND_TIMER->CCR1 + (channel - 1); // Select correct CCR for the channel + uint32_t *ccmr = (channel <= 2) ? &SOUND_TIMER->CCMR1 : &SOUND_TIMER->CCMR2; + uint32_t *ccer = &SOUND_TIMER->CCER; + 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 { - // 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; - } + // Melody finished, no more repeats + melody->notes = NULL; + return; } } + + sound_play_note(melody->notes[melody->current_note], channel); + melody->current_note++; } // 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; + for (uint8_t ch = 0; ch < 4; ++ch) { + if (note_end_time_ch[ch] != 0 && millis() >= note_end_time_ch[ch]) { + SOUND_TIMER->CCER &= ~(TIM_CCER_CC1E << (ch * 4)); // Disable output + note_end_time_ch[ch] = 0; + + if (melodies[ch].notes != NULL) { + play_next_note(ch + 1); // Play next note for channel + } + } } } -// 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; +// Create a note +Note_t sound_create_note(MusicalNote_t note, NoteDuration_t duration, uint8_t duty_cycle) { + uint32_t note_duration = (duration == T0) ? 0 : (60000 * 4 / (bpm * duration)); + return (Note_t){ .frequency = note, .duration = note_duration, .duty_cycle = duty_cycle }; } diff --git a/Core/Src/main.c b/Core/Src/main.c index f7fadb7..e13f445 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -208,6 +208,113 @@ int main(void) 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) { IR_CMD_Handler(); @@ -240,10 +347,6 @@ int main(void) 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 */