1
\$\begingroup\$

I am trying to program the SAM3x8E chip on the Arduino Due through Atmel Studio. I would like to be able to control a digital resistor over the SPI interface with the SAM3 chip. I am running Atmel Studio 6.2. I have NO PROBLEM getting things to work on the Arduino IDE, but I really want to figure out how to program this the "hard way" in Atmel. I can successfully program the chip in Atmel Studio, by using a batch file which loads bosssa.exe.

Here is my WORKING Arduino IDE code:

Code:

// inslude the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 13;

void setup() {
  // initialize SPI:
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
 // SPI.setClockDivider(slaveSelectPin, 8);
  SPI.setBitOrder(LSBFIRST);
  pinMode (slaveSelectPin, OUTPUT);
}

void loop() {

  // Raise the volume from off to loud

  // Increase brightness of LED
  for (int i = 63; i>=0; i--) {
    digitalPotWrite(i, i);

    if(i>20)
    delay(10);
    else
    delay(100);
  }

      digitalPotWrite(0, 0);
       // delay(500);


  // Decrease brightness of LED
  for (int i = 0; i <= 63; i++) {
    digitalPotWrite(i, i);     

    if(i>20)
    delay(10);
    else
    delay(100);
  }
}

void digitalPotWrite(int left, int right) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin, HIGH);
  //  send in the address and value via SPI:
  SPI.transfer(left);
  SPI.transfer(right);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin, LOW);
}

Now (sadly), this is my Atmel code:

Code:

#include <asf.h>
#include "spi_master.h"


int main (void)
{
   /* Initialize the SAM system. */
   sysclk_init();
   board_init();

}

As you can see, I don't have the slightest clue how to go about setting up the SPI interface in Atmel studio. I've looked at example code such as the built in "SPI_EXAMPLE1" but I do NOT understand it. Can anyone help me to understand how you initialize the SPI? How do I know what pins to use in the initialization code?

\$\endgroup\$
  • \$\begingroup\$ so what you are saying is, you have not done any code at all for SPI in Atmel Studio. Have you included the SPI drivers using Atmel Software Framework (ASF)? That may help a lot. There is documentation online about how to use the ASF SPI driver code \$\endgroup\$ – KyranF Apr 24 '15 at 17:48
  • \$\begingroup\$ Quickstart for using the ASF drivers for SPI: asf.atmel.com/docs/latest/sam.drivers.spi.example.arduino_due_x/… \$\endgroup\$ – KyranF Apr 24 '15 at 17:52
  • \$\begingroup\$ Documentation for the Atmel Studio example project which you need to request/grab from the ASF examples menu, for the Arduino style SAM3XE8 asf.atmel.com/docs/latest/sam.drivers.spi.example.arduino_due_x/… \$\endgroup\$ – KyranF Apr 24 '15 at 17:53
  • \$\begingroup\$ I have to agree with you though, there is terrible instructions/tutorials/example code and far too much fat to cut through to get something simple working. \$\endgroup\$ – KyranF Apr 24 '15 at 18:01
2
\$\begingroup\$

You have two options here. You can manually set the configuration registers directly for the SPI peripheral, or you can use the "Atmel Software Framework" which abstracts a lot of the details into a higher-level API. In either case, you will have to deal with a lot of things that the Arduino environment handles for you.

You'll need to setup the processor's clock, which means selecting a source (Typically you have the option of an internal high speed, internal low speed, external high frequency crystal, external low frequency crystal, or external clock) and a combination of multipliers/dividers that derive your desired core clock frequency. Then you need to ensure that the SPI peripheral and the GPIO modules receive a clock signal (by default, clocks are not routed to any peripherals to save power) and that the clock signal is divided down to an appropriate speed (the MCU data sheet will specify min/max timing, as will the data sheet of your external IC, and you'll need to make sure you satisfy the requirements of both).

You'll need to properly configure the GPIO pins to enable the SPI peripheral to control them. Many MCUs of this class will have several SPI units, and the datasheet will specify which pins can be used by which unit. Most pins will have many peripherals which can be selected, so you'll need to set the pin multiplexer to select the proper SPI unit. You'll also need to set pin direction and pullups/pulldowns as needed.

Finally, you'll need to configure the SPI peripheral itself. This will include setting clock speed, which clock edges to use, master/slave, pin routing, and many other options that will be spelled out by the datasheet.

If you're just starting out, then ASF is the way to go. There are examples in the ASF documentation linked in previous comments that should help you out, and if you examine them closely you should see that the example applications do all of the things I've outlined above. However, the ASF documentation does an exceptionally poor job of actually explaining what's going on and why, so you'll need to carefully study the MCU datasheet sections on clock, power management, GPIO, and SPI to really figure out what you're doing. Unfortunately the datasheet also does a poor job of explaining how peripherals are meant to be used, so some reading between the lines will be required. As a general rule, vendor APIs and documentation for ARM MCUs (not just Atmel's SAMs) are not very good, so a lot of reading and experimentation will be required. Use the right click->"Go to implementation" feature of Atmel Studio liberally to learn more about what ASF is doing under the hood. Referring back to the datasheet descriptions of the various hardware registers' functions will help.

So all of that is a very general answer--your specific solution will depend on the particular hardware setup you have, so you'll need to look at the Arduino Due schematics to figure out which pins you'll need to use for SPI and what clock settings are appropriate. The MCU will start up running from an internal oscillator by default, so I'd suggest you start by just getting the GPIO working--just blink an LED to make sure you have the hang of the GPIO, then start working with the SPI module.

Above all, remember that the datasheet is your friend!

\$\endgroup\$
  • \$\begingroup\$ Hey thank you! I am going to simplify things by focusing on a GPIO LED blink as you mentioned. I don't intend to bash Atmel, but I have to agree, their documentation has so many lines of invisible ink.... =( I wish they would fill those spaces in a little better if you know what I mean. It's tough for an absolute beginner. Thank you for the help!! \$\endgroup\$ – John August Apr 24 '15 at 22:06
  • \$\begingroup\$ As already mentioned, the ASF documentation is a complete mess. I've written SPI drivers before for the AVR and am moving to the ARM and thought I'd save a bit of time by adopting the ASF. Big mistake! It's so difficult to work out what is being driven, what you have to provide to configure the drivers, and what they actually do that it's actually quicker just to read the datasheet and write your own drivers (particularly when you don't need all the configuration complexity and can just hardwire in the settings you need). \$\endgroup\$ – user80534 Jul 7 '15 at 14:14
2
\$\begingroup\$

Though, experienced developers say that beginners should go with ASF (which is actually true), it doesn't mean that you can achieve with less effort the same thing. You just need different type of effort. (see: http://sam4dev.blogspot.co.at/2015/10/should-i-use-asf.html)

Anyway, to answer the question: I've implemented SPI with ASF in master mode, SPI mode 0 on SAM4S16C. Check out this post for details: http://sam4dev.blogspot.co.at/2015/10/spi-with-asf.html I provide here the essential parts.

Add the following services/drivers to your project with ASF Wizard:

  • GPIO
  • IOPORT
  • SPI
  • Interrupt management
  • System Clock Control

In your init.c file initialise the drivers and services.

#include <asf.h>
#include <board.h>
#include <conf_board.h>

void board_init(void)
{

 WDT->WDT_MR = WDT_MR_WDDIS; //switch off watchdog

 sysclk_init();
 ioport_init();
 irq_initialize_vectors();
 cpu_irq_enable();

 //Set Led Pin directions
 ioport_set_pin_dir(LED0_PIN,IOPORT_DIR_OUTPUT);
 ioport_set_pin_dir(LED1_PIN,IOPORT_DIR_OUTPUT);
}

SPI init: This init method will set up SPI in master mode, SPI mode will be SPI mode 0, clock rate around 1MHz.

#define SPI_ID            ID_SPI
#define SPI_MASTER_BASE     SPI
#define SPI_MISO_GPIO  (PIO_PA12_IDX)
#define SPI_MISO_FLAGS (PIO_PERIPH_A | PIO_DEFAULT)
#define SPI_MOSI_GPIO  (PIO_PA13_IDX)
#define SPI_MOSI_FLAGS (PIO_PERIPH_A | PIO_DEFAULT)
#define SPI_SPCK_GPIO  (PIO_PA14_IDX)
#define SPI_SPCK_FLAGS (PIO_PERIPH_A | PIO_DEFAULT)
#define SPI_NPCS0_GPIO            (PIO_PA11_IDX)
#define SPI_NPCS1_GPIO            (PIO_PA31_IDX)
#define SPI_NPCS1_FLAGS           (PIO_PERIPH_A | PIO_DEFAULT)
#define SPI_CHIP_SEL 1    //Use SPI Chip Select 1 (SPI_NPCS1_GPIO) for CS
#define SPI_CHIP_PCS spi_get_pcs(SPI_CHIP_SEL)

/* Clock polarity. */
#define SPI_CLK_POLARITY 0

/* Clock phase. */
#define SPI_CLK_PHASE 0

/* Delay before SPCK. */
//#define SPI_DLYBS 0x40
#define SPI_DLYBS 0xFF

/* Delay between consecutive transfers. */
#define SPI_DLYBCT 0x10
/* SPI clock setting (Hz). */
static uint32_t gs_ul_spi_clock = 1000000;

//Paramteres:
//  data: data to be sent
//  last_byte: either 0 or 1. 
//   0-There are following bytes to be sent. The following bytes and the current byte composes one data-unit, CS should be kept low after sending out the current byte
//   1-Last byte of a data unit. CS will be set to HIGH when the current byte has been sent out.
void spi_tx(uint8_t data,uint8_t last_byte)
{
 spi_write(SPI,data,SPI_CHIP_SEL,last_byte);
 while ((spi_read_status(SPI) & SPI_SR_RDRF) == 0);/* Wait transfer done. */
}

uint8_t spi_rx(void)
{
 uint8_t tmp;
 spi_read (SPI,&tmp,SPI_CHIP_SEL);
 return tmp;
}

void spi_init(void)
{
        //Assign I/O lines to peripheral
 gpio_configure_pin(SPI_MISO_GPIO, SPI_MISO_FLAGS);
 gpio_configure_pin(SPI_MOSI_GPIO, SPI_MOSI_FLAGS);
 gpio_configure_pin(SPI_SPCK_GPIO, SPI_SPCK_FLAGS);
 gpio_configure_pin(SPI_NPCS1_GPIO, SPI_NPCS1_FLAGS);

 spi_enable_clock(SPI_MASTER_BASE);
 spi_disable(SPI_MASTER_BASE);
 spi_reset(SPI_MASTER_BASE);
 spi_set_lastxfer(SPI_MASTER_BASE);
 spi_set_master_mode(SPI_MASTER_BASE);
 spi_disable_mode_fault_detect(SPI_MASTER_BASE);
 spi_set_peripheral_chip_select_value(SPI_MASTER_BASE, SPI_CHIP_PCS);
 spi_configure_cs_behavior(SPI, 1, SPI_CS_RISE_NO_TX);
 spi_set_clock_polarity(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_POLARITY);
 spi_set_clock_phase(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_PHASE);
 spi_set_bits_per_transfer(SPI_MASTER_BASE, SPI_CHIP_SEL,
 SPI_CSR_BITS_8_BIT);
 spi_set_baudrate_div(SPI_MASTER_BASE, SPI_CHIP_SEL,(sysclk_get_cpu_hz() / gs_ul_spi_clock));
 spi_set_transfer_delay(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_DLYBS,SPI_DLYBCT);
 spi_enable(SPI_MASTER_BASE);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.