Driving GPIO with PWM peripheral, using nrfx. (https://infocenter.nordicsemi.com/topic/ps_nrf5340/pwm.html?cp=4_0_0_6_22)
LEDs chosen for visual feedback. (GPIO 28-31 for PWM chans)
This shows how to use nrfx to interface with the Nordic PWM peripheral, which is quite a handy and unique peripheral since it can also generate arbitrary waveforms. This application can run without CPU intervention, but the main task only switches which sequence is played by the peripheral every SLEEP_TIME_MS
. Given the low CPU intervention, it would be quite arbitrary to add whatever else you need, such as BLE or other peripherals (like ADC)
- nRF5340DK / nRF5340 doc, nRF5340DK doc
![](https://private-user-images.githubusercontent.com/63935881/261063835-12612a0e-9f81-4431-8b22-f69704248f89.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk2NDkyNjgsIm5iZiI6MTczOTY0ODk2OCwicGF0aCI6Ii82MzkzNTg4MS8yNjEwNjM4MzUtMTI2MTJhMGUtOWY4MS00NDMxLThiMjItZjY5NzA0MjQ4Zjg5LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE1VDE5NDkyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTJlMzIwYmU1M2RmMWRmODY2NzllY2ZiMWM1YzZkNzI5NGQ4YThhZGZjOGU0NjY5ZTIxOGM4ZDkzZTA1MDk5YjImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.lfcvBhKmXULlMXW9j7nTiQFoSKsZEjtApa04N4j1_JE)
![](https://private-user-images.githubusercontent.com/63935881/304490631-86076c67-dce1-4ebb-aa1a-80e0461b0765.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk2NDkyNjgsIm5iZiI6MTczOTY0ODk2OCwicGF0aCI6Ii82MzkzNTg4MS8zMDQ0OTA2MzEtODYwNzZjNjctZGNlMS00ZWJiLWFhMWEtODBlMDQ2MWIwNzY1LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE1VDE5NDkyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWJmMmM4Y2IyYzVmZjhkNDU1Y2YzZmU0ZWQwMWVmMjBmYmMzMGUzNjAwMDlkZTlhMTI2YjBjYzgyZDQ2ZmU3YWQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.nyNYasrLtRtVjDqRKRzYOwIf1xb14KzQ95k1fq6n1b4)
![](https://private-user-images.githubusercontent.com/63935881/304491134-e9ffa475-7a6f-4d53-829b-03adf76bd0a4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk2NDkyNjgsIm5iZiI6MTczOTY0ODk2OCwicGF0aCI6Ii82MzkzNTg4MS8zMDQ0OTExMzQtZTlmZmE0NzUtN2E2Zi00ZDUzLTgyOWItMDNhZGY3NmJkMGE0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE1VDE5NDkyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU1ZDI1NjE4ZWMxMGI4NzJkNjkxY2FiN2Q3NzA3NmVmM2NhM2I4YjFkZmVjYjg3ZjliNzExNjYyZDM4ZDI3NzQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.tBhArmtUBDFAvSBH4BrIGZIhd47FR98YNEjOt6cr1Z8)
Boxed in red is header location if you want to measure the waveforms for yourself.
Note: Readme C code for sequences is formatted slightly differently to make visualizing the columns for each sequence easier.
Your run of the mill set-duty-cycle and forget
static nrf_pwm_values_individual_t sequence_A; // sequence A is a pretty basic PWM duty cycle.
sequence_A.channel_0 = PWM_COUNTERTOP / 10 | (1 << 15); // flips really early, inverted polarity
sequence_A.channel_1 = PWM_COUNTERTOP / 4;
sequence_A.channel_2 = PWM_COUNTERTOP / 2; // should flip half way through
sequence_A.channel_3 = PWM_COUNTERTOP / 10;
Stretching resolution windows, with inversion and toggling-off
static nrf_pwm_values_individual_t sequence_B[] = {
{0x8000 | (CYCLE_A / 2), 0x8000 | (CYCLE_B / 2), 0x8000 | (CYCLE_C / 2), CYCLE_A},
{(CYCLE_A / 2), (CYCLE_B / 2), (CYCLE_C / 2), CYCLE_B},
{0, 0, 0, CYCLE_C},
{CYCLE_A, CYCLE_B, CYCLE_C, CYCLE_D},
};
Time delayed pulses that are off majority of the time (turn off by setting the value to COUNTERTOP
in the seq)
const uint16_t short_pulse = PWM_COUNTERTOP / 10 | (1 << 15);
static nrf_pwm_values_individual_t sequence_C[] = {
{short_pulse, PWM_COUNTERTOP, PWM_COUNTERTOP, PWM_COUNTERTOP},
{PWM_COUNTERTOP, short_pulse, PWM_COUNTERTOP, PWM_COUNTERTOP},
{PWM_COUNTERTOP, PWM_COUNTERTOP, short_pulse, PWM_COUNTERTOP},
{PWM_COUNTERTOP, PWM_COUNTERTOP, PWM_COUNTERTOP, short_pulse},
};
Time delayed pulses with alternating short and long pulses
const uint16_t long_pulse = PWM_COUNTERTOP / 2 | (1 << 15);
static nrf_pwm_values_individual_t sequence_D[] = {
{short_pulse, PWM_COUNTERTOP, long_pulse, PWM_COUNTERTOP},
{PWM_COUNTERTOP, long_pulse, PWM_COUNTERTOP, short_pulse},
{long_pulse, PWM_COUNTERTOP, short_pulse, PWM_COUNTERTOP},
{PWM_COUNTERTOP, short_pulse, PWM_COUNTERTOP, long_pulse},
};
in pwm_set_duty_cycle
, there are three sequences for sample outputs that it rotates through.
when using waveform mode you only get 3 output channels instead of 4. If you need an adjustable countertop, use waveform mode.
You'll need to modify config0
such that NRF_PWM_PIN_NOT_CONNECTED
is 31 instead for LED4, the load_mode
to be NRF_PWM_LOAD_INDIVIDUAL
, and p_wave_form
can be p_individual
where you pass sequence.
You can also have multiple PWM instances.
//if you had a single channel, this is an interesting one.
// nrf_pwm_values_wave_form_t peculiarSequenceSingleChannel[] = {
// // Index Normal pin Inverted (Spare) Top Value
// // ===== =================== ========== ======= =========
// { /* 0 */ 0x8000|(CYCLE_A/2), (CYCLE_A/2), 0, CYCLE_A },
// { /* 1 */ 0x8000|(CYCLE_B/2), (CYCLE_B/2), 0, CYCLE_B },
// { /* 2 */ 0x8000|(CYCLE_C/2), (CYCLE_C/2), 0, CYCLE_C },
// { /* 3 */ 0x8000|(CYCLE_D/2), (CYCLE_D/2), 0, CYCLE_D },
// };
And transposed if you are using multiple channels. i.e. each column is each channel.
multiple-instance
for distinct frequencies on each i/o. this branch is kind of unfinished because to generate the above screenshots only needed one instance. (thanks @inductivekickback)
snapshot_version
old version that has you manually change the structs you feed the playback fxn.