ECE2049: Embedded Computing in Engineering Design C Term Spring 2018 Lecture #20: Using SPI The DAC Reading for Today: Users Guide Ch 35, MCP4921, data sheet, on-line articles Reading for Next Class: Users Guide 1.4, Davies 6.6-10 HW #5 (on web): Lab #4 (on web): Due Tuesday 2/27 (in class) Sign-off & code (on-line) submit Fri 3/2 (in class, 9 am) Exam #3 THURSDAY 3/1/2018 at 9 a.m. in AK-216 Last Class: >> Configuring USCI_B0 for SPI mode data format, clock selection, etc >> Implement CS and other required IO to the SPI device => digital I/O! >> Writing serial data to Sharp LCD Serial Peripheral Interface Bus (SPI) >> Used primarily for synchronous serial comms between a CPU and peripherals within the box = off-chip but on same PCB --> Synchronous = shared clock (supplied by master) = Transfer speeds ~Mbps MSP430 (master) SIMO SOMI SCLK some SPI device 1 (slave) some SPI device n (slave) GND CS 1 CS 1 CS n >> SPI usually (almost always) sends/receives data Most Significant Bit (MSB) first!! >> User must implement CS logic for each device Only 1 device and Master can use SPI bus at a time
Overview of MCP4921 SPI Digital-to-Analog Convert (DAC) >> Converts integer value (code) to a voltage >> MCP4921 is 12-bit DAC that outputs voltages rail-to-rail (0 to Vcc) >> To use SPI to run the DAC the programmer must... 1) Enable UCSI B0 for SPI mode 2) Select data format 3) Setup synchronous clock >> Wait. Isn't this what we did for the LCD screen? Can't we just use those SPI settings? YES! That is the whole behind SPI The DAC is connected to USCI B0 SCLK and data lines, exactly like LCD Since Sharp96x96_Init() is called at the beginning of configdisplay() which is called at the beginning of our demo project these is nothing more to do to setup USCI B0 registers SPI for the DAC Then programmer must... --> They are already set! 4) Select peripheral (CS) >> DAC actually uses 2 pins to enable serial data transfer -- first CS then LDAC CS => P8.2 LDAC => P3.7 Both of these are Active LOW
// From demo project peripherals.h /* * DAC pin assignment is as follows * LDAC P3.7 * CS P8.2 * MOSI/SDI P3.0 * SCLK P3.2 */ #define DAC_PORT_LDAC_SEL #define DAC_PORT_LDAC_DIR #define DAC_PORT_LDAC_OUT #define DAC_PORT_CS_SEL #define DAC_PORT_CS_DIR #define DAC_PORT_CS_OUT #define DAC_PIN_CS #define DAC_PIN_LDAC // etc... P3SEL P3DIR P3OUT P8OUT P8DIR P8OUT BIT2 BIT7 /** ********************************************** * Initialize the DAC and its associated SPI bus, * using parameters defined in peripherals.h ************************************************/ void DACInit(void) { // Configure LDAC and CS for digital IO outputs DAC_PORT_LDAC_SEL &= ~DAC_PIN_LDAC; DAC_PORT_LDAC_DIR = DAC_PIN_LDAC; DAC_PORT_LDAC_OUT = DAC_PIN_LDAC; // Deassert LDAC DAC_PORT_CS_SEL DAC_PORT_CS_DIR DAC_PORT_CS_OUT &= ~DAC_PIN_CS; = DAC_PIN_CS; = DAC_PIN_CS; // Deassert CS 5) Send or Receive data which? -- What do we want to send? --> then Poll to see if transfer complete
void DACSetValue(unsigned int dac_code) { // Start the SPI transmission by asserting CS (active low) // This assumes DACInit() already called DAC_PORT_CS_OUT &= ~DAC_PIN_CS; // Write in DAC configuration bits. From DAC data sheet // 3h=0011 to highest nibble. // 0=DACA, 0=buffered, 1=Gain=1, 1=Out Enbl dac_code = 0x3000; // Add control bits to DAC word uint8_t lo_byte = (unsigned char)(dac_code & 0x00FF); uint8_t hi_byte = (unsigned char)((dac_code & 0xFF00) >> 8); // First, send the high byte DAC_SPI_REG_TXBUF = hi_byte; // Wait for the SPI peripheral to finish transmitting while(!(dac_spi_reg_ifg & UCTXIFG)) { _no_operation(); // Then send the low byte DAC_SPI_REG_TXBUF = lo_byte; // Wait for the SPI peripheral to finish transmitting while(!(dac_spi_reg_ifg & UCTXIFG)) { _no_operation(); // We are done transmitting, so de-assert CS (set = 1) DAC_PORT_CS_OUT = DAC_PIN_CS; // This DAC is designed such that the code we send does not // take effect on the output until we toggle the LDAC pin. // This is because the DAC has multiple outputs. This design // enables a user to send voltage codes to each output and // have them all take effect at the same time. DAC_PORT_LDAC_OUT &= ~DAC_PIN_LDAC; // Assert LDAC delay_cycles(10); // small delay DAC_PORT_LDAC_OUT = DAC_PIN_LDAC; // De-assert LDAC
Now, how do we do useful stuff with the DAC? Ex: Create DC voltage 1.2 V assuming DAC V ref = 3.3V. Assuming DAC V ref = 5V. Ex: Create a 100 Hz square wave (0 to 1.2 V peak)
Ex: How would you create a saw-tooth wave?