Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPI device driver #3

Open
Zetlark28 opened this issue Jan 19, 2021 · 8 comments
Open

SPI device driver #3

Zetlark28 opened this issue Jan 19, 2021 · 8 comments

Comments

@Zetlark28
Copy link

Hi, I'm working with your SPI device driver but for the MCP4822 component, and it's not working.
There isn't any compiler or run-time errors, but it seems that the MCP4822 doesn't receive data.
This is the edited code of the driver:

`static rtems_libi2c_tfr_mode_t tfr_mode = {
  .baudrate = 20000000,
  .bits_per_char = 8,
  .lsb_first = FALSE,
  .clock_inv = TRUE,
  .clock_phs = FALSE
};

static rtems_status_code spi_mcp4822_write(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *arg
) {
	rtems_status_code sc = RTEMS_SUCCESSFUL;
	rtems_libio_rw_args_t *rwargs = arg;
	int cnt = rwargs -> count;
	unsigned char *buf = (unsigned char *) rwargs -> buffer;
	/*send start*/
	sc = rtems_libi2c_send_start(minor);
	if(sc<0){
		printf("send start error \n");
		fflush(stdout);
		return sc;
	}else{
		printf("start sended \n");
		fflush(stdout);
	}
	/* ioctl*/
	sc = rtems_libi2c_ioctl(minor, RTEMS_LIBI2C_IOCTL_SET_TFRMODE, &tfr_mode);
	 if ( sc != RTEMS_SUCCESSFUL ) {
		printf("ioctl error \n");
		fflush(stdout);
	    return sc;
	 }else{
		printf("ioctl setted \n");
		fflush(stdout);
	 }
	/*send addr*/
	sc = rtems_libi2c_send_addr(minor, FALSE);
      if ( sc != RTEMS_SUCCESSFUL ) {
          printf("send address error \n");
	  fflush(stdout);
         return sc;
      }else{
         printf("address sended \n");
	 fflush(stdout);
    }

	/*write bytes*/
	int data_sent_cnt = rtems_libi2c_write_bytes(minor, buf, cnt);
    if (data_sent_cnt < 0) {
	  printf("write bytes error \n");
	  fflush(stdout);
          return RTEMS_IO_ERROR;
    }else{
          printf("wrote %d bytes \n", data_sent_cnt);
          fflush(stdout);
    }
	/*send stop*/
    sc = rtems_libi2c_send_stop(minor);
    if ( sc != RTEMS_SUCCESSFUL ) {
	  printf("send stop error \n");
	  fflush(stdout);
         return sc;
    }else{
	printf("stop sended \n");
	fflush(stdout);
    }
	return RTEMS_SUCCESSFUL;
}

/*read not used, data cannot be read from mcp4822 */
static rtems_status_code spi_mcp4822_read(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *arg
) {
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  return sc;
};
...`

rtems_status_code spi_mcp4822_read is empty because the MCP4822 can't be read.

I read the issue about the I2C device driver, and I understand that the libi2c is deprecated

I'm working with RTEMS v5.1 and I see that they add the SPI framework: https://git.rtems.org/rtems/commit/?h=5&id=a42be52bbf2b3a549d4b9635a5a93215dacd0657.
Do you have any advice?

Thank you in advice for your help

@asuol
Copy link
Owner

asuol commented Jan 20, 2021

Hello Zetlark28,

Few points to consider:

  • check that your device driver is configured to the correct RPI CS pin where your MCP4822 CS line is connected. This is done through the last parameter of the rtems_libi2c_register_drv function call (can be 0 (for CE0) or 1 (for CE1), as the original RPI for which the RPI driver was developed only had 2 CS lines)
  • double check that the transfer settings (the tfr_mode structure in your driver) match your device requirements
  • double check that the data you send to the MCP4822 (via the rtems_libi2c_write_bytes in your driver) has the correct size and content
  • check your cables length (the shorter the better, maximum should be around 20cm, as SPI is for short distance communication)

Your application should follow the same SPI bus and device driver initialization and usage flow as in https://github.com/asuol/RTEMS_rpi_testing/blob/master/SPI_bus/Test_cases/SPI_23k256_TEST/init.c

As for the new SPI framework, both API's (libi2c and the linux ported one) are available on RTEMS. The RPI SPI device driver (https://git.rtems.org/rtems/tree/bsps/arm/raspberrypi/spi/spi.c) is developed using the libi2c API, so currently that is the API required for using SPI with the RPI. The libi2c is deprecated in the sense that it should not be used to develop new BSP drivers, but it is still available as older drivers (such as the RPI SPI driver) were not ported yet to the new SPI framework.

@Zetlark28
Copy link
Author

Zetlark28 commented Jan 25, 2021

Hi asuol, sorry to bother you again.

I check all points:

  • In rtems_libi2c_register_drv function I setted the last parameter 0x00, to use CE0.
    return rtems_libi2c_register_drv("mcp4822", &spi_mcp4822_rw_drv_t, spi_bus, 0x00 ); It's correct?
  • I check the datasheet of my device and the requirements are the same as yours
  • The size and content are correct. I have to send 2 bytes.
  • My cables are 20 cm in length.

It seems that the CE0 pin doesn't switch to level high when the write operation it's finished. I connect the CE0 pin to a LED and it's turned off during the write operation, but it doesn't turn on when it's finished. It's possible that the function rtems_libi2c_send_stop(minor) doesn't work?

Thank you.

@asuol
Copy link
Owner

asuol commented Jan 28, 2021

The SPI interface supported in the RTEMS SPI driver is the standard 4-wire (or 3-wire if the driver is configured for bidirectional mode) SPI protocol, meaning it should only support full-duplex communication, where to send a byte to the slave device the slave device must send a (possibly dummy) byte back.

However since your device does not send data back, the raspberry may not actually send data to it, hence why I believe you see the CS line still low after the transfer call returned - the data was placed in the TX FIFO but the RPI is waiting for (dummy) data from the slave so it can send your data (full-duplex communication).

I never tested the RTEMS RPI SPI driver with an unidirectional slave device, and I believe the way the RTEMS driver is implemented today does not support this type of slave device.

I realize now that I actually stated in the driver itself that write-only devices were not supported (https://git.rtems.org/rtems/tree/bsps/arm/raspberrypi/spi/spi.c#n19)

Sorry for not making this clearer sooner, it has been a while since I last had a look into this driver.

@Zetlark28
Copy link
Author

Hi asuol,
thank you very much for the explanation.

@Zetlark28
Copy link
Author

Hi asuol, sorry I reopened the issue but I have some news and I was wondering if you could help me to understand this behavior.
I bought an oscilloscope to see the signal of the pin before I was using a multimeter so I wasn't very accurate.

It's seems that CS0 pin is inverted (yellow = MOSI, CS = cyan) :
spi_write_graph

But the VoutA change the value:
spi_write_graph2

Do you know if the behaviour of CS can be inverted?

Thank you

@Zetlark28 Zetlark28 reopened this Feb 4, 2021
@asuol
Copy link
Owner

asuol commented Feb 5, 2021

You can invert (active high instead of low) the CS line by setting the CSPOL or CSPOL0 field to 1 in the RPI SPI CS register (check the RPI datasheet at https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf page 155). In the RTEMS RPI driver this register is named "BCM2835_SPI_CS"

However the datasheet for your device states that the CS line is to be held low during writes. Also, is the MOSI wave representative of the data you are sending? Are you sending 0xFFFF in the first image?

You should also check the clock line to see if the data transfer on the MOSI line matches the clock pulses, or compare your waveforms with the ones in page 23 of https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf

As for your second diagram, assuming the yellow line is VoutA, it is expected from looking at the write command waveforms in the device datasheet referenced above for the Vout line to be idle during writes (i.e.: when the CS line is low)

@Zetlark28
Copy link
Author

Zetlark28 commented Feb 8, 2021

Hi, thank you for the explanation.

I invert the CS line before the write commande like you said:

uint32_t *pSpiCSReg = (uint32_t*) BCM2835_SPI_CS;

*pSpiCSReg |= (1<<6);

But the CS line is low only on the first write command, and then its behavior is inverted.
I edit the program to execute the write command 5 times and this is the result:
spi_write_graph3
MOSI -yellow, CS - cyan.

I noticed that the MOSI wave shown in the diagram I sent wasn't correct, I fix the bug and now it sends 0x3FFF correctly
spi_write_graph4
MOSI - yellow, SCLK - cyan.
I compared the waveforms of SCKL and MOSI with the ones in the datasheet and they seem equals.

About the second diagram, I don't know why the VoutA line increase at the last 2 write command:
image
If I have any news about it I will notice you.

Thank you for your patience and support.

p.s. I'm using the libi2c API and not the framework

@asuol
Copy link
Owner

asuol commented Feb 10, 2021

When writing values to hardware registers it is best to use the volatile keyword to ensure that the the compiler does not optimize the operation out.

Regarding your ps at the end, are you talking about the new SPI driver framework? Are you developing a separate SPI driver or modifying the existing RPI spi driver (at https://git.rtems.org/rtems/tree/bsps/arm/raspberrypi/spi/spi.c)?

As for the VoutA line increase, what are your commands trying to do? Are all commands 0x3FFF?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants