0
\$\begingroup\$

Let the problem be to exactly generate n clock pulses at half the clock frequency of the Atmel Atmega328p (datasheet here).

To generate a PWM at a prescaled clock frequency of the Atmel Amega328p, we need to properly set up the registers of the 8-bit timer and the counting sequence. See the image below, where the PWM at pin OCnA/OCnB is generated using the time counter TCNTn and a comparator against the register OCRnA/OCRnB.

Waveform generation based on the microcontroller's clock frequency with prescaler.

Next, assume that we need to generate a very precise number of clock pulses to communicate with a peripheral. For instance, in the image below, we need to generate exactly 22 pulses to send to and acquire data from the chip. The 5 first bits are the command sent to the chip, and the 16 last bits are the data received from the chip.

Generation of 22 pulses to communicate with a chip.

In principle, if the message size is a multiple of 8 bits, we can directly use the SPI hardware/interface to communicate with the chip. The SPI module is directly wired in hardware using an 8-bit shift register (see image below).

SPI description using byte-sized data.

However, in our case, 22 is not a multiple of 8, so we should set things up manually. My idea was to generate 22 clock pulses using interrupt service routines (ISR), but I realized that I was not able to stop the timer instantaneously because the instruction takes several clock cycles from the CPU to reach out to the waveform generator. Recall that our PWM works at half the speed of the microcontroller, so any instruction that is longer than a clock pulse will result in a noticeable delay. In fact, I've noticed that there were always more than 22 pulses on the oscilloscope using this method.

Is it correct to say that it is just impossible to generate exactly 22 clock pulses at half the frequency of the microcontroller using the CPU (neither in C nor Assembler)? I think it's due to the fact that stopping the clock would take several clock cycles from the CPU (need to reach out to the correct register to stop the clock). Is it correct to say that generating 22 clock pulses at half of the microcontroller clock speed can only be done in specialized hardware?

The only solution I could think of in software is to bit bang the clock manually for 5 pulses (way slower than the microcontroller's clock frequency) then use the SPI hardware to read the 2-byte data. Do you have any idea if there is a better way to do so at the microcontroller's clock speed?

For your reference, I paste my Arduino code here to show you that we cannot generate exactly 22 clock pulses.

#include <avr/interrupt.h>

volatile unsigned long count = 0;

ISR(TIMER1_COMPA_vect)
{
  count++;
  if (count == 21)
  {
    TCCR1B = 0x00; // Stop clock.
  }
}

void setup() { 
  Serial.begin(115200);

  cli(); // Disable global interrupts.

  TCCR1A = 0x00; // Initialize TCCR1A register to 0.
  TCCR1B = 0x00; // Initialize TTCR1B register to 0.

  TCNT1 = 0; // Initialize time counter to 0.
  OCR1A = 1; // Duty cycle 50%.
  TCCR1A = bit(COM1A0); // Set OC1A on compare match.
  TCCR1B |= (1 << WGM12); // CTC mode.
  TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt.
  
  pinMode(9, OUTPUT);

  sei(); // Enable all interrupts.

  Serial.println("Wait 1s..."); 
  delay(1000);
  Serial.println("Start PWM"); 
  TCCR1B |= (1 << CS10); // No prescaler.
}

void loop() {
  Serial.println(count);
  delay(200);
}
\$\endgroup\$
9
  • \$\begingroup\$ What do you mean by "at the microcontroller's clock speed"? I'm not aware of any possibility to generate signals at the same speed as the CPU clock in an Atmega328. \$\endgroup\$
    – asdfex
    Oct 8, 2022 at 12:57
  • \$\begingroup\$ Thanks for your comment. Yes you are right, it is half the speed of the CPU clock. I've corrected it in my post. \$\endgroup\$
    – chckx592
    Oct 8, 2022 at 13:10
  • \$\begingroup\$ These are two different problems: "exactly generate n clock pulses at half the clock frequency" [implied: and nothing else], and "...to communicate with a peripheral". Which one is it? \$\endgroup\$ Oct 8, 2022 at 13:36
  • \$\begingroup\$ In fact, the end purpose is to communicate with a peripheral. I know that there is no lower bound for the clock signal, so we can use a bit-bang strategy and that's it. But I wanted to know if it's possible to sync the data transmission with 1/2 or 1/4 the speed of the internal clock speed. Also from a theoretical perspective, is it possible to generate exactly those n clock pulses? When trying to use ISR and stop the timer, it takes some instruction cycles and I have more pulses than wanted. Just to know if it's technically possible to do so in software. \$\endgroup\$
    – chckx592
    Oct 8, 2022 at 13:38
  • \$\begingroup\$ @TimWilliams Also, I tried to see how the SPI library works in Arduino. Here is the link of the source code: github.com/arduino/ArduinoCore-avr/blob/master/libraries/SPI/… . As seen, the clock generated by the SPI library can go up to half the speed of the CPU clock, but everything is handled in hardware using the 8-bit shift register. I was just wondering if it's possible to do the same in software using assembler using simple ISR. But do I understand well that it's just impossible because just stopping the timer takes, say, 5 clock cycles? \$\endgroup\$
    – chckx592
    Oct 8, 2022 at 13:44

1 Answer 1

0
\$\begingroup\$

A simpler example: The clock is the fastest signal per def on your system, so if you're piping out bits from a shift register (22 pulses is 0101... aka 0xA repeated 11 times followed by zeroes) Each bit shift will happen at the rising edge of the clock. Because one pulse is the 0-1 part, the pulses will be at exactly HALF the clock frequency. QED.

Besides, signals happening at the same frequency as the clock in a digital system is super problematic, as logic stops to work with hold/setup times violated.

You could also think of the Nyquist-Shannon sampling theorem, from a theory perspective. You always need your system to clock at least twice as fast as your fastest changing external signal.

Now this does not even begin to take into account the issues of peripherials and synchronizing digital signals, which might introduce significant latency into the system.

In other words, no.

\$\endgroup\$
2
  • \$\begingroup\$ I'm sorry I made a mistake in my statement. It should be half the speed of the CPU clock. I've corrected it in my original post. \$\endgroup\$
    – chckx592
    Oct 8, 2022 at 13:12
  • 1
    \$\begingroup\$ That would be 0xA repeated 5 and a half times, not 11 times. \$\endgroup\$
    – Hearth
    Oct 8, 2022 at 14:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.