diff --git a/arduino_1280.h b/arduino_1280.h index 4baca97..e2ef24f 100644 --- a/arduino_1280.h +++ b/arduino_1280.h @@ -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) diff --git a/arduino_168_328p.h b/arduino_168_328p.h index e00586e..e3b02f0 100644 --- a/arduino_168_328p.h +++ b/arduino_168_328p.h @@ -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) diff --git a/arduino_32U4.h b/arduino_32U4.h index 4fd85d1..014f678 100644 --- a/arduino_32U4.h +++ b/arduino_32U4.h @@ -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) diff --git a/arduino_644.h b/arduino_644.h index 1fe5035..3fe34d1 100644 --- a/arduino_644.h +++ b/arduino_644.h @@ -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) diff --git a/arduino_usb1286.h b/arduino_usb1286.h index 0233e0d..05e0d74 100644 --- a/arduino_usb1286.h +++ b/arduino_usb1286.h @@ -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 diff --git a/arduino_usb1287.h b/arduino_usb1287.h index fccba7e..ce4e545 100644 --- a/arduino_usb1287.h +++ b/arduino_usb1287.h @@ -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 diff --git a/mendel.c b/mendel.c index e581129..bc59a7c 100644 --- a/mendel.c +++ b/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 (;;) { diff --git a/sd.c b/sd.c new file mode 100644 index 0000000..3d4f3f9 --- /dev/null +++ b/sd.c @@ -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 */ diff --git a/sd.h b/sd.h new file mode 100644 index 0000000..de20a8b --- /dev/null +++ b/sd.h @@ -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 */ diff --git a/spi.c b/spi.c new file mode 100644 index 0000000..e38b1c0 --- /dev/null +++ b/spi.c @@ -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(); +} diff --git a/spi.h b/spi.h new file mode 100644 index 0000000..a301296 --- /dev/null +++ b/spi.h @@ -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 */ diff --git a/temp.c b/temp.c index 1c2c8bc..3cdcde7 100644 --- a/temp.c +++ b/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) {