|
|
View previous topic :: View next topic |
Author |
Message |
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
32bit SPI transfers using PIC16F1513? |
Posted: Tue May 01, 2018 11:16 am |
|
|
I am trying to talk to a MAX30003 ECG chip via the SPI but the MAX30003 has a 32bit SPI read/write cycle. The 16F1513 seems rather intent on an 8bit cycle, but I may just be ignorant of a way around this.
The MAX30003 appears to reset the state machine on every assertion of chip select, which happens on 8bit boundaries, so the DO of the MAX30003 is always zero.
I have tried to fool the processor, telling it the wrong pin for chip select and manually controlling the real chip select pin, but when I do that, the PIC doesn't generate the SClk.
So, my question to the greater knowledge is - is there a way to get the 16F1513 to perform a 32bit SPI transfer? I see hints of 32bit transfer versions of the spi_xfer routine, but don't know whether these are available on the 8bit PICs.
Failing a 32bit transfer, can someone please tell me how to set up a single chip select cycle for four 8bit transfers?
Here's what I have so far:
Code: |
#include <uart.h>
// Now trying to spoof the PIC - tell it enable is on A2, and control C2 manually.
#use spi (MASTER, CLK=PIN_C3, DI=PIN_C4, DO=PIN_C5, ENABLE=PIN_C2, BAUD=500000, MODE=3, BITS=8, STREAM=MAX30003_SPI)
#include "MAX30003.h"
#use fast_io(A)
#use fast_IO(B)
#use fast_IO(C)
int32 chip_status;
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1|RTCC_8_BIT);
spi_init( MAX30003_SPI, 500000);
// set_tris_c( 0xFB); // With set_tris_c, there is no clock. Without
// set_tris_c, there is a clock.
while(TRUE)
{
chip_status = MAX30003_ReadReg( MNGR_DYN); // s/b 0x3f0000
delay_us( 250);
}
}
/**
******************************************************************************
* @file MAX30003.c
* @brief Code for the MAX30003 ECG chip
* @author John Griswold
******************************************************************************
*
*
* COPYRIGHT( C )2018 Vivonics,Inc.
*
*/
/**
* @brief Enable the Max chip by lowering its Chip Select line
* @param None
* @retval None
*/
void MAX30003_ChipSelect( void)
{
output_low( MAX30003_CS);
delay_us( 500 );
}
/**
* @brief Disable the Max chip by raising its Chip Select line
* @param None
* @retval None
*/
void MAX30003_ChipDeselect(void)
{
output_high( MAX30003_CS);
delay_us( 500);
}
/**
* @brief Read 32-bit data from register
* @param Addr register address
* @retval 24-bit data read in a 32-bit word
* @note - MAX30003 spec says the data on a read is a don't-care
* so I used 0x00.
*/
unsigned int32 MAX30003_ReadReg(unsigned int8 addr)
{
int32 data = 0;
int8 ReadData;
int32 outdata = ((addr | RREG30003) << 24);
MAX30003_ChipSelect( );
spi_xfer( addr | RREG30003); //Send register location
ReadData = spi_xfer( MAX30003_SPI,0xFF );//Read Data
data |= ReadData; // bits 23..16
ReadData = spi_xfer( MAX30003_SPI,0xFF );//Read Data
data <<= 8;
data |= ReadData; // bits 15..8
ReadData = spi_xfer( MAX30003_SPI,0xFF );//Read Data
data <<= 8;
data |= ReadData; // bits 7..0
MAX30003_ChipDeselect( );
return data;
}
.
.
.
|
Oh, and when I tell the #use SPI that enable is on C2, and manually lower and raise C2 to effect a 32bit transfer, the PIC deasserts and asserts chip select on C2 anyway. This just confuses the bejesus out of the MAX30003 state machine.
Thanks,
John _________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue May 01, 2018 2:14 pm |
|
|
Two choices.
1) Remove the enable from #USE_SPI.
You can then operate the CS yourself.
2) However you can set the #USE to do 32bit transfers, but you then need to tell the xfer command to do 32bit transfers.
Understand if you call:
Code: |
OutData = spi_xfer( MAX30003_SPI,0xFFFFFFFF,32 ); //This will do a 32bit transfer
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
Re: 32bit SPI transfers using PIC16F1513? |
Posted: Tue May 01, 2018 3:42 pm |
|
|
johngriswold wrote: |
#use spi (MASTER, CLK=PIN_C3, DI=PIN_C4, DO=PIN_C5, ENABLE=PIN_C2, BAUD=500000, MODE=3, BITS=8, STREAM=MAX30003_SPI)
// set_tris_c( 0xFB); // With set_tris_c, there is no clock.
|
The reason is because your TRIS value is wrong. In binary, it's:
The PIC data sheet says:
Quote: |
• SDI must have corresponding TRIS bit set
• SDO must have corresponding TRIS bit cleared (= output)
• SCK (Master mode) must have corresponding TRIS bit cleared (= output)
|
SCLK, SDO, and Enable all need to be set as output pins. Your TRIS
statement only sets pin C2 as an output. That's why it didn't work.
But in CCS, their functions will automatically set the correct TRIS
for any pins used by the function. This will not be done if you
specify #fast_io() mode for the i/o port. |
|
|
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
|
Posted: Wed May 02, 2018 8:29 am |
|
|
Thanks to you both. I'm closer now.
Yeah, the set_tris_c() was kind of dumb on my part. I assumed that the #use SPI statement would initialize things. I was wrong!
Now I get proper chip select without having to manually control it, the clock seems OK, data out from the PIC seems OK (I need to look a bit more closely) but the data in doesn't seem right - but that might be the slave chip.
The code (with corrected set_tris_c(), PIC-generated chip select, and 32-bit #use and transfers) now looks like:
Code: |
#include <uart.h>
// Now trying to configure the PIC for 32-bit SPI
#use spi (MASTER, CLK=PIN_C3, DI=PIN_C4, DO=PIN_C5, ENABLE=PIN_C2, BAUD=500000, MODE=3, BITS=32, STREAM=MAX30003_SPI)
#include "MAX30003.h"
int32 chip_status;
void main()
{
// setup_adc_ports(sAN0|sAN1);
// setup_adc(ADC_CLOCK_INTERNAL);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1|RTCC_8_BIT);
spi_init( MAX30003_SPI, 500000);
// Port C - clock on C3, DI on C4, DO on C5, enable on C2
// 76543210
// IIOIOOII == D3 (DOH!)
set_tris_c( 0xD3);
while(TRUE)
{
chip_status = MAX30003_ReadReg( MNGR_DYN); // s/b 0x3f0000
delay_us( 250);
}
}
/**
******************************************************************************
* @file MAX30003.c
* @brief Code for the MAX30003 ECG chip
* @author John Griswold
******************************************************************************
*
*
* COPYRIGHT( C )2018 Vivonics,Inc.
*
*/
/**
* @brief Enable the Max chip by lowering its Chip Select line
* @param None
* @retval None
* @note Notice the in - built time delay so no need to wait after
* chipselect assertion
*/
void MAX30003_ChipSelect( void)
{
// output_low( MAX30003_CS);
// delay_us( 500 );
}
/**
* @brief Disable the Max chip by raising its Chip Select line
* @param None
* @retval None
* @note Notice the in-built time delay so no need to wait before
* chipselect deassertion
*/
void MAX30003_ChipDeselect(void)
{
// output_high( MAX30003_CS);
// delay_us( 500);
}
/**
* @brief Write 32-bit data to a register via SPI
* @param Addr-register address
* @param Data-32-bit data
* @retval None-no data is returned
*/
void MAX30003_WriteReg(unsigned int8 addr,unsigned int32 data)
{
MAX30003_ChipSelect( );
data |= addr << 24;
spi_xfer( MAX30003_SPI, data, 32 );
MAX30003_ChipDeselect( );
}
/**
* @brief Read 32-bit data from register
* @param Addr register address
* @retval 24-bit data read in a 32-bit word
* @note - MAX30003 spec says the data on a read is a don't-care
* so I used 0x00.
*/
unsigned int32 MAX30003_ReadReg(unsigned int8 addr)
{
int32 data = 0;
int8 ReadData;
int32 outdata = ((addr | RREG30003) << 24);
MAX30003_ChipSelect( );
data = spi_xfer( MAX30003_SPI, outdata, 32);
MAX30003_ChipDeselect( );
return data;
}
|
_________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Wed May 02, 2018 9:51 am |
|
|
I've always thought (right or wrong) that it's best NOT to use fast_io() and set_tris() during the 'R&D' phase of the project. Like WDT, you can get into a hornet's nest of problems YOU create by not letting the compler 'do the dirty work'. I think (again right or wrong) that if the compiler sees you using them, it may modify the USE ??? to ignore their 'internal' settings and let you fail...
I've been bit a couple of times in 25 years of using fastio() and only 2 or 3 projects actually needed it, for speed.
just food for thought. |
|
|
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
|
Posted: Wed May 02, 2018 10:23 am |
|
|
Indeed - I took out the set_tris_c() statement altogether, and I get the same results - still get chip select and clock, data out to the slave looks good, but the slave returns only 1's, which, like so many things, puzzles me.
Thanks for the tip, temtronic. _________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed May 02, 2018 11:09 am |
|
|
Are you sure about mode=3?. Looking at the data sheet mode=0 looks more correct. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 02, 2018 11:51 am |
|
|
Quote: | but the slave returns only 1's |
Post both of your current driver files, Max30003.c and Max30003.h.
Also post a list of connections between your PIC and the Max30003.
I want to make sure that you have:
PIC Max30003
SDO to SDI
SDI to SDO
SCLK to SCLK
CS to CSB
A common mistake is connecting SDO to SDO, and SDI to SDI. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Wed May 02, 2018 12:12 pm |
|
|
PCM programmer wrote: | Also post a list of connections between your PIC and the Max30003.
I want to make sure that you have:
PIC Max30003
SDO to SDI
SDI to SDO
SCLK to SCLK
CS to CSB
A common mistake is connecting SDO to SDO, and SDI to SDI. |
Pet peeve of mine, that's why I always name my pins MOSI and MISO. Master Out Slave In and Master In Slave Out. No ambiguity. |
|
|
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
|
Posted: Wed May 02, 2018 12:16 pm |
|
|
I'm not sure about mode=3. In fact, after changing to mode 0, I seem to be getting data back from the MAX30003.
I am sure about the connections, which are correct. I have an EE to beat up on for this
The file MAX30003.c is incorporated in the main c file. I ran into a problem a few weeks back causing Microchip support to suggest I not use multiple compilation units. Seriously.
Here is the header file:
Code: |
/**
******************************************************************************
* @file MAX30003.h
* @brief Register definitions for MAX30003 ECG chip
* @author John Griswold
******************************************************************************
*
*
* COPYRIGHT(C) 2018 Vivonics, Inc.
*
*/
#ifndef MAX30003_H_
#define MAX30003_H_
#define BIT(X) 1<<(X)
/**
* @brief Bit definitions for MAX30003 ECG chip
*/
/**
* Write and Read control
*/
#define WREG30003 0x00
#define RREG30003 0x01
/**
* @note - Register addressing for MAX30003 requires shifting the stated address
* one bit left, and ORing in the Read/-Write bit. Do the shifting here
* so it doesn't have to be done for each operation at run time.
*/
#define NO_OP 0x00 << 1
/**
* @brief Status register
* @note Oddly populated, this register uses 23:20, 11:8, and 3:0
*/
#define STATUS 0x01 << 1
#define EINT BIT(23)
#define EOVF BIT(22)
#define FSTINT BIT(21)
#define DCLOFFINT BIT(20)
#define LONINT BIT(11)
#define RRINT BIT(10)
#define SAMP BIT(9)
#define PLLINT BIT(8)
#define LDOFF_PH BIT(3)
#define LDOFF_PL BIT(2)
#define LDOFF_NH BIT(1)
#define LDOFF_NL BIT(0)
/**
* @brief Interrupt Enable register
*/
#define EN_INT 0x02 << 1
#define EN_EINT BIT(23)
#define EN_EOVF BIT(22)
#define EN_FSTINT BIT(21)
#define EN_DCLOFFINT BIT(20)
#define EN_LONINT BIT(11)
#define EN_RRINT BIT(10)
#define EN_SAMP BIT(9)
#define EN_PLLINT BIT(8)
#define INTB_TYPE BIT(1)|BIT(0)
#define DISABLE 0
#define CMOS_DRIVER 1
#define NMOS_DRIVER 2
#define OD_NMOS_DRIVER 3
/**
* @brief Interrupt Enable register 2
* @note - appears to be the same as EN_INT, which strikes me as very odd.
*/
#define EN_INT2 0x03 << 1
/**
* @brief
* @note
*/
#define MNGR_INT 0x04 << 1
/**
* @brief Manage operation of configurable interrupt buts for ECG FIFO
* @note
*/
#define EFIT 0xF000
#define CLR_FAST BIT(6)
#define CLR_RRINT BIT(5)|BIT(4)
#define CLR_SAMP BIT(2)
#define SAMP_IT BIT(1)|BIT(0)
/**
* @brief Dynamic mode management
* @note
*/
#define MNGR_DYN 0x05 << 1
#define FAST BIT(23)|BIT(22)
#define FAST_TH BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)
/**
* @brief Software Reset
* @note Resets device after SPI write cycle - value 0
*/
#define SW_RST 0x08 << 1
/**
* @brief Resynchs the device after reconfiguration
* @note Write a zero
*/
#define SYNCH 0x09 << 1
/**
* @brief Reset the FIFO (e.g. after a FIFO overflow)
* @note Write a zero
*/
#define FIFO_RST 0x0A << 1
/**
* @brief Chip revision and ID information
* @note
*/
#define INFO 0x0F << 1
/**
* @brief General configuration register
* @note
*/
#define CNFG_GEN 0x10 << 1
#define EN_ULP_LON BIT(23)|BIT(22)
#define FMSTR BIT(21)|BIT(20)
#define EN_ECG BIT(19)
#define EN_DCLOFF BIT(13)|BIT(12)
#define IPOL BIT(1)
#define IMAG BIT(10)|BIT(9)|BIT(8)
#define VTH BIT(7)|BIT(6)
#define EN_RBIAS BIT(3)|BIT(2)
#define RBIASP BIT(1)
#define RBIASN BIT(0)
/**
* @brief Configure operation of Internal calibration voltage sources
* @note
*/
#define CNFG_CAL 0x12 << 1
#define EN_VCAL BIT(22)
#define VMODE BIT(21)
#define VMAG BIT(20)
#define FCAL BIT(14)|BIT(13)|BIT(12)|BIT(11)
#define FIFTY BIT(10)
#define THIGH BIT(9)|BIT(8)|BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0)
/**
* @brief Configure input mux
* @note
*/
#define CNFG_EMUX 0x14 << 1
#define POL BIT(23)
#define OPENP BIT(21)
#define OPENN BIT(20)
#define CALP_SEL BIT(19)|BIT(18)
#define CALN_SEL BIT(17)|BIT(16)
/**
* @brief Configure ECG channel
* @note
*/
#define CNFG_ECG 0x15 << 1
#define RATE BIT(23)|BIT(22)
#define ECG_GAIN BIT(17)|BIT(16)
#define DHPF BIT(14)
#define DLPF BIT(13)|BIT(12)
/**
* @brief Configure heart rate detection block
* @note
*/
#define CNFG_RTOR1 0x1D << 1
#define WNDW BIT(23)|BIT(22)|BIT(21)|BIT(20)
#define GAIN BIT(19)|BIT(18)|BIT(17)|BIT(16)
#define EN_RTOR BIT(15)
#define PAVG BIT(13)|BIT(12)
#define PTSF BIT(11)|BIT(10)|BIT(9)|BIT(8)
/**
* @brief
* @note
*/
#define CNFG_RTOR2 0x1E << 1
#define HOFF BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)
#define RAVG BIT(13)|BIT(12)
#define RHSF BIT(10)|BIT(9)|BIT(8)
/**
* @brief ECG Burst and ECG data
* @note Registers use the same bitmasks
*/
#define ECG_FIFO_BURST 0x20 << 1
#define ECG_FIFO 0x21 << 1
#define ECG_SAMPLE 0x00FFFFC0
#define ETAG BIT(5)|BIT(4)|BIT(3)
#define PTAG BIT(2)|BIT(1)|BIT(0)
/**
* @brief RTOR Interval register
* @note
*/
#define RTOR 0x25 << 1
#define RTOR_MASK BIT(23)|BIT(22)|BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)
#define NO_OP0 0x7F << 1
void MAX30003_WriteReg( unsigned int8 addr, unsigned int32 data);
unsigned int32 MAX30003_ReadReg( unsigned int8 addr);
void MAX30003_Reset( void);
void MAX30003_Synch( void);
void MAX30003_Initialize( void);
void MAX30003_ChipSelect( void);
void MAX30003_ChipDeselect( void);
void MAX30003_EnableInterrupts( void);
void MAX30003_DisableInterrupts( void);
void MAX30003_ResetFIFO( void);
#define MAX30003_CS PIN_C2
#define MAX30003_CS_ON 0
#define MAX30003_CS_OFF 1
#endif // MAX30003_H_
|
So, this looks like some progress.
Thank you all so much. I'll very likely be back!
_________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 02, 2018 4:16 pm |
|
|
johngriswold wrote: |
The file MAX30003.c is incorporated in the main c file. I ran into a
problem a few weeks back causing Microchip support to suggest I not use
multiple compilation units.
|
Multiple compilation units are quite different than having your driver files
in separate files.
Method 1:
The standard way to use CCS is to put your driver code in separate files.
Then use #include lines in your main C file to include them in the program.
So in your main.c, you should have:
Code: |
#include "MAX30003.c" |
This is the way that nearly all of us do it.
Method 2:
When using Multiple Compilation Units, individual files can be compiled
separately and then linked together later. This is what Microchip support
recommended that you don't do. This FAQ article explains how to use MCUs:
https://www.ccsinfo.com/faq.php?page=multi_comp_units
Most of us don't use MCUs.
Method 3:
This is what you said you are doing. Combine all driver files with the
main.c file. Don't use #include statements. Just have one big file.
This is not normally done. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu May 03, 2018 1:37 am |
|
|
johngriswold wrote: | I'm not sure about mode=3. In fact, after changing to mode 0, I seem to be getting data back from the MAX30003.
|
OK. Well that sounds more hopeful.
The timing diagrams show data being sampled for both transmission and reception on the rising edge of the clock, with data stable around this point. This is mode 0, hence my suggestion.
Question now is whether the data has any resemblance to what you expect?.
Do something basic, like setting a register to some specific value, and then read this back. You need to be getting back exactly what you have written before trying to do anything more complex. |
|
|
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
|
Posted: Thu May 03, 2018 7:02 am |
|
|
Ttelmah - I anticipate read/write tests today.
PCMProgrammer - I am not in the habit of combining all of the files into one - I was just following the suggestions of the compiler vendor. By putting all of the code into one file (rather than #include), I had hoped to make it somewhat more convenient for folks in this forum to view my code.
Thank you all for your help. You rock! _________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu May 03, 2018 9:18 am |
|
|
With many SPI devices, for example memories, there is no fixed transfer length as there can be with things like ADCs and DACs. Also with multiple devices on one SPI port it's not so easy to deal with the mulitple selects: you would have to have multiple #use spi directives and stream names.
So, for many SPI situations its not great or even practical to specify enable in the #use spi. Instead, while you sound like you think it's "cheating" - you use the terms "spoofing" and "fooling the processor" (you're not, of course, the processor has nothing to do with it) - it's normal practice to use normal I/O bits for the select and control them in user code, with as many byte transfers as you need between enabling and disabling the device. This is the way I normally use SPI.
When you are using a device like a DAC, where all transfers are the same length, say 24 bits: three bytes, you can set up the SPI to so just that. The SPI peripheral hardware, if used, doesn't do the enable, instead the compiler will provide code to wrap the transfer, of as many bytes as required, to enable/select the device being talked to. If think if you do not specify a binary power for the number of bits, it cannot use the SPI hardware and implements a software emulation instead. Generally, though by no means all, devices that require a non-binary number of bits will often allow extra dummy bits at the end, allowing the next higher binary power number of bits to be used.
Personally, I'm so used to doing my own chip enables and to transferring byte-by-byte that that's what I always use, but that's essentially just habit. |
|
|
johngriswold
Joined: 04 Apr 2018 Posts: 13
|
|
Posted: Thu May 03, 2018 9:27 am |
|
|
Yeah, I know I wasn't fooling the processor... Fortunately for me, there is only one SPI device in this project, and thanks to the kind folks in this forum, I'm actually writing and reading the chip over SPI. I found that the straightforward(?) use of this works just fine:
Code: | #use spi (MASTER, CLK=PIN_C3, DI=PIN_C4, DO=PIN_C5, ENABLE=PIN_C2, BAUD=SPI_BAUD, MODE=0, BITS=32, STREAM=MAX30003_SPI) |
(SPI_BAUD is defined as 1000000)
Thank you for your input as well, RF. I'll tuck this away in my little pea brain for the next project. _________________ John Griswold KK1X
High Tech Migrant Worker |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|