spicmds: Rework SPI message transmission

Improve the SPI message transmit system.  Add support for bus speed
and bus mode.  Add support for sending SPI messages on shutdown.

Signed-off-by: Petri Honkala <cruwaller@gmail.com>
Signed-off-by: Douglas Hammond <wizhippo@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2018-05-04 12:27:52 -04:00
parent 838da992e8
commit 22487d95e9
7 changed files with 244 additions and 45 deletions

View File

@ -49,13 +49,6 @@ Common startup commands:
and 255 indicating a full on state. This command may be useful for
enabling CPU and nozzle cooling fans.
* `send_spi_message pin=%u msg=%*s` : This command can be used to
transmit messages to a serial-peripheral-interface (SPI) component
connected to the micro-controller. It has been used to configure the
startup settings of AD5206 digipots. The 'pin' parameter specifies
the chip select line to use during the transmission. The 'msg'
indicates the binary message to transmit to the given chip.
Low-level micro-controller configuration
========================================
@ -182,6 +175,22 @@ This section lists some commonly used config commands.
specifies the maximum number of steppers that this endstop may need
to halt during a homing operation (see end_stop_home below).
* `config_spi oid=%c bus=%u pin=%u mode=%u rate=%u shutdown_msg=%*s` :
This command creates an internal SPI object. It is used with
spi_transfer and spi_send commands (see below). The "bus"
identifies the SPI bus to use (if the micro-controller has more than
one SPI bus available). The "pin" specifies the chip select (CS) pin
for the device. The "mode" is the SPI mode (should be between 0 and
3). The "rate" parameter specifies the SPI bus rate (in cycles per
second). Finally, the "shutdown_msg" is an SPI command to send to
the given device should the micro-controller go into a shutdown
state.
* `config_spi_without_cs oid=%c bus=%u mode=%u rate=%u
shutdown_msg=%*s` : This command is similar to config_spi, but
without a CS pin definition. It is useful for SPI devices that do
not have a chip select line.
Common commands
===============
@ -283,3 +292,15 @@ It is the responsibility of the host to ensure that there is available
space in the queue before sending a queue_step command. The host does
this by calculating when each queue_step command completes and
scheduling new queue_step commands accordingly.
SPI Commands
------------
* `spi_transfer oid=%c data=%*s` : This command causes the
micro-controller to send 'data' to the spi device specified by 'oid'
and it generates a "spi_transfer_response" response message with the
data returned during the transmission.
* `spi_send oid=%c data=%*s` : This command is similar to
"spi_transfer", but it does not generate a "spi_transfer_response"
message.

View File

@ -11,21 +11,24 @@ class ad5206:
enable_pin_params = ppins.lookup_pin('digital_out', enable_pin)
if enable_pin_params['invert']:
raise ppins.error("ad5206 can not invert pin")
self.mcu = enable_pin_params['chip']
self.pin = enable_pin_params['pin']
self.mcu.add_config_object(self)
mcu = enable_pin_params['chip']
pin = enable_pin_params['pin']
scale = config.getfloat('scale', 1., above=0.)
self.channels = [None] * 6
for i in range(len(self.channels)):
channels = [None]*6
for i in range(len(channels)):
val = config.getfloat('channel_%d' % (i+1,), None,
minval=0., maxval=scale)
if val is not None:
self.channels[i] = int(val * 256. / scale + .5)
def build_config(self):
for i, val in enumerate(self.channels):
channels[i] = int(val * 256. / scale + .5)
oid = mcu.create_oid()
mcu.add_config_cmd(
"config_spi oid=%d bus=%d pin=%s mode=%u rate=%u shutdown_msg=" % (
oid, 0, pin, 0, 25000000))
for i, val in enumerate(channels):
if val is not None:
self.mcu.add_config_cmd(
"send_spi_message pin=%s msg=%02x%02x" % (self.pin, i, val))
mcu.add_config_cmd(
"spi_send oid=%d data=%02x%02x" % (oid, i, val),
is_init=True)
def load_config_prefix(config):
return ad5206(config)

View File

@ -352,29 +352,103 @@ gpio_adc_cancel_sample(struct gpio_adc g)
****************************************************************/
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328
static const uint8_t SS = GPIO('B', 2), SCK = GPIO('B', 5), MOSI = GPIO('B', 3);
static const uint8_t SS = GPIO('B', 2), SCK = GPIO('B', 5);
static const uint8_t MOSI = GPIO('B', 3), MISO = GPIO('B', 4);
#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
static const uint8_t SS = GPIO('B', 4), SCK = GPIO('B', 7), MOSI = GPIO('B', 5);
static const uint8_t SS = GPIO('B', 4), SCK = GPIO('B', 7);
static const uint8_t MOSI = GPIO('B', 5), MISO = GPIO('B', 6);
#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646 || CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
static const uint8_t SS = GPIO('B', 0), SCK = GPIO('B', 1), MOSI = GPIO('B', 2);
static const uint8_t SS = GPIO('B', 0), SCK = GPIO('B', 1);
static const uint8_t MOSI = GPIO('B', 2), MISO = GPIO('B', 3);
#endif
void
spi_config(void)
static void
spi_init(void)
{
gpio_out_setup(SS, 1);
gpio_out_setup(SCK, 0);
gpio_out_setup(MOSI, 0);
gpio_in_setup(MISO, 0);
SPCR = (1<<MSTR) | (1<<SPE);
SPSR = 0;
}
struct spi_config
spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
if (bus || mode > 3)
shutdown("Invalid spi_setup parameters");
// Make sure the SPI interface is enabled
spi_init();
// Setup rate
struct spi_config config = {0, 0};
if (rate >= (CONFIG_CLOCK_FREQ / 2)) {
config.spsr = (1<<SPI2X);
} else if (rate >= (CONFIG_CLOCK_FREQ / 4)) {
config.spcr = 0;
} else if (rate >= (CONFIG_CLOCK_FREQ / 8)) {
config.spcr = 1;
config.spsr = (1<<SPI2X);
} else if (rate >= (CONFIG_CLOCK_FREQ / 16)) {
config.spcr = 1;
} else if (rate >= (CONFIG_CLOCK_FREQ / 32)) {
config.spcr = 2;
config.spsr = (1<<SPI2X);
} else if (rate >= (CONFIG_CLOCK_FREQ / 64)) {
config.spcr = 2;
} else {
config.spcr = 3;
}
// Setup mode
config.spcr |= (1<<SPE) | (1<<MSTR);
switch(mode) {
case 0: {
// MODE 0 - CPOL=0, CPHA=0
break;
}
case 1: {
// MODE 1 - CPOL=0, CPHA=1
config.spcr |= (1<<CPHA);
break;
}
case 2: {
// MODE 2 - CPOL=1, CPHA=0
config.spcr |= (1<<CPOL);
break;
}
case 3: {
// MODE 3 - CPOL=1, CPHA=1
config.spcr |= (1<<CPOL) | (1<<CPHA);
break;
}
}
return config;
}
void
spi_transfer(char *data, uint8_t len)
spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data)
{
while (len--) {
SPDR = *data;
while (!(SPSR & (1<<SPIF)))
;
*data++ = SPDR;
SPCR = config.spcr;
SPSR = config.spsr;
if (receive_data) {
while (len--) {
SPDR = *data;
while (!(SPSR & (1<<SPIF)))
;
*data++ = SPDR;
}
} else {
while (len--) {
SPDR = *data++;
while (!(SPSR & (1<<SPIF)))
;
}
}
}

View File

@ -34,7 +34,11 @@ uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
void spi_config(void);
void spi_transfer(char *data, uint8_t len);
struct spi_config {
uint8_t spcr, spsr;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
#endif // gpio.h

View File

@ -30,7 +30,11 @@ uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
void spi_config(void);
void spi_transfer(char *data, uint8_t len);
struct spi_config {
uint32_t cfg;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
#endif // gpio.h

View File

@ -36,7 +36,13 @@ uint16_t gpio_adc_read(struct gpio_adc g) {
void gpio_adc_cancel_sample(struct gpio_adc g) {
}
void spi_config(void) {
struct spi_config
spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
return (struct spi_config){ };
}
void spi_transfer(char *data, uint8_t len) {
void
spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data)
{
}

View File

@ -1,22 +1,109 @@
// Commands for sending messages on an SPI bus
//
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memcpy
#include "board/gpio.h" // gpio_out_write
#include "basecmd.h" // oid_alloc
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_SHUTDOWN
struct spidev_s {
struct spi_config spi_config;
struct gpio_out pin;
uint8_t flags;
uint8_t shutdown_msg_len;
uint8_t shutdown_msg[];
};
enum {
SF_HAVE_PIN = 1,
};
void
command_send_spi_message(uint32_t *args)
command_config_spi(uint32_t *args)
{
// For now, this only implements enough to program an ad5206 digipot
uint8_t len = args[1];
char *msg = (void*)(size_t)args[2];
spi_config();
struct gpio_out pin = gpio_out_setup(args[0], 0);
spi_transfer(msg, len);
gpio_out_write(pin, 1);
sendf("spi_response response=%*s", len, msg);
uint8_t shutdown_msg_len = args[5];
struct spidev_s *spi = oid_alloc(args[0], command_config_spi
, sizeof(*spi) + shutdown_msg_len);
spi->pin = gpio_out_setup(args[2], 1);
spi->flags = SF_HAVE_PIN;
spi->spi_config = spi_setup(args[1], args[3], args[4]);
spi->shutdown_msg_len = shutdown_msg_len;
uint8_t *shutdown_msg = (void*)(size_t)args[6];
memcpy(spi->shutdown_msg, shutdown_msg, shutdown_msg_len);
}
DECL_COMMAND(command_send_spi_message, "send_spi_message pin=%u msg=%*s");
DECL_COMMAND(command_config_spi,
"config_spi oid=%c bus=%u pin=%u mode=%u rate=%u shutdown_msg=%*s");
void
command_config_spi_without_cs(uint32_t *args)
{
uint8_t shutdown_msg_len = args[4];
struct spidev_s *spi = oid_alloc(args[0], command_config_spi
, sizeof(*spi) + shutdown_msg_len);
spi->spi_config = spi_setup(args[1], args[2], args[3]);
spi->shutdown_msg_len = shutdown_msg_len;
uint8_t *shutdown_msg = (void*)(size_t)args[5];
memcpy(spi->shutdown_msg, shutdown_msg, shutdown_msg_len);
}
DECL_COMMAND(command_config_spi_without_cs,
"config_spi_without_cs oid=%c bus=%u mode=%u rate=%u"
" shutdown_msg=%*s");
static void
spidev_transfer(struct spidev_s *spi, uint8_t receive_data
, uint8_t data_len, uint8_t *data)
{
if (spi->flags & SF_HAVE_PIN) {
gpio_out_write(spi->pin, 0);
spi_transfer(spi->spi_config, receive_data, data_len, data);
gpio_out_write(spi->pin, 1);
} else {
spi_transfer(spi->spi_config, receive_data, data_len, data);
}
}
void
command_spi_transfer(uint32_t *args)
{
uint8_t oid = args[0];
struct spidev_s *spi = oid_lookup(oid, command_config_spi);
uint8_t data_len = args[1];
uint8_t *data = (void*)(size_t)args[2];
spidev_transfer(spi, 1, data_len, data);
sendf("spi_transfer_response oid=%c response=%*s", oid, data_len, data);
}
DECL_COMMAND(command_spi_transfer, "spi_transfer oid=%c data=%*s");
void
command_spi_send(uint32_t *args)
{
uint8_t oid = args[0];
struct spidev_s *spi = oid_lookup(oid, command_config_spi);
uint8_t data_len = args[1];
uint8_t *data = (void*)(size_t)args[2];
spidev_transfer(spi, 0, data_len, data);
}
DECL_COMMAND(command_spi_send, "spi_send oid=%c data=%*s");
void
spidev_shutdown(void)
{
// Cancel any transmissions that may be in progress
uint8_t oid;
struct spidev_s *spi;
foreach_oid(oid, spi, command_config_spi) {
if (spi->flags & SF_HAVE_PIN)
gpio_out_write(spi->pin, 1);
}
// Send shutdown messages
foreach_oid(oid, spi, command_config_spi) {
if (spi->shutdown_msg_len)
spidev_transfer(spi, 0, spi->shutdown_msg_len, spi->shutdown_msg);
}
}
DECL_SHUTDOWN(spidev_shutdown);