I am worried that there might be an issue with PWM on the RP2040 family -- at least for my use case.
I need to create a signal of varying PWM output. The signal generated are interpreted as '1's and '0's based on their length. A zero is twice as long as a '1'. To set it up simply, I am running an output nearly as slowly as possible to see what the signal looks like on my oscilloscope.
When producing the PWM using the 'normal' sawtooth waveform, it seems to work. But when producing a waveform using phase correct type of PWM, it is not being created correctly.
Since the output waveform needs to change, there is a bit pattern assigned for the generated waveform. When PWM hits TOP, and starts to roll around to reset, it issues an interrupt which I catch. This is the time when the code checks to see what the next bit will be and sets both the TOP and the LEVEL for the next bit.
When using a normal sawtooth pattern, it seems that the waveform generated follows the bit pattern. But when I set it to generate a phase correct output, I don't think the TOP and LEVEL are being set in time before the next output is being delivered. The result is that extra '1's or '0's are being produced.
The code is below. If I am doing anything wrong, would sure like to know it.
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// Generate a PWM on MAINGPIO.
// Setup an IRQ that interrupts every time TOP has been reached.
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include <hardware/clocks.h>
#include <hardware/gpio.h>
#include <hardware/timer.h>
const uint MILLION = 1000000;
const uint MAINGPIO = 22;
uint TOP;
uint LEVEL;
const uint ONEBIT = 16384;
const uint ZEROBIT = 32767;
const uint ONETOP = 32767;
const uint ZEROTOP = 65535;
uint CLKSPD = 0;
uint Main_Slice;
uint Main_Chan;
uint counter;
uint bitpattern;
pwm_config Main_Cfg;
bool ledOnOff = false;
uint cnt = 0;
void on_pwm_wrap()
{
pwm_clear_irq(Main_Slice);
if (bitpattern & (1 << cnt))
{
pwm_set_wrap(Main_Slice, ONETOP);
pwm_set_chan_level(Main_Slice, Main_Chan, ONEBIT);
}
else
{
pwm_set_wrap(Main_Slice, ZEROTOP);
pwm_set_chan_level(Main_Slice, Main_Chan, ZEROBIT);
}
++cnt;
if(cnt >= 32)
cnt = 0;
}
void initTimings()
{
printf("Init Timings\n");
cnt = 0;
// Check if the bit at the given position is 1
if (bitpattern & (1 << cnt))
{
printf("Bit at %d is 1\n", cnt);
TOP = ONETOP;
LEVEL = ONEBIT;
}
else
{
printf("Bit at %d is 0\n", cnt);
TOP = ZEROTOP;
LEVEL = ZEROBIT;
}
++cnt;
}
void initCFG(uint top, uint level, uint clkspeed)
{
printf("Init CFG: Top: %d, Level: %d, Divider: %d\n", top, level, clkspeed);
Main_Slice = pwm_gpio_to_slice_num(MAINGPIO);
Main_Chan = pwm_gpio_to_channel(MAINGPIO);
Main_Cfg = pwm_get_default_config();
//set the clock divider to be CLKSPD
pwm_config_set_clkdiv(&Main_Cfg, clkspeed);
pwm_config_set_wrap(&Main_Cfg, top);
pwm_init(Main_Slice, &Main_Cfg, false);
//Uncomment this line for phase correct generation
//pwm_set_phase_correct(Main_Slice, true);
pwm_set_wrap(Main_Slice, top);
pwm_set_chan_level(Main_Slice, Main_Chan, level);
//Resetting these since the cfg was reset.
gpio_set_function(MAINGPIO, GPIO_FUNC_PWM);
printf("Slice is %d, Channel: %d\n",Main_Slice, Main_Chan);
printf("Setting PWM Phase Slice.csr: %d, Slice.top: %d, Slice.div: %d, Slice.cc: %d\n",
pwm_hw->slice[Main_Slice].csr,
pwm_hw->slice[Main_Slice].top,
pwm_hw->slice[Main_Slice].div,
pwm_hw->slice[Main_Slice].cc);
}
void init_clkdiv()
{
CLKSPD = clock_get_hz(clk_sys) / MILLION; //Should be 133 or 125
printf("Clock Speed is %d\n", CLKSPD);
}
int main() {
stdio_init_all();
uint bitpatterx;
bitpatterx = 0b11001100110011001100110011001100;
//bitpattern = 0b01101110001110001100110010101010;
bitpattern = bitpatterx;
//for (int i = 0; i++; i <= 10)
int i = 0;
while (i < 5)
{
printf("Hello, TestVARPWM!\n");
sleep_ms(1000);
++i;
}
printf("Test IRQ and PWM.\n");
init_clkdiv();
initTimings();
initCFG(TOP, LEVEL, CLKSPD);
// Mask our slice's IRQ output into the PWM block's single interrupt line,
// and register our interrupt handler
pwm_clear_irq(Main_Slice);
pwm_set_irq_enabled(Main_Slice, true);
irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
irq_set_enabled(PWM_IRQ_WRAP, true);
printf("Starting VARPWM\n");
pwm_set_enabled(Main_Slice, true);
while(1)
{
}
}
I need to create a signal of varying PWM output. The signal generated are interpreted as '1's and '0's based on their length. A zero is twice as long as a '1'. To set it up simply, I am running an output nearly as slowly as possible to see what the signal looks like on my oscilloscope.
When producing the PWM using the 'normal' sawtooth waveform, it seems to work. But when producing a waveform using phase correct type of PWM, it is not being created correctly.
Since the output waveform needs to change, there is a bit pattern assigned for the generated waveform. When PWM hits TOP, and starts to roll around to reset, it issues an interrupt which I catch. This is the time when the code checks to see what the next bit will be and sets both the TOP and the LEVEL for the next bit.
When using a normal sawtooth pattern, it seems that the waveform generated follows the bit pattern. But when I set it to generate a phase correct output, I don't think the TOP and LEVEL are being set in time before the next output is being delivered. The result is that extra '1's or '0's are being produced.
The code is below. If I am doing anything wrong, would sure like to know it.
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// Generate a PWM on MAINGPIO.
// Setup an IRQ that interrupts every time TOP has been reached.
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include <hardware/clocks.h>
#include <hardware/gpio.h>
#include <hardware/timer.h>
const uint MILLION = 1000000;
const uint MAINGPIO = 22;
uint TOP;
uint LEVEL;
const uint ONEBIT = 16384;
const uint ZEROBIT = 32767;
const uint ONETOP = 32767;
const uint ZEROTOP = 65535;
uint CLKSPD = 0;
uint Main_Slice;
uint Main_Chan;
uint counter;
uint bitpattern;
pwm_config Main_Cfg;
bool ledOnOff = false;
uint cnt = 0;
void on_pwm_wrap()
{
pwm_clear_irq(Main_Slice);
if (bitpattern & (1 << cnt))
{
pwm_set_wrap(Main_Slice, ONETOP);
pwm_set_chan_level(Main_Slice, Main_Chan, ONEBIT);
}
else
{
pwm_set_wrap(Main_Slice, ZEROTOP);
pwm_set_chan_level(Main_Slice, Main_Chan, ZEROBIT);
}
++cnt;
if(cnt >= 32)
cnt = 0;
}
void initTimings()
{
printf("Init Timings\n");
cnt = 0;
// Check if the bit at the given position is 1
if (bitpattern & (1 << cnt))
{
printf("Bit at %d is 1\n", cnt);
TOP = ONETOP;
LEVEL = ONEBIT;
}
else
{
printf("Bit at %d is 0\n", cnt);
TOP = ZEROTOP;
LEVEL = ZEROBIT;
}
++cnt;
}
void initCFG(uint top, uint level, uint clkspeed)
{
printf("Init CFG: Top: %d, Level: %d, Divider: %d\n", top, level, clkspeed);
Main_Slice = pwm_gpio_to_slice_num(MAINGPIO);
Main_Chan = pwm_gpio_to_channel(MAINGPIO);
Main_Cfg = pwm_get_default_config();
//set the clock divider to be CLKSPD
pwm_config_set_clkdiv(&Main_Cfg, clkspeed);
pwm_config_set_wrap(&Main_Cfg, top);
pwm_init(Main_Slice, &Main_Cfg, false);
//Uncomment this line for phase correct generation
//pwm_set_phase_correct(Main_Slice, true);
pwm_set_wrap(Main_Slice, top);
pwm_set_chan_level(Main_Slice, Main_Chan, level);
//Resetting these since the cfg was reset.
gpio_set_function(MAINGPIO, GPIO_FUNC_PWM);
printf("Slice is %d, Channel: %d\n",Main_Slice, Main_Chan);
printf("Setting PWM Phase Slice.csr: %d, Slice.top: %d, Slice.div: %d, Slice.cc: %d\n",
pwm_hw->slice[Main_Slice].csr,
pwm_hw->slice[Main_Slice].top,
pwm_hw->slice[Main_Slice].div,
pwm_hw->slice[Main_Slice].cc);
}
void init_clkdiv()
{
CLKSPD = clock_get_hz(clk_sys) / MILLION; //Should be 133 or 125
printf("Clock Speed is %d\n", CLKSPD);
}
int main() {
stdio_init_all();
uint bitpatterx;
bitpatterx = 0b11001100110011001100110011001100;
//bitpattern = 0b01101110001110001100110010101010;
bitpattern = bitpatterx;
//for (int i = 0; i++; i <= 10)
int i = 0;
while (i < 5)
{
printf("Hello, TestVARPWM!\n");
sleep_ms(1000);
++i;
}
printf("Test IRQ and PWM.\n");
init_clkdiv();
initTimings();
initCFG(TOP, LEVEL, CLKSPD);
// Mask our slice's IRQ output into the PWM block's single interrupt line,
// and register our interrupt handler
pwm_clear_irq(Main_Slice);
pwm_set_irq_enabled(Main_Slice, true);
irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
irq_set_enabled(PWM_IRQ_WRAP, true);
printf("Starting VARPWM\n");
pwm_set_enabled(Main_Slice, true);
while(1)
{
}
}
Statistics: Posted by tcroswell — Mon Nov 11, 2024 7:07 pm