I am trying to create a simple pose data app using Intel realsense with T265.
Since C examples database is very limited I am really struggling to start with this API.
Anyway here's my code:
/* Include the librealsense C header files */
#include <librealsense2/rs.h>
#include <librealsense2/h/rs_pipeline.h>
#include <librealsense2/h/rs_option.h>
#include <librealsense2/h/rs_frame.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// These parameters are reconfigurable //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define STREAM RS2_STREAM_ANY
#define FORMAT RS2_FORMAT_ANY
#define WIDTH 0
#define HEIGHT 0
#define FPS 0
#define STREAM_INDEX -1
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void check_error(rs2_error* e)
{
if (e)
{
printf("rs_error was raised when calling %s(%s):\n", rs2_get_failed_function(e), rs2_get_failed_args(e));
printf(" %s\n", rs2_get_error_message(e));
exit(EXIT_FAILURE);
}
}
void print_device_info(rs2_device* dev)
{
rs2_error* e = 0;
printf("\nUsing device 0, an %s\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_NAME, &e));
check_error(e);
printf(" Serial number: %s\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_SERIAL_NUMBER, &e));
check_error(e);
printf(" Firmware version: %s\n\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_FIRMWARE_VERSION, &e));
check_error(e);
}
int main()
{
rs2_error* e = 0;
// Create a context object. This object owns the handles to all connected realsense devices.
// The returned object should be released with rs2_delete_context(...)
rs2_context* ctx = rs2_create_context(RS2_API_VERSION, &e);
check_error(e);
/* Get a list of all the connected devices. */
// The returned object should be released with rs2_delete_device_list(...)
rs2_device_list* device_list = rs2_query_devices(ctx, &e);
check_error(e);
int dev_count = rs2_get_device_count(device_list, &e);
check_error(e);
printf("There are %d connected RealSense devices.\n", dev_count);
if (0 == dev_count)
return EXIT_FAILURE;
// Get the first connected device
// The returned object should be released with rs2_delete_device(...)
rs2_device* dev = rs2_create_device(device_list, 0, &e);
check_error(e);
print_device_info(dev);
// Create a pipeline to configure, start and stop camera streaming
// The returned object should be released with rs2_delete_pipeline(...)
rs2_pipeline* pipeline = rs2_create_pipeline(ctx, &e);
check_error(e);
// Create a config instance, used to specify hardware configuration
// The returned object should be released with rs2_delete_config(...)
rs2_config* config = rs2_create_config(&e);
check_error(e);
// Request a specific configuration
rs2_config_enable_stream(config, STREAM, STREAM_INDEX, WIDTH, HEIGHT, FORMAT, FPS, &e);
check_error(e);
// Start the pipeline streaming
// The retunred object should be released with rs2_delete_pipeline_profile(...)
rs2_pipeline_profile* pipeline_profile = rs2_pipeline_start_with_config(pipeline, config, &e);
check_error(e);
while (1)
{
// This call waits until a new composite_frame is available
// composite_frame holds a set of frames. It is used to prevent frame drops
// The returned object should be released with rs2_release_frame(...)
rs2_frame* frames = rs2_pipeline_wait_for_frames(pipeline, RS2_DEFAULT_TIMEOUT, &e);
check_error(e);
// Returns the number of frames embedded within the composite frame
int num_of_frames = rs2_embedded_frames_count(frames, &e);
check_error(e);
int i;
for (i = 0; i < num_of_frames; ++i)
{
// The retunred object should be released with rs2_release_frame(...)
rs2_frame* frame = rs2_extract_frame(frames, i, &e);
check_error(e);
// Check if the given frame can be extended to depth frame interface
// Accept only depth frames and skip other frames
if (0 == rs2_is_frame_extendable_to(frame, RS2_EXTENSION_POSE, &e))
continue;
rs2_pose pose_data;
rs2_pose_frame_get_pose_data(frame, &pose_data, &e);
// Print the distance
printf("data %f, %f, %f\n", pose_data.translation.x, pose_data.translation.y, pose_data.translation.z);
rs2_release_frame(frame);
}
rs2_release_frame(frames);
}
// Stop the pipeline streaming
rs2_pipeline_stop(pipeline, &e);
check_error(e);
// Release resources
rs2_delete_pipeline_profile(pipeline_profile);
rs2_delete_config(config);
rs2_delete_pipeline(pipeline);
rs2_delete_device(dev);
rs2_delete_device_list(device_list);
rs2_delete_context(ctx);
return EXIT_SUCCESS;
}
And here's what I get from the console:
There are 2 connected RealSense devices.
Using device 0, an Intel RealSense T265
Serial number: my cams serialnumber
Firmware version: 0.2.0.951
rs_error was raised when calling
rs2_pipeline_start_with_config(pipe:00000000021 90DB0,
config:0000000002190E30):
No device connected
I always fail in rs2_pipeline_start_with_config function. I tried different config parameters but result is the same.
Let me also mention that I can run C++ pose sample which was delivered with the SDK with no problems. But I want to make it run using C.
Also, I don't know why it recognizes two same devices with the same serial number while only one T265 is connected to my PC.
Related
I have a windows 7 laptop (Sony pcg 81113M) with 3 USB ports, When I connect 2 * Ft 4222HQ and I run the "Getting start code for the Ft4222" (C++ Qtcreator) I get a wrong USB location which is 0x00. The same when I connect only one in that specific 2 ports.
I am verifying the USB-location of connected FTDI using the software USBview.
I am using "connect by location" in my program and if I connect two at the same time It will conseder it as one device (cause the Location Id is the same)
The FTDI driver is the latest version and i see all the interfaces in my device manager
Note: the third USB port works normally and I get a correct Location Id:
The os is a win7 64bit, the FTDI code runs on the 32bit (I also get the same result with 64bit)
Do anyone has an Idea about that? is there some other test that I can do it to figure it out the problem?
Here is Getting started FTDI code:
[source] https://www.ftdichip.com/Support/SoftwareExamples/LibFT4222-v1.4.4.zip
void ListFtUsbDevices()
{
FT_STATUS ftStatus = 0;
DWORD numOfDevices = 0;
ftStatus = FT_CreateDeviceInfoList(&numOfDevices);
for(DWORD iDev=0; iDev<numOfDevices; ++iDev)
{
FT_DEVICE_LIST_INFO_NODE devInfo;
memset(&devInfo, 0, sizeof(devInfo));
ftStatus = FT_GetDeviceInfoDetail(iDev, &devInfo.Flags, &devInfo.Type, &devInfo.ID, &devInfo.LocId,
devInfo.SerialNumber,
devInfo.Description,
&devInfo.ftHandle);
if (FT_OK == ftStatus)
{
printf("Dev %d:\n", iDev);
printf(" Flags= 0x%x, (%s)\n", devInfo.Flags, DeviceFlagToString(devInfo.Flags).c_str());
printf(" Type= 0x%x\n", devInfo.Type);
printf(" ID= 0x%x\n", devInfo.ID);
printf(" LocId= 0x%x\n", devInfo.LocId);
printf(" SerialNumber= %s\n", devInfo.SerialNumber);
printf(" Description= %s\n", devInfo.Description);
printf(" ftHandle= 0x%x\n", devInfo.ftHandle);
const std::string desc = devInfo.Description;
if(desc == "FT4222" || desc == "FT4222 A")
{
g_FT4222DevList.push_back(devInfo);
}
}
}
}
Main program:
int main(int argc, char const *argv[])
{
ListFtUsbDevices();
if(g_FT4222DevList.empty()) {
printf("No FT4222 device is found!\n");
return 0;
}
ftStatus = FT_OpenEx((PVOID)g_FT4222DevList[0].LocId, FT_OPEN_BY_LOCATION, &ftHandle);
if (FT_OK != ftStatus)
{
printf("Open a FT4222 device failed!\n");
return 0;
}
printf("\n\n");
printf("Init FT4222 as SPI master\n");
ftStatus = FT4222_SPIMaster_Init(ftHandle, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_LOW, CLK_LEADING, 0x01);
if (FT_OK != ftStatus)
{
printf("Init FT4222 as SPI master device failed!\n");
return 0;
}
printf("TODO ...\n");
printf("\n");
printf("UnInitialize FT4222\n");
FT4222_UnInitialize(ftHandle);
printf("Close FT device\n");
FT_Close(ftHandle);
return 0;
}
From https://ftdichip.com/wp-content/uploads/2020/08/TN_152_USB_3.0_Compatibility_Issues_Explained.pdf Section 2.1.2 Location ID Returned As 0
LocationIDs are not strictly part of the USB spec in the format
provided by FTDI. The feature was added as an additional option to
back up identifying and opening ports by index, serial number or
product description strings.
When connected to a USB 2.0 port the location is provided on the basis
of the USB port that the device is connected to. These values are
derived from specific registry keys. As the registry tree for 3rd
party USB 3.0 host drivers is different to the Microsoft generic
driver the Location ID cannot be calculated.
There is no workaround to this current issue and as such devices
should be listed and opened by index, serial number or product
description strings.
So this is known behaviour. Btw Win 7 EOL date was january 2020. Changing the OS is strongly adviced.
I am using ESP-IDF SDK to develop a small project to take sensor data through UART. I am following the data sheet which is provided by the manufacturer to parse and calculate the value of different parameter. But the output on serial is not correct and every time I am getting different output which is wrong.
Code:-
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
static const int RX_BUF_SIZE = 1024;
#define TXD_PIN (GPIO_NUM_4)
#define RXD_PIN (GPIO_NUM_5)
#define DELAY_IN_MS(t) (((portTickType)t*configTICK_RATE_HZ)/(portTickType)1000)
void init(void) {
const uart_config_t uart_config = {
.baud_rate = 4800,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 129, 0, NULL, 0);
uart_param_config(UART_NUM_1, &uart_config);
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
static void rx_task(void *arg)
{
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t data[7] = {0};
uint8_t PR=0,spo2=0,temprature=0;
while (1) {
const int rxBytes = uart_read_bytes(UART_NUM_1, data, 7, 1);
if (rxBytes == 7) {
printf("The rxbytes %d and %s\n",rxBytes,data);
PR = (data[3] & 0x7F) + ((data[2] & 0x40)<<1);
spo2 = (data[4] & 0x7F);
temprature = (data[5] & 0x7F);
printf("PR is %d , Spo2 is %d , temperature is %d \n",PR,spo2,temprature);
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
memset(data,0,7);
}
}
}
void app_main(void)
{
init();
xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
}
The output of the program on serial monitor is:-
The data sheet provided by the manufacturer is:-
https://drive.google.com/file/d/1lPATxeXXreVZkg9Ufg9BnyCrl4EsbJAj/view?usp=sharing
Correct me if I misaligned any data format to calculate the values.
Assembling messages from an unreliable channel (serial) means you can't really rely on them always arriving in the order you expect without any issues, so you have to take precautions that you don't get junk.
The code assumes that it will always receive these 7-byte messages in 7-byte chunks, and it doesn't always work that way. Line noise or timeouts could cause a proper message to be received in multiple chunks (say, 4 bytes then 3 bytes), or it could cause bytes to be lost.
To see if this is part of the problem, add logging on every read, not just on the ones that you expect:
static void rx_task(void *arg)
{
...
while (1) {
const int rxBytes = uart_read_bytes(UART_NUM_1, data, 7, 1);
// Log ALL reads, not just the ones you expect
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
if (rxBytes == 7) {
///
}
}
}
This will probably confirm my hunch.
In any case, you can't ever rely on the fixed-size messages because if it gets out of sync once, it won't ever recover. This means you have to build in your own protections.
Reading the data sheet for the sensor, it says that the first byte of every 7-byte message has the high bit set, so this is perfect for resynchronization: you ignore everything until you get that start byte, then read 6 more bytes, then you have a full message.
So you end up needing two buffers: one for the message you're assembling, and one for doing raw I/O from the sensor, copying to the real message buffer as you verify the sync.
A quick-and-dirty method would look like this:
static void rx_task(void *arg)
{
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
// sensor message we're trying to build
uint8_t message[7] = {0};
uint8_t *msgnext = message;
while (1) {
uint8_t inbuf[7];
const int rxBytes = uart_read_bytes(UART_NUM_1, inbuf, sizeof inbuf, 1);
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, inbuf);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, inbuf, rxBytes, ESP_LOG_INFO);
// error/timeout? do something?
if (rxBytes <= 0) continue;
for (int i = 0; i < rxBytes; i++)
{
const uint8_t b = inbuf[i];
if (b & 0x80)
{
// First byte of a message, reset the buffer
msgnext = message;
*msgnext++ = b;
}
else if (msgnext == message)
{
// not synced yet, ignore this byte
continue;
}
else
{
*msgnext++ = b;
if ((msgnext - message) == sizeof message)
{
// WE FOUND A FULL MESSAGE
uint8_t PR = (message[3] & 0x7F) + ((message[2] & 0x40)<<1);
uint8_t spo2 = (message[4] & 0x7F);
uint8_t temperature = (message[5] & 0x7F);
printf("PR is %d, Spo2 is %d, temperature is %d\n",
PR,spo2,temperature);
msgnext = message; // reset to empty the buffer
}
}
}
}
}
The idea is your raw I/O is done into inbuf, and it starts by looking for the sync byte (with the high bit set), and that tells you to start copying data to the real sensor buffer message. Once you get 7 bytes, it shows the result and resets the buffer.
And even if you have a few bytes of valid message data, if another SYNC byte comes in, it assumes the previous message was messed up, so it throws it away and starts a new fresh buffer.
You can add more here, such as support for timeouts, or detecting/logging when a partial message is discarded, but in no case can you avoid this data-framing layer.
Also, it's not necessary that the I/O buffer inbuf to be the same as the message size, and it might make sense to read from the UART in one-byte chunks; in a multi-tasking operating system I probably wouldn't do this, but in the ESP environment it might make sense - dunno. That would simplify the looping some.
EDIT Looking at your actual data dumps, it's clear that your messages are not framed properly because even though you have 7 bytes, the SYNC byte (with the high bit set) is found somewhere in the middle, but not the same place each time. Clearly this is a framing issue.
This is my first post here and I'm fairly new to programming and especially with C. A couple weeks ago I started working through the Audio Programming Book(MIT press) and have been expand on some examples to try to understand things further.
I think my question lies with how I'm trying to pass data (retrieved from the user in an initialization function) to a PortAudio callback. I feel like what I've done isn't that different from the examples (both from the book and PortAudio's examples like paex_sine.c), but for some reason I can't my code to work and I've been banging my head against a wall trying to understand why. I've tried searching pretty extensively for solutions or example code to study, but I kind of don't know what I don't know, so that hasn't returned much.
How do I get user data into the callback?
Am I just not understanding how pointers and structs work and trying to force them to do things they don't want to?
Or, am I just overlooking something really obvious?
The following code either gives a really high pitched output, short high pitched blips, or no (audible) output:
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define FRAME_BLOCK_LEN 64
#define SAMPLING_RATE 44100
#define TWO_PI (3.14159265f * 2.0f)
PaStream *audioStream;
double si = 0;
typedef struct
{
float frequency;
float phase;
}
paTestData;
int audio_callback (const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeinfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
// data->frequency = 400;
for(i = 0; i < framesPerBuffer; i++){
si = TWO_PI * data->frequency / SAMPLING_RATE; // calculate sampling-incr
*out++ = sin(data->phase);
*out++ = sin(data->phase);
data->phase += si; // add sampling-incr to phase
}
return paContinue;
}
void init_stuff()
{
float frequency;
int i;
PaStreamParameters outputParameters;
paTestData data;
printf("type the modulator frequency in Hz: ");
scanf("%f", &data.frequency); // get modulator frequency
printf("you chose data.frequency %.2f\n",data.frequency);
data.phase = 0.0;
printf("initializing Portaudio. Please wait...\n");
Pa_Initialize(); // initialize Portaudio
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
Pa_OpenStream( // open paStream object
&audioStream, // portaudio stream object
NULL, // input params
&outputParameters, // output params
SAMPLING_RATE, // SampleRate
FRAME_BLOCK_LEN, // frames per buffer
paNoFlag, // set no Flag
audio_callback, // callbak function address
&data ); // user data
Pa_StartStream(audioStream); // start the callback mechanism
printf("running... press space bar and enter to exit\n");
}
void terminate_stuff()
{
Pa_StopStream(audioStream); // stop callback mechanism
Pa_CloseStream(audioStream); // destroy audio stream object
Pa_Terminate(); // terminate portaudio
}
int main(void)
{
init_stuff();
while(getchar() != ' ') Pa_Sleep(100);
terminate_stuff();
return 0;
}
Uncommenting data->frequency = 400; at least plays a 400hz sine wave, but that ignores any user input done in init_stuff()
If I put a printf("%f\n",data->frequency); inside the callback, it prints 0.000000 or something like -146730090609497866240.000000.
It's pretty unpredictable, and this really makes me think it's pointer related.
My goal for this code is to eventually incorporate envelope generators to change the pitch and possibly incorporate wavetable oscillators so I'm not calculating sin(x) for every iteration.
I can get envelopes and wavetables to work while using a blocking API like portsf that's used in the book, but trying to adapt any of that code from earlier chapters to use PortAudio callbacks is turning my brain to mush.
Thanks so much!
The problem you're having with your callback data is that it goes out of scope and memory is deallocated as soon as init_stuff finishes execution.
You should allocate memory for your callback data using malloc or new and passing the pointer to it for the callback.
For example:
void init_stuff()
{
float frequency;
int i;
PaStreamParameters outputParameters;
paTestData *data = (paTestData *) malloc(sizeof(paTestData));
printf("type the modulator frequency in Hz: ");
scanf("%f", &(data->frequency)); // get modulator frequency
printf("you chose data.frequency %.2f\n",data->frequency);
data->phase = 0.0;
...
Pa_OpenStream( // open paStream object
&audioStream, // portaudio stream object
NULL, // input params
&outputParameters, // output params
SAMPLING_RATE, // SampleRate
FRAME_BLOCK_LEN, // frames per buffer
paNoFlag, // set no Flag
audio_callback, // callbak function address
data );
...
I wasn't able to get the original code working using malloc but based on both suggestions, I realized another workable solution. Because running init_stuff() caused my data to get deallocated, I'm for now just making all my assignments and calls to Pa_OpenStream() from main.
Works beautifully and I can now send whatever data I want to the callback. Thanks for the help!
I am trying to use libsndfile to write a multichannel wav that can be read by MATLAB 2010+.
the following code writes a 4 channel interleaved wav. all samples on channel 1 should be 0.1, on channel 2 they are 0.2, on channel 3 ... etc.
Each channel is 44100 samples in length.
I drag the wave file onto the MATLAB workspace and unfortunately MATLAB keeps returning "File contains uninterpretable data".
It may also be worth noting that when all samples are set to 0.0, MATLAB successfully reads the file, although very slowly.
I have successfully used libsndfile to read multichannel data written by MATLAB's wavwrite.m, so the library is setup up correctly I believe.
Audacity can read the resulting file from the code below.
VS 2012 64 bit compiler,
Win7 64bit, MATLAB 2015a
ref: the code has been adapted from http://www.labbookpages.co.uk/audio/wavFiles.html
Any suggestions, I presume i'm making a simple error here?
Thanks
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Create interleaved audio data
int numFrames_out = 44100;
int channels = 4;
float *int_y;
int_y = (float*)malloc(channels*numFrames_out*sizeof(float));
long q=0;
for (long i = 0; i<numFrames_out; i++)
{
for (int j = 0; j<channels; j++)
{
int_y[q+j] = ((float)(j+1))/10.0;
}
q+=channels;
}
// Set multichannel file settings
SF_INFO info;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_32;
info.channels = channels;
info.samplerate = 44100;
// Open sound file for writing
char out_filename[] = "out_audio.wav";
SNDFILE *sndFile = sf_open(out_filename, SFM_WRITE, &info);
if (sndFile == NULL)
{
fprintf(stderr, "Error opening sound file '%s': %s\n", out_filename, sf_strerror(sndFile));
return -1;
}
// Write frames
long writtenFrames = sf_writef_float(sndFile, int_y, numFrames_out);
// Check correct number of frames saved
if (writtenFrames != numFrames_out) {
fprintf(stderr, "Did not write enough frames for source\n");
sf_close(sndFile);
free(int_y);
return -1;
}
sf_close (sndFile);
}
It looks like you are only closing the output file (using sf_close()) in the error case. The output file will not be a well formed WAV file unless you call sf_close() at the end of your program.
I'm trying to access a SPI sensor using the SPIDEV driver but my code gets stuck on IOCTL.
I'm running embedded Linux on the SAM9X5EK (mounting AT91SAM9G25). The device is connected to SPI0. I enabled CONFIG_SPI_SPIDEV and CONFIG_SPI_ATMEL in menuconfig and added the proper 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 accepted by the sensor, I tried 500kHz but I get an error during Linux boot (too slow apparently). .bus_num and .chips_select should correct (I also tried all other combinations). SPI_MODE_3 I checked the datasheet for it.
I get no error while booting and devices appear correctly as /dev/spidevX.X. I manage to open the file and obtain a valid file descriptor. I'm now trying to access the device with the following code (inspired by examples I found online).
#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'm using 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 run it, the code get stuck on IOCTL!
I did this way because, in order to read a register on the sensor, I need to send a byte with its address in it and then get the answer back without changing CS (however, when I tried using write() and read() functions, while learning, I got the same result, stuck on them).
I'm aware that specifying .speed_hz causes a ENOPROTOOPT error on Atmel (I checked spidev.c) so I commented that part.
Why does it get stuck? I though it can be as the device is created but it actually doesn't "feel" any hardware. As I wasn't sure if hardware SPI0 corresponded to bus_num 0 or 1, I tried both, but still no success (btw, which one is it?).
UPDATE: I managed to have the SPI working! Half of it.. MOSI is transmitting the right data, but CLK doesn't start... any idea?
When I'm working with SPI I always use an oscyloscope to see the output of the io's. If you have a 4 channel scope ypu can easily debug the issue, and find out if you're axcessing the right io's, using the right speed, etc. I usually compare the signal I get to the datasheet diagram.
I think there are several issues here. First of all SPI is bidirectional. So if yo want to send something over the bus you also get something. Therefor always you have to provide a valid buffer to rx_buf and tx_buf.
Second, all members of the struct spi_ioc_transfer have to be initialized with a valid value. Otherwise they just point to some memory address and the underlying process is accessing arbitrary data, thus leading to unknown behavior.
Third, why do you use a for loop with ioctl? You already tell ioctl you haven an array of spi_ioc_transfer structs. So all defined transaction will be performed with one ioctl call.
Fourth ioctl needs a pointer to your struct array. So ioctl should look like this:
ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), &mesg);
You see there is room for improvement in your code.
This is how I do it in a c++ library for the raspberry pi. The whole library will soon be on github. I'll update my answer when it 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);
}
}