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 MISO DIO50
|
||||
#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
|
||||
|
||||
// TWI (I2C)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@
|
|||
#define SCK DIO13
|
||||
#define MISO DIO12
|
||||
#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
|
||||
|
||||
// TWI (I2C)
|
||||
|
|
|
|||
|
|
@ -30,10 +30,6 @@
|
|||
#define SCK DIO1
|
||||
#define MISO DIO3
|
||||
#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
|
||||
|
||||
// TWI (I2C)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@
|
|||
#define SCK DIO7
|
||||
#define MISO DIO6
|
||||
#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
|
||||
|
||||
// TWI (I2C)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@
|
|||
#define SCK DIO9
|
||||
#define MISO DIO11
|
||||
#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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
#define SCK DIO9
|
||||
#define MISO DIO11
|
||||
#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
|
||||
|
||||
|
||||
|
|
|
|||
51
mendel.c
51
mendel.c
|
|
@ -49,6 +49,8 @@
|
|||
#include "clock.h"
|
||||
#include "intercom.h"
|
||||
#include "simulator.h"
|
||||
#include "spi.h"
|
||||
#include "sd.h"
|
||||
|
||||
#ifdef SIMINFO
|
||||
#include "../simulavr/src/simulavr_info.h"
|
||||
|
|
@ -66,9 +68,15 @@
|
|||
void io_init(void) {
|
||||
// disable modules we don't use
|
||||
#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
|
||||
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)
|
||||
// don't use USART2 or USART3- leave USART1 for GEN3 and derivatives
|
||||
PRR1 |= MASK(PRUSART3) | MASK(PRUSART2);
|
||||
|
|
@ -179,20 +187,18 @@ void io_init(void) {
|
|||
power_off();
|
||||
#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
|
||||
WRITE(DEBUG_LED_PIN, 0);
|
||||
SET_OUTPUT(DEBUG_LED_PIN);
|
||||
#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) {
|
||||
// set up watchdog
|
||||
wd_init();
|
||||
|
|
@ -206,6 +212,10 @@ void init(void) {
|
|||
// set up inputs and outputs
|
||||
io_init();
|
||||
|
||||
#if defined TEMP_MAX6675 || defined SD
|
||||
spi_init();
|
||||
#endif
|
||||
|
||||
// set up timers
|
||||
timer_init();
|
||||
|
||||
|
|
@ -222,6 +232,10 @@ void init(void) {
|
|||
// set up temperature inputs
|
||||
temp_init();
|
||||
|
||||
#ifdef SD
|
||||
sd_init();
|
||||
#endif
|
||||
|
||||
// enable interrupts
|
||||
sei();
|
||||
|
||||
|
|
@ -249,6 +263,23 @@ int main (void)
|
|||
#endif
|
||||
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
|
||||
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
|
||||
|
||||
#ifdef TEMP_MAX6675
|
||||
#include "spi.h"
|
||||
#endif
|
||||
|
||||
#ifdef TEMP_THERMISTOR
|
||||
|
|
@ -83,8 +84,9 @@ void temp_init() {
|
|||
switch(temp_sensors[i].temp_type) {
|
||||
#ifdef TEMP_MAX6675
|
||||
case TT_MAX6675:
|
||||
WRITE(SS, 1); // Turn sensor off.
|
||||
SET_OUTPUT(SS);
|
||||
// Note that MAX6675's Chip Select pin is currently hardcoded to SS.
|
||||
// This isn't neccessary. See also spi.h.
|
||||
spi_deselect_max6675();
|
||||
// Intentionally no break, we might have more than one sensor type.
|
||||
#endif
|
||||
|
||||
|
|
@ -132,33 +134,18 @@ void temp_sensor_tick() {
|
|||
switch(temp_sensors[i].temp_type) {
|
||||
#ifdef TEMP_MAX6675
|
||||
case TT_MAX6675:
|
||||
#ifdef PRR
|
||||
PRR &= ~MASK(PRSPI);
|
||||
#elif defined PRR0
|
||||
PRR0 &= ~MASK(PRSPI);
|
||||
#endif
|
||||
|
||||
SPCR = MASK(MSTR) | MASK(SPE) | MASK(SPR0);
|
||||
|
||||
// enable TT_MAX6675
|
||||
WRITE(SS, 0);
|
||||
|
||||
// Note: value reading in this section was rewritten without
|
||||
// testing when spi.c/.h was introduced. --Traumflug
|
||||
spi_select_max6675();
|
||||
// No delay required, see
|
||||
// https://github.com/triffid/Teacup_Firmware/issues/22
|
||||
|
||||
// read MSB
|
||||
SPDR = 0;
|
||||
for (;(SPSR & MASK(SPIF)) == 0;);
|
||||
temp = SPDR;
|
||||
temp <<= 8;
|
||||
|
||||
temp = spi_rw(0) << 8;
|
||||
// read LSB
|
||||
SPDR = 0;
|
||||
for (;(SPSR & MASK(SPIF)) == 0;);
|
||||
temp |= SPDR;
|
||||
temp |= spi_rw(0);
|
||||
|
||||
// disable TT_MAX6675
|
||||
WRITE(SS, 1);
|
||||
spi_deselect_max6675();
|
||||
|
||||
temp_sensors_runtime[i].temp_flags = 0;
|
||||
if ((temp & 0x8002) == 0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue