USE of SPI bus with CS1

ArkEngr
 
Posts: 21
Joined: Tue Oct 20, 2015 1:58 pm

USE of SPI bus with CS1

Thu Aug 02, 2018 10:40 pm

Here is some code to use the SPI port with CS1 I have tested it.
It was designed to work with a FM25V02A 32Kx8 FRAM memory which is simular to EE

The SPI port is very interesting it does chunks of DATA in 32 bytes max in the mode implemented here.
These functions loop until all the data is written/read so you can pass a large buffer to them.

The CS1 is driven for you, you do not need to worry about it.

/*******************************************************************************
* NAME: RWframVocore2
*
* COMPONENT HISTORY:
* Ver: User Date Action Description.
* --------- ---------- -----------------------------------
* 1.0 ArkEngr 05/31/2018 Created file
*
* Uses SPI with CS1, MOSI, MISO, CLK pins
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

typedef unsigned char USIGN8;
typedef unsigned short USIGN16;
typedef unsigned long USIGN32;
typedef unsigned int UINT;
typedef signed char INT8;
typedef signed short INT16;
typedef signed long INT32;
typedef char CHAR;

/* For use with FM25V02A 32Kx8 FerroElectric Memory */
#define PAGE_SIZE 4096
#define FRAM_WREN 6
#define FRAM_WRDIS 4
#define FRAM_RDSR 5
#define FRAM_WRSR 1
#define FRAM_READ 3
#define FRAM_WRITE 2

#define MEM_BASE 0x10000000
#define SPI_BUSY 0x00010000
#define SPI_START 0x00000100

#define SPI_MODE 0x0B28
#define SPI_MORE 0x0B2C
#define SPI_ADDR 0x0B04
#define SPI_CS1_ADDR 0x0B38
#define SPI_MST_ADDR 0x0B00
#define SPI_DIDO_0 0x0B08
#define SPI_DIDO_1 0x0B0C
#define SPI_DIDO_2 0x0B10
#define SPI_DIDO_3 0x0B14
#define SPI_DIDO_4 0x0B18
#define SPI_DIDO_5 0x0B1C
#define SPI_DIDO_6 0x0B20
#define SPI_DIDO_7 0x0B24

USIGN32 *mmap_page(void);
void fram_write(USIGN8 *ptr_src_dat, USIGN16 dest_adr, USIGN16 len);
void fram_read(USIGN16 src_adr, USIGN8 *ptr_dest, USIGN16 len);
void fram_byte_xchg(void);

USIGN32 *mem = NULL;

int main()
{
USIGN8 i, buffer[200];

printf("ver 1.0\n");

mem = mmap_page();

if(mem == NULL)
{
printf("can not map memory.\n");
return -1;
}

for(i = 0; i < 200; i++) /* fill with test data */
{
buffer[i] = i;
}

fram_write(buffer, 0, 200);

for(i = 0; i < 200; i++)
{
buffer[i] = 0xA5; /* fill with idiot data */
}

fram_read(0, buffer, 200);

for(i = 0; i < 200; i++) /* test */
{
printf("Addr: %4d, Data: %4d\n", i, buffer[i]);
}

munmap(mem, PAGE_SIZE);
}


USIGN32 *mmap_page(void)
{
INT32 fd = -1;
USIGN32 *fmem = NULL;

fd = open("/dev/mem", O_RDWR);

if(fd < 0)
{
return NULL;
}

fmem = (USIGN32 *)mmap(0, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, MEM_BASE);

close(fd);

return fmem;
}

/*****************************************************************************
FUNCTIONAL_DESCRIPTION

Write a Data Block to FRAM

RETURN_VALUE: none
*****************************************************************************/
void fram_write(USIGN8 *ptr_src_dat, USIGN16 dest_adr, USIGN16 len)
{
USIGN16 i;

USIGN32 U8cmd, U8cmdCount, U16fram_addr, mosiCount, mosiBitCnt, ModeSave, ModeSet, WindowCnt;


// Wait until SPI not busy
while((*((USIGN32 *)(mem + (SPI_MST_ADDR >> 2))) & SPI_BUSY) == SPI_BUSY)
{
usleep(100000); // sleep for 100 milliseconds
} // Wait for SPI BUSY clear

// Change clock speed
ModeSave = *((USIGN32 *)(mem + (SPI_MODE >> 2)));
ModeSet = 0x215E8984; // more_buf_mode = 1, half duplex
*((USIGN32 *)(mem + (SPI_MODE >> 2))) = ModeSet;
WindowCnt = 0;

// loop until done
do
{
// Do Write Enable
U16fram_addr = FRAM_WREN;
U8cmdCount = 8; // bits in enable command
U8cmdCount = U8cmdCount << 24;

*((USIGN32 *)(mem + (SPI_MORE >> 2))) = U8cmdCount; // 2C Command and Data len
*((USIGN32 *)(mem + (SPI_ADDR >> 2))) = U16fram_addr; // 04

fram_byte_xchg(); // transfer the data

// Now Write Data
U8cmd = FRAM_WRITE;

U16fram_addr = U8cmd;
U16fram_addr = (U16fram_addr << 16) + dest_adr;

if(len >= 32)
{
mosiCount = 32;
len = len - 32;
}
else
{
mosiCount = len;
len = 0;
}

U8cmdCount = 24; // bits in write command
U8cmdCount = U8cmdCount << 24;
mosiBitCnt = mosiCount * 8; // Bits to write
U8cmdCount = U8cmdCount + mosiBitCnt; // Add outgoing msg len to command len

// Start FRAM Write
*((USIGN32 *)(mem + (SPI_MORE >> 2))) = U8cmdCount; // 2C Command and Data len
*((USIGN32 *)(mem + (SPI_ADDR >> 2))) = U16fram_addr; // 04

// printf("transfer data to regs\n");
// Load Dest with data
for(i = 0; i < mosiCount; i+=4)
{
*((((USIGN32 *)mem) + ((SPI_DIDO_0 + i) >> 2))) = *((USIGN32 *)(ptr_src_dat + i + WindowCnt)); // Write next data byte to FRAM
}

fram_byte_xchg(); // transfer the data

WindowCnt = WindowCnt + mosiCount;
dest_adr = dest_adr + mosiCount;

// End Loop
} while(len > 0);

usleep(10); // sleep for 100 milliseconds

*((USIGN32 *)(mem + (SPI_MODE >> 2))) = ModeSave;

return;
}

/*****************************************************************************
FUNCTIONAL_DESCRIPTION

Read a Data Block from FRAM

RETURN_VALUE: none
*****************************************************************************/
void fram_read(USIGN16 src_adr, USIGN8 *ptr_dest, USIGN16 len)
{
USIGN16 i;
USIGN32 int_buff;

USIGN32 U8cmd, U8cmdCount, U16fram_addr, misoCount, misoBitCnt, ModeSave, ModeSet, WindowCnt;

// Wait until SPI not busy
while((*((USIGN32 *)(mem + (SPI_MST_ADDR >> 2))) & SPI_BUSY) == SPI_BUSY)
{
usleep(100000); // sleep for 100 milliseconds
} // Wait for SPI BUSY clear

// Change clock speed
ModeSave = *((USIGN32 *)(mem + (SPI_MODE >> 2)));
ModeSet = 0x215E8984; // more_buf_mode = 1, half duplex
*((USIGN32 *)(mem + (SPI_MODE >> 2))) = ModeSet;

U8cmd = FRAM_READ;
WindowCnt = 0;

// loop until done
do
{
U16fram_addr = U8cmd;
U16fram_addr = (U16fram_addr << 16) + src_adr;

if(len >= 32)
{
misoCount = 32;
len = len - 32;
}
else
{
misoCount = len;
len = 0;
}

U8cmdCount = 24; // bits in read command
U8cmdCount = U8cmdCount << 24;
misoBitCnt = misoCount * 8; // Bits to read
U8cmdCount = U8cmdCount + (misoBitCnt << 12); // Add outgoing msg len to command len

// Start FRAM Read
*((USIGN32 *)(mem + (SPI_MORE >> 2))) = U8cmdCount; // 2C Command and Data len
*((USIGN32 *)(mem + (SPI_ADDR >> 2))) = U16fram_addr; // 04

fram_byte_xchg(); // transfer the data

// printf("transfer data from regs\n");
// Load Dest with data
for(i = 0; i < misoCount; i+=4)
{
int_buff = *((((USIGN32 *)mem) + ((SPI_DIDO_0 + i) >> 2))); // Read next data byte from FRAM

*((USIGN8 *)(ptr_dest + i + WindowCnt)) = *(((USIGN8 *)&int_buff));

if((i + 1) < misoCount)
{
*((USIGN8 *)(ptr_dest + i + 1 + WindowCnt)) = *(((USIGN8 *)&int_buff) + 1);

if((i + 2) < misoCount)
{
*((USIGN8 *)(ptr_dest + i + 2 + WindowCnt)) = *(((USIGN8 *)&int_buff) + 2);

if((i + 3) < misoCount)
{
*((USIGN8 *)(ptr_dest + i + 3 + WindowCnt)) = *(((USIGN8 *)&int_buff) + 3);
}
}
}
}

WindowCnt = WindowCnt + misoCount;
src_adr = src_adr + misoCount;

// End Loop
} while(len > 0);

usleep(10); // sleep for 100 milliseconds

*((USIGN32 *)(mem + (SPI_MODE >> 2))) = ModeSave;

return;
}

/*****************************************************************************
FUNCTIONAL_DESCRIPTION

SPI Byte Transfer to or from FRAM

RETURN_VALUE: none
*****************************************************************************/
void fram_byte_xchg(void)
{
USIGN8 usval;

*((USIGN32 *)(mem + (SPI_MST_ADDR >> 2))) = SPI_START; // Start data transfer


while((*((USIGN32 *)(mem + (SPI_MST_ADDR >> 2))) & SPI_BUSY) == SPI_BUSY)
{
// printf("waiting on spi 0x%08X\n", *((USIGN32 *)(mem + (SPI_MST_ADDR >> 2))));
usleep(10); // sleep for 100 milliseconds
} // Wait for SPI BUSY clear

return;
}

Vonger
 
Posts: 679
Joined: Sun Oct 19, 2014 6:00 am

Re: USE of SPI bus with CS1

Fri Aug 03, 2018 1:49 pm

nice code :)

ArkEngr
 
Posts: 21
Joined: Tue Oct 20, 2015 1:58 pm

Re: USE of SPI bus with CS1

Thu Aug 09, 2018 1:07 am

I have found a caution on using this code. If your application is making changes to the file system while using the SPI peripheral, the Linux OS may corrupt the SPI registers and cause errors. I have found 2 ways to improve this. First is to only write files to /tmp or /var (same place) while using the SPI peripheral. Files in this location are not permanent. Second alternative is to check the contents of the register at 0x10000B04 at the end of each Read or Write and if it corrupted assume that the use of the SPI registers was pre-empted by Linux and repeat the previous operation.

Return to VoCore2/Lite/Ultimate

Who is online

Users browsing this forum: No registered users and 1 guest