void setup()
{
pinMode(8, OUTPUT);
}
void loop()
{
while (true)
{
digitalWrite(8, HIGH);
digitalWrite(8, LOW);
}
}
This bit of code will allow the generation of a 200 kHz square wave on digital pin 8. The while loop in the loop function may not be obvious as to it's function. If we comment out this while statement line, our output frequency changes from 200 kHz to approximately 144.5 kHz and the waveform is no longer symmetrical 50% duty cycle. This is caused by the routine that calls the loop function doing some checking of the serial port on each iteration. So, to eliminate this overhead, I use my own while loop.
The next most obvious way to improve on this performance is to eliminate the overhead of the digitalWrite call and manipulate the port directly.
void setup()
{
pinMode(8, OUTPUT);
}
void loop()
{
while (true)
{
g_APinDescription[8].pPort -> PIO_SODR = g_APinDescription[8].ulPin;
g_APinDescription[8].pPort -> PIO_CODR = g_APinDescription[8].ulPin;
}
}
With this code I can now obtain approximately 16.9 MHz square wave output on pin 8. The waveform has a bit of overshoot at this frequency. This approach also suffers from the inability to accurately control the frequency.
When looking to see just how fast we can clock the PWM outputs, as noted in my previous post you will find that the highest PWM clock frequency that can be accomodated is 42 MHz. If you look at pmc.c and pwmc.c in hardware/archino/sam/system/libsam/source you will notice that a function "FindClockConfiguration" is used to ensure that the frequency parameter passed is less than the master clock (MCK) frequency of 84 MHz. With integer dividers used as a prescaler, the smallest prescale value of 2 results in a 42 MHz frequency.
So, if you are looking for a way to output the highest frequency possible, use a one bit PWM which will essentially output a square wave with a duty cycle of one. Setting the prescaler to 2 will obtain an 84 MHz waveform.
uint32_t pwmPin = 8;
uint32_t maxDutyCount = 2;
uint32_t clkAFreq = 42000000ul;
uint32_t pwmFreq = 42000000ul;
void setup() {
pmc_enable_periph_clk(PWM_INTERFACE_ID);
PWMC_ConfigureClocks(clkAFreq, 0, VARIANT_MCK);
PIO_Configure(
g_APinDescription[pwmPin].pPort,
g_APinDescription[pwmPin].ulPinType,
g_APinDescription[pwmPin].ulPin,
g_APinDescription[pwmPin].ulPinConfiguration);
uint32_t channel = g_APinDescription[pwmPin].ulPWMChannel;
PWMC_ConfigureChannel(PWM_INTERFACE, channel , pwmFreq, 0, 0);
PWMC_SetPeriod(PWM_INTERFACE, channel, maxDutyCount);
PWMC_EnableChannel(PWM_INTERFACE, channel);
PWMC_SetDutyCycle(PWM_INTERFACE, channel, 1);
pmc_mck_set_prescaler(2);
}
void loop()
{
}
Many thanks to Kerry Wong for providing most of the details above.
No comments:
Post a Comment