add SPI support (using interrupts)
This commit is contained in:
parent
b248b82033
commit
c25693e14c
|
@ -0,0 +1,157 @@
|
|||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the Serial Peripheral Interface (SPI)
|
||||
* it uses the SPI port and can but used with or without interrupt (non-block or blocking)
|
||||
*/
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdlib.h> // General utilities
|
||||
#include <stdbool.h> // Boolean
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
#include <avr/sleep.h> // Power Management and Sleep Modes
|
||||
|
||||
#include <spi.h> // SPI configuration
|
||||
|
||||
volatile uint8_t* spi_b = NULL; // the byte address to transmit/receive
|
||||
volatile size_t spi_i = NULL; // how many remaining bytes to transmit
|
||||
|
||||
/* receive SPI byte and transmit next on previous transmit completion when transfer is interrupt based (non-blocking) */
|
||||
ISR(SPI_STC_vect)
|
||||
{ /* SPI transfer complete interrupt */
|
||||
*spi_b = SPDR; // save received byte
|
||||
spi_i--; // decrement remaining bytes to transmit/receive
|
||||
if (spi_i==0) { // no remaining bytes to transmit/receive
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
} else { // bytes to transmit/receive remain
|
||||
spi_b++; // go to next byte
|
||||
SPDR = *spi_b; // transmit next byte
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize SPI
|
||||
* returns true if initialisation succeeded, else false
|
||||
*/
|
||||
void spi_init(void)
|
||||
{
|
||||
/* configure IO */
|
||||
MCUCR &= ~(1<<PUD); // enable global pull-up
|
||||
SPI_DDR |= (1<<SCK_IO)|(1<<MOSI_IO)|(1<<SS_IO); // set SCK, MOSI, and SS as output
|
||||
SPI_DDR &= ~(1<<MISO_IO); // set MISO as input
|
||||
SPI_PORT |= (1<<MISO_IO); // enable pull-up on MISO
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
/* configure SPI */
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
SPCR &= ~(1<<DORD); // MSB first
|
||||
SPCR &= ~(1<<CPOL); // SCK low on idle
|
||||
SPCR &= ~(1<<CPHA); // sample on leading SCK edge
|
||||
SPCR |= (1<<MSTR); // set as master
|
||||
// use nearset lowest clock divider value
|
||||
if (SCK_DIV>=128) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=64) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=32) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR |= (1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
} else if (SCK_DIV>=16) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=8) {
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR |= (1<<SPR0);
|
||||
} else if (SCK_DIV>=4) {
|
||||
SPSR &= ~(1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
} else { // use 2 divider
|
||||
SPSR |= (1<<SPI2X);
|
||||
SPCR &= ~(1<<SPR1);
|
||||
SPCR &= ~(1<<SPR0);
|
||||
}
|
||||
SPCR |= (1<<SPE); // enable SPI
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* this function returns only when communication finished
|
||||
*/
|
||||
void spi_transfer_blocking(uint8_t* data, size_t length)
|
||||
{
|
||||
if ((data==NULL) || (length==0)) { // verify is there is data to transmit
|
||||
return;
|
||||
}
|
||||
SPCR &= ~(1<<SPIE); // disable SPI interrupt
|
||||
SPI_PORT &= ~(1<<SS_IO); // select slave
|
||||
for (size_t i=0; i<length; i++) {
|
||||
SPDR = data[i]; // send byte
|
||||
while(!(SPSR & (1<<SPIF))); // wait until transmission completed
|
||||
data[i] = SPDR; // read byte
|
||||
}
|
||||
SPI_PORT |= (1<<SS_IO); // de-select slave
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns immediatly, while data is transfered
|
||||
* to ensure the transfer is complete, use spi_transfer_wait()
|
||||
* if data is already being transfered it will idle until the last transfer is completed
|
||||
*/
|
||||
void spi_transfer_nonblocking(uint8_t* data, size_t length)
|
||||
{
|
||||
if ((data==NULL) || (length==0)) { // verify is there is data to transfer
|
||||
return;
|
||||
}
|
||||
spi_transfer_wait(NULL, 0); // wait for transfer to complete
|
||||
spi_b = data; // save data pointer
|
||||
spi_i = length; // save length pointer
|
||||
SPCR |= (1<<SPIE); // enable SPI interrupt
|
||||
SPI_PORT &= ~(1<<SS_IO); // select slave
|
||||
SPDR = *data; // send first byte
|
||||
}
|
||||
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns when all the data is transfered
|
||||
* while the data is transfered it goes into idle mode
|
||||
* if <data> is NULL or <length> is 0 it just ensures the previous transfer is complete
|
||||
*/
|
||||
void spi_transfer_wait(uint8_t* data, size_t length)
|
||||
{
|
||||
// wait until the previous transfer is finished
|
||||
while (spi_i>0) {
|
||||
// go to sleep and wait for next interrupt (SPI will interrupt a one point)
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
if (data != NULL && length!=0) { // verify is there is data to transfer
|
||||
spi_transfer_nonblocking(data, length); // start new transfer
|
||||
spi_transfer_wait(NULL, 0); // wait for transfer to complete
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the Serial Peripheral Interface (SPI) */
|
||||
|
||||
/* SPI pins */
|
||||
#define SPI_PORT PORTB
|
||||
#define SPI_DDR DDRB
|
||||
#define SPI_PIN PINB
|
||||
#define SCK_IO PB5
|
||||
#define MISO_IO PB4
|
||||
#define MOSI_IO PB3
|
||||
#define SS_IO PB2
|
||||
|
||||
/* SCK frequency divider (F_osc/<SCK_DIV>, with <SCK_DIV> within 2-128) */
|
||||
#define SCK_DIV 16
|
||||
|
||||
/* initialize SPI */
|
||||
void spi_init(void);
|
||||
/* receive SPI byte and transmit next on previous transmit completion when transfer is interrupt based (non-blocking) */
|
||||
ISR(SPI_STC_vect);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* this function returns only when communication finished
|
||||
*/
|
||||
void spi_transfer_blocking(uint8_t* data, size_t length);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns immediatly, while data is transfered
|
||||
* to ensure the transfer is complete, use spi_transfer_wait()
|
||||
* if data is already being transfered it will idle until the last transfer is completed
|
||||
*/
|
||||
void spi_transfer_nonblocking(uint8_t* data, size_t length);
|
||||
/* transmit and receive data over SPI
|
||||
* transmits than receives each byte from <data> for <length> bytes
|
||||
* the read bytes are saved back in <data>
|
||||
* global interrupts are required to be enabled
|
||||
* the function returns when all the data is transfered
|
||||
* while the data is transfered it goes into idle mode
|
||||
* if <data> is NULL or <length> is 0 it just ensures the previous transfer is complete
|
||||
*/
|
||||
void spi_transfer_wait(uint8_t* data, size_t length);
|
Loading…
Reference in New Issue