Problem with SPI (Serial Port Comm) stuck on ioctl ()

I am trying to access the SPI sensor using the SPIDEV driver, but my code is stuck on IOCTL.

I am running embedded Linux on SAM9X5EK (mounting AT91SAM9G25). The device is connected to SPI0. I included CONFIG_SPI_SPIDEV and CONFIG_SPI_ATMEL in menuconfig and added the correct code to the BSP file:

static struct spi_board_info spidev_board_info[] { { .modalias = "spidev", .max_speed_hz = 1000000, .bus_num = 0, .chips_select = 0, .mode = SPI_MODE_3, }, ... }; spi_register_board_info(spidev_board_info, ARRAY_SIZE(spidev_board_info)); 

1MHz is the maximum received by the sensor, I tried 500 kHz, but when I boot Linux I get an error (too slow) .. bus_num and .chips_select should fix (I also tried all the other combinations). SPI_MODE_3 I checked the data for it.

I am not getting boot errors, and the devices appear correctly as /dev/spidevX.X. I manage to open the file and get a valid file descriptor. Now I am trying to access the device with the following code (inspired by the examples I found on the Internet).

 #define MY_SPIDEV_DELAY_USECS 100 // #define MY_SPIDEV_SPEED_HZ 1000000 #define MY_SPIDEV_BITS_PER_WORD 8 int spidevReadRegister(int fd, unsigned int num_out_bytes, unsigned char *out_buffer, unsigned int num_in_bytes, unsigned char *in_buffer) { struct spi_ioc_transfer mesg[2] = { {0}, }; uint8_t num_tr = 0; int ret; // Write data mesg[0].tx_buf = (unsigned long)out_buffer; mesg[0].rx_buf = (unsigned long)NULL; mesg[0].len = num_out_bytes; // mesg[0].delay_usecs = MY_SPIDEV_DELAY_USECS, // mesg[0].speed_hz = MY_SPIDEV_SPEED_HZ; mesg[0].bits_per_word = MY_SPIDEV_BITS_PER_WORD; mesg[0].cs_change = 0; num_tr++; // Read data mesg[1].tx_buf = (unsigned long)NULL; mesg[1].rx_buf = (unsigned long)in_buffer; mesg[1].len = num_in_bytes; // mesg[1].delay_usecs = MY_SPIDEV_DELAY_USECS, // mesg[1].speed_hz = MY_SPIDEV_SPEED_HZ; mesg[1].bits_per_word = MY_SPIDEV_BITS_PER_WORD; mesg[1].cs_change = 1; num_tr++; // Do the actual transmission if(num_tr > 0) { ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), mesg); if(ret == -1) { printf("Error: %d\n", errno); return -1; } } return 0; } 

Then I use this function:

 #define OPTICAL_SENSOR_ADDR "/dev/spidev0.0" ... int fd; fd = open(OPTICAL_SENSOR_ADDR, O_RDWR); if (fd<=0) { printf("Device not found\n"); exit(1); } uint8_t buffer1[1] = {0x3a}; uint8_t buffer2[1] = {0}; spidevReadRegister(fd, 1, buffer1, 1, buffer2); 

When I ran it, the code was stuck on IOCTL!

I did this because to read the register on the sensor, I need to send a byte with its address to it, and then return the response without changing the CS (however, when I tried to use write () and read (), during training I received same result stuck on them). I know that specifying .speed_hz causes an ENOPROTOOPT error on Atmel (I checked spidev.c), so I commented on this part.

Why is he stuck? I, although it may be, because the device is created, but in fact it does not "feel" any equipment. Since I was not sure that the hardware SPI0 corresponds to bus_num 0 or 1, I tried both, but still do not have time (by the way, which one?).

UPDATE: I managed to get SPI to work! Half of that. MOSI is sending the correct data, but the CLK does not start ... any idea?

+4
source share
2 answers

When I work with SPI, I always use an oscyloscope to see the result of io. If you have a 4-channel area, ypu can easily debug this problem, and find out if you recycle the correct io using the correct speed, etc. I usually compare the signal that I get in the data diagram.

+3
source

I think there are a few questions here. First of all, SPI is bidirectional. Therefore, if you want to send something by bus, you also get something. Therefore, you always need to provide a valid buffer for rx_buf and tx_buf.

Secondly, all members of struct spi_ioc_transfer must be initialized with a valid value. Otherwise, they simply point to some memory address, and the main process - to arbitrary data, which leads to unknown behavior.

Third, why are you using a for loop with ioctl? You already said that you have an array of spi_ioc_transfer structures. This way, the entire specific transaction will be executed in a single ioctl call.

The fourth ioctl needs a pointer to your struct array. Therefore, ioctl should look like this:

  ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), &mesg); 

You see that there is an opportunity to improve your code.

Here's how I do it in the C ++ library for raspberries pi. The whole library will be on github soon. I will update my answer when this is done.

 void SPIBus::spiReadWrite(std::vector<std::vector<uint8_t> > &data, uint32_t speed, uint16_t delay, uint8_t bitsPerWord, uint8_t cs_change) { struct spi_ioc_transfer transfer[data.size()]; int i = 0; for (std::vector<uint8_t> &d : data) { //see <linux/spi/spidev.h> for details! transfer[i].tx_buf = reinterpret_cast<__u64>(d.data()); transfer[i].rx_buf = reinterpret_cast<__u64>(d.data()); transfer[i].len = d.size(); //number of bytes in vector transfer[i].speed_hz = speed; transfer[i].delay_usecs = delay; transfer[i].bits_per_word = bitsPerWord; transfer[i].cs_change = cs_change; i++ } int status = ioctl(this->fileDescriptor, SPI_IOC_MESSAGE(data.size()), &transfer); if (status < 0) { std::string errMessage(strerror(errno)); throw std::runtime_error("Failed to do full duplex read/write operation " "on SPI Bus " + this->deviceNode + ". Error message: " + errMessage); } } 
+2
source

Source: https://habr.com/ru/post/1415296/


All Articles