/* Control the mcp 4921/4922 DACs with hardware SPI of the Arduino UNO * ...without all the overhead of the Arduino SPI lib... * Just the needed functions in a runtime optimized way by "Theremingenieur" Thierry Frenkel * This file is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ #ifndef SPImcpDac_h #define SPImcpDac_h #include // Data direction & Port register & Bit number for DAC Latch: #define MCP_DAC_LDAC_DDR DDRD #define MCP_DAC_LDAC_PORT PORTD #define MCP_DAC_LDAC_BIT 7 // Data direction & Port register & Bit number for DAC CS #define MCP_DAC_CS_DDR DDRB #define MCP_DAC_CS_PORT PORTB #define MCP_DAC_CS_BIT 2 // Data direction & Port register & Bit number for DAC2 CS #define MCP_DAC2_CS_DDR DDRB #define MCP_DAC2_CS_PORT PORTB #define MCP_DAC2_CS_BIT 1 // Data direction & Port registers & Bit numbers for Hardware SPI #define HW_SPI_DDR DDRB #define HW_SPI_SCK_BIT 5 #define HW_SPI_MISO_BIT 4 // unused in this configuration #define HW_SPI_MOSI_BIT 3 static inline void SPImcpDACinit() { // initialize the latch pin: MCP_DAC_LDAC_DDR |= _BV(MCP_DAC_LDAC_BIT); MCP_DAC_LDAC_PORT |= _BV(MCP_DAC_LDAC_BIT); // initialize the CS pins: MCP_DAC_CS_DDR |= _BV(MCP_DAC_CS_BIT); MCP_DAC_CS_PORT |= _BV(MCP_DAC_CS_BIT); MCP_DAC2_CS_DDR |= _BV(MCP_DAC2_CS_BIT); MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT); // initialize the hardware SPI pins: HW_SPI_DDR |= _BV(HW_SPI_SCK_BIT); HW_SPI_DDR |= _BV(HW_SPI_MOSI_BIT); // initialize the hardware SPI registers SPCR = _BV(SPE) | _BV(MSTR); // no interrupt, SPI enable, MSB first, SPI master, SPI mode 0, clock = f_osc/4 (maximum) SPSR = _BV(SPI2X); // double the SPI clock, ideally we get 8 MHz, so that a 16bit word goes out in 3.5us (5.6us when called from an interrupt) including CS asserting/deasserting } static inline void SPImcpDACtransmit(uint16_t data) { // Send highbyte and wait for complete SPDR = highByte(data); asm("nop"); while (!(SPSR & _BV(SPIF))) ; // Send lowbyte and wait for complete SPDR = lowByte(data); asm("nop"); while (!(SPSR & _BV(SPIF))) ; } static inline void SPImcpDAClatch() { MCP_DAC_LDAC_PORT &= ~_BV(MCP_DAC_LDAC_BIT); MCP_DAC_LDAC_PORT |= _BV(MCP_DAC_LDAC_BIT); } static inline void SPImcpDACsend(uint16_t data) { MCP_DAC_CS_PORT &= ~_BV(MCP_DAC_CS_BIT); // Sanitize input data and add DAC config MSBs data &= 0x0FFF; data |= 0x7000; SPImcpDACtransmit(data); MCP_DAC_CS_PORT |= _BV(MCP_DAC_CS_BIT); // Do not latch immpediately, let's do it at the very beginning of the next interrupt to get consistent timing } static inline void SPImcpDAC2Asend(uint16_t data) { MCP_DAC2_CS_PORT &= ~_BV(MCP_DAC2_CS_BIT); // Sanitize input data and add DAC config MSBs data &= 0x0FFF; data |= 0x7000; SPImcpDACtransmit(data); MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT); SPImcpDAClatch(); } static inline void SPImcpDAC2Bsend(uint16_t data) { MCP_DAC2_CS_PORT &= ~_BV(MCP_DAC2_CS_BIT); // Sanitize input data and add DAC config MSBs data &= 0x0FFF; data |= 0xF000; SPImcpDACtransmit(data); MCP_DAC2_CS_PORT |= _BV(MCP_DAC2_CS_BIT); SPImcpDAClatch(); } #endif