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. :-)

Should I use ASF?


I have already spent around 20 hours with implementing SPI communication between SAM4S16C and Wiznet W5100. For that I'm using a SAM4S XPlained board and a WIZ812MJ. W5100 is a well-known chip for me, I've already used with ATmega 128 many times. Still, to get it working, I had to power on my old good Tektronix to check the lines one-by-one and go step-by-step.

Here is the story and my first impression of ASF:
So, I've started with a quick research on others opinion about ASF. Those who already have experience with it say that, beginners should start with ASF since it's easier to write some working program with ASF. That's true if you:

  • are happy with the pre-written sample projects
  • don't want to write a program which is more complex than blinking a LED
However, if you would rather do something on your own, questions start to rise immediately which you will rarely find answers for in ASF's documentation.

BTW documentation. Atmel released a series of "ASF - Getting started video". First part available here: https://www.youtube.com/watch?v=r9UFzNEC62E

In this videos they are very proud of that, help and documentation is available directly from AtmelStudio's ASF Explorer. Well, not really.
ASF Doc inside AtmelStudio

Otherwise, the video series is helpful and helps to get a basic understanding of ASF.

Since people are suggesting to start with ASF, I thought, I give it a try. I've setup an ASF project and decided to:
  1. implement USB CDC-stdio  to get an interface which helps me to debug further code
  2. implement SPI between the MCU and W5100

I didn't have any issue with the first objective. I have used the sample implementation and didn't change anything.

Implementing the second objective was much more painful. Atmel provides a so called "Quickstart Guide" for each driver and service. Here is the one for SPI if you want to see. Well, a "Read the datasheet and implement it yourself!" sentence would have been more useful and time saving than the actual content of that guide.

It just doesn't work. It is missing instructions how to assign the I/O lines with the peripheral. Also, no code for sending or receiving data. The example SPI Project is a bit more verbose. Still, it was not enough. Also, for the SPI example they use the deprecated GPIO service (may be for the others too). Since it's deprecated, I didn't want to use it. Instead, I've added it's successor, IOPORT Service to the project. However, I was not able to figure it out how to assign  the I/O lines to SPI with IOPORT service. IOPORT services documentation doesn't say a word about that... Neither GPIO's documentation, however, at least, we can find an example for that in the SPI example project's init.c file Configure SPI pins section. I needed around 15 hours to get the code working. For that, I had to:
  • use an oscilloscope to check the outputs
  • read the datasheet and check the registers
  • kind of reverse engineer ASF to find out what happens in the background.
I have to say, if I would have started implementing the code without ASF, just using the datasheet then I would have finished it earlier.

For the same task(SPI comm. with W5100) I needed approx 2 hours for the implementation on ATmega128 and I didn't need an oscilloscope to make sure the things goes well. 


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

The first post

Hello All,

For some reason I decided to start a technical blog about sw development for Atmel's SAM4S MCU. In my first post I would write a few words about myself and my motivation.

About me: I'm a software developer who works mainly with high level languages such as C# and Java. I just started to learn C also I've never worked with 32bit MCUs, so, this blog is going to be a beginners blog in this regard. In the other hand, I have 6 years experience in software design also have experience with Atmel's mega family which means, I'm not completely newbie for the topic.

As you may already noticed my English knowledge is still on a low level, I'm sorry about that. However, I think I can share here useful content with those who are interested in SAM4 development. That's why started it, regardless the lack of English skills. Since literature is out of scope here, I think that's not a big issue.

Why did I start this blog:
Internet is full with tutorials and working examples for AVRs. A significant part of my AVR related knowledge derives from the net. I thought that I will find a similar community, a similar support for SAM controllers too. Well, I didn't. I've found a bunch of forums full with unanswered questions. When there are answers for the questions, they are rarely accurate or complete so the quality of them is questionable.

Atmel provides nice sample projects which usually work out of the box. Almost all sample project for the SAM evaluation boards based on Atmel Software Framework (ASF). This framework supposed to hide the low level register operations and provide a higher level API. Such a framework is a very good initiative. However, the documentation of this framework is almost useless which results in a useless framework. Even if I have a good framework, it is useless if I don't know how to use it.

Anyways, it was much more harder to implement even a simple SPI communication in master mode compared to AVR because of the poor documentation of the framework and because I tried to collect the necessary knowledge only from the forums. Finally, I realized that this approach just wont work now. I have to actively use the datasheet and understand all the registers. Yes, I know, that's the right way. If somebody want to create something using an MCU, she/he should know the device well. But then what is the point of a framework (pushed hardly by the manufacturer) which tries to hide the chip?

So, I decided to document my way of learning SAM4, more specifically SAM4S16C. I will share with you my experiences and provide with use-cases and code snippets.


Regarding the methodology:
I've started working on a (hobby) project which involves a lot of technology I don't have (much) experience with. I'm going to implement a Building Automation system, which for I need a central device with a relatively cheap but fast processor. This system is also out of scope of this blog (just like literature), however, all the necessary knowledge needed to implement the firmware of that device will be documented here.

That will involve:

  • SPI communication
  • UART communication
  • interrupts
  • external RAM
  • TCP/IP communicaton (using W5100)
  • RS-485
  • ZigBee with XBee®
  • etc


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