// Copyright 2011 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // This program 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. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef AVRLIBX_IO_SPI_H_ #define AVRLIBX_IO_SPI_H_ #include #include "avrlibx/avrlibx.h" #include "avrlibx/io/gpio.h" namespace avrlibx { enum SPIPrescaler { SPI_PRESCALER_CLK_2 = 2, SPI_PRESCALER_CLK_4 = 4, SPI_PRESCALER_CLK_8 = 8, SPI_PRESCALER_CLK_16 = 16, SPI_PRESCALER_CLK_32 = 32, SPI_PRESCALER_CLK_64 = 64, SPI_PRESCALER_CLK_128 = 128, }; template struct SPIWrapper { }; #define WRAP_SPI(port) \ template<> \ struct SPIWrapper { \ static inline SPI_t& spi() { \ return SPI ## port; \ } \ static volatile inline uint8_t data() { \ return SPI ## port ## _DATA; \ } \ static inline void set_data(uint16_t value) { \ SPI ## port ## _DATA = value; \ } \ static inline uint8_t readable() { \ return SPI ## port ## _STATUS & SPI_IF_bm; \ } \ static inline uint8_t writable() { \ return SPI ## port ## _STATUS & SPI_IF_bm; \ } \ static inline uint8_t dma_rx_trigger() { \ return DMA_CH_TRIGSRC_SPI ## port ## _gc; \ } \ static inline uint8_t dma_tx_trigger() { \ return DMA_CH_TRIGSRC_SPI ## port ## _gc; \ } \ static inline volatile void* dma_data() { \ return &(SPI ## port ## _DATA); \ } \ }; WRAP_SPI(C) WRAP_SPI(D) #ifdef SPIE WRAP_SPI(E) #endif #ifdef SPIF WRAP_SPI(F) #endif template< typename Port, typename SlaveSelect, DataOrder order = MSB_FIRST, SPIPrescaler speed = SPI_PRESCALER_CLK_4, bool pull_up_miso=false> struct SPIMaster { typedef SPIWrapper SPI; typedef Gpio SS; typedef Gpio MOSI; typedef Gpio MISO; typedef Gpio SCK; static inline void Init() { SCK::set_direction(OUTPUT); MOSI::set_direction(OUTPUT); MISO::set_direction(INPUT); if (pull_up_miso) { PullUpMISO(); } SS::set_direction(OUTPUT); SS::High(); SlaveSelect::set_direction(OUTPUT); SlaveSelect::High(); uint8_t control = SPI_ENABLE_bm | SPI_MASTER_bm; if (order == LSB_FIRST) { control |= SPI_DORD_bm; } if (speed == SPI_PRESCALER_CLK_2) { control |= SPI_CLK2X_bm; } else if (speed == SPI_PRESCALER_CLK_4) { } else if (speed == SPI_PRESCALER_CLK_8) { control |= SPI_CLK2X_bm | 0x1; } else if (speed == SPI_PRESCALER_CLK_16) { control |= 0x1; } else if (speed == SPI_PRESCALER_CLK_32) { control |= SPI_CLK2X_bm | 0x2; } else if (speed == SPI_PRESCALER_CLK_64) { control |= 0x2; } else if (speed == SPI_PRESCALER_CLK_128) { control |= 0x3; } SPI::spi().CTRL = control; } static inline void PullUpMISO() { MISO::set_mode(PORT_MODE_PULL_UP); } static inline void Begin() { SlaveSelect::Low(); } static inline void End() { SlaveSelect::High(); } static inline void Strobe() { SlaveSelect::High(); SlaveSelect::Low(); } static inline void Write(uint8_t v) { Begin(); Send(v); End(); } static inline uint8_t Read() { Begin(); uint8_t result = Receive(); End(); return result; } static inline void Send(uint8_t v) { Overwrite(v); Wait(); } static inline uint8_t Receive() { Send(0xff); return ImmediateRead(); } static inline uint8_t ImmediateRead() { return SPI::data(); } static inline void Wait() { while (!SPI::writable()); } static inline void OptimisticWait() { Wait(); } static inline void Overwrite(uint8_t v) { SPI::set_data(v); } static inline void WriteWord(uint8_t a, uint8_t b) { Begin(); Send(a); Send(b); End(); } static inline uint8_t dma_rx_trigger() { return SPI::dma_rx_trigger(); } static inline uint8_t dma_tx_trigger() { return SPI::dma_rx_trigger(); } static inline volatile void* dma_data() { return SPI::dma_data(); } typedef uint8_t Value; }; template struct SPISlave { typedef SPIWrapper SPI; typedef Gpio SS; typedef Gpio MOSI; typedef Gpio MISO; typedef Gpio SCK; static inline void Init() { SCK::set_direction(INPUT); MOSI::set_direction(INPUT); MISO::set_direction(OUTPUT); SS::set_direction(INPUT); SS::High(); uint8_t control = SPI_ENABLE_bm; if (order == LSB_FIRST) { control |= SPI_DORD_bm; } SPI::spi().CTRL = control; } static inline void Reply(uint8_t value) { SPI::set_data(value); } static inline uint8_t readable() { return SPI::readable(); } static inline uint8_t ImmediateRead() { return SPI::data(); } static inline uint8_t Read() { while (!readable()); return ImmediateRead(); } static inline uint8_t dma_rx_trigger() { return SPI::dma_rx_trigger(); } static inline uint8_t dma_tx_trigger() { return SPI::dma_rx_trigger(); } static inline volatile void* dma_data() { return SPI::dma_data(); } typedef uint8_t Value; }; } // namespace avrlibx #endif // AVRLIBX_IO_SPI_H_