mac-rom-simm-programmer/mcp23s17.c
Doug Brown 407f6831a9 Initial import of my test code for the SIMM programmer board. Right now
it contains an (untested) MCP23S17 driver complete with AVR SPI support,
and an (untested) external memory interface driver that uses it.
2011-11-25 23:10:30 -08:00

188 lines
4.8 KiB
C

/*
* mcp23s17.c
*
* Created on: Nov 25, 2011
* Author: Doug
*/
#include "mcp23s17.h"
#include <avr/io.h>
#include <stdbool.h>
static bool MCP23S17_Inited = false;
// Pin definitions on PORTB
#define SPI_CS (1 << 0)
#define SPI_SCK (1 << 1)
#define SPI_MOSI (1 << 2)
#define SPI_MISO (1 << 3)
#define MCP23S17_RESET (1 << 7)
#define ASSERT_CS() PORTB &= ~SPI_CS
#define DEASSERT_CS() PORTB |= SPI_CS;
// A few register defines
#define MCP23S17_CONTROL_WRITE(address) (0x40 | (address << 1))
#define MCP23S17_CONTROL_READ(address) (0x40 | (address << 1) | 1)
#define MCP23S17_IODIRA 0x00
#define MCP23S17_IODIRB 0x01
#define MCP23S17_IPOLA 0x02
#define MCP23S17_IPOLB 0x03
#define MCP23S17_GPINTENA 0x04
#define MCP23S17_GPINTENB 0x05
#define MCP23S17_DEFVALA 0x06
#define MCP23S17_DEFVALB 0x07
#define MCP23S17_INTCONA 0x08
#define MCP23S17_INTCONB 0x09
#define MCP23S17_IOCON 0x0A
#define MCP23S17_IOCON_AGAIN 0x0B
#define MCP23S17_GPPUA 0x0C
#define MCP23S17_GPPUB 0x0D
#define MCP23S17_INTFA 0x0E
#define MCP23S17_INTFB 0x0F
#define MCP23S17_INTCAPA 0x10
#define MCP23S17_INTCAPB 0x11
#define MCP23S17_GPIOA 0x12
#define MCP23S17_GPIOB 0x13
#define MCP23S17_OLATA 0x14
#define MCP23S17_OLATB 0x15
void MCP23S17_Init()
{
// If it has already been initialized, no need to do it again.
if (MCP23S17_Inited)
{
return;
}
// Initialize the SPI pins
// Set MOSI, SCLK, and CS as outputs, MISO as input
// (Also, set the MCP23S17 reset line as an output)
DDRB |= SPI_CS | SPI_SCK | SPI_MOSI | MCP23S17_RESET;
DDRB &= ~SPI_MISO;
// Initialize the SPI peripheral
// We can run it at 8 MHz (divider of 2 from 16 MHz system clock -- maximum speed of MCP23S17 10 MHz)
#if ((F_CPU / 2) > 10000000)
#error This code assumes that the CPU clock divided by 2 is less than or equal to the MCP23S17's maximum speed of 10 MHz, and in this case, it's not.
#endif
SPCR = (0 << SPIE) | // No SPI interrupts
(1 << SPE) | // Enable SPI
(0 << DORD) | // MSB first
(1 << MSTR) | // Master mode
(0 << CPOL) | // SPI mode 0,0
(0 << CPHA) |
(0 << SPR0); // SCK frequency = F_CPU/2 (because of SPI2X being set below
SPSR = (1 << SPI2X); // Double the SPI clock rate -- allows /2 instead of /4
// Leave CS deasserted
DEASSERT_CS();
// Pull the MCP23S17 out of reset (it's pulled down to GND on the board with a 100k pulldown
// so it won't activate during AVR ISP programming)
PORTB |= MCP23S17_RESET;
// All done!
MCP23S17_Inited = true;
}
void MCP23S17_SetDDR(uint16_t ddr)
{
ASSERT_CS();
// Just a temporary variable so we read
// the returned byte from the SPI transfer
volatile uint8_t tmp;
// Start off the communication by telling the MCP23S17 that we are writing to a register
SPDR = MCP23S17_CONTROL_WRITE(0);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Tell it which register we're writing to (IODIRA)
SPDR = MCP23S17_IODIRA;
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Write the first byte of DDR
SPDR = (uint8_t)(ddr & 0xFF);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// It should auto-increment to IODIRB now
SPDR = (uint8_t)((ddr >> 8) & 0xFF);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
DEASSERT_CS();
}
void MCP23S17_SetPins(uint16_t data)
{
ASSERT_CS();
// Just a temporary variable so we read
// the returned byte from the SPI transfer
volatile uint8_t tmp;
// Start off the communication by telling the MCP23S17 that we are writing to a register
SPDR = MCP23S17_CONTROL_WRITE(0);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Tell it which register we're writing to (GPIOA)
SPDR = MCP23S17_GPIOA;
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Write the first byte of GPIO
SPDR = (uint8_t)(data & 0xFF);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// It should auto-increment to GPIOB now
SPDR = (uint8_t)((data >> 8) & 0xFF);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
DEASSERT_CS();
}
uint16_t MCP23S17_ReadPins(void)
{
uint16_t returnVal;
ASSERT_CS();
// Just a temporary variable so we read
// the returned byte from the SPI transfer
volatile uint8_t tmp;
// Start off the communication by telling the MCP23S17 that we are reading from a register
SPDR = MCP23S17_CONTROL_READ(0);
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Tell it which register we're reading from (GPIOA)
SPDR = MCP23S17_GPIOA;
while ((SPSR & (1 << SPIF)) == 0);
tmp = SPDR;
// Read the first byte of GPIOA
SPDR = 0;
while ((SPSR & (1 << SPIF)) == 0);
returnVal = SPDR;
// It should auto-increment to IODIRB now
SPDR = 0;
while ((SPSR & (1 << SPIF)) == 0);
returnVal |= (((uint16_t)SPDR) << 8);
DEASSERT_CS();
return returnVal;
}