EmbeddedRelated.com
Forums
Memfault Beyond the Launch

SPI chip select questions

Started by mla_o_matic June 17, 2005
Greetings Fellas,

Im only having one device on my SPI bus. This device needs CS (SS or
NPCS.. pick your choice), as delimiter between transactions. I use DMA
for posted writes (I dont use interrupts but trying to gain some
performance).

The problem is that AT91C_SPI_DLYBS only affects delay between two
_different_ chipselects. The Chipselect in my code will only go high
for about half a clockcycle, which appears to be to little for this
device... Does anyone with greater (or less for that matter) SPI
experience, have any great ideas if this is something I can pulloff
beside doint a stupid wait?

Here is some code write_reg() can be called in various places in the
code.

Note Im using the lib_AT91SAMS64.h and GCC 3.4 on the IAR develkit.

Oh, and the PDC (DMA) is really sweet in the SAM7's

Regards, Magnus

%<--------------------------------- code

#define spi AT91C_BASE_SPI
#define spi_pdc AT91C_BASE_PDC_SPI

#define SPIBAUD 500000
#define CS_BAUD MCK / SPIBAUD static inline void
spi_sendframe(uint8_t *pBuffer, uint32_t szBuffer, uint8_t
*pNextBuffer,
uint32_t szNextBuffer)
{
if (AT91F_SPI_SendFrame(spi, (char *)pBuffer, szBuffer,
(char *)pNextBuffer, szNextBuffer) ==0) {
printf("SPI broken");
}
} void
spi_init(void)
{
/* disable and reset */
spi->SPI_CR = AT91C_SPI_SPIDIS | AT91C_SPI_SWRST;

AT91F_SPI_CfgPMC(); /* enable clock */

spi->SPI_MR = ((32 << 24) | AT91C_SPI_MSTR | /* Master */
AT91C_SPI_PS_FIXED); /* Fixed periheral (== 0) */

AT91F_SPI_CfgCs(spi, 0,
AT91C_SPI_CPOL |
(AT91C_SPI_BITS & AT91C_SPI_BITS_8) |
(AT91C_SPI_SCBR & (CS_BAUD << 8)) |
(AT91C_SPI_DLYBS & (128 << 16)) |
(AT91C_SPI_DLYBCT & (0 << 24))
);

AT91F_PIO_CfgPeriph( AT91C_BASE_PIOA, // PIO controller base address
((unsigned int) AT91C_PA11_NPCS0 ) |// Peripheral A
((unsigned int) AT91C_PA13_MOSI ) |
((unsigned int) AT91C_PA12_MISO ) |
((unsigned int) AT91C_PA14_SPCK ), 0); AT91F_PDC_Open(spi_pdc);
AT91F_SPI_Enable(spi); }

void
write_reg(uint8_t page, uint8_t addr, void *val, int len)
{
static uint8_t cmd[2] = {SD_WRITE(0), };

cmd[1] = addr;

while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
spi_sendframe(cmd, sizeof(cmd), val, len);
}



I think what I wound up doing was using the mode where the CS number was in
the data word, and setting up a second device as a dummy with the actual CS
output not enabled. That way I could queue a byte destined for the dummy
device and it'd de-assert CS on the real device while it sent the dummy byte
to the other device. I forget the register names, but if that doesn't make
sense let me know and I'll find the code.

Scott

> -----Original Message-----
> From: AT91SAM7@AT91...
> [mailto:AT91SAM7@AT91...] On Behalf Of mla_o_matic
> Sent: Friday, June 17, 2005 1:58 AM
> To: AT91SAM7@AT91...
> Subject: [AT91SAM7] SPI chip select questions
>
> Greetings Fellas,
>
> Im only having one device on my SPI bus. This device needs CS
> (SS or NPCS.. pick your choice), as delimiter between
> transactions. I use DMA for posted writes (I dont use
> interrupts but trying to gain some performance).
>
> The problem is that AT91C_SPI_DLYBS only affects delay
> between two _different_ chipselects. The Chipselect in my
> code will only go high for about half a clockcycle, which
> appears to be to little for this device... Does anyone with
> greater (or less for that matter) SPI experience, have any
> great ideas if this is something I can pulloff beside doint a
> stupid wait?
>
> Here is some code write_reg() can be called in various places
> in the code.
>
> Note Im using the lib_AT91SAMS64.h and GCC 3.4 on the IAR develkit.
>
> Oh, and the PDC (DMA) is really sweet in the SAM7's
>
> Regards, Magnus
>
> %<--------------------------------- code
>
> #define spi AT91C_BASE_SPI
> #define spi_pdc AT91C_BASE_PDC_SPI
>
> #define SPIBAUD 500000
> #define CS_BAUD MCK / SPIBAUD > static inline void
> spi_sendframe(uint8_t *pBuffer, uint32_t szBuffer, uint8_t
> *pNextBuffer,
> uint32_t szNextBuffer)
> {
> if (AT91F_SPI_SendFrame(spi, (char *)pBuffer, szBuffer,
> (char *)pNextBuffer, szNextBuffer) ==0) {
> printf("SPI broken");
> }
> } > void
> spi_init(void)
> {
> /* disable and reset */
> spi->SPI_CR = AT91C_SPI_SPIDIS | AT91C_SPI_SWRST;
>
> AT91F_SPI_CfgPMC(); /* enable clock */
>
> spi->SPI_MR = ((32 << 24) | AT91C_SPI_MSTR | /* Master */
> AT91C_SPI_PS_FIXED); /* Fixed periheral (== 0) */
>
> AT91F_SPI_CfgCs(spi, 0,
> AT91C_SPI_CPOL |
> (AT91C_SPI_BITS & AT91C_SPI_BITS_8) |
> (AT91C_SPI_SCBR & (CS_BAUD << 8)) |
> (AT91C_SPI_DLYBS & (128 << 16)) |
> (AT91C_SPI_DLYBCT & (0 << 24))
> );
>
> AT91F_PIO_CfgPeriph( AT91C_BASE_PIOA, // PIO controller
> base address
> ((unsigned int) AT91C_PA11_NPCS0 ) |// Peripheral A
> ((unsigned int) AT91C_PA13_MOSI ) |
> ((unsigned int) AT91C_PA12_MISO ) |
> ((unsigned int) AT91C_PA14_SPCK ), 0); > AT91F_PDC_Open(spi_pdc);
> AT91F_SPI_Enable(spi); > }
>
> void
> write_reg(uint8_t page, uint8_t addr, void *val, int len) {
> static uint8_t cmd[2] = {SD_WRITE(0), };
>
> cmd[1] = addr;
>
> while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
> spi_sendframe(cmd, sizeof(cmd), val, len); } >
> Yahoo! Groups Links


Hi Scott,

Thanks for the reply.. Although, this was not the bug. Changing the
send function in the following way, fixed the problem. I leave out to
the reader to figure why the latter works and the previous dont... ;)

/Magnus

p.s. Think parallellism, DMA and memory overwrite .d.s.

void
write_reg(uint8_t page, uint8_t addr, void *val, int len)
{
static uint8_t cmd[10] = {SD_WRITE(0), };

while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
cmd[1] = addr;
spi_sendframe(cmd, sizeof(cmd), val, len);
} --- In AT91SAM7@AT91..., <scott@o...> wrote:
> I think what I wound up doing was using the mode where the CS number
was in
> the data word, and setting up a second device as a dummy with the
actual CS
> output not enabled. That way I could queue a byte destined for the
dummy
> device and it'd de-assert CS on the real device while it sent the
dummy byte
> to the other device. I forget the register names, but if that
doesn't make
> sense let me know and I'll find the code.
>
> Scott

> >
> > void
> > write_reg(uint8_t page, uint8_t addr, void *val, int len) {
> > static uint8_t cmd[2] = {SD_WRITE(0), };
> >
> > cmd[1] = addr;
> >
> > while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
> > spi_sendframe(cmd, sizeof(cmd), val, len);
> > }
> >
> >


Magnus,

Does all this code work for you? I have looked at several posts and
everyone seems to be doing something different. Should I be using
AT91F_PMC_EnablePeriphClock() or AT91F_SPI_CfgPMC() ?

Also why are you pulling the clock up? Don't you wan the clock to be
low when it is not active?

And what is the variable "SPIO" is that jsut the structure AT91S_SPI?

Thanks for the clarification.

-Henk

--- In AT91SAM7@AT91..., "mla_o_matic" <mla@p...> wrote:
>
> %<--------------------------------- code
>
> #define spi AT91C_BASE_SPI
> #define spi_pdc AT91C_BASE_PDC_SPI
>
> #define SPIBAUD 500000
> #define CS_BAUD MCK / SPIBAUD > static inline void
> spi_sendframe(uint8_t *pBuffer, uint32_t szBuffer, uint8_t
> *pNextBuffer,
> uint32_t szNextBuffer)
> {
> if (AT91F_SPI_SendFrame(spi, (char *)pBuffer, szBuffer,
> (char *)pNextBuffer, szNextBuffer) ==0) {
> printf("SPI broken");
> }
> } > void
> spi_init(void)
> {
> /* disable and reset */
> spi->SPI_CR = AT91C_SPI_SPIDIS | AT91C_SPI_SWRST;
>
> AT91F_SPI_CfgPMC(); /* enable clock */
>
> spi->SPI_MR = ((32 << 24) | AT91C_SPI_MSTR | /* Master */
> AT91C_SPI_PS_FIXED); /* Fixed periheral (== 0) */
>
> AT91F_SPI_CfgCs(spi, 0,
> AT91C_SPI_CPOL |
> (AT91C_SPI_BITS & AT91C_SPI_BITS_8) |
> (AT91C_SPI_SCBR & (CS_BAUD << 8)) |
> (AT91C_SPI_DLYBS & (128 << 16)) |
> (AT91C_SPI_DLYBCT & (0 << 24))
> );
>
> AT91F_PIO_CfgPeriph( AT91C_BASE_PIOA, // PIO controller base address
> ((unsigned int) AT91C_PA11_NPCS0 ) |// Peripheral A
> ((unsigned int) AT91C_PA13_MOSI ) |
> ((unsigned int) AT91C_PA12_MISO ) |
> ((unsigned int) AT91C_PA14_SPCK ), 0); > AT91F_PDC_Open(spi_pdc);
> AT91F_SPI_Enable(spi); > }
>
> void
> write_reg(uint8_t page, uint8_t addr, void *val, int len)
> {
> static uint8_t cmd[2] = {SD_WRITE(0), };
>
> cmd[1] = addr;
>
> while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
> spi_sendframe(cmd, sizeof(cmd), val, len);
> }




Henk,

I still think the problem is related to what is mentioned on page 234 about
SPI_MR and the PCS field.
It says no transfer will be started when writing into the SPI_TDR if PCS
field does not select a slave. I presume you would see an idle chip select
if this is the case.

Pehaps this help function is required?

//*-
---
//* \fn AT91F_SPI_CfgPCS
//* \brief Switch to the correct PCS of SPI Mode Register : Fixed Peripheral
Selected
//*-
---
__inline void AT91F_SPI_CfgPCS (
AT91PS_SPI pSPI, // pointer to a SPI controller
char PCS_Device) // PCS of the Device
{
//* Write to the MR register
pSPI->SPI_MR &= 0xFFF0FFFF;
pSPI->SPI_MR |= ( (PCS_Device<<16) & AT91C_SPI_PCS );
} Al Welch -----Original Message-----
From: AT91SAM7@AT91... [mailto:AT91SAM7@AT91...] On Behalf
Of pacific_beach_dude
Sent: Tuesday, July 12, 2005 12:13 PM
To: AT91SAM7@AT91...
Subject: [AT91SAM7] Re: SPI chip select questions

Magnus,

Does all this code work for you? I have looked at several posts and
everyone seems to be doing something different. Should I be using
AT91F_PMC_EnablePeriphClock() or AT91F_SPI_CfgPMC() ?

Also why are you pulling the clock up? Don't you wan the clock to be low
when it is not active?

And what is the variable "SPIO" is that jsut the structure AT91S_SPI?

Thanks for the clarification.

-Henk

--- In AT91SAM7@AT91..., "mla_o_matic" <mla@p...> wrote:
>
> %<--------------------------------- code
>
> #define spi AT91C_BASE_SPI
> #define spi_pdc AT91C_BASE_PDC_SPI
>
> #define SPIBAUD 500000
> #define CS_BAUD MCK / SPIBAUD > static inline void
> spi_sendframe(uint8_t *pBuffer, uint32_t szBuffer, uint8_t
> *pNextBuffer,
> uint32_t szNextBuffer)
> {
> if (AT91F_SPI_SendFrame(spi, (char *)pBuffer, szBuffer,
> (char *)pNextBuffer, szNextBuffer) ==0) {
> printf("SPI broken");
> }
> } > void
> spi_init(void)
> {
> /* disable and reset */
> spi->SPI_CR = AT91C_SPI_SPIDIS | AT91C_SPI_SWRST;
>
> AT91F_SPI_CfgPMC(); /* enable clock */
>
> spi->SPI_MR = ((32 << 24) | AT91C_SPI_MSTR | /* Master */
> AT91C_SPI_PS_FIXED); /* Fixed periheral (== 0) */
>
> AT91F_SPI_CfgCs(spi, 0,
> AT91C_SPI_CPOL |
> (AT91C_SPI_BITS & AT91C_SPI_BITS_8) |
> (AT91C_SPI_SCBR & (CS_BAUD << 8)) |
> (AT91C_SPI_DLYBS & (128 << 16)) |
> (AT91C_SPI_DLYBCT & (0 << 24))
> );
>
> AT91F_PIO_CfgPeriph( AT91C_BASE_PIOA, // PIO controller base address
> ((unsigned int) AT91C_PA11_NPCS0 ) |// Peripheral A
> ((unsigned int) AT91C_PA13_MOSI ) |
> ((unsigned int) AT91C_PA12_MISO ) |
> ((unsigned int) AT91C_PA14_SPCK ), 0); > AT91F_PDC_Open(spi_pdc);
> AT91F_SPI_Enable(spi); > }
>
> void
> write_reg(uint8_t page, uint8_t addr, void *val, int len) {
> static uint8_t cmd[2] = {SD_WRITE(0), };
>
> cmd[1] = addr;
>
> while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
> spi_sendframe(cmd, sizeof(cmd), val, len); }

Yahoo! Groups Links


Al,

I believe you are correct. If I want chip select 0 (NPCS0) selected what
should PCS_Device be set to 0x0E?

-Henk Al Welch wrote:
> Henk,
>
> I still think the problem is related to what is mentioned on page 234 about
> SPI_MR and the PCS field.
> It says no transfer will be started when writing into the SPI_TDR if PCS
> field does not select a slave. I presume you would see an idle chip select
> if this is the case.
>
> Pehaps this help function is required?
>
> //*-
> ---
> //* \fn AT91F_SPI_CfgPCS
> //* \brief Switch to the correct PCS of SPI Mode Register : Fixed Peripheral
> Selected
> //*-
> ---
> __inline void AT91F_SPI_CfgPCS (
> AT91PS_SPI pSPI, // pointer to a SPI controller
> char PCS_Device) // PCS of the Device
> {
> //* Write to the MR register
> pSPI->SPI_MR &= 0xFFF0FFFF;
> pSPI->SPI_MR |= ( (PCS_Device<<16) & AT91C_SPI_PCS );
> } > Al Welch > -----Original Message-----
> From: AT91SAM7@AT91... [mailto:AT91SAM7@AT91...] On Behalf
> Of pacific_beach_dude
> Sent: Tuesday, July 12, 2005 12:13 PM
> To: AT91SAM7@AT91...
> Subject: [AT91SAM7] Re: SPI chip select questions
>
> Magnus,
>
> Does all this code work for you? I have looked at several posts and
> everyone seems to be doing something different. Should I be using
> AT91F_PMC_EnablePeriphClock() or AT91F_SPI_CfgPMC() ?
>
> Also why are you pulling the clock up? Don't you wan the clock to be low
> when it is not active?
>
> And what is the variable "SPIO" is that jsut the structure AT91S_SPI?
>
> Thanks for the clarification.
>
> -Henk >
>
> --- In AT91SAM7@AT91..., "mla_o_matic" wrote:
>>
>> %<--------------------------------- code
>>
>> #define spi AT91C_BASE_SPI
>> #define spi_pdc AT91C_BASE_PDC_SPI
>>
>> #define SPIBAUD 500000
>> #define CS_BAUD MCK / SPIBAUD
>>
>>
>> static inline void
>> spi_sendframe(uint8_t *pBuffer, uint32_t szBuffer, uint8_t
>> *pNextBuffer,
>> uint32_t szNextBuffer)
>> {
>> if (AT91F_SPI_SendFrame(spi, (char *)pBuffer, szBuffer,
>> (char *)pNextBuffer, szNextBuffer) ==0) {
>> printf("SPI broken");
>> }
>> }
>>
>>
>> void
>> spi_init(void)
>> {
>> /* disable and reset */
>> spi->SPI_CR = AT91C_SPI_SPIDIS | AT91C_SPI_SWRST;
>>
>> AT91F_SPI_CfgPMC(); /* enable clock */
>>
>> spi->SPI_MR = ((32 << 24) | AT91C_SPI_MSTR | /* Master */
>> AT91C_SPI_PS_FIXED); /* Fixed periheral (== 0) */
>>
>> AT91F_SPI_CfgCs(spi, 0,
>> AT91C_SPI_CPOL |
>> (AT91C_SPI_BITS & AT91C_SPI_BITS_8) |
>> (AT91C_SPI_SCBR & (CS_BAUD << 8)) |
>> (AT91C_SPI_DLYBS & (128 << 16)) |
>> (AT91C_SPI_DLYBCT & (0 << 24))
>> );
>>
>> AT91F_PIO_CfgPeriph( AT91C_BASE_PIOA, // PIO controller base address
>> ((unsigned int) AT91C_PA11_NPCS0 ) |// Peripheral A
>> ((unsigned int) AT91C_PA13_MOSI ) |
>> ((unsigned int) AT91C_PA12_MISO ) |
>> ((unsigned int) AT91C_PA14_SPCK ), 0);
>>
>>
>> AT91F_PDC_Open(spi_pdc);
>> AT91F_SPI_Enable(spi);
>>
>>
>> }
>>
>> void
>> write_reg(uint8_t page, uint8_t addr, void *val, int len) {
>> static uint8_t cmd[2] = {SD_WRITE(0), };
>>
>> cmd[1] = addr;
>>
>> while (!(spi->SPI_SR & AT91C_SPI_TXEMPTY));
>> spi_sendframe(cmd, sizeof(cmd), val, len); } >
>
> >. >
>




Memfault Beyond the Launch