0

I have an Arduino UNO which is connected to an KLZ25 board, the Arduino sends an 1MHz Clock at Pin 9 and now I should write the PDM-Data with 1 Mhz at pin 8. But i don't know how to write with such a speed, because if I write in the Loop:

loop{
digitalWrite(1); 
digitalWrite(0);
}

I get only a frequency of 36Khz, I know it depends on the count of commands in the loop, but I don't know how to write with a certain frequency in my case 1Mhz.

I get the PDM-Data (pulse density modulation) in form of 1 and 0 from a python script on a Host-PC and now I want to stream the Data with 1MHz to an other developer board which makes further calculation with the PDM-Data. I store the PDM-Data in queues because I have not enough memory to store the whole Data.

Does anybody know how to write PDM-Data with a 1MHz frequency to an output pin ?

8
  • What does the acronym PDM mean? It would help to state it in your question.
    – MichaelT
    Commented Jun 3, 2019 at 14:20
  • Use one of the timers on the UNO. Those are the ones that are used for PWM. Set the prescaler to /8. Set the mode to fast-pwm. Set the output pin to no-inverting mode. Set OCxA/OCxB to 2 (since the timer runs at 2Mhz). Then change the TOP value based on the PDM value you want to send.
    – Gerben
    Commented Jun 3, 2019 at 14:21
  • PDM means pulse density modulation, basically I write 1 or 0 with 1Mhz frequency. @Gerben What do you mean by TOP value ? Does the change of the timer changes also the loop speed because I don't get how to write in 1Mhz if the loop is slower.
    – FoldFence
    Commented Jun 3, 2019 at 15:02
  • DigitalWrite itself is super slow. It takes a lot of cycles. loop itself also takes a few cycle to get called. You could use direct port manipulation instead, which is a lot faster. But to run at 1Mhz, you only have 16 cycles to toggle the pin and run the loop, leaving you with nearly no time left to do anything else. Also other interrupts could occur and slow your loop at "random" intervals, giving you a unstable 1Mhz signal. That's why I'm suggesting using timers, as they run in parallel.
    – Gerben
    Commented Jun 3, 2019 at 15:14
  • The timer is for example used to PWM an LED. This timer runs parallel to your code. The timer has a counter that it increments every clock cycle. You can select a TOP value, so that, when it reaches this value, it reset the counter to zero. It also has a compare registers. You can instruct the timer that, once the counter reaches the value in the compare register it should set one of the output pins LOW. You can also instruct it to set the output pin HIGH when it's set back to zero. So by combining this the output will be HIGH for 1µs, followed by a number of µs LOW, depending on value of TOP.
    – Gerben
    Commented Jun 3, 2019 at 15:27

1 Answer 1

2

If I understand correctly, you want to output a synchronous serial bit stream at 1 MHz. Something like this:

        ___     ___     ___
clk  __/   \___/   \___/   \___⋅⋅⋅
     ______ _______ _______ ___
dat  __D0__X___D1__X___D2__X___⋅⋅⋅

where the data bits come from the PC.

If you want this to be a continuous bit stream, it will not be easy to generate on an Arduino Uno.

Receiving the data

Since the data comes from the PC, it comes presumably through the serial link. You have to account for the fact that this link adds a start bit and a stop bit to every byte. Thus, you have 10 bits on the wire for every 8 bits of actual data. Since you have to sustain 1 Mb/s average of actual data, this means that you must configure the serial port for at least 1.25 Mb/s. However, the only baud rate higher that 1 Mb/s supported by the Arduino Uno is 2 Mb/s.

Does your PC support this baud rate? Is the communication reliable at this speed?

If the speed is too high, I would suggest you try to have the PC send the data as PCM at a lower speed, and have the Arduino handle the PCM → PDM conversion.

Transmitting the data

This data format is very similar to an SPI transmission. Then, the SPI hardware port seems like the easiest way to generate it. However, the SPI transmitter on the Uno is not double-buffered. This means that you have to wait for the current byte transmission to be finished before providing the next byte to the SPI hardware. This in turn means that you will inevitably have small delays between consecutive bytes, and one out of every 8 clock cycles will be stretched beyond the expected 1 µs. If this unsteadiness is acceptable for your application, then do use the SPI transmitter. If not, you will have to find another solution.

The serial port on the Uno has a special mode of operation where it behaves like a master SPI port. Unlike the actual SPI port, this port is double-buffered, which makes it possible to send a continuous bit stream with a steady clock. The problem is, you cannot use this mode for transmission and at the same time use the serial port receiver in its regular asynchronous mode. Thus you will not be able to receive the data from the PC when using this mode. If you can manage to route the PC → Arduino communication through SPI, this mode of operation of the serial port may be a good choice.

If none of the options above works for you I suggest, as last resort, to generate the bitstream entirely by software. The timings are tight, as each level of the clock has to be held for only 8 CPU cycles. But it is likely doable in hand-written assembly, provided that interrupts are disabled. Here is roughly how I may try to do that:

    in output, _SFR_IO_ADDR(PORT_OUT)   ; save a copy of the port reg.

loop:
    lds data, _SFR_MEM_ADDR(UDR0)       ; load data from USART

    ; output bit 0
    andi output, ~_BV(1)                ; clear clock bit
    bst data, 0                         ; save data[0]
    bld output, 0                       ; load it into output[0]
    ; no delay needed here
    out _SFR_IO_ADDR(PORT_OUT), output  ; output
    ori output, _BV(1)                  ; set the clock bit
    delay 6                             ; assumed to be a macro
    out _SFR_IO_ADDR(PORT_OUT), output  ; output

    ; output bit 1
    andi output, ~_BV(1)                ; clear clock bit
    bst data, 1                         ; save data[1]
    bld output, 0                       ; load it into output[0]
    delay 4
    out _SFR_IO_ADDR(PORT_OUT), output  ; output
    ori output, _BV(1)                  ; set the clock bit
    delay 6
    out _SFR_IO_ADDR(PORT_OUT), output  ; output

    ; etc...

    rjmp loop

The code above assumes the UART is synchronized with the output stream, which will likely not be the case in reality. Your real application may then be more complex, and you will have to make that extra complexity fit within the delay slots above.

Note that if you handle the PCM → PDM conversion on the Arduino the resulting code may end up being simpler, as you will be able to read the UART at every loop iteration without worrying about synchronization. I have written a program that does something very similar a while ago, and I just pushed it to GitHub for you to take a look: avr-dac: Turn an AVR into a 1-bit D/A converter. Note that this program is meant to be usable on AVRs without a hardware UART. It expects the PCM data to be fed in the fashion of a shift register. Using the UART would make the program simpler.

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.