Saturday, November 13, 2021

MCP4922 Library for Atmel 328 MCU--Simple Low Cost Dual DAC Made Easy

Quick one this time.  I'm continuing coding in Embedded C for Atmel 328 MCU's, this time to write a driver for an inexpensive and useful Digital to Analog Converter--a DAC--The MCP4922 from Microchip. 





Datasheet is here--the 4922 is inexpensive, available in a DIP package for easy breadboarding, and  easy to program. This is a good chip for Audio DiWHY.

Let's get to it.

For my tests I wired it up like this: 

Wired this up on the bench:





The toolchain is my usual: Windows 10 running Atmel Studio 7; an Atmel ICE programmer, and a minimalist 328 development board of my own design that I got from my generous sponsor--PCBWAY;  read about the board here and order or get the gerber here.

The library didn't work at first--that's because I made a stupid mistake. The datasheet clearly says to make this chip work with SPI you do a CS pulldown, send 16 bits on each clock, then do a CS pullup. 

But I missed that while reviewing the datasheet, and the SPI3.h library I wrote/borrowed/revised  (post here) didn't have this exact cadence. I literally woke up in the middle of the night realizing what I had done wrong, and it took some will power not to go to the bench at 3AM to fix it. Next morning I wrote this addition to the preexisting Atmel 328 SPI3.h and SPI3.c libraries:

uint8_t SPI_TransferTx16_SingleCS(unsigned char a, unsigned char b)
 {
unsigned char x;
SELECT();
x = SPI_Transfer(a);
x = SPI_Transfer(b);
DESELECT();
return x;

 }

where a is MSB and b is LSB.

Fixed!

 Now I can see both analog outs on my scope:



And can also spy on the SPI using Pulseview--looks right:


Using the MCP4922 library.  The files you need are MCP4922.c and MCP4922.h. The rest of what I githubbed are simple examples and support files.

As of the writing of this blog post, here's a really quick rundown of the functions in the library:

/************/

/* output_4922  send data to outputs of chip.  

In my example, 0 = 0V, 4095 = about 5V.

  data is 0 to 4095; channel is 0 for channel A, any other uint8_t for Chan B 

 default chan A, unbuffered ref, gain 1, no shutdown */

 output_4922(uint16_t data, uint8_t channel) 

/* the 4922 has a buffer for the reference signal.  In case you want to use a reference that has a very low source current.

enter 0 to shut off the ref buffer; any other number < 255 to turn it on.

*/

 buffer_4922(uint8_t buff)

/* gain_4922--for highly accurate output, you can use a 2.048V reference.

enter damnloud = 1 for gain = 1, enter 2, any other number < 255 that's not = 1, for gain = 2 

Apply your 2.048 ref, then turn the gain up 2x. Now you should get, 0 = 0mV at output, 100 = 100mV a output, 1022 = 1022mV at output. 

Should be damn accurate! Very cool  */


/* 

enable_4922_output

enter 0 for silence to kill the 4922 analog outs and put the chip into low power mode

any other uint8_t to make it operate normally.  */

enable_4922_output(uint8_t silence)


/************/

That's about all the chip can do, so that might be all I code for this, at least for now.

You get my embedded C library for MCP4922, along with the example code for making the traces move at my github, here. The updated SPI library is here. I'll be using it in future posts. 

Until then, don't breathe the fumes!


No comments:

Post a Comment

EFM/KORG770 VCF--BUILD: yes, SOUND: no

Ahoy!  This time, I wanted to refine the quick prototyping idea discussed midway through this previous post : Minimal breadboarding--I hate ...