CVpal DAC code

Hey people,
can somebody explain me this piece of code

enum UsiFlags {
    TICK = _BV(USIWM0) | _BV(USITC),
    TOCK = _BV(USIWM0) | _BV(USITC) | _BV(USICLK)
  };

  static inline void SpiSend(uint16_t word) {
    USIDR = word >> 8;

    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;

    USIDR = word & 0xff;

    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
    USICR = TICK;
    USICR = TOCK;
  }

found on cvpals github home

I cannot understand why these assignments are happening, as I don’t see the USICR variable being returned or assigned to anything else.

Thank you!

I might be wrong but USI stands for Universal Serial Interface, CR likely for Control Register, and DR likely for Data Register. You’re writing directly into the MCU’s registers to control its USI hardware. To understand what exactly is going on you could check what bits are set or cleared in above code and compare with the register info in the MCU’s datasheet.

Oh and another thing I am noticing, you can see you have a 16-bit integer “word”. Looks like the CVPal is using SPI to talk to the DAC? SPI data is usually sent one byte at a time.

That’s why you’re shifting the 8 higher bits of ‘word’ into the USIDR first, then do (I count) eight TICK-TOCKs which might just be a low-level way of sending 8 clock pulses (and one byte does have 8 bits…)

Next, you’re writing the lower 8 bits into the USIDR register and do another eight TICK-TOCKs… another byte sent…

So this all might just be a low-level way of sending a 16-bit ‘word’ via SPI through the MCU’s USI peripheral. (Don’t know)

The strange thing I’m seeing is that the code does not shift something into USICR. It sets the USICR but USICR doesnt do anything.

It is the code that sends data to the dac via spi indeed. :slight_smile:

I assume it is just code to pass some (a specific fixed) amount of time (cpu cycles)
Cheers

These are not variables but memory mapped registers of the MCU - writing to these registers control hardware blocks of the MCU - in this particular case a hardware block called the USI (universal serial interface).

Please read the datasheet!

Starting from page 105, especially pages 112 to 114.

The USI is a low-level hardware block that can be used for serial data transfer (SPI or I2C). Contrary to the SPI or I2C devices found on more expensive MCUs that are “set and forget” (you write your data in a register and then the MCU shifts it in the background), you still have to do a lot of stuff manually in software. The USI can only do basic things like shifting bits in the data register, toggling clock pins, and setting I/O ports according to a bit of the data register or the state of the clock.

The USIDR register contains the byte to be output.

Then you manually control the USICR register to toggle the clock pin (TICK), then toggle the clock pin back and shift a data bit (TOCK).

So what the code really does is:

Write a byte in the data register
Toggle the clock
Toggle back the clock, output a bit of the data register, shift the data register

8 times
Write a second byte in the data register
Toggle the clock
Toggle back the clock, output a bit of the data register, shift the data register

8 times

A lot of tedious work, but still less work than a software implementation of SPI!

Nope!

1 Like

EDIT: ignore as Emilie just replied simultaneously and clarified things

I’m sure USICR has a purpose in this universe. Check page 111 of the attiny data sheet. The code looks awfully familiar to me.

Also check page 116, setting certain bits of USICR indeed makes it do stuff like pushing a counter forward and changing data transfer direction, something like that?

WOW, now I get it!
Thank you @pichenettes.

So it is a thing applicable to attiny but in atmega328 you already have a Master/slave SPI serial interface and there is no need to do such things, right? (I’m trying to build the s&h arduino things we were discussing in another thread).

Yes, you just configure the SPI device, write a byte to the data register and it’ll be shifted out in the background.

To write a word, you have two strategies:

  1. write a byte, wait for the transmission bit to be cleared, write the second byte (as done on the Anushri).
  2. write a byte, do some other stuff that takes more clock cycles than shifting the first byte (blinking some status LEDs or doing some arithmetic to figure out the next byte to write), write the second byte. This is how things are done on the Shruthi-1 DSP board.

Note that on the Atmega328p, the USART can also be configured for SPI transmission. The advantage is that it has a two-byte buffer! So to write two bytes, you write the first byte in the data register, it’s immediately copied to the transmission buffer and transmission starts, then you can write immediately a second byte in the data register that will be sent later. This is how I do things on the Ambika voicecards.

2 Likes