You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OpenTheremin_V3_with_MIDI/Open_Theremin_V3/SPImcpDAC.h

105 lines
3.2 KiB

/* 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 <Arduino.h>
// 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