diff --git a/src/boards.c b/src/boards.c index d0b203d..32a9e82 100644 --- a/src/boards.c +++ b/src/boards.c @@ -35,6 +35,7 @@ /**************************************************************************/ #include "boards.h" +#include "nrf_pwm.h" #include "app_scheduler.h" #include "app_timer.h" @@ -83,7 +84,11 @@ void board_init(void) // use neopixel for use enumeration #ifdef LED_NEOPIXEL + extern void neopixel_init(void); + neopixel_init(); + uint8_t grb[] = { 0, 255, 0 }; + neopixel_write(grb); #endif // Init scheduler @@ -98,6 +103,11 @@ void board_teardown(void) // Disable and reset PWM for LED led_pwm_teardown(LED_RED); +#ifdef LED_NEOPIXEL + extern void neopixel_teardown(void); + neopixel_teardown(); +#endif + led_off(LED_BLUE); led_off(LED_RED); @@ -119,25 +129,26 @@ uint32_t tusb_hal_millis(void) return ( ( ((uint64_t)app_timer_cnt_get())*1000*(APP_TIMER_CONFIG_RTC_FREQUENCY+1)) / APP_TIMER_CLOCK_FREQ ); } + +void pwm_teardown(NRF_PWM_Type* pwm ) +{ + pwm->TASKS_SEQSTART[0] = 0; + pwm->ENABLE = 0; + + pwm->PSEL.OUT[0] = 0xFFFFFFFF; + + pwm->MODE = 0; + pwm->COUNTERTOP = 0x3FF; + pwm->PRESCALER = 0; + pwm->DECODER = 0; + pwm->LOOP = 0; + pwm->SEQ[0].PTR = 0; + pwm->SEQ[0].CNT = 0; +} + void led_pwm_init(uint32_t led_pin) { - NRF_PWM_Type* pwm = (led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1; - - pwm->MODE = PWM_MODE_UPDOWN_UpAndDown; - pwm->COUNTERTOP = PWM_MAXCOUNT; - pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_128; - pwm->DECODER = PWM_DECODER_LOAD_Individual; - pwm->LOOP = 0; - - pwm->SEQ[0].PTR = (uint32_t) (led_pin == LED_RED ? _pwm_red_seq : _pwm_blue_seq); - pwm->SEQ[0].CNT = PWM_CHANNEL_NUM; // default mode is Individual --> count must be 4 - pwm->SEQ[0].REFRESH = 0; - pwm->SEQ[0].ENDDELAY = 0; - - pwm->PSEL.OUT[0] = led_pin; - - pwm->ENABLE = 1; - pwm->TASKS_SEQSTART[0] = 1; + pwm_teardown ((led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1); } void led_pwm_teardown(uint32_t led_pin) @@ -186,3 +197,92 @@ void led_red_blink_fast(bool enable) } } +#if LED_NEOPIXEL + +// WS2812B (rev B) timing is 0.4 and 0.8 us +#define MAGIC_T0H 6UL | (0x8000) // 0.375us +#define MAGIC_T1H 13UL | (0x8000) // 0.8125us +#define CTOPVAL 20UL // 1.25us + +#define NEO_NUMBYTE 3 + +static uint16_t pixels_pattern[NEO_NUMBYTE * 8 + 2]; + +void neopixel_init(void) +{ + // To support both the SoftDevice + Neopixels we use the EasyDMA + // feature from the NRF25. However this technique implies to + // generate a pattern and store it on the memory. The actual + // memory used in bytes corresponds to the following formula: + // totalMem = numBytes*8*2+(2*2) + // The two additional bytes at the end are needed to reset the + // sequence. + NRF_PWM_Type* pwm = NRF_PWM2; + + // Set the wave mode to count UP + // Set the PWM to use the 16MHz clock + // Setting of the maximum count + // but keeping it on 16Mhz allows for more granularity just + // in case someone wants to do more fine-tuning of the timing. + nrf_pwm_configure(pwm, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, CTOPVAL); + + // Disable loops, we want the sequence to repeat only once + nrf_pwm_loop_set(pwm, 0); + + // On the "Common" setting the PWM uses the same pattern for the + // for supported sequences. The pattern is stored on half-word of 16bits + nrf_pwm_decoder_set(pwm, PWM_DECODER_LOAD_Common, PWM_DECODER_MODE_RefreshCount); + + // The following settings are ignored with the current config. + nrf_pwm_seq_refresh_set(pwm, 0, 0); + nrf_pwm_seq_end_delay_set(pwm, 0, 0); + + // The Neopixel implementation is a blocking algorithm. DMA + // allows for non-blocking operation. To "simulate" a blocking + // operation we enable the interruption for the end of sequence + // and block the execution thread until the event flag is set by + // the peripheral. + // pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled< 0; mask >>= 1 ) + { + pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; + pos++; + } + } + + // Zero padding to indicate the end of sequence + pixels_pattern[++pos] = 0 | (0x8000); // Seq end + pixels_pattern[++pos] = 0 | (0x8000); // Seq end + + + NRF_PWM_Type* pwm = NRF_PWM2; + + + nrf_pwm_seq_ptr_set(pwm, 0, pixels_pattern); + nrf_pwm_seq_cnt_set(pwm, 0, sizeof(pixels_pattern)/2); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND0); + nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_SEQSTART0); +} +#endif + diff --git a/src/boards.h b/src/boards.h index 4194202..b81bf6e 100644 --- a/src/boards.h +++ b/src/boards.h @@ -14,6 +14,7 @@ #include #include +#include #include "nrf_gpio.h" #if defined BOARD_FEATHER_NRF52840_EXPRESS @@ -71,6 +72,10 @@ void led_pwm_enable(uint32_t led_pin); void led_red_blink_fast(bool enable); +#ifdef LED_NEOPIXEL + void neopixel_write(uint8_t *pixels); +#endif + //--------------------------------------------------------------------+ // BUTTONS //--------------------------------------------------------------------+ diff --git a/src/usb/usb.c b/src/usb/usb.c index 57e4a93..5333daa 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -46,6 +46,8 @@ #include "tusb.h" #include "usb_desc.h" +#include "boards.h" + //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ @@ -155,11 +157,17 @@ void usb_teardown(void) //--------------------------------------------------------------------+ void tud_mount_cb(void) { - +#ifdef LED_NEOPIXEL + uint8_t grb[] = { 255, 0, 0 }; + neopixel_write(grb); +#endif } void tud_umount_cb(void) { - +#ifdef LED_NEOPIXEL + uint8_t grb[] = { 0, 255, 0 }; + neopixel_write(grb); +#endif }