标签:
蜂鸣器的资料网上也有很多……这里就简单记录一下……有有源蜂鸣器和无源蜂鸣器两种 这里我用的是无源蜂鸣器的模块 ,自带了放大电路,否则要自己焊一个……
这里我设想的是用不同频率的pwm波驱动蜂鸣器发出不同频率的乐音,对于钢琴上的 CDEFGAB。
一共搞了两个程序,第一个没有实现功能 ,第二个实现了。
(一)只能输出固定频率可调占空比的pwm波形
nrf_pwm.c
#include "nrf_gpiote.h" #include "nrf_gpio.h" #if(USE_WITH_SOFTDEVICE == 1) #include "nrf_sdm.h" #endif static uint32_t pwm_max_value,pwm_next_value[PWM_MAX_CHANNELS], pwm_next_max_value, pwm_io_ch[PWM_MAX_CHANNELS], pwm_running[PWM_MAX_CHANNELS]; static bool pwm_modified[PWM_MAX_CHANNELS]; static uint8_t pwm_gpiote_channel[PWM_MAX_CHANNELS]; static uint32_t pwm_num_channels; static uint32_t pwm_cc_update_margin_ticks = 10; static const uint8_t pwm_cc_margin_by_prescaler[] = {80, 40, 20, 10, 5, 2, 1, 1, 1, 1}; #define PWM_TIMER_CURRENT PWM_TIMER->CC[3] #define PWM_TIMER2_CURRENT PWM_TIMER2->CC[3] void PWM_IRQHandler(void); static void apply_pan73_workaround(NRF_TIMER_Type *timer, bool enable) { if(timer == NRF_TIMER0) { *(uint32_t *)0x40008C0C = (enable ? 1 : 0); } else if(timer == NRF_TIMER1) { *(uint32_t *)0x40009C0C = (enable ? 1 : 0); } else if(timer == NRF_TIMER2) { *(uint32_t *)0x4000AC0C = (enable ? 1 : 0); } } static __INLINE bool safe_margins_present(uint32_t timer_state, uint32_t compare_state) // Approx runtime ~2us { if(compare_state <= pwm_cc_update_margin_ticks) { if(timer_state >= compare_state && timer_state < (pwm_max_value + compare_state - pwm_cc_update_margin_ticks)) return true; else return false; } else { if(timer_state < (compare_state - pwm_cc_update_margin_ticks) || timer_state >= compare_state) return true; else return false; } } static void ppi_enable_channel(uint32_t ch_num, volatile uint32_t *event_ptr, volatile uint32_t *task_ptr) { if(ch_num >= 16) return; else { #if(USE_WITH_SOFTDEVICE == 1) sd_ppi_channel_assign(ch_num, event_ptr, task_ptr); sd_ppi_channel_enable_set(1 << ch_num); #else // Otherwise we configure the channel and return the channel number NRF_PPI->CH[ch_num].EEP = (uint32_t)event_ptr; NRF_PPI->CH[ch_num].TEP = (uint32_t)task_ptr; NRF_PPI->CHENSET = (1 << ch_num); #endif } } #if(USE_WITH_SOFTDEVICE == 1) nrf_radio_signal_callback_return_param_t *nrf_radio_signal_callback(uint8_t signal_type) { static nrf_radio_signal_callback_return_param_t return_params; return_params.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_END; switch(signal_type) { case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START: /**< This signal indicates the start of the radio timeslot. */ PWM_IRQHandler(); break; case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0: /**< This signal indicates the NRF_TIMER0 interrupt. */ break; case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO: /**< This signal indicates the NRF_RADIO interrupt. */ break; case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED: /**< This signal indicates extend action failed. */ break; case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED: /**< This signal indicates extend action succeeded. */ break; } return &return_params; } #endif uint32_t nrf_pwm_init(nrf_pwm_config_t *config) { if(config->num_channels == 0 || config->num_channels > PWM_MAX_CHANNELS) return 0xFFFFFFFF; switch(config->mode) { case PWM_MODE_C: // 8-bit resolution, 520Hz PWM frequency, 1us 1923 PWM_TIMER->PRESCALER = 4; pwm_max_value = 1923; nrf_pwm_set_value(0, 960); break; case PWM_MODE_D: // 8-bit resolution, 585Hz PWM frequency, 1us 1709 PWM_TIMER->PRESCALER = 4; pwm_max_value = 1709; nrf_pwm_set_value(0, 850); break; case PWM_MODE_E: // 0-1000 resolution, 650Hz PWM frequency, 1us 1538 PWM_TIMER->PRESCALER = 4; pwm_max_value = 1538; nrf_pwm_set_value(0, 765); break; case PWM_MODE_F: // 0-100 resolution, 693Hz PWM frequency, 1us PWM_TIMER->PRESCALER = 4; pwm_max_value = 1443; nrf_pwm_set_value(0, 720); break; case PWM_MODE_G: // 8-bit resolution, 780Hz PWM frequency, 1us PWM_TIMER->PRESCALER = 4; pwm_max_value = 1282; nrf_pwm_set_value(0, 640); break; case PWM_MODE_A: // 8-bit resolution, 867Hz PWM frequency, 1us PWM_TIMER->PRESCALER = 4; pwm_max_value = 1153; nrf_pwm_set_value(0, 575); break; case PWM_MODE_B: // 8-bit resolution, 975Hz PWM frequency, 1us PWM_TIMER->PRESCALER = 4; pwm_max_value = 1026; nrf_pwm_set_value(0, 510); break; default: return 0xFFFFFFFF; } pwm_cc_update_margin_ticks = pwm_cc_margin_by_prescaler[PWM_TIMER->PRESCALER]; pwm_num_channels = config->num_channels; for(int i = 0; i < pwm_num_channels; i++) { pwm_io_ch[i] = (uint32_t)config->gpio_num[i]; nrf_gpio_cfg_output(pwm_io_ch[i]); pwm_running[i] = 0; pwm_gpiote_channel[i] = config->gpiote_channel[i]; } PWM_TIMER->TASKS_CLEAR = 1; PWM_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; PWM_TIMER->CC[2] = pwm_next_max_value = pwm_max_value; PWM_TIMER->MODE = TIMER_MODE_MODE_Timer; PWM_TIMER->SHORTS = TIMER_SHORTS_COMPARE2_CLEAR_Msk; PWM_TIMER->EVENTS_COMPARE[0] = PWM_TIMER->EVENTS_COMPARE[1] = PWM_TIMER->EVENTS_COMPARE[2] = PWM_TIMER->EVENTS_COMPARE[3] = 0; if(pwm_num_channels > 2) { PWM_TIMER2->TASKS_CLEAR = 1; PWM_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; PWM_TIMER2->CC[2] = pwm_next_max_value = pwm_max_value; PWM_TIMER2->MODE = TIMER_MODE_MODE_Timer; PWM_TIMER2->SHORTS = TIMER_SHORTS_COMPARE2_CLEAR_Msk; PWM_TIMER2->EVENTS_COMPARE[0] = PWM_TIMER2->EVENTS_COMPARE[1] = PWM_TIMER2->EVENTS_COMPARE[2] = PWM_TIMER2->EVENTS_COMPARE[3] = 0; PWM_TIMER->PRESCALER = PWM_TIMER2->PRESCALER; } for(int i = 0; i < pwm_num_channels && i < 2; i++) { ppi_enable_channel(config->ppi_channel[i*2], &PWM_TIMER->EVENTS_COMPARE[i], &NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]]); ppi_enable_channel(config->ppi_channel[i*2+1],&PWM_TIMER->EVENTS_COMPARE[2], &NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]]); pwm_modified[i] = false; } for(int i = 2; i < pwm_num_channels; i++) { ppi_enable_channel(config->ppi_channel[i*2], &PWM_TIMER2->EVENTS_COMPARE[i-2], &NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]]); ppi_enable_channel(config->ppi_channel[i*2+1],&PWM_TIMER2->EVENTS_COMPARE[2], &NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]]); pwm_modified[i] = false; } #if(USE_WITH_SOFTDEVICE == 1) sd_radio_session_open(nrf_radio_signal_callback); #else NVIC_SetPriority(PWM_IRQn, 0); NVIC_EnableIRQ(PWM_IRQn); #endif apply_pan73_workaround(PWM_TIMER, true); PWM_TIMER->TASKS_START = 1; if(pwm_num_channels > 2) { apply_pan73_workaround(PWM_TIMER2, true); PWM_TIMER2->TASKS_START = 1; } return 0; } void nrf_pwm_set_value(uint32_t pwm_channel, uint32_t pwm_value) { pwm_next_value[pwm_channel] = pwm_value; pwm_modified[pwm_channel] = true; #if(USE_WITH_SOFTDEVICE == 1) nrf_radio_request_t radio_request; radio_request.request_type = NRF_RADIO_REQ_TYPE_EARLIEST; radio_request.params.earliest.hfclk = NRF_RADIO_HFCLK_CFG_DEFAULT; radio_request.params.earliest.length_us = 250; radio_request.params.earliest.priority = NRF_RADIO_PRIORITY_HIGH; radio_request.params.earliest.timeout_us = 100000; sd_radio_request(&radio_request); #else NVIC_SetPendingIRQ(PWM_IRQn); #endif } void nrf_pwm_set_values(uint32_t pwm_channel_num, uint32_t *pwm_values) { for(int i = 0; i < pwm_channel_num; i++) { pwm_next_value[i] = pwm_values[i]; pwm_modified[i] = true; } #if(USE_WITH_SOFTDEVICE == 1) nrf_radio_request_t radio_request; radio_request.request_type = NRF_RADIO_REQ_TYPE_EARLIEST; radio_request.params.earliest.hfclk = NRF_RADIO_HFCLK_CFG_DEFAULT; radio_request.params.earliest.length_us = 250; radio_request.params.earliest.priority = NRF_RADIO_PRIORITY_HIGH; radio_request.params.earliest.timeout_us = 100000; sd_radio_request(&radio_request); #else NVIC_SetPendingIRQ(PWM_IRQn); #endif } void nrf_pwm_set_max_value(uint32_t max_value) { pwm_next_max_value = max_value; } void nrf_pwm_set_enabled(bool enabled) { if(enabled) { PWM_TIMER->TASKS_START = 1; if(pwm_num_channels > 2) PWM_TIMER2->TASKS_START = 1; } else { PWM_TIMER->TASKS_STOP = 1; if(pwm_num_channels > 2) PWM_TIMER2->TASKS_STOP = 1; for(uint32_t i = 0; i < pwm_num_channels; i++) { nrf_gpiote_unconfig(pwm_gpiote_channel[i]); nrf_gpio_pin_write(pwm_io_ch[i], 0); pwm_running[i] = 0; } } } void PWM_IRQHandler(void) { static uint32_t i, new_capture, old_capture; PWM_TIMER->CC[2] = pwm_max_value = pwm_next_max_value; if(pwm_num_channels > 2) PWM_TIMER2->CC[2] = pwm_max_value; for(i = 0; i < pwm_num_channels; i++) { if(pwm_modified[i]) { pwm_modified[i] = false; if(pwm_next_value[i] == 0) { nrf_gpiote_unconfig(pwm_gpiote_channel[i]); nrf_gpio_pin_write(pwm_io_ch[i], 0); pwm_running[i] = 0; } else if (pwm_next_value[i] >= pwm_max_value) { nrf_gpiote_unconfig(pwm_gpiote_channel[i]); nrf_gpio_pin_write(pwm_io_ch[i], 1); pwm_running[i] = 0; } else { if(i < 2) { new_capture = pwm_next_value[i]; old_capture = PWM_TIMER->CC[i]; if(!pwm_running[i]) { nrf_gpiote_task_config(pwm_gpiote_channel[i], pwm_io_ch[i], NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_HIGH); pwm_running[i] = 1; PWM_TIMER->TASKS_CAPTURE[3] = 1; if(PWM_TIMER->CC[3] > new_capture) NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]] = 1; PWM_TIMER->CC[i] = new_capture; } else { while(1) { PWM_TIMER->TASKS_CAPTURE[3] = 1; if(safe_margins_present(PWM_TIMER_CURRENT, old_capture) && safe_margins_present(PWM_TIMER_CURRENT, new_capture)) break; } if((PWM_TIMER_CURRENT >= old_capture && PWM_TIMER_CURRENT < new_capture) || (PWM_TIMER_CURRENT < old_capture && PWM_TIMER_CURRENT >= new_capture)) { NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]] = 1; } PWM_TIMER->CC[i] = new_capture; } } else { new_capture = pwm_next_value[i]; old_capture = PWM_TIMER2->CC[i-2]; if(!pwm_running[i]) { nrf_gpiote_task_config(pwm_gpiote_channel[i], pwm_io_ch[i], NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_HIGH); pwm_running[i] = 1; PWM_TIMER2->TASKS_CAPTURE[3] = 1; if(PWM_TIMER2->CC[3] > new_capture) NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]] = 1; PWM_TIMER2->CC[i-2] = new_capture; } else { while(1) { PWM_TIMER2->TASKS_CAPTURE[3] = 1; if(safe_margins_present(PWM_TIMER2_CURRENT, old_capture) && safe_margins_present(PWM_TIMER2_CURRENT, new_capture)) break; } if((PWM_TIMER2_CURRENT >= old_capture && PWM_TIMER2_CURRENT < new_capture) || (PWM_TIMER2_CURRENT < old_capture && PWM_TIMER2_CURRENT >= new_capture)) { NRF_GPIOTE->TASKS_OUT[pwm_gpiote_channel[i]] = 1; } PWM_TIMER2->CC[i-2] = new_capture; } } } } } }
main.c
#include <stdbool.h> #include <stdint.h> #include "nrf.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "nrf_pwm.h" #include "boards.h" #define FREQ_HALF_NOTE_FACTOR 1.059463f nrf_pwm_config_t pwm_config = PWM_DEFAULT_CONFIG; void pwm_init(nrf_pwm_mode_t nrf_pwm_mode) { pwm_config.mode = nrf_pwm_mode; // Initialize the PWM library nrf_pwm_init(&pwm_config); nrf_delay_ms(300); } int main(void) { pwm_config.num_channels = 1; pwm_config.gpio_num[0] = 7; // Start the external 16 MHz clock for a more accurate PWM frequency NRF_CLOCK->TASKS_HFCLKSTART = 1; //pwm_init(PWM_MODE_C); while (true) { pwm_init(PWM_MODE_C); pwm_init(PWM_MODE_D); pwm_init(PWM_MODE_E); pwm_init(PWM_MODE_F); pwm_init(PWM_MODE_G); pwm_init(PWM_MODE_A); pwm_init(PWM_MODE_B); } }
仿真一下发现,出现的波形前后只会有占空比的不同,频率是相同的。。这里为了方便看 用了四个通道
若把分频改成-->5,则之后出现的波形频率都是5分频,更改最大值的话 则还是依从配置的所有最大值里最大的一个……出波形……很神奇
(二)频率可调的pwm控制蜂鸣器发出乐音
#include <stdbool.h> #include <stdint.h> #include "nrf.h" #include "nrf_assert.h" #include "nrf_gpiote.h" #include "nrf_gpio.h" #include "boards.h" #include "nrf_delay.h" #define PWM_OUTPUT_PIN_NUMBER 7 /**< Pin number for PWM output. */ unsigned long MAX_SAMPLE_LEVELS = 1923; /**< Maximum number of sample levels. */ #define TIMER_PRESCALERS 4U /**< Prescaler setting for timer. */ /** @brief Function for getting the next sample. * @return sample_value computed sample. */ static __INLINE uint32_t next_sample_get(void) { static uint32_t sample_value = 8; // Read button input. sample_value = (~NRF_GPIO->IN & 0x000000FFUL); // This is to avoid having two CC events happen at the same time, // CC1 will always create an event on 0 so CC0 and CC2 should not. if (sample_value == 0) { sample_value = 8; } return (uint32_t)sample_value; } /** @brief Function for handling timer 2 peripheral interrupts. */ void TIMER2_IRQHandler(void) { static bool cc0_turn = false; /**< Keeps track of which CC register to be used. */ if ((NRF_TIMER2->EVENTS_COMPARE[1] != 0) && ((NRF_TIMER2->INTENSET & TIMER_INTENSET_COMPARE1_Msk) != 0)) { // Sets the next CC1 value NRF_TIMER2->EVENTS_COMPARE[1] = 0; NRF_TIMER2->CC[1] = (NRF_TIMER2->CC[1] + MAX_SAMPLE_LEVELS); // Every other interrupt CC0 and CC2 will be set to their next values. uint32_t next_sample = next_sample_get(); if (cc0_turn) { NRF_TIMER2->CC[0] = NRF_TIMER2->CC[1] + MAX_SAMPLE_LEVELS*0.5; //duty } else { NRF_TIMER2->CC[2] = NRF_TIMER2->CC[1] + MAX_SAMPLE_LEVELS*0.5; //duty } // Next turn the other CC will get its value. cc0_turn = !cc0_turn; } } /** @brief Function for initializing the Timer 2 peripheral. */ static void timer2_init(void) { // Start 16 MHz crystal oscillator . NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; NRF_CLOCK->TASKS_HFCLKSTART = 1; // Wait for the external oscillator to start up. while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { //Do nothing. } NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer; NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos; NRF_TIMER2->PRESCALER = TIMER_PRESCALERS; // Clears the timer, sets it to 0. NRF_TIMER2->TASKS_CLEAR = 1; // Load the initial values to TIMER2 CC registers. NRF_TIMER2->CC[0] = MAX_SAMPLE_LEVELS + next_sample_get(); NRF_TIMER2->CC[1] = MAX_SAMPLE_LEVELS; // CC2 will be set on the first CC1 interrupt. NRF_TIMER2->CC[2] = 0; // Interrupt setup. NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos); } /** @brief Function for initializing the GPIO Tasks/Events peripheral. */ static void gpiote_init(void) { // Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output. nrf_gpio_range_cfg_input(BUTTON_START, BUTTON_STOP, NRF_GPIO_PIN_NOPULL); nrf_gpio_cfg_output(PWM_OUTPUT_PIN_NUMBER); NRF_GPIO->OUT = 0x00000000UL; // Configure GPIOTE channel 0 to toggle the PWM pin state // @note Only one GPIOTE task can be connected to an output pin. nrf_gpiote_task_config(0, PWM_OUTPUT_PIN_NUMBER, NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW); } /** @brief Function for initializing the Programmable Peripheral Interconnect peripheral. */ static void ppi_init(void) { // Configure PPI channel 0 to toggle PWM_OUTPUT_PIN on every TIMER2 COMPARE[0] match. NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[0]; NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0]; // Configure PPI channel 1 to toggle PWM_OUTPUT_PIN on every TIMER2 COMPARE[1] match. NRF_PPI->CH[1].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[1]; NRF_PPI->CH[1].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0]; // Configure PPI channel 1 to toggle PWM_OUTPUT_PIN on every TIMER2 COMPARE[2] match. NRF_PPI->CH[2].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[2]; NRF_PPI->CH[2].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0]; // Enable PPI channels 0-2. NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos) | (PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos) | (PPI_CHEN_CH2_Enabled << PPI_CHEN_CH2_Pos); } /** * @brief Function for application main entry. */ int main(void) { gpiote_init(); ppi_init(); timer2_init(); // Enabling constant latency as indicated by PAN 11 "HFCLK: Base current with HFCLK // running is too high" found at Product Anomaly document found at // https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads // // @note This example does not go to low power mode therefore constant latency is not needed. // However this setting will ensure correct behaviour when routing TIMER events through // PPI (shown in this example) and low power mode simultaneously. NRF_POWER->TASKS_CONSTLAT = 1; // Enable interrupt on Timer 2. NVIC_EnableIRQ(TIMER2_IRQn); __enable_irq(); // Start the timer. NRF_TIMER2->TASKS_START = 1; while (true) { MAX_SAMPLE_LEVELS = 1923; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1709; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1538; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1443; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1282; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1153; nrf_delay_ms(200); MAX_SAMPLE_LEVELS = 1026; nrf_delay_ms(200); } }
标签:
原文地址:http://blog.csdn.net/kallyshall/article/details/51337134