spi_master.c (5496B)
1 /* Copyright 2020 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <https://www.gnu.org/licenses/>. 15 */ 16 17 #include "spi_master.h" 18 19 #include "timer.h" 20 21 #if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) 22 # define SPI_SCK_PIN B1 23 # define SPI_MOSI_PIN B2 24 # define SPI_MISO_PIN B3 25 #elif defined(__AVR_ATmega32A__) 26 # define SPI_SCK_PIN B7 27 # define SPI_MOSI_PIN B5 28 # define SPI_MISO_PIN B6 29 #elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) 30 # define SPI_SCK_PIN B5 31 # define SPI_MOSI_PIN B3 32 # define SPI_MISO_PIN B4 33 #endif 34 35 #ifndef SPI_TIMEOUT 36 # define SPI_TIMEOUT 100 37 #endif 38 39 static pin_t current_slave_pin = NO_PIN; 40 static bool current_cs_active_low = true; 41 static uint8_t current_slave_config = 0; 42 static bool current_slave_2x = false; 43 44 static inline void spi_select(void) { 45 gpio_write_pin(current_slave_pin, current_cs_active_low ? 0 : 1); 46 } 47 48 static inline void spi_unselect(void) { 49 gpio_write_pin(current_slave_pin, current_cs_active_low ? 1 : 0); 50 } 51 52 void spi_init(void) { 53 gpio_write_pin_high(SPI_SS_PIN); 54 gpio_set_pin_output(SPI_SCK_PIN); 55 gpio_set_pin_output(SPI_MOSI_PIN); 56 gpio_set_pin_input(SPI_MISO_PIN); 57 58 SPCR = (_BV(SPE) | _BV(MSTR)); 59 } 60 61 bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) { 62 spi_start_config_t start_config = {0}; 63 start_config.slave_pin = slavePin; 64 start_config.lsb_first = lsbFirst; 65 start_config.mode = mode; 66 start_config.divisor = divisor; 67 start_config.cs_active_low = true; 68 return spi_start_extended(&start_config); 69 } 70 71 bool spi_start_extended(spi_start_config_t *start_config) { 72 if (current_slave_pin != NO_PIN || start_config->slave_pin == NO_PIN) { 73 return false; 74 } 75 76 current_slave_config = 0; 77 78 if (start_config->lsb_first) { 79 current_slave_config |= _BV(DORD); 80 } 81 82 switch (start_config->mode) { 83 case 1: 84 current_slave_config |= _BV(CPHA); 85 break; 86 case 2: 87 current_slave_config |= _BV(CPOL); 88 break; 89 case 3: 90 current_slave_config |= (_BV(CPOL) | _BV(CPHA)); 91 break; 92 } 93 94 uint16_t roundedDivisor = 1; 95 while (roundedDivisor < start_config->divisor) { 96 roundedDivisor <<= 1; 97 } 98 99 switch (roundedDivisor) { 100 case 16: 101 current_slave_config |= _BV(SPR0); 102 break; 103 case 64: 104 current_slave_config |= _BV(SPR1); 105 break; 106 case 128: 107 current_slave_config |= (_BV(SPR1) | _BV(SPR0)); 108 break; 109 case 2: 110 current_slave_2x = true; 111 break; 112 case 8: 113 current_slave_2x = true; 114 current_slave_config |= _BV(SPR0); 115 break; 116 case 32: 117 current_slave_2x = true; 118 current_slave_config |= _BV(SPR1); 119 break; 120 } 121 122 SPCR |= current_slave_config; 123 if (current_slave_2x) { 124 SPSR |= _BV(SPI2X); 125 } 126 current_slave_pin = start_config->slave_pin; 127 current_cs_active_low = start_config->cs_active_low; 128 gpio_set_pin_output(current_slave_pin); 129 spi_select(); 130 131 return true; 132 } 133 134 spi_status_t spi_write(uint8_t data) { 135 SPDR = data; 136 137 uint16_t timeout_timer = timer_read(); 138 while (!(SPSR & _BV(SPIF))) { 139 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) { 140 return SPI_STATUS_TIMEOUT; 141 } 142 } 143 144 return SPDR; 145 } 146 147 spi_status_t spi_read() { 148 SPDR = 0x00; // Dummy 149 150 uint16_t timeout_timer = timer_read(); 151 while (!(SPSR & _BV(SPIF))) { 152 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) { 153 return SPI_STATUS_TIMEOUT; 154 } 155 } 156 157 return SPDR; 158 } 159 160 spi_status_t spi_transmit(const uint8_t *data, uint16_t length) { 161 spi_status_t status; 162 163 for (uint16_t i = 0; i < length; i++) { 164 status = spi_write(data[i]); 165 166 if (status < 0) { 167 return status; 168 } 169 } 170 171 return SPI_STATUS_SUCCESS; 172 } 173 174 spi_status_t spi_receive(uint8_t *data, uint16_t length) { 175 spi_status_t status; 176 177 for (uint16_t i = 0; i < length; i++) { 178 status = spi_read(); 179 180 if (status >= 0) { 181 data[i] = status; 182 } else { 183 return status; 184 } 185 } 186 187 return SPI_STATUS_SUCCESS; 188 } 189 190 void spi_stop(void) { 191 if (current_slave_pin != NO_PIN) { 192 gpio_set_pin_output(current_slave_pin); 193 spi_unselect(); 194 current_slave_pin = NO_PIN; 195 SPSR &= ~(_BV(SPI2X)); 196 SPCR &= ~(current_slave_config); 197 current_slave_config = 0; 198 current_slave_2x = false; 199 } 200 }