ICM20948 sequence of use - c

I´m currently developing basic routines in order to communicate with an IMU or Inertial Measurement Unit, the ICM20948 from TDK - InvenSense using the I2C protocol through a PCB I have develop myself, I currently have functions to communicate with it like:
int writeRegister(uint8 slaveAdress, uint8 registerAdress, uint8 data);
uint8 readRegisters(uint8 slaveAdress, uint8_t subAddress, uint8 count, uint8 dest);
int selectAutoClockSource(uint8 slaveAdress)
And some more which are working properly (I have read registers who tell me this is working) the point is that the ICM20948 datasheet device doesn´t specifies me the order to configurate it in order to read the acceleration data, I have found a code on a GitHub, more concretely https://github.com/dtornqvist/icm-20948-arduino-library which is written in Arduino, I tried to port it to c, the language I'm currently using on it, getting the current configuration function
int begin(uint8 slaveAdress){
if (changeUserBank_force(slaveAdress, USER_BANK_0) < 0) {
return -1;
}
if (selectAutoClockSource(slaveAdress) < 0) {
return -2;
}
// enable I2C master mode
if(enableI2cMaster(slaveAdress) < 0){
return -3;
}
if (powerDownMag(slaveAdress) < 0) {
return -4;
}
reset(slaveAdress);
CyDelay(10);
if (selectAutoClockSource(slaveAdress) < 0) {
return -6;
}
whoAreU(slaveAdress);
if(enableAccelGyro(slaveAdress, UB0_PWR_MGMNT_2, UB0_PWR_MGMNT_2_SEN_ENABLE) == 1){
} else {
UART_DEBUG_PutString("\r\n Error al habilitar el giroscopio y acelerometro");
return -7;
}
if(configAccel(slaveAdress, ACCEL_RANGE_2G, ACCEL_DLPF_BANDWIDTH_246HZ) != 1){
return -8;
}
if (setAccelSrd(slaveAdress,0) < 0) {
return -12;
}
return 1;
}
After that I call my:
int16_t getAccelX_mss(uint8 slaveAdress){
uint8 _axcountsH, _axcountsL;
int16_t _axcounts;
if (changeUserBank_force(slaveAdress, USER_BANK_0) < 0) {
return -1;
}
_axcountsH = readRegisters(slaveAdress, 0x45u, 1, _axcountsH);
_axcountsL = readRegisters(slaveAdress, 0x46u, 1, _axcountsL);
_axcounts = (((int16_t)_axcountsH) << 8) | _axcountsL;
sprintf(Rx_aux, " 0x%d ", _axcounts);
UART_DEBUG_PutString("\r\n Acce Read: ");
UART_DEBUG_PutString(Rx_aux);
return 1;
}
Function, which is supposed to return me trough UART the mix of 45u and 46u (acceleration) registers info, but I'm receiving the next output (I'm using Hterm 0.8.3 to capture the uart output)
UART ok...<\r><\n>
Begin.<\r><\n>
Begin with no errors.<\r><\n>
Acce Read: 0x0
I have no real clue about, even when I move my pcb, generating an acceleration the result keeps as 0x0. Have anyone worked with this class of software and can give me some advice about how to keep working? Or I have any important error on my code that I haven't seen?

Related

Is there a way in C to start playing a sound of a given frequency?

I'm currently working on a (plain) C project, on Windows 7, and I need help.
The program is supposed to start playing a sound of given frequency when I press a key, and then stop it when I press another key.
Once it starts playing, it shouldn't stop until the user presses the second key, so I can't specify a duration.
Is there a way to do it with WinApi? I'm searching for a function like:
sound(frequency);
that would start playing a sound at frequency frequency.
If frequency is 0, then all sounds should stop.
I searched all over Stackoverflow, but I couldn't find any solution except Beep(); that needs a duration, and PlaySound(); that needs a wav file. I'd like to keep it simple. I don't want a big program that generates wav files, only a built-in function.
Ah, I did found such a function on Stack a few days ago, but I can't find it again.
(P.S: this question has no answer with WAV file synthesis from scratch. It's another question. So please do not tell me the close question has not been answered, because it's FALSE.)
Here is a program I wrote that uses waveOutOpen and waveOutWrite to generate 48 kHz stereo audio in real time. It is possible to generate the audio using the main thread, but this program launches a separate thread so that it can provide the nice kind of API you wanted.
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#define BUFFER_COUNT 4
#define BUFFER_SAMPLE_COUNT 1000
#define SAMPLES_PER_SECOND 48000
HWAVEOUT waveOut;
HANDLE waveEvent;
WAVEHDR headers[BUFFER_COUNT];
int16_t buffers[BUFFER_COUNT][BUFFER_SAMPLE_COUNT * 2];
WAVEHDR * currentHeader;
double volume = 6000;
double phase;
double phase_increment;
double audio_value;
void sound(double frequency) {
if (frequency == 0) {
phase_increment = 0;
return;
}
phase_increment = 2 * M_PI / SAMPLES_PER_SECOND * frequency;
}
void fill_buffer(int16_t * buffer) {
for (size_t i = 0; i < BUFFER_SAMPLE_COUNT * 2; i += 2) {
if (phase_increment == 0) {
phase = 0;
audio_value *= 0.9;
}
else {
phase += phase_increment;
if (phase > 0) { phase -= 2 * M_PI; }
audio_value = sin(phase) * volume;
}
buffer[i + 0] = audio_value; // Left channel
buffer[i + 1] = audio_value; // Right channel
}
}
__stdcall DWORD audio_thread(LPVOID param) {
while (1) {
DWORD waitResult = WaitForSingleObject(waveEvent, INFINITE);
if (waitResult) {
fprintf(stderr, "Failed to wait for event.\n");
return 1;
}
BOOL success = ResetEvent(waveEvent);
if (!success) {
fprintf(stderr, "Failed to reset event.\n");
return 1;
}
while (currentHeader->dwFlags & WHDR_DONE) {
fill_buffer((int16_t *)currentHeader->lpData);
MMRESULT result = waveOutWrite(waveOut, currentHeader, sizeof(WAVEHDR));
if (result) {
fprintf(stderr, "Failed to write wave data. Error code %u.\n", result);
return 1;
}
currentHeader++;
if (currentHeader == headers + BUFFER_COUNT) { currentHeader = headers; }
}
}
}
int audio_init() {
WAVEFORMATEX format = { 0 };
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = SAMPLES_PER_SECOND;
format.wBitsPerSample = 16;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec;
waveEvent = CreateEvent(NULL, true, false, NULL);
if (waveEvent == NULL) {
fprintf(stderr, "Failed to create event.");
return 1;
}
MMRESULT result = waveOutOpen(&waveOut, WAVE_MAPPER, &format,
(DWORD_PTR)waveEvent, 0, CALLBACK_EVENT);
if (result) {
fprintf(stderr, "Failed to start audio output. Error code %u.\n", result);
return 1;
}
for (size_t i = 0; i < BUFFER_COUNT; i++) {
headers[i] = (WAVEHDR) {
.lpData = (char *)buffers[i],
.dwBufferLength = BUFFER_SAMPLE_COUNT * 4,
};
result = waveOutPrepareHeader(waveOut, &headers[i], sizeof(WAVEHDR));
if (result) {
fprintf(stderr, "Failed to prepare header. Error code %u.\n", result);
return 1;
}
headers[i].dwFlags |= WHDR_DONE;
}
currentHeader = headers;
HANDLE thread = CreateThread(NULL, 0, audio_thread, NULL, 0, NULL);
if (thread == NULL) {
fprintf(stderr, "Failed to start thread");
return 1;
}
return 0;
}
int main() {
if (audio_init()) { return 1; }
sound(400); Sleep(500);
sound(300); Sleep(500);
sound(600); Sleep(500);
for (int i = 0; i < 3; i++) {
sound(200); Sleep(50);
sound(0); Sleep(200);
sound(300); Sleep(50);
sound(0); Sleep(200);
sound(400); Sleep(50);
sound(0); Sleep(200);
sound(500); Sleep(50);
sound(0); Sleep(200);
sound(600); Sleep(50);
sound(0); Sleep(200);
}
}
I compiled in the MinGW 64-bit environment of MSYS2 using this command:
gcc -g -O2 -Wall -Wextra -Wno-unused-parameter wave.c -lwinmm -owave
Note the extra library I linked in: -lwinmm.
This code uses 4 buffers with 1000 samples each, so at any given time there will be no more than 83 ms worth of sound queued up to be played. This works fine on my system, but perhaps on other systems you might need to increase these parameters to ensure smooth playback. The buffer count is probably the first one I would try increasing if there are problems.
There might be some multithreading things I am getting wrong in the code. The phase_increment variable is written in the main thread and read in the audio thread. Perhaps I should explicitly make that be an atomic variable, and insert a memory barrier after I write to it.

FTDI FT232H communication with LM77 over I2C

I am trying to get a temperature readout from a LM77 using I2C communication with and FTDI FT232H. I don't think it is a hardware problem since I have checked the connections along with multiple colleagues. The communication with the PC to the FT232H is okay, it is initializing and everything is good on that end. The FT232H sends a read setup byte and gets an ACK from the LM77. After that there is no more data. I expect the LM77 to send 10 bits of data for a temperature readout, but it is not. This is what the readout looks like on a logic probe.
I would expect to then see an additional two bytes come in after the ACK but am getting nothing. The code is pretty straightforward and I am using the libMPSSE I2C API. The address I am using 0x48 comes from the address given in the datasheet bit shifted right by 1. I do not understand why I am getting an ACK but no temperature readout after. The ftStatus for the read gives an error code FT_DEVICE_NOT_FOUND. I am not sure why it's giving this error code if there is an ACK.
#include <stdio.h>
#include <stdlib.h>
#include "libMPSSE_i2c.h"
#define DEVICE_ADDR 0x48
FT_STATUS ftStatus;
FT_HANDLE ftHandle;
int i2cInit(void)
{
int numChannels = 0;
FT_DEVICE_LIST_INFO_NODE chanInfo;
ChannelConfig chConfig = {.ClockRate = 100000,
.LatencyTimer = 255,
.Options = 0x0000};
ftStatus = I2C_GetNumChannels(&numChannels);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Number of channels: %d\n", numChannels);
}
ftStatus = I2C_GetChannelInfo(0, &chanInfo);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel info obtained\n");
}
ftStatus = I2C_OpenChannel(0, &ftHandle);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel opened\n");
}
ftStatus = I2C_InitChannel(ftHandle, &chConfig);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel initialized\n");
}
}
int lm77ReadTemp()
{
unsigned char writeBuffer[20] = {0};
unsigned char readBuffer[20] = {0};
unsigned char bytesTransferred = 0;
unsigned int bytesRead = 0;
ftStatus = I2C_DeviceRead(ftHandle, DEVICE_ADDR, 2, readBuffer, &bytesRead, I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT);
if (ftStatus != FT_OK)
{
printf("Read failed status code %d\n", ftStatus);
}
}
int main(void)
{
i2cInit();
lm77ReadTemp();
return 0;
}
The problem was on the hardware side. The MPSSE engine does not have bidirectional pins. On the FTDI chip side, you need a separate pin for SDA_out and SDA_in. There is a diagram in the FTDI documentation that shows it. If you don't have the pins connected like this you won't receive any data from the I2C slave.

How can I get data from a GPS module coming with NMEA protocol through a Serial port with c

I need to get data from a GPS module which is coming in NMEA protocol through a Serial port, and the input looks something like this:
$GPRMC,190335.389,V,3754.931,N,08002.496,W,33.6,0.59,110619,,E*47
$GPGGA,190336.389,3754.931,N,08002.496,W,0,00,,,M,,M,,*52
$GPGLL,3754.931,N,08002.496,W,190337.389,V*33
$GPVTG,0.59,T,,M,33.6,N,62.2,K*5C
$GPRMC,190339.389,V,3754.932,N,08002.494,W,11.9,0.62,110619,,E*4D
$GPGGA,190340.389,3754.932,N,08002.494,W,0,00,,,M,,M,,*52
$GPGLL,3754.932,N,08002.494,W,190341.389,V*33
The thing is, I only need the lines starting with GPRMC. And the problem is the data is coming asynchronous, first comes the first half of a line, for example, then the other half and some of the other line and so on. Now how can I get the input line-by-line and only get the ones starting with GPRMC. The input is coming incessant and I need to get the correct line real-time. How could I do this with C?
I don't really know how to read from a Serial port, I tried something but because the input's coming asynchronous, I couldn't get the correct lines. Oh, and one more thing, the maximum length of a line is 83.
Here's some code I tried, I know it's bad
int a = 0;
int test = 0;
int gprmc_find(char* gps)
{
while(a < strlen(gps))
{
if(gps[a] =='$' && gps[a+1] == 'G' && gps[a+2] == 'P' && gps[a+3] == 'R' )
{
test = 1;
break;
}
else
{
test = 0;
}
a++;
return test;
}
}
int main()
{
DWORD accessdirection =GENERIC_READ | GENERIC_WRITE;
HANDLE hSerial = CreateFile("COM4",
accessdirection,
0,
0,
OPEN_EXISTING,
0,
0);
if (hSerial == INVALID_HANDLE_VALUE) {
printf("Invalid\n");
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
printf("could not get the state of the comport\n");
}
dcbSerialParams.BaudRate=9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams)){
printf("Error\n");
}
COMMTIMEOUTS timeouts={0};
timeouts.ReadIntervalTimeout=50;
timeouts.ReadTotalTimeoutConstant=50;
timeouts.ReadTotalTimeoutMultiplier=10;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=10;
if(!SetCommTimeouts(hSerial, &timeouts)){
printf("handle error1");
}
char buf[83] = {0};
while(1)
{
DWORD dwBytesRead = 0;
//CloseHandle(hSerial);
if(!ReadFile(hSerial, buf, 82, &dwBytesRead, NULL)){
printf("handle error");
}
printf(" %d \n", test);
if(gprmc_find(buf) == 1)
{
printf(buf);
}
memset(buf, 0, 83);
delay(1);
}
return 0;
}
GPS NMEA 0183 is a text-based protocol and can be processed with a platform-independent C language standard library.
Many libraries and parsers have already been developed, and you can use them without redevelopment.
For example, you can find this article on the StackOverflow site:
NMEA library - nmeaINFO empty / NMEA Library
C Gps nmea parser from linux serial port does not parse the last line of the read buffer
Parsing code for GPS NMEA string
Other site:
MULTI-PLATFORM SERIAL INTERFACING USING BOOST: A GPS SENSOR AND OPENDDS - PART I / PART II
These are parsers, libraries that parse the read data.
jacketizer/libnmea
kosma/minmea
MaJerle/GPS_NMEA_parser
After analyzing in the above library, you should use only the data you want.

Temperature sensor not working [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Greetins,
I recently bought a temperature & humidity sensor (aosong am2302). I hooked it to a rasp pi 3 and it works like charm with the adafruit library. The problem comes when i try to make it work on another board (described here). I used this library for gpio reading. I modified the file of beaglebone for gpio mapping and that's it. I run the tests and they work, so basically the lib looks like it works. So after that, I code the sensor reader and it doesnt work and I dont know why.
After I run the sensor reader, If I check the file system, the gpio is exported.
The sensor is installed like this:
-Power to 5V (also tried with 3.3v)
-VCC to gpio
-Ground to GND.
and here the code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "libsoc_gpio.h" //library for gpio reading
#define MAXTIMINGS 10
#define SILENT 0
int bits[MAXTIMINGS+1],data[5];
int readDHT(int pin,int allowRetry);
int
main(int argc, char **argv)
{ int dhtpin;
if (argc>1)dhtpin=atoi(argv[1]);
else printf("Introduce pin");
if( SILENT < 1 ) {
printf("Using pin #%d\n", dhtpin);
}
readDHT(dhtpin,5);
return 0;
}
int
readDHT(int pin, int allowRetry)
{
int bitidx=0;
int counter = 0;
int i=0,j=0;
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
gpio *gpio_output = libsoc_gpio_request(pin, LS_SHARED); //export GPIO
if (gpio_output == NULL)
{
fprintf(stderr, "Failed to open GPIO %d\n", pin);
return -1;
}
libsoc_gpio_set_direction(gpio_output, OUTPUT);
if (libsoc_gpio_get_direction(gpio_output) != OUTPUT)
{
fprintf(stderr, "Failed to set direction to OUTPUT\n");
if (gpio_output)
{
libsoc_gpio_free(gpio_output);
}
return -1;
}
libsoc_gpio_set_level(gpio_output, LOW);
libsoc_gpio_set_level(gpio_output, HIGH);
libsoc_gpio_set_direction(gpio_output, INPUT);
/* Wait for pin to drop */
while (libsoc_gpio_get_level(gpio_output) == HIGH)
{
if(counter++>10000){
printf("ERROR: Pin never dropped\n");
return 1;
}
}
if (i<= MAXTIMINGS)
{
counter =0;
while (libsoc_gpio_get_level(gpio_output) == LOW){
if(counter++ == 1000)
break;
}
counter =0;
while(libsoc_gpio_get_level(gpio_output) == HIGH){
if (counter++==1000)
break;
}
bits[bitidx++] = counter;
i++;
}
/* read data */
for (i = 1; i < bitidx; i++) {
data[j / 8] <<= 1;
if(bits[i]>200){
data[j/8] |= 1;
}
j++;
}
if( SILENT < 1 ) {
printf("Data (%d): 0x%x 0x%x 0x%x 0x%x 0x%x\n", j, data[0], data[1], data[2], data[3], data[4]);
}
if ((j >= 39) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {
float f, h;
h = data[0] * 256 + data[1];
h /= 10;
f = (data[2] & 0x7F)* 256 + data[3];
f /= 10.0;
if (data[2] & 0x80) f *= -1;
printf("CTemp: %.1f\nFTemp: %.1f\nHum: %.1f%\n", f, ((f*9)/5)+32, h);
} else if( allowRetry > 0 ) {
sleep(1);
if( SILENT < 1 ) {
printf( "Error getting information. Retrying\n" );
}
return readDHT(pin, --allowRetry );
} else {
if( SILENT < 1 ) {
printf( "Error getting information. Retries exhausted.\n" );
}
return 1;
}
return 0;
if (gpio_output)
{
libsoc_gpio_free(gpio_output);
}
/* Check we got all the data and checksum matches */
}
With this code I get "Pin never dropped", so pin never goes to 0 so it doesnt report data. So i decided to try with bash and see if pin drops to 0. I coded the same as on the previous code but in bash and see the value of the pin (always 1, not dropping). Comming to this point, I run out of options, the sensor works (it's not broken), the library works but the sensor on this machine no. Any clue or idea on how to approach to find a solution?
Thanks :)
Some things to try that might get you going....
First increase the counter "counter++>10000" to something much larger, It could be that the new processor you are running increments the counter at a much faster rate and you just timeout before the pin drops.
If that doesn't work remove the timeout counter and loop forever in your source, also remove the sensor, then physically pull the data line to ground with a piece of wire and see if your code/new processor catches the signal change, at least then you know your source/hardware is configured correctly so you can focus your efforts elsewhere.
Double check the voltage compatibility between your new processor 'high' level and what the sensors 'high' threshold is to ensure it is catching a 'one' on the data line.
Lettus know how you go!
Tony

poll() on raspberry-gpio (sysfs) raspberry

as the title states, I have a problem porting some userspace-interrupt code from another armv7 embedded linux platform onto the Raspberry Pi 2 Model B.
I'm aware of the wiringPi library (and got it to work that way), but for evaluation reasons I want to do run as much identical code as possible on both platforms. For that reason I have to interface with sysfs by hand.
So, here's the relevant code snippet
#define GPIO_TRIGGER_MODE "rising"
#define SYS_GPIO_PIN "2"
#define SYS_GPIO_DIRECTION "/sys/class/gpio/gpio2/direction"
#define SYS_GPIO_EDGE "/sys/class/gpio/gpio2/edge"
#define SYS_GPIO_VALUE "/sys/class/gpio/gpio2/value"
static int fd_gpio;
{...}
//Setup sysfs-Pin
if ((fd_gpio = open("/sys/class/gpio/export", O_WRONLY)) < 0) {
exit(-1);
} else {
write(fd_gpio, SYS_GPIO_PIN, strlen((char*) SYS_GPIO_PIN));
close(fd_gpio);
if ((fd_gpio = open(SYS_GPIO_DIRECTION, O_WRONLY)) < 0) {
exit(-1);
} else {
write(fd_gpio, "in", strlen("in"));
close(fd_gpio);
if ((fd_gpio = open(SYS_GPIO_EDGE, O_WRONLY)) < 0) {
exit(-1);
} else {
write(fd_gpio, GPIO_TRIGGER_MODE, strlen((char*) GPIO_TRIGGER_MODE));
close(fd_gpio);
}
}
}
static int fd_gpio_value;
struct pollfd *fd_poll;
if ((fd_gpio_value = open(SYS_GPIO_VALUE, O_RDWR)) < 0) {
exit(-1);
} else {
fd_poll = malloc(sizeof (*fd_poll));
fd_poll->fd = fd_gpio_value;
fd_poll->events = POLLPRI;
char buf;
while (1) {
read(fd_gpio_value, &buf, 1);
if (poll(fd_poll, 1, -1) == -1) {
exit(-1);
} else {
some_logging_occurs();
}
}
So, whats working is the setup of the Pin: (cat /sys/class/gpio/gpio2/$stuff echoes the right settings). As long as there is no Trigger, the programm waits correctly (on poll(), as intended).
After the first rising edge came, poll() always returns immediately, and thus executes my logging function everytime, not only on rising edges.
What baffles me, is that the exact same code works exactly as intended on the other platform and it's the same interface to the GPIOs.
finally found the answer: a simple
lseek(fd_gpio_value,0,SEEK_SET);
was missing before read()

Resources