generic SPI driver for AT91SAM7S256

I am looking for a generic SPI driver for the AT91SAM7S256. Preferably something that uses Atmel’s header files. I just want something to configure the peripheral and a couple of functions to send a receive data. I have my own code but it isnt working and I would like to look at someone elses code to see where I am going wrong before I start posting a ton of questions.

Ryan

You can try this. It’s the SPI routines that I made up bundled with a test program I made just to pull data from a touchscreen controller. Haven’t played with this in a long time, so I’m not sure how good it is. I pulled the UART stuff out of the main program. It uses the AT91SAM7S256.h header file and definitions and I think there’s some decent comments in there.

http://www.higginstribe.com/sam7/at91sam7-spi.zip

thankyou

WOW! I just spent an hour or so looking over your code. This is EXACTLY what I needed, doing it EXACTLY the way I would have done it. Really easy to follow as well. I have a few questions but nothing the data sheet wont answer. I really appreciate this. I will probably use my slightly modified version of this as a template for every SPI driver I ever have to write for the AT91SAM7S.

You should make this file searchable within the forum.

If you’re using the SAM7, you should read up on the PDC (Peripheral DMA). It is very easy to use and saves a lot of cycles. Basically you can order the PDC to receive/send a block of data to/from memory through the SPI, without CPU intervention.

I hope you are monitoring this because I have a few questions.

You appear to be using the NCPS0 and NCPS1 pins as your chip selects. My hardware is using GPIO as chip selects. I have two devices, an FPGA and flash.

I am not sure how to program the SPI_MR and the SPI_CSR given the situation. Especially using your line of code:

pSPI->SPI_CSR[npcs] = npcs_configuration;

My guess is I call spi_init twice, once for each peripheral with (0,config) and (1,config). No that doesn’t make sense because every line of code is redundant except the above. I don’t see how else to set up multiple SPI_CSR’s though. Bug? Just wasn’t paying attention? I think I am going to pull your parameters out of your function and hardcode SPI_CSR[0] and SPI_CSR[1] in there. BTW, where is SPI_CSR defined as an array? I cant find it anywhere in AT91SAM7S256.h.

Bottome line, how do I set SPI up so that it leaves NPCS0:3 as GPIO but still allows me to switch between the configurations in SPI_CSR[0] and SPI_CSR[1]?

Why are there multiple NPCS pins? Like I think there are two NPCS1’s, two NPCS2’s and three NPCS3’s. Whats up with that?

Monstrum,

Easy to use huh? I have some code that some friends gave me from a project they worked on but my understanding was they could never get it to work. They were smart kids so I got a little nervous about using it but yes I would prefer to use it. I will be sucking in data off of UART0 and spitting it out SPI to flash memory. From there I toggle a few bits on my Xilinx FPGA and it sucks in all the data from the flash. Sounds like an extra step but the FPGA has to have the config file available every time it comes out of reset. This way I only have to program it once.

Well, I will admit that “easy to use” is always a relative term. It’s easy if you know how to do it…

The basics: You have one PDC transmitter and reciever for each perhipheral. So pretty much you just set it’s address to point to some memory and set a counter register to the number of transfers you want it to make and set the start transfer bit. When it has recieved or transmitted the number of words it flags that it is done, and of course you can have it set off an interrupt.

It’s actually pretty clear in the datasheet how to use it.

I have a love/hate relationship with ARM. Nothing like the feeling when I get it to work but oh the frustration of getting there…

I haven’t used the SAM7 in a while and I really don’t plan on using it again if I can help it. I hate to harp on this, but the CM3 parts are just easier. Dealing with a million registers is just such a pain. I had to go back to the datasheet for this stuff.

I noticed I missed the header file.

http://www.higginstribe.com/sam7/lib_spi.h

This is something you’re going to have to read the spec on. I’m using variable peripheral select. There are 4 chip select registers, one for each peripheral NPCS0 thru NPCS3. I’m not certain what you are looking at, but there is just one of each. Ya, I kinda shortcutted on this part because I just needed two selects, so it’s a bit hardcoded in the initialization.

If you want to use GPIO for chip select, do not assign the NPCS pins to their alternate functions. I’m assuming you’ll want to use fixed peripheral select for this, but you can keep the variable peripheral setup especially if you have different setup for your two slaves.

I implemented DMA SPI with a FATFS library based on someone else’s code. Yes, it seems easy after you’ve done it.

My job is to make this work on the given hardware. This was designed by a professor who teaches a class on designing a preemptive multitasking operating system for a 32 bit microcontroller. The given hardware for the class has been the AT91SAM7S for the past couple of years.

The other half of my job is to find a new development kit which uses a Cortex M3 and rewrite the labs to work with the new development platform. I am excited to dive into something that seems so much simpler.

Any suggestions on a development kit? It has to have at least 64k of RAM. It must als be compatible with free open source development tools such as GCC but I am pretty sure all ARM cores are.

My only experience is with the Luminary Micros and I make up my own boards, so I don’t have any experience with dev boards. I have a few LPC11xx and LPC13XX chips here, but I haven’t used them for anything because they only support Serial Wire Debug and not full JTAG like the Luminary chips do. I’m looking forward to Rowley putting out an SWD adapter and using the LPC chips. SWD is nicer to design a board around, because of the fewer connections.

This is my new setup function:

void spi_init()
{
   volatile AT91PS_SPI pSPI = AT91C_BASE_SPI;  // Pointer to SPI data structure
   volatile AT91PS_PMC pPMC = AT91C_BASE_PMC;  // Pointer to PMC data structure
   volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA;  // Pointer to PIO data structure
 
   AT91F_SPI_Reset(pSPI); // software reset
   AT91F_SPI_CfgPMC(); // enable SPI peripheral clock 

   // All I/O lines are inputs with the pullup enabled at rest (burn that into memory)
   AT91C_BASE_PIOA->PIO_OER = BIT13 | BIT14; //BIT13=MOSI=output, BIT14=SPICLK=output
   AT91C_BASE_PIOA->PIO_PPUDR = BIT13 | BIT14;
   pPIO->PIO_PDR = BIT12 | BIT13 | BIT14; //Set PIO to NOT use the SPI pins. PA12_MISO PA13_MOSI PA14_SPCK
   pPIO->PIO_ASR = BIT12 | BIT13 | BIT14; //Set Alternate PIO A for SPI pins.
   
   // Setup mode register
   pSPI->SPI_MR =  AT91C_SPI_MSTR // Enable master mode
      | AT91C_SPI_PS_VARIABLE // Watch out for this...  We want the features but we arnt using the pins...
      | AT91C_SPI_MODFDIS; // Disable mode fault detection
     
   // this needs to be fixed // AT91F_SPI_DisableIt(pSPI); // All interupts are off

   // Setup Chip Select Register for flash memory
   pSPI->SPI_CSR[0] = AT91C_SPI_NCPHA  // SPI Bus Protocol Mode 0 (The AT26F004 supports modes 0 and 3)
      | AT91C_SPI_BITS_8 // 8 bit data transfers
      | ((2<<8) & AT91C_SPI_SCBR) // Baud Rate = MCK/SCBR = 24MHz (The AT26F004 supports a maximum of 33MHz)
      | ((6<<16) & AT91C_SPI_DLYBS)  // Dont know about this one,
      | ((6<<24) & AT91C_SPI_DLYBCT); // or this one yet...
      
   // Enable SPI
   AT91F_SPI_Enable(pSPI);
}

When I use this:

void spi_send(unsigned char ss_num, unsigned int data)
{
   volatile AT91PS_SPI pSPI = AT91C_BASE_SPI;
   
   AT91F_SPI_PutChar(pSPI, 'U', 1);  // lets see if this works for now?

   //while(!(pSPI->SPI_SR & (AT91C_SPI_TDRE))); //wait for a clear transmit register
   //pSPI->SPI_TDR = ((data & 0xFFFF) | (ss_num<<16)); //assign the transmit register plus the chip select
}

to initiate a transfer I get nothing.

Here is where I think the problem is. I have two slave devices connected to the processor. Neither one of them are using the designated chip select pins but are instead using GPIO. The chip select pins are already being used for other things. Don’t ask my why, the hardware designer doesn’t really remember why he did it this way either.

I would like to take avantage of the features available in variable chip select such as writing to bits 19:16 in the transmit data register during a transmission in order to determine the configuration of the SPI port but I am not sure how given my current setup.

As you can see in my setup code I do not pass control of the chip select pins to the SPI port so I am wondering if that is why I am not seeing a transmission when I call my send function.

Does anyone know if I can still use variable chip select mode while still using the dedicated chip select pins for things other than SPI?

A bit late. Let’s hope people are still interested.

As you can see in my setup code I do not pass control of the chip select pins to the SPI port so I am wondering if that is why I am not seeing a transmission when I call my send function.

I lost a few days due to this. It is very well disguised as hardware bug.

If you intend to use fixed peripheral mode without hardware SPI-control of the NPCS-pin, you must set the MODFDIS-bit in SPI Mode Register to disable mode-fault detection.

The feature is supposed to be used when you have several masters on the bus. If one master claims the bus by pulling an NPCS-pin low, the SAM7 will stall the SPI until it is released. All good, but, what happens when you don’t give SPI control of the NPCS-pin and manually pull it low? Well, the SPI seems to think another master has claimed it and stalls.

So, disable the mode-fault detection and you’re all good.

Maybe we should start a SAM7-peripheral wiki to collect all these booby-traps and how to avoid them.

Actually you can see in the code I posted previously that I did take care of that. But I do appreciate the response. I scratched my head for a day or two while staring at the scope crossing my fingers. I had the idea in the back of my head that changing from VARIABLE to FIXED peripheral select would probably get me up and running but instead I scoured the forms for a definite answer. I learned some great stuff, I just never found an answer. In the end I just tried my initial idea and things came right up.

Seems like this setup would be more common than it appears. I couldn’t find more than a forum post or two with no helpful answers. But I realize now that I really don’t need that functionality. It looks like it is there more for DMA than anything else and I hear you cant use DMA without using the NPCS pins anyways so whats the use. I wont be switching devices that often anyways. I have a DataFlash and an FPGA with a program that sends a file to one or the other. Once that is done I give the bus back to the hardware developer and he is free to use the bus as he pleases.

Thanks again for the post. It was like pulling teeth, trying to get answers. In the end I got it working without one. I wish there was an AT91SAM7 forum half as good as avrfreaks. I swear I post a question at 3:00AM and I have 5 responses by 4:00AM.

Oh I did find this though.

http://www.atmel.com/dyn/products/tools … ol_id=3784

Scroll down to “AT91SAM7S-EK Software Package for IAR 5.2, Keil and GNU” under Software and you have a zip file with a lot of complete example applications. There is a driver for everything in there. They even had a driver for the at26 DataFlash which uses the same command set as the one that I am using but it turned out to be faster to write my own than attempt to port.