MPU6050 motion driver library function call cause hardfault exception - c

Hi I'm try to use mpu6050 on my stm32 project.
I copy the motion driver library from SparkFun_MPU-9250-DMP_Arduino_Library and replace arduino function like arduino_i2c_write to stm32 write function. The replace part work fine. The stm32 board did write bytes into the imu and read from it.
However, when I try to setup the dmp funtion and use the mpu_load_firmware function, I encouter a weird situation.
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate)
{
unsigned short ii;
unsigned short this_write;
/* Must divide evenly into st.hw->bank_size to avoid bank crossings. */
#define LOAD_CHUNK (16)
unsigned char cur[LOAD_CHUNK], tmp[2];
if (st.chip_cfg.dmp_loaded)
/* DMP should only be loaded once. */
return -1;
if (!firmware)
return -1;
for (ii = 0; ii < length; ii += this_write) {
this_write = min(LOAD_CHUNK, length - ii);
if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii])))
return -1;
if (mpu_read_mem(ii, this_write, cur))
return -1;
if (memcmp(firmware+ii, cur, this_write))
return -2;
}
/* Set program start address. */
tmp[0] = start_addr >> 8;
tmp[1] = start_addr & 0xFF;
if (i2c_write(st.hw->addr, st.reg->prgm_start_h, 2, tmp))
return -1;
st.chip_cfg.dmp_loaded = 1;
st.chip_cfg.dmp_sample_rate = sample_rate;
return 0;
}
When the code run to the line if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii]))), it cause a hardfault. And I found out that the code cannnot call the mpu_write_mem function porperly. When I start to step in the function, the hardfault exception immediately occurred. I cannot figure it out what cause the exception and how to fix it.
I've checked the pointer, &(firmware[ii]), address, it looks just fine. But the weired thing is that in the mpu_write_mem function, the argument value all differ from the mpu_load_firmware function.(mem_addr != ii, length != this_write, ...) I'm not sure if this cause the exception or the other way around.
Can anyone give me some idea? Thank you very much ~~
Here is the mpu_write_mem function, both function are in the inv_mpu.c file
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_write(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}

Related

ioctl giving error in different executions

For the next system: VAR-DART-MX8M (https://variwiki.com/index.php?title=DART-MX8M)(It is a ARM A-53 cortex based system)
Kernel version: Linux Debian Stretch version (kernel 4.14.78)
I use Eclipse to write the code and then cross compile to the specified system.
I want to use an SPI bus in my embedded system to communicate with an FPGA. I have read that casting pointers can lead to undefined behaviour: Passing pointer of unsigned int to pointer of long int
The problem is that I have to cast some parameters in order to meet the spi_ioc_transfer struct requirements:
int transfer16(int fd, uint16_t *tx, uint16_t *rx, uint32_t len){
int ret;
errno=0;
tr.tx_buf = (unsigned long)tx;
tr.rx_buf = (unsigned long)rx;
tr.len = len;
tr.delay_usecs = 1;
tr.speed_hz = spi_speed;
tr.bits_per_word = 16;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (errno != 0){
printf("SPI IOCTL ret(%d), error(%d) %s\n", ret, errno,
strerror(errno));
}
return ret;
}
Where tr is the struct spi_ioc_transfer provided by spidev.h (declared as global variable).
The function transfer16 is called by the following function:
int send_command_readadc(int fd, int16_t *rx, uint16_t ndata_adc, uint8_t membank) {
int ret;
uint16_t tx[2];
uint16_t crc16_o, crc16_i;
uint8_t rcommand;
uint8_t ack;
uint32_t len = 2*(ndata_adc+NUMELS(tx)+2); // 2*sizeof(rx)
int i = 0;
tx[0] = (THE_READADC_COMMAND << 8) + membank;
tx[1] = crc16_uint16_false(tx,NUMELS(tx)-1);
ret = transfer16(fd, tx, rx, len);
if (ret==-1) {
return -1;
}
And I call this function using the following parameters:
ret = send_command_readadc(fd_spi, (int16_t *)(data_adc+(ndata_adc+4)*i), ndata_adc, membank);
Where data_adc is a pointer to a allocated part of memory where I want to save the ADC readings:
data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t));
So, for example, if M is 3 I save the quantity of (ndata_adc+4) starting from the register data_adc. Then (ndata_adc+4) starting from the register data_adc+(ndata_adc+4) etc.
The function transfer16 sometimes doesn't work properly, as same exact executions can have different results:
Any tip on how to proceed with this issue?
If you need more information about the problem, ask without hesitation.
Thanks!
I have solutioned this problem this way:
Instead of using data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t)); , I declared an array int16_t data_adc_static[(ndata_adc+4)*M]; and I give the next address int16_t *address = &data_adc_static[(ndata_adc+4)*i]; to the send_command_readadc command.
This way the problem that I had desappeared.
Thanks everybody for your answers

realloc overwrite variable (Xilinx SDK on a Zynq SoC (Cortex A9))

As mentioned I have a Zynq SoC (ZC706 Eval Board) and I'm trying to read an image from the SD Card. To do this I'm using the FatFs lib (http://elm-chan.org/fsw/ff/00index_e.html).
In my code I read 4096 Byte from the file and save it to a buffer. After that i copy the buffer to an unsigned char pointer that size I increase after every read operation.
Then I'm using realloc, the for loop in the copyU32ArrayToUnsignedCharArray function 'failed' because the size variable is overwritten by the out array.
Code that overwrite the "size" in the copyU32ArrayToUnsignedCharArray function:
u32 buffer[1024];
unsigned char *img = NULL;
bytesreaded = 0;
for (;;) {
br=0;
fr = f_read(&fil, buffer, sizeof(buffer), &br); /* Read a chunk of source file */
if (fr || br == 0)
break; /* error or eof */
img = realloc(img,br);
copyU32ArrayToUnsignedCharArray(buffer, &img[bytesreaded], br/4); // /4 because u32(32 bit) in to unsigned char(8 bit)
bytesreaded += br; // update readed bytes
}
The code that worked:
u32 buffer[1024];
unsigned char *img = NULL;
img = malloc(512*512*3+100);
bytesreaded = 0;
for (;;) {
br=0;
fr = f_read(&fil, buffer, sizeof(buffer), &br); /* Read a chunk of source file */
if (fr || br == 0)
break; /* error or eof */
copyU32ArrayToUnsignedCharArray(buffer, &img[bytesreaded], br/4); // /4 because u32(32 bit) in to unsigned char(8 bit)
bytesreaded += br; // update readed bytes
}
The copyU32ArrayToUnsignedCharArray function:
void copyU32ArrayToUnsignedCharArray(u32 *in, unsigned char* out, uint size){
int i,x;
x = 0;
for (i = 0; i < size; i++) {
if(size != 1024)
break;
in[i] = Xil_In32BE(&in[i]);
out[x] = (u32) in[i] >> 24;
out[x + 1] = (u32) in[i] >> 16 & 0x00FF;
out[x + 2] = (u32) in[i] >> 8 & 0x0000FF;
out[x + 3] = (u32) in[i] & 0x000000FF;
x += 4;
}
}
I want to use realloc because I don't know how big the image will be that I read.
Update:
Some further information to the code that doesn't work. I debugged it and the pointer to *img isn't null, so the realloc was successfully. If I'm using gdb the following things happen in the copyU32ArrayToUnsignedCharArray function:
- pointer to the variable "out" is 0x001125a8
- the address of the "size" variable is 0x0011309c (the value that is stored at this location is correct)
- the space in memory between this two variables is 0xaf4 = 2804 dec (difference of the two addresses)
- if the for loop within the copyU32ArrayToUnsignedCharArray function reached i=702 and x=2808 the size variable is changed to another value
Sincerely,
Arno
I solved the problem with the hint from Notlikethat. The problem was the small heap size. Increasing the heap is done by editing the linker script file

difference between reading proc files via shell (cat) or via program (fread)

I have a kernel module creating an entry in proc-fs and a userspace-program that reads the file.
My proc read-function looks like:
typedef struct {
int integer;
unsigned long ulong;
char string[100];
float floatt;
bool booll;
u16 crc;
} struktur;
static int round = 0, len = 0, temp = 0, err;
struktur *pde_data_p;
int proc_read(struct file *filp, char *buf, size_t count, loff_t *offp) {
int i;
unsigned char *crcbuf;
struktur struct_buf;
pde_data_p = PDE_DATA(file_inode(filp));
crcbuf = (unsigned char*)pde_data_p;
memcpy(&struct_buf, pde_data_p, sizeof(struktur));
if (pde_data_p == NULL) {
printk(KERN_ERR "pde->data == NULL\n");
round = 0;
return 0;
}
if (round == 0)
temp = sizeof(struktur);
if (count > temp)
count = temp;
struct_buf.crc = crc16(struct_buf.crc, crcbuf, sizeof(struktur)-sizeof(unsigned short));
err = copy_to_user(buf, pde_data_p, count);
//if (err == 0) { // copy_to_user finished
round = 0;
temp = 0; // taking this line out makes it work in the prog but not with my cat
return temp;
//} else { // copy_to_user failed -> return number of bytes that have not been copied
//temp = err;
//round++;
//return temp;
//}
}
My program code is:
typedef struct {
int integer;
unsigned long ulong;
char string[100];
float floatt;
bool booll;
unsigned short crc;
} struktur;
int main(void) {
int i;
struktur str_inp;
unsigned short crc = 0;
unsigned char *str_p = (unsigned char*)&str_inp;
FILE *fp = fopen("/proc/sen/entry", "r");
fread(&str_inp, 1, sizeof(struktur), fp);
fclose(fp);
}
As you can see my proc read-function returns the number of bytes read, not zero.
This way my program works fine but when I try to read via cat (cat /proc/sen/entry) it never finishes because it never returns 0.
When I change my code and return 0 after copy_to_user has finished, reading via cat works fine but my program seems reads random memory. When I return half of the number of copied bytes, just half of the data read by the user-space program is correct.
What has to be returned by your proc_read function depends on the position which is handed over in *offp. If it is only required to work for your given userspace program code and for cat (not for partial reads of struktur) it suffices to
if (*offp) return 0; // no more data to return
else *offp = sizeof (struktur); // file position after data returned
- this statement goes before the copy_to_user().

Memory comparison causes system halt

I am working on a kernel module and I need to compare two buffers to find out if they are equivalent. I am using the memcmp function defined in the Linux kernel to do so. My first buffer is like this:
cache_buffer = (unsigned char *)vmalloc(4097);
cache_buffer[4096] = '/0';
The second buffer is from a page using the page_address() function.
page = bio_page(bio);
kmap(page);
write_buffer = (char *)page_address(page);
kunmap(page);
I have printed the contents of both buffers before hand and not only to they print correctly, but they also have the same content. So next, I do this:
result = memcmp(write_buffer, cache_buffer, 2048); // only comparing up to 2048 positions
This causes the kernel to freeze up and I cannot figure out why. I checked the implementation of memcmp and saw nothing that would cause the freeze. Can anyone suggest a cause?
Here is the memcmp implementation:
int memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}
EDIT: The function causing the freeze is memcmp. When I commented it out, everything worked. Also, when I did I memcmp as follows
memcmp(write_buffer, write_buffer, 2048); //comparing two write_buffers
Everything worked as well. Only when I throw the cache_buffer into the mix is when I get the error. Also, above is a simplification of my actual code. Here is the entire function:
static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
struct dm_io_region where;
unsigned long bits;
int segno;
struct bio_vec * bvec;
struct page * page;
unsigned char * cache_data;
char * temp_data;
char * write_data;
int result, length, i;
cache_data = (unsigned char *)vmalloc((dmc->block_size * 512) + 1);
where.bdev = dmc->cache_dev->bdev;
where.count = dmc->block_size;
where.sector = location << dmc->block_shift;
printk(KERN_DEBUG "place: %llu\n", where.sector);
dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);
length = 0;
bio_for_each_segment(bvec, bio, segno)
{
if(segno == 0)
{
page = bio_page(bio);
kmap(page);
write_data = (char *)page_address(page);
//kunmap(page);
length += bvec->bv_len;
}
else
{
page = bio_page(bio);
kmap(page);
temp_data = strcat(write_data, (char *)page_address(page));
//kunmap(page);
write_data = temp_data;
length += bvec->bv_len;
}
}
printk(KERN_INFO "length: %u\n", length);
cache_data[dmc->block_size * 512] = '\0';
for(i = 0; i < 2048; i++)
{
printk("%c", write_data[i]);
}
printk("\n");
for(i = 0; i < 2048; i++)
{
printk("%c", cache_data[i]);
}
printk("\n");
result = memcmp(write_data, cache_data, length);
return result;
}
EDIT #2: Sorry guys. The problem was not memcmp. It was the result of memcmp. When ever it returned a positive or negative number, the function that called my function would play with some pointers, one of which was uninitialized. I don't know why I didn't realize it before. Thanks for trying to help though!
I'm no kernel expert, but I would assume you need to keep this memory mapped while doing the comparison? In other words, don't call kunmap until after the memcmp is complete. I would presume that calling it before will result in write_buffer pointing to a page which is no longer mapped.
Taking your code in the other question, here is a rough attempt at incremental. Still needs some cleanup, I'm sure:
static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
struct dm_io_region where;
unsigned long bits;
int segno;
struct bio_vec * bvec;
struct page * page;
unsigned char * cache_data;
char * temp_data;
char * write_data;
int length, i;
int result = 0;
size_t position = 0;
size_t max_size = (dmc->block_size * 512) + 1;
cache_data = (unsigned char *)vmalloc(max_size);
where.bdev = dmc->cache_dev->bdev;
where.count = dmc->block_size;
where.sector = location << dmc->block_shift;
printk(KERN_DEBUG "place: %llu\n", where.sector);
dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);
bio_for_each_segment(bvec, bio, segno)
{
// Map the page into memory
page = bio_page(bio);
write_data = (char *)kmap(page);
length = bvec->bv_len;
// Make sure we don't go past the end
if(position >= max_size)
break;
if(position + length > max_size)
length = max_size - position;
// Compare the data
result = memcmp(write_data, cache_data + position, length);
position += length;
kunmap(page);
// If the memory is not equal, bail out now and return the result
if(result != 0)
break;
}
cache_data[dmc->block_size * 512] = '\0';
return result;
}

Data loss when converting byte to int on Arduino

I have a 3-byte array that buffers incoming bytes through the Serial port. Once it is full I want to use the bytes to call a function that takes a byte and an int as a parameter. This should theoretically not be a problem, but for some reason the bytes are not being converten into an int properly. Here is the code I have:
// for serialEvent()
uint8_t buffer[3] = {0, 0, 0};
uint8_t index = 0;
void serialEvent() {
while (Serial.available()) {
if (index > 2) {
// buffer is full so process it
uint16_t argument = (uint16_t)buffer[1];
argument <<= 8;
argument |= buffer[2];
processSerial(buffer[0], argument);
index = 0;
}
buffer[index] = Serial.read();
index++;
}
}
void processSerial(uint8_t action, uint16_t argument) { ... }
The problem appears to be in the line where the first bit is shifted to the left to make space for the second one. I have tried outputting the variable over the Serial port again and after the bit shift operation, it is 0.
The same thing happens when I try to replace the bit shift operation with a multiplication by 256 (which has the same result in theory).
Irritatingly, when I assign a static value like so, everything works fine:
uint16_t argument = 0x00CD;
argument <<= 8;
Is this a type cast problem? Am I missing something here?
Have not found the solution as to why this happens, but using the word() function does exactly what I want:
uint16_t argument = word(buffer[1], buffer[2]);
processSerial(buffer[0], argument);
Annoyingly, it is defined as:
unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }

Resources