SD card: establish spi.c/.h and sd.c/.h and get SPI running.
For now this is just a nice demonstration on how to send bytes over SPI. Add SD_CARD_SELECT_PIN to your configuration board file manually to see data signals on MOSI dancing on the scope. The TODO's about SS in arduino*.h were wrong, SS does have a chip-specific special meaning (used in SPI multi-master or SPI slave mode). Still, a #define MAX6675_SELECT_PIN is missing. Squashed in this commit from the SPI development topic branch to get this first step working: Author: jbernardis <jeff.bernardis@gmail.com> 2015-02-04 22:35:07 mendel.c: disable SPI in power management only when not needed. If we want to talk to a SD card connected to SPI, we need SPI powered, of course. From Traumflug: nice catch, Jeff!
This commit is contained in:
parent
b4145e683a
commit
1fb3ece31e
|
|
@ -6,10 +6,6 @@
|
||||||
#define SCK DIO52
|
#define SCK DIO52
|
||||||
#define MISO DIO50
|
#define MISO DIO50
|
||||||
#define MOSI DIO51
|
#define MOSI DIO51
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO53
|
#define SS DIO53
|
||||||
|
|
||||||
// TWI (I2C)
|
// TWI (I2C)
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@
|
||||||
#define SCK DIO13
|
#define SCK DIO13
|
||||||
#define MISO DIO12
|
#define MISO DIO12
|
||||||
#define MOSI DIO11
|
#define MOSI DIO11
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO10
|
#define SS DIO10
|
||||||
|
|
||||||
// TWI (I2C)
|
// TWI (I2C)
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,6 @@
|
||||||
#define SCK DIO1
|
#define SCK DIO1
|
||||||
#define MISO DIO3
|
#define MISO DIO3
|
||||||
#define MOSI DIO2
|
#define MOSI DIO2
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO0
|
#define SS DIO0
|
||||||
|
|
||||||
// TWI (I2C)
|
// TWI (I2C)
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@
|
||||||
#define SCK DIO7
|
#define SCK DIO7
|
||||||
#define MISO DIO6
|
#define MISO DIO6
|
||||||
#define MOSI DIO5
|
#define MOSI DIO5
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO4
|
#define SS DIO4
|
||||||
|
|
||||||
// TWI (I2C)
|
// TWI (I2C)
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@
|
||||||
#define SCK DIO9
|
#define SCK DIO9
|
||||||
#define MISO DIO11
|
#define MISO DIO11
|
||||||
#define MOSI DIO10
|
#define MOSI DIO10
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO8
|
#define SS DIO8
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
#define SCK DIO9
|
#define SCK DIO9
|
||||||
#define MISO DIO11
|
#define MISO DIO11
|
||||||
#define MOSI DIO10
|
#define MOSI DIO10
|
||||||
// TODO: This depends on the board, so this definition is misplaced here,
|
|
||||||
// should be more appropriately named and go to config.h. It's used in
|
|
||||||
// temp.c and simulator.h and defines the Chip Select pin for an eventual
|
|
||||||
// MAX6675 temperature sensor.
|
|
||||||
#define SS DIO8
|
#define SS DIO8
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
51
mendel.c
51
mendel.c
|
|
@ -49,6 +49,8 @@
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
#include "intercom.h"
|
#include "intercom.h"
|
||||||
#include "simulator.h"
|
#include "simulator.h"
|
||||||
|
#include "spi.h"
|
||||||
|
#include "sd.h"
|
||||||
|
|
||||||
#ifdef SIMINFO
|
#ifdef SIMINFO
|
||||||
#include "../simulavr/src/simulavr_info.h"
|
#include "../simulavr/src/simulavr_info.h"
|
||||||
|
|
@ -66,9 +68,15 @@
|
||||||
void io_init(void) {
|
void io_init(void) {
|
||||||
// disable modules we don't use
|
// disable modules we don't use
|
||||||
#ifdef PRR
|
#ifdef PRR
|
||||||
PRR = MASK(PRTWI) | MASK(PRADC) | MASK(PRSPI);
|
PRR = MASK(PRTWI) | MASK(PRADC);
|
||||||
|
#if ! defined TEMP_MAX6675 && ! defined SD
|
||||||
|
PRR |= MASK(PRSPI);
|
||||||
|
#endif
|
||||||
#elif defined PRR0
|
#elif defined PRR0
|
||||||
PRR0 = MASK(PRTWI) | MASK(PRADC) | MASK(PRSPI);
|
PRR0 = MASK(PRTWI) | MASK(PRADC);
|
||||||
|
#if ! defined TEMP_MAX6675 && ! defined SD
|
||||||
|
PRR0 |= MASK(PRSPI);
|
||||||
|
#endif
|
||||||
#if defined(PRUSART3)
|
#if defined(PRUSART3)
|
||||||
// don't use USART2 or USART3- leave USART1 for GEN3 and derivatives
|
// don't use USART2 or USART3- leave USART1 for GEN3 and derivatives
|
||||||
PRR1 |= MASK(PRUSART3) | MASK(PRUSART2);
|
PRR1 |= MASK(PRUSART3) | MASK(PRUSART2);
|
||||||
|
|
@ -179,20 +187,18 @@ void io_init(void) {
|
||||||
power_off();
|
power_off();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TEMP_MAX6675
|
|
||||||
// setup SPI
|
|
||||||
WRITE(SCK, 0); SET_OUTPUT(SCK);
|
|
||||||
WRITE(MOSI, 1); SET_OUTPUT(MOSI);
|
|
||||||
WRITE(MISO, 1); SET_INPUT(MISO);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_LED_PIN
|
#ifdef DEBUG_LED_PIN
|
||||||
WRITE(DEBUG_LED_PIN, 0);
|
WRITE(DEBUG_LED_PIN, 0);
|
||||||
SET_OUTPUT(DEBUG_LED_PIN);
|
SET_OUTPUT(DEBUG_LED_PIN);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Startup code, run when we come out of reset
|
/** Initialise all the subsystems.
|
||||||
|
|
||||||
|
Note that order of appearance is critical here. For example, running
|
||||||
|
spi_init() before io_init() makes SPI fail (for reasons not exactly
|
||||||
|
investigated).
|
||||||
|
*/
|
||||||
void init(void) {
|
void init(void) {
|
||||||
// set up watchdog
|
// set up watchdog
|
||||||
wd_init();
|
wd_init();
|
||||||
|
|
@ -206,6 +212,10 @@ void init(void) {
|
||||||
// set up inputs and outputs
|
// set up inputs and outputs
|
||||||
io_init();
|
io_init();
|
||||||
|
|
||||||
|
#if defined TEMP_MAX6675 || defined SD
|
||||||
|
spi_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
// set up timers
|
// set up timers
|
||||||
timer_init();
|
timer_init();
|
||||||
|
|
||||||
|
|
@ -222,6 +232,10 @@ void init(void) {
|
||||||
// set up temperature inputs
|
// set up temperature inputs
|
||||||
temp_init();
|
temp_init();
|
||||||
|
|
||||||
|
#ifdef SD
|
||||||
|
sd_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
// enable interrupts
|
// enable interrupts
|
||||||
sei();
|
sei();
|
||||||
|
|
||||||
|
|
@ -249,6 +263,23 @@ int main (void)
|
||||||
#endif
|
#endif
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
#ifdef SD
|
||||||
|
// This demonstrates SPI. Gives a nice dance on the scope.
|
||||||
|
// Will go away as soon as we can do something useful with the SD card.
|
||||||
|
#include "delay.h"
|
||||||
|
uint8_t c = 0;
|
||||||
|
while (1) {
|
||||||
|
// Trigger signal for the scope.
|
||||||
|
WRITE(STEPPER_ENABLE_PIN, 0);
|
||||||
|
delay_us(10);
|
||||||
|
WRITE(STEPPER_ENABLE_PIN, 1);
|
||||||
|
|
||||||
|
spi_rw(c);
|
||||||
|
c++;
|
||||||
|
delay_ms(50);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
/** \file Coordinating reading and writing of SD cards.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sd.h"
|
||||||
|
|
||||||
|
#ifdef SD
|
||||||
|
|
||||||
|
|
||||||
|
void sd_init(void) {
|
||||||
|
WRITE(SD_CARD_SELECT_PIN, 1);
|
||||||
|
SET_OUTPUT(SD_CARD_SELECT_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SD */
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
/** \file Coordinating reading and writing of SD cards.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SD_H
|
||||||
|
#define _SD_H
|
||||||
|
|
||||||
|
#include "config_wrapper.h"
|
||||||
|
|
||||||
|
#ifdef SD_CARD_SELECT_PIN
|
||||||
|
#define SD
|
||||||
|
|
||||||
|
void sd_init(void);
|
||||||
|
|
||||||
|
#endif /* SD_CARD_SELECT_PIN */
|
||||||
|
|
||||||
|
#endif /* _SD_H */
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
\brief SPI subsystem
|
||||||
|
|
||||||
|
This is much simpler than Teacup's serial subsystem. No ring buffers, no
|
||||||
|
"write string" functions. Usually using SPI directly is faster than fiddling
|
||||||
|
with buffers. For example, reading or writing a byte can be done in as few
|
||||||
|
as 20 clock cycles.
|
||||||
|
|
||||||
|
Other than serial, SPI has to deal with multiple devices. Device selection
|
||||||
|
happens before reading and writing, data exchange its self is the same for
|
||||||
|
each device, then.
|
||||||
|
*/
|
||||||
|
#include "spi.h"
|
||||||
|
|
||||||
|
#include "arduino.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** Initialise serial subsystem.
|
||||||
|
|
||||||
|
Code copied from ATmega164/324/644/1284 data sheet, section 18.2, page 160,
|
||||||
|
or moved here from mendel.c.
|
||||||
|
*/
|
||||||
|
void spi_init() {
|
||||||
|
|
||||||
|
// Set SCK (clock) and MOSI line to output, ie. set USART in master mode.
|
||||||
|
SET_OUTPUT(SCK);
|
||||||
|
SET_OUTPUT(MOSI);
|
||||||
|
SET_INPUT(MISO);
|
||||||
|
// SS must be set as output to disconnect it from the SPI subsystem.
|
||||||
|
// Too bad if something else tries to use this pin as digital input.
|
||||||
|
// See ATmega164/324/644/1284 data sheet, section 18.3.2, page 162.
|
||||||
|
// Not written there: this must apparently be done before setting the SPRC
|
||||||
|
// register, else future R/W-operations may hang.
|
||||||
|
SET_OUTPUT(SS);
|
||||||
|
|
||||||
|
#ifdef SPI_2X
|
||||||
|
SPSR = 0x01;
|
||||||
|
#else
|
||||||
|
SPSR = 0x00;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This sets the whole SPRC register.
|
||||||
|
spi_speed_100_400();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
#ifndef _SPI_H
|
||||||
|
#define _SPI_H
|
||||||
|
|
||||||
|
#include "config_wrapper.h"
|
||||||
|
#include "arduino.h"
|
||||||
|
|
||||||
|
// Uncomment this to double SPI frequency from (F_CPU / 4) to (F_CPU / 2).
|
||||||
|
//#define SPI_2X
|
||||||
|
|
||||||
|
|
||||||
|
/** Initialise SPI subsystem.
|
||||||
|
*/
|
||||||
|
void spi_init(void);
|
||||||
|
|
||||||
|
/** SPI device selection.
|
||||||
|
|
||||||
|
Because out famous WRITE() macro works with constant pins, only, we define
|
||||||
|
a (de)select function for each of them. In case you add another SPI device,
|
||||||
|
you also have to define a pair of these functions.
|
||||||
|
*/
|
||||||
|
#ifdef SD
|
||||||
|
static void spi_select_sd(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_select_sd(void) {
|
||||||
|
WRITE(SD_CARD_SELECT_PIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_deselect_sd(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_deselect_sd(void) {
|
||||||
|
WRITE(SD_CARD_SELECT_PIN, 1);
|
||||||
|
}
|
||||||
|
#endif /* SD */
|
||||||
|
|
||||||
|
#ifdef TEMP_MAX6675
|
||||||
|
// Note: the pin choosen with DEFINE_TEMP_SENSOR() in the board configuration
|
||||||
|
// should be used here. Currently it's a requirement that this device's
|
||||||
|
// Chip Select pin is actually SS, while any other pin would work just
|
||||||
|
// as fine.
|
||||||
|
static void spi_select_max6675(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_select_max6675(void) {
|
||||||
|
WRITE(SS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_deselect_max6675(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_deselect_max6675(void) {
|
||||||
|
WRITE(SS, 1);
|
||||||
|
}
|
||||||
|
#endif /* TEMP_MAX6675 */
|
||||||
|
|
||||||
|
/** Set SPI clock speed to something between 100 and 400 kHz.
|
||||||
|
|
||||||
|
This is needed for initialising SD cards. We set the whole SPCR register
|
||||||
|
in one step, because this is faster than and'ing in bits.
|
||||||
|
|
||||||
|
About dividers. We have:
|
||||||
|
SPCR = 0x50; // normal mode: (F_CPU / 4), 2x mode: (F_CPU / 2)
|
||||||
|
SPCR = 0x51; // normal mode: (F_CPU / 16), 2x mode: (F_CPU / 8)
|
||||||
|
SPCR = 0x52; // normal mode: (F_CPU / 64), 2x mode: (F_CPU / 32)
|
||||||
|
SPCR = 0x53; // normal mode: (F_CPU / 128), 2x mode: (F_CPU / 64)
|
||||||
|
|
||||||
|
For now we always choose the /128 divider, because it fits nicely in all
|
||||||
|
expected situations:
|
||||||
|
F_CPU 16 MHz 20 MHz
|
||||||
|
SPI clock normal mode 125 kHz 156 kHz
|
||||||
|
SPI clock 2x mode 250 kHz 312 kHz
|
||||||
|
|
||||||
|
About the other bits:
|
||||||
|
0x50 = (1 << SPE) | (1 << MSTR);
|
||||||
|
This is Master SPI mode, SPI enabled, interrupts disabled, polarity mode 0
|
||||||
|
(right for SD cards).
|
||||||
|
See ATmega164/324/644/1284 data sheet, section 18.5.1, page 164.
|
||||||
|
*/
|
||||||
|
static void spi_speed_100_400(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_speed_100_400(void) {
|
||||||
|
SPCR = 0x53;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set SPI clock speed to maximum.
|
||||||
|
*/
|
||||||
|
static void spi_speed_max(void) __attribute__ ((always_inline));
|
||||||
|
inline void spi_speed_max(void) {
|
||||||
|
SPCR = 0x50; // See list at spi_speed_100_400().
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Exchange a byte over SPI.
|
||||||
|
|
||||||
|
Yes, SPI is that simple and you can always only swap bytes. To retrieve
|
||||||
|
a byte, simply send a dummy value, like: mybyte = spi_rw(0);
|
||||||
|
|
||||||
|
As we operate in master mode, we don't have to fear to hang due to
|
||||||
|
communications errors (e.g. missing a clock beat).
|
||||||
|
|
||||||
|
Note: insisting on inlinig (attribute always_inline) costs about 80 bytes
|
||||||
|
with the current SD card code.
|
||||||
|
*/
|
||||||
|
static uint8_t spi_rw(uint8_t) __attribute__ ((always_inline));
|
||||||
|
inline uint8_t spi_rw(uint8_t byte) {
|
||||||
|
SPDR = byte;
|
||||||
|
loop_until_bit_is_set(SPSR, SPIF);
|
||||||
|
return SPDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _SPI_H */
|
||||||
33
temp.c
33
temp.c
|
|
@ -27,6 +27,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TEMP_MAX6675
|
#ifdef TEMP_MAX6675
|
||||||
|
#include "spi.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TEMP_THERMISTOR
|
#ifdef TEMP_THERMISTOR
|
||||||
|
|
@ -83,8 +84,9 @@ void temp_init() {
|
||||||
switch(temp_sensors[i].temp_type) {
|
switch(temp_sensors[i].temp_type) {
|
||||||
#ifdef TEMP_MAX6675
|
#ifdef TEMP_MAX6675
|
||||||
case TT_MAX6675:
|
case TT_MAX6675:
|
||||||
WRITE(SS, 1); // Turn sensor off.
|
// Note that MAX6675's Chip Select pin is currently hardcoded to SS.
|
||||||
SET_OUTPUT(SS);
|
// This isn't neccessary. See also spi.h.
|
||||||
|
spi_deselect_max6675();
|
||||||
// Intentionally no break, we might have more than one sensor type.
|
// Intentionally no break, we might have more than one sensor type.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -132,33 +134,18 @@ void temp_sensor_tick() {
|
||||||
switch(temp_sensors[i].temp_type) {
|
switch(temp_sensors[i].temp_type) {
|
||||||
#ifdef TEMP_MAX6675
|
#ifdef TEMP_MAX6675
|
||||||
case TT_MAX6675:
|
case TT_MAX6675:
|
||||||
#ifdef PRR
|
// Note: value reading in this section was rewritten without
|
||||||
PRR &= ~MASK(PRSPI);
|
// testing when spi.c/.h was introduced. --Traumflug
|
||||||
#elif defined PRR0
|
spi_select_max6675();
|
||||||
PRR0 &= ~MASK(PRSPI);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SPCR = MASK(MSTR) | MASK(SPE) | MASK(SPR0);
|
|
||||||
|
|
||||||
// enable TT_MAX6675
|
|
||||||
WRITE(SS, 0);
|
|
||||||
|
|
||||||
// No delay required, see
|
// No delay required, see
|
||||||
// https://github.com/triffid/Teacup_Firmware/issues/22
|
// https://github.com/triffid/Teacup_Firmware/issues/22
|
||||||
|
|
||||||
// read MSB
|
// read MSB
|
||||||
SPDR = 0;
|
temp = spi_rw(0) << 8;
|
||||||
for (;(SPSR & MASK(SPIF)) == 0;);
|
|
||||||
temp = SPDR;
|
|
||||||
temp <<= 8;
|
|
||||||
|
|
||||||
// read LSB
|
// read LSB
|
||||||
SPDR = 0;
|
temp |= spi_rw(0);
|
||||||
for (;(SPSR & MASK(SPIF)) == 0;);
|
|
||||||
temp |= SPDR;
|
|
||||||
|
|
||||||
// disable TT_MAX6675
|
spi_deselect_max6675();
|
||||||
WRITE(SS, 1);
|
|
||||||
|
|
||||||
temp_sensors_runtime[i].temp_flags = 0;
|
temp_sensors_runtime[i].temp_flags = 0;
|
||||||
if ((temp & 0x8002) == 0) {
|
if ((temp & 0x8002) == 0) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue