Showing posts with label SPI. Show all posts
Showing posts with label SPI. Show all posts

Friday, October 16, 2015

SAM4S W5100 driver

In the past several days I was working on many different things, including my project which involves SAM4S development with ASF. I'm working on a device which will communicate on a local network, using W5100 (http://www.wiznet.co.kr/product-item/w5100/). I wrote a "driver" for this ethernet controller in the last days, so, I can start to implement the higher level code which will use this driver to send/receive packets, but that's a different story.

They say that is good to have same pictures in the post to make it more interesting. So, here is my config which helps me to learn the basics of ARM and get some experience with ASF.





Since I didn't find much about SAM4S, W5100 and SPI on the net, I decided to share my code here, it's free to use. 
It has the following features:

  • SPI interface init
  • SPI handling on low level (sending/receiving bytes)
  • w5100 register reading/writing
  • w5100 TX/RX Memory writing/reading (data sending/receiving) including all the calculations needed for that

The driver uses ASF, so you will need the drivers and services which I specified earlier in one of my previous posts about SPI with ASF.

My driver is not a general driver, most likely it won't work out of the box on your board. It's customized on a way as I needed it, however, if you just start to implement something similar, my code could be a very good starting point for you. I tried to comment the less obvious parts, hopefully you will understand it.

You can download the files from here: SAM4 SPI w5100 driver
If you have any question do not hesitate to ask...



If you like my post and/or it was useful, please, leave some comment. If you don't, let me know that too. :-)

Wednesday, October 7, 2015

Assigning I/O to peripheral with IOPORT

Previously I mentioned that I couldn't assign the I/O PINs to SPI (which is the 'A' peripherial) with IOPORT. That was frustrating, because I had to use GPIO for that purpose which is a deprecated service.

Today I managed to solve this issue, here is the solution:


#define SPI_MISO_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA12_IDX)
#define SPI_MOSI_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA13_IDX)
#define SPI_SPCK_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA14_IDX)
#define SPI_NPCS1_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA31_IDX)


 ioport_set_pin_mode(SPI_MISO_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_MISO_IOPIN);
 ioport_set_pin_mode(SPI_MOSI_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_MOSI_IOPIN);
 ioport_set_pin_mode(SPI_SPCK_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_SPCK_IOPIN);
 ioport_set_pin_mode(SPI_NPCS1_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_NPCS1_IOPIN);

The trick is that you have to disable the pin after assigning it to one of the peripherals.

If you implemented your SPI communication based on my previous post and you want to get rid of GPIO, add the defines to your header file and overwrite the spi_init function with the followings:


void spi_init(void)
{
 
 ioport_set_pin_mode(SPI_MISO_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_MISO_IOPIN);
 ioport_set_pin_mode(SPI_MOSI_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_MOSI_IOPIN);
 ioport_set_pin_mode(SPI_SPCK_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_SPCK_IOPIN);
 ioport_set_pin_mode(SPI_NPCS1_IOPIN, PIO_PERIPH_A);
 ioport_disable_pin(SPI_NPCS1_IOPIN);
 
 
 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_CS_KEEP_LOW
 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);
}

If you like my post and/or it was useful, please, leave some comment. If you don't, let me know that too. :-)

Tuesday, October 6, 2015

SPI with ASF

That's my first SPI master implementation.
I didn't find out yet how to initialize the PINs correctly with IOPORT, that's why I still use GPIO.

Here you are:

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);
}

Call spi_init either in init.c or in your main block. After that, you can start to send/receive data through SPI with spi_tx/spi_rx functions.



If you like my post and/or it was useful, please, leave some comment. If you don't, let me know that too. :-)