Compare commits

..

1 commit
dev ... main

Author SHA1 Message Date
0a703c5267 Merge pull request 'dev' (#1) from dev into main
Reviewed-on: #1
2023-09-01 01:22:29 +00:00
5 changed files with 81 additions and 556 deletions

View file

@ -46,16 +46,5 @@ ctest
- add functions to load saved configs - add functions to load saved configs
###Mocking the TWI/I2C
The twi needs to be mocked or faked correctly to make the testing
of the functions doable.
The best way I can find to handle this is with a buffer of twi data
that simulate registers that the program reads/writes to.
This is handled by using function pointers for the tx and rx of i2c/twi data.

View file

@ -4,18 +4,7 @@
* Description: The implimentation file for the gy521 module, assumes TWI/I2c * Description: The implimentation file for the gy521 module, assumes TWI/I2c
*/ */
#include "gy521_driver.h" #include "gy521_driver.h"
#include <stdint.h>
/*
* ############################
* MACROS
* ############################
*/
/*DEBUG macro*/
#define DEBUG
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
/*Stuff for cmocka*/ /*Stuff for cmocka*/
#if UNIT_TESTING #if UNIT_TESTING
@ -27,16 +16,51 @@ extern void _test_free(void* const ptr, const char* file, const int line);
#define malloc(size) _test_malloc(size, __FILE__, __LINE__) #define malloc(size) _test_malloc(size, __FILE__, __LINE__)
#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__) #define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__)
#define free(ptr) _test_free(ptr, __FILE__, __LINE__) #define free(ptr) _test_free(ptr, __FILE__, __LINE__)
/*Mock assertions: redefines assert() calls*/
extern void mock_assert(const int result, const char* const expression,
const char * const file, const int line);
#undef assert
#define assert(expression) \
mock_assert((int)(expression), #expression, __FILE__, __LINE__);
#endif // UNIT_TESTING #endif // UNIT_TESTING
#define NUM_ACCEL_REGS 6
#define NUM_GYRO_REGS 6
/*
* ############################
* REGISTER MAP
* ############################
*/
enum gy521_map {
accel_xouth = 0x3B,
accel_xoutl,
accel_youth,
accel_youtl,
accel_zouth,
accel_zoutl,
temp_outh,
temp_outl,
gyro_xouth,
gyro_xoutl,
gyro_youth,
gyro_youtl,
gyro_zouth,
gyro_zoutl,
fifo_couth = 0x72,
fifo_contl,
fifo_r_w,
who_am_i,
};
/*
* ############################
* Structures
* ############################
*/
struct gy521_module{
uint8_t slave_address;
gyro_values_struct gyro;
accel_values_struct accel;
};
/* /*
* ############################ * ############################
@ -58,8 +82,7 @@ uint8_t read_register(gy521_module *m, enum gy521_map reg)
void write_register(gy521_module *m, enum gy521_map reg, uint8_t data) void write_register(gy521_module *m, enum gy521_map reg, uint8_t data)
{ {
uint8_t in_data = reg; gy521_twi_tx(m->slave_address, (uint8_t *) &reg, data);
gy521_twi_tx(m->slave_address, &in_data, 2);
} }
@ -109,10 +132,8 @@ void gy521_update_accel(gy521_module *m)
/*We shift the High byte over by 8 for a u16*/ /*We shift the High byte over by 8 for a u16*/
m->accel.x = read_register(m, accel_xouth) <<8; m->accel.x = read_register(m, accel_xouth) <<8;
m->accel.x |= read_register(m, accel_xoutl); m->accel.x |= read_register(m, accel_xoutl);
m->accel.y = read_register(m, accel_youth) <<8; m->accel.y = read_register(m, accel_youth) <<8;
m->accel.y |= read_register(m, accel_youtl); m->accel.y |= read_register(m, accel_youtl);
m->accel.z = read_register(m, accel_zouth) <<8; m->accel.z = read_register(m, accel_zouth) <<8;
m->accel.z |= read_register(m, accel_zoutl); m->accel.z |= read_register(m, accel_zoutl);
} }
@ -122,10 +143,8 @@ void gy521_update_gyro(gy521_module *m)
/*We shift the High byte over by 8 for a u16*/ /*We shift the High byte over by 8 for a u16*/
m->gyro.x = read_register(m, gyro_xouth) <<8; m->gyro.x = read_register(m, gyro_xouth) <<8;
m->gyro.x |= read_register(m, gyro_xoutl); m->gyro.x |= read_register(m, gyro_xoutl);
m->gyro.y = read_register(m, gyro_youth) <<8; m->gyro.y = read_register(m, gyro_youth) <<8;
m->gyro.y |= read_register(m, gyro_youtl); m->gyro.y |= read_register(m, gyro_youtl);
m->gyro.z = read_register(m, gyro_zouth) <<8; m->gyro.z = read_register(m, gyro_zouth) <<8;
m->gyro.z |= read_register(m, gyro_zoutl); m->gyro.z |= read_register(m, gyro_zoutl);
} }
@ -149,101 +168,3 @@ struct gyro_values gy521_get_gyro(struct gy521_module* m)
s.z = m->gyro.z; s.z = m->gyro.z;
return s; return s;
} }
void enable_self_test_gyro(struct gy521_module* m)
{
/*Set the values in the GYRO_CONFIG register*/
/*According to the datasheet setting it for 250 is needed*/
uint8_t reg = XG_ST + YG_ST + ZG_ST + FS_SEL_250;
write_register(m, gyro_config, reg);
}
void disable_self_test_gyro(struct gy521_module* m)
{
write_register(m, gyro_config, 0x00);
}
struct ft_vals self_test_ft_calculation(struct gy521_module* m)
{
/*We make an array with 0 --> x, 1--> y, 2-->z*/
float factory_trim_vals[3];
uint8_t gyro_test[3] = {0};
for(uint8_t i = 0; i < 3; i++) {
/*Reads the registers for self testing the gyro*/
gyro_test[i] = read_register(m, self_test_x + i) & 0x0F;
/*Skip doing calculations if the test value is zero*/
if(gyro_test[i] == 0){
factory_trim_vals[i] = 0;
continue;
}
else if(gyro_test[i] == 1) {
factory_trim_vals[i] = 1;
}
else {
factory_trim_vals[i] = 1.046;
for(uint8_t j = 0; j < (gyro_test[i] - 1); j++){
factory_trim_vals[i] *= 1.046;
}
}
factory_trim_vals[i] *= (25 * 131);
}
struct ft_vals ft;
ft.x = factory_trim_vals[0];
ft.y = factory_trim_vals[1];
ft.z = factory_trim_vals[2];
return ft;
}
/*Self Test register functions*/
self_test_results gy521_self_test(struct gy521_module* m)
{
/*0. Enable self test*/
enable_self_test_gyro(m);
/*1. read the reg FT values*/
/*2. calulate the FT using the formula from the datasheet*/
struct ft_vals factory_trim = self_test_ft_calculation(m);
/*3. Read the STR data from the gyro*/
gy521_update_gyro(m);
struct gyro_values STR = m->gyro;
/*4. Disable the self test mode.*/
disable_self_test_gyro(m);
/*5. Now get the normal gyro data*/
gy521_update_gyro(m);
/*6. Finish the STR calculation*/
STR.x -= m->gyro.x;
STR.y -= m->gyro.y;
STR.z -= m->gyro.z;
/*7. Now we calculate the change from the FT of the STR*/
float testing_value = ( ((float)STR.x) - factory_trim.x) / factory_trim.x;
if(testing_value > GYRO_MAX_ST || testing_value < GYRO_MIN_ST) {
return gyro_failed;
}
testing_value = ( ((float)STR.y) - factory_trim.y) / factory_trim.y;
if(testing_value > GYRO_MAX_ST || testing_value < GYRO_MIN_ST) {
return gyro_failed;
}
testing_value = ( ((float)STR.z) - factory_trim.z) / factory_trim.z;
if(testing_value > GYRO_MAX_ST || testing_value < GYRO_MIN_ST) {
return gyro_failed;
}
/*Return that we passed the self test if it's within parameters*/
return passed;
}

View file

@ -6,7 +6,8 @@
#ifndef GY521_DRIVER_H #ifndef GY521_DRIVER_H
#define GY521_DRIVER_H #define GY521_DRIVER_H
#include "gy521_interface.h"
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
/* /*
@ -15,31 +16,15 @@
* ############################ * ############################
*/ */
#define NUM_ACCEL_REGS 6 #define STARTUP_DELAY 30 //in ms
#define NUM_GYRO_REGS 6 #define TWI_GY521_ADDR1 0x68 //ADO Logic Low
#define TWI_GY521_ADDR2 0x69 //ADO Logic High
/*GYRO MIN MAX PARAMETERS(percentage)*/
#define GYRO_MIN_ST -14
#define GYRO_MAX_ST 14
/*GYRO_CONFIG*/
#define XG_ST (1<<7)
#define YG_ST (1<<6)
#define ZG_ST (1<<5)
#define FS_SEL_MSK 0x18//0b00011000
#define FS_SEL_250 0
#define FS_SEL_500 (1<<3)
#define FS_SEL_1000 (1<<4)
#define FS_SEL_2000 (1<<4)|(1<<3)
/* /*
* ############################ * ############################
* Types/Structures * Types/Structures
* ############################ * ############################
*/ */
typedef struct gyro_values{ typedef struct gyro_values{
uint16_t x; uint16_t x;
uint16_t y; uint16_t y;
@ -52,115 +37,7 @@ typedef struct accel_values{
uint16_t z; uint16_t z;
}accel_values_struct; }accel_values_struct;
struct gy521_module{ typedef struct gy521_module gy521_module;
uint8_t slave_address;
gyro_values_struct gyro;
accel_values_struct accel;
};
struct ft_vals{
float x;
float y;
float z;
};
/*
* ############################
* REGISTER MAP
* ############################
*/
/*This thing is fairly huge, but it's got pretty much all the registers
* on it.*/
enum gy521_map {
self_test_x = 0x0D,
self_test_y,
self_test_z,
self_test_a,
smplrt_div = 0x19,
config,
gyro_config,
accel_config,
fifo_en = 0x23,
i2c_mst_ctrl,
i2c_slv0_addr,
i2c_slv0_reg,
i2c_slv0_ctrl,
i2c_slv1_addr,
i2c_slv1_reg,
i2c_slv1_ctrl,
i2c_slv2_addr,
i2c_slv2_reg,
i2c_slv2_ctrl,
i2c_slv3_addr,
i2c_slv3_reg,
i2c_slv3_ctrl,
i2c_slv4_addr,
i2c_slv4_reg,
i2c_slv4_do,
i2c_slv4_ctrl,
i2c_slv4_di,
i2c_mst_status,
int_pin_cfg,
int_enable,
int_status,
accel_xouth = 0x3B,
accel_xoutl,
accel_youth,
accel_youtl,
accel_zouth,
accel_zoutl,
temp_outh,
temp_outl,
gyro_xouth,
gyro_xoutl,
gyro_youth,
gyro_youtl,
gyro_zouth,
gyro_zoutl,
ext_sens_data_00,
ext_sens_data_01,
ext_sens_data_02,
ext_sens_data_03,
ext_sens_data_04,
ext_sens_data_05,
ext_sens_data_06,
ext_sens_data_07,
ext_sens_data_08,
ext_sens_data_09,
ext_sens_data_10,
ext_sens_data_11,
ext_sens_data_12,
ext_sens_data_13,
ext_sens_data_14,
ext_sens_data_15,
ext_sens_data_16,
ext_sens_data_17,
ext_sens_data_18,
ext_sens_data_19,
ext_sens_data_20,
ext_sens_data_21,
ext_sens_data_22,
ext_sens_data_23,
i2c_slv0_do,
i2c_slv1_do,
i2c_slv2_do,
i2c_slv3_do,
i2c_mst_delay_ctrl,
signal_path_reset,
user_ctrl,
pwr_mgmt_1,
pwr_mgmt_2,
fifo_couth = 0x72,
fifo_contl,
fifo_r_w,
who_am_i,
};
/* /*
* ############################ * ############################
@ -168,22 +45,22 @@ enum gy521_map {
* ############################ * ############################
*/ */
gy521_module* gy521_new(void);
void disable_self_test_gyro(struct gy521_module* m); /*Function pointers for the TX and RX fuctionality*/
void enable_self_test_gyro(struct gy521_module* m);
struct ft_vals self_test_ft_calculation(struct gy521_module* m);
self_test_results gy521_self_test(struct gy521_module* m);
uint8_t read_register(gy521_module *m, enum gy521_map reg); extern void (*gy521_twi_tx)(uint8_t, uint8_t*, uint8_t);
void write_register(gy521_module *m, enum gy521_map reg, uint8_t data); extern void (*gy521_twi_rx)(uint8_t, uint8_t*, uint8_t);
struct gy521_module* gy521_new(void);
_Bool gy521_init(struct gy521_module *m, uint8_t slave_address);
void gy521_update_gyro(struct gy521_module* m);
void gy521_update_accel(struct gy521_module* m);
void gy521_free(struct gy521_module *m);
/*These are used instead of direct access; assuming interrupt driven*/ /*These are used instead of direct access; assuming interrupt driven*/
/*updates to the gy521 then it protects the user from using volatile values*/ /*updates to the gy521 then it protects the user from using volatile values*/
struct accel_values gy521_get_accel(struct gy521_module* m); struct accel_values gy521_get_accel(struct gy521_module* m);
struct gyro_values gy521_get_gyro(struct gy521_module* m); struct gyro_values gy521_get_gyro(struct gy521_module* m);
#endif /* GY521_DRIVER_H */ #endif /* GY521_DRIVER_H */

View file

@ -1,56 +0,0 @@
/*
* Author: Jake Goodwin
* Filename: interface.h
* Description: The file you should include for normal use.
*/
/*Function pointers for the TX and RX fuctionality*/
#ifndef _GY521_INTERFACE_H
#define _GY521_INTERFACE_H
#include "stdint.h"
/*
* ############################
* DEFINES
* ############################
*/
#define STARTUP_DELAY 30 //in ms
#define TWI_GY521_ADDR1 0x68 //ADO Logic Low
#define TWI_GY521_ADDR2 0x69 //ADO Logic High
/*
* ############################
* Structures & Typedefs
* ############################
*/
typedef struct gy521_module gy521_module;
typedef enum{
passed = 0,
gyro_failed,
accel_failed,
}self_test_results;
/*
* ############################
* Fuction Prototypes
* ############################
*/
extern void (*gy521_twi_tx)(uint8_t, uint8_t*, uint8_t);
extern void (*gy521_twi_rx)(uint8_t, uint8_t*, uint8_t);
struct gy521_module* gy521_new(void);
_Bool gy521_init(struct gy521_module *m, uint8_t slave_address);
void gy521_update_gyro(struct gy521_module* m);
void gy521_update_accel(struct gy521_module* m);
void gy521_free(struct gy521_module *m);
#endif

View file

@ -1,4 +1,3 @@
#include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -8,7 +7,6 @@
#include "gy521_driver.h" #include "gy521_driver.h"
#define WRITE_BIT (1<<7) #define WRITE_BIT (1<<7)
#define REG_SIZE 32
typedef struct reg_addr{ typedef struct reg_addr{
uint8_t dev_addr; uint8_t dev_addr;
@ -16,61 +14,40 @@ typedef struct reg_addr{
uint8_t value; uint8_t value;
}reg_addr; }reg_addr;
reg_addr reg_addr_arr[REG_SIZE] = {{0x0, 0x0, 0x0}}; reg_addr reg_addr_arr[16] = {{0x0, 0x0, 0x0}};
uint8_t idx = 0; uint8_t idx = 0;
void mock_twi_tx(uint8_t slave_addr, uint8_t *data, uint8_t size)
{
check_expected(slave_addr);
check_expected_ptr(data);
check_expected(size);
}
/* Fake Object for TWI_TX*/ /* Fake Object for TWI_TX*/
void fake_twi_tx(uint8_t slave_addr, uint8_t *data, uint8_t size) void fake_twi_tx(uint8_t slave_addr, uint8_t *data, uint8_t size)
{ {
/*read the device address.*/ /*Save the slave address passed*/
reg_addr_arr[idx].dev_addr = slave_addr; reg_addr_arr[0].dev_addr = slave_addr;
/*Save the register it's trying to access.*/
reg_addr_arr[idx].addr = *data;
/*Save the data it's sending to that address.*/
reg_addr_arr[idx].value = *(data + 1);
/*Now write all the passed data*/
for(; idx < size; idx++) {
reg_addr_arr[idx].addr = *(data);
reg_addr_arr[idx].value = *(++data);
}
/*Post increment the global index*/ /*Post increment the global index*/
idx++; idx++;
if(idx > REG_SIZE){
idx = 0;
}
/*Make use of recursion if there is still more data*/
if((size - 2) > 0) {
fake_twi_tx(slave_addr, data + 2, size - 2);
}
} }
/* Fake Object for TWI_RX*/ /* Fake Object for TWI_RX*/
void fake_twi_rx(uint8_t slave_addr, uint8_t *data, uint8_t size) void fake_twi_rx(uint8_t slave_addr, uint8_t *data, uint8_t size)
{ {
/*Save the slave address passed*/
/*Read the device address*/ reg_addr_arr[0].dev_addr = slave_addr;
reg_addr_arr[idx].dev_addr = slave_addr;
/*Put down the register address it's trying to read.*/
reg_addr_arr[idx].addr = *data;
/*Fill it with the data from the register*/ /*Read the registers requested*/
*data = reg_addr_arr[idx].value; for(uint8_t i = 0; i < size; i++) {
/*Wridxte the register read address*/
reg_addr_arr[i + idx].addr = *(data + i);
/*Read the response value into the pased data ptr*/
*(data + i) = reg_addr_arr[i + idx].value;
}
/*Post increment the global index*/ /*Post increment the global index*/
idx++; idx++;
if(idx > REG_SIZE){
idx = 0;
}
} }
@ -79,59 +56,6 @@ void fake_twi_rx(uint8_t slave_addr, uint8_t *data, uint8_t size)
void (*gy521_twi_tx)(uint8_t, uint8_t*, uint8_t) = &fake_twi_tx; void (*gy521_twi_tx)(uint8_t, uint8_t*, uint8_t) = &fake_twi_tx;
void (*gy521_twi_rx)(uint8_t, uint8_t*, uint8_t) = &fake_twi_rx; void (*gy521_twi_rx)(uint8_t, uint8_t*, uint8_t) = &fake_twi_rx;
/*
* ############################
* Helper functions
* ############################
*/
void print_reg_arr(void)
{
printf("FAKE REGISTERS::\n");
for(uint8_t i = 0; i < REG_SIZE; i++){
print_error("%d:: val: %d, addr: %d, dev_addr: %d\n",
i,
reg_addr_arr[i].value,
reg_addr_arr[i].addr,
reg_addr_arr[i].dev_addr);
}
}
void clear_twi(void)
{
idx = 0;
for(int i = 0; i < REG_SIZE; i++){
reg_addr_arr[i].value = 0;
reg_addr_arr[i].addr = 0;
reg_addr_arr[i].dev_addr = 0;
}
}
static int setup_gy521_instance(void **state)
{
clear_twi();
*state = gy521_new();
assert_non_null(*state);
return 0;
}
static int teardown_gy521_instance(void **state)
{
gy521_free(*state);
clear_twi();
return 0;
}
/*
* ############################
* TESTS
* ############################
*/
/* Tests the donothing function */ /* Tests the donothing function */
static void test_gy521_init(void **state) { static void test_gy521_init(void **state) {
gy521_module *m = gy521_new(); gy521_module *m = gy521_new();
@ -212,7 +136,7 @@ static void test_gy521_update_gyro(void **sate)
gy521_init(m, TWI_GY521_ADDR1); gy521_init(m, TWI_GY521_ADDR1);
/*Setup the fake gyro values.*/ /*Setup the fake gyro values.*/
for(uint8_t i = 1; i < 7; i++) { for(uint8_t i = 0; i < 6; i++) {
reg_addr_arr[i].value = i * 8; reg_addr_arr[i].value = i * 8;
} }
@ -243,144 +167,14 @@ static void test_gy521_update_gyro(void **sate)
} }
static void test_gy521_self_test_gyro(void **sate)
{
/*Create instance of struct.*/
gy521_module *m = gy521_new();
reg_addr_arr[0].addr = TWI_GY521_ADDR1;
gy521_init(m, TWI_GY521_ADDR1);
/*Zero the global index for the twi*/
clear_twi();
/*Setup the full-scale range to +-250dps*/
/*Load up the gyro regs with passing test values*/
/*Load up the selftest regs with passing test values.*/
/*Load up the gyro regs for disabled ST reads*/
for(uint8_t i = 1; i < 17; i++) { /*(6regs * 2) + 3ST regs = 15*/
reg_addr_arr[i].value = 2;
}
self_test_results result = gy521_self_test(m);
assert_false(result); /*The value should be zero, aka zero faults*/
/*Check to make sure it can fail.*/
clear_twi();
for(uint8_t i = 1; i < 5; i++){
reg_addr_arr[i].value = 2;
}
for(int i = 4; i < 9; i++){
reg_addr_arr[i].value = 255;
}
for(int i = 9; i < 17; i++){
reg_addr_arr[i].value = 2;
}
result = gy521_self_test(m);
/*Print out the register values*/
print_reg_arr();
assert_true(result);
/*Free the struct*/
gy521_free(m);
}
static void test_gy521_self_test_accel(void **sate)
{
/*Create instance of struct.*/
gy521_module *m = gy521_new();
reg_addr_arr[0].addr = TWI_GY521_ADDR1;
gy521_init(m, TWI_GY521_ADDR1);
/*Zero the global index for the twi*/
clear_twi();
/*Setup the full-scale range to +-250dps*/
/*Load up the gyro regs with passing test values*/
/*Load up the selftest regs with passing test values.*/
/*Load up the gyro regs for disabled ST reads*/
for(uint8_t i = 1; i < 17; i++) { /*(6regs * 2) + 3ST regs = 15*/
reg_addr_arr[i].value = 2;
}
self_test_results result = gy521_self_test(m);
assert_false(result); /*The value should be zero, aka zero faults*/
/*Check to make sure it can fail.*/
clear_twi();
for(uint8_t i = 1; i < 5; i++){
reg_addr_arr[i].value = 2;
}
for(int i = 4; i < 9; i++){
reg_addr_arr[i].value = 255;
}
for(int i = 9; i < 17; i++){
reg_addr_arr[i].value = 2;
}
result = gy521_self_test(m);
/*Print out the register values*/
print_reg_arr();
assert_true(result);
/*Free the struct*/
gy521_free(m);
}
/*Mock Object/function for TWI RX*/
static void test_gy521_testing(void **state)
{
assert_false(1);
}
static void test_gyro_update(void **state)
{
assert_true(1);
}
static void test_enable_self_test_gyro(void **state)
{
//expect_value(func, param, val);
/*The read register function should be called with */
expect_value(read_register, reg, 27);
enable_self_test_gyro(*state);
}
int main(void) int main(void)
{ {
int result = 0;
/*For testing TWI/I2C related stuff*/
const struct CMUnitTest twi_group[] = {
cmocka_unit_test_setup_teardown(test_gy521_testing, setup_gy521_instance, teardown_gy521_instance),
cmocka_unit_test_setup_teardown(test_gyro_update, setup_gy521_instance, teardown_gy521_instance),
cmocka_unit_test_setup_teardown(test_enable_self_test_gyro, setup_gy521_instance, teardown_gy521_instance),
};
const struct CMUnitTest tests[] = { const struct CMUnitTest tests[] = {
cmocka_unit_test(test_gy521_init), cmocka_unit_test(test_gy521_init),
cmocka_unit_test(test_gy521_update_accel), cmocka_unit_test(test_gy521_update_accel),
cmocka_unit_test(test_gy521_update_gyro), cmocka_unit_test(test_gy521_update_gyro),
cmocka_unit_test(test_gy521_self_test_gyro), };
cmocka_unit_test(test_gy521_self_test_accel), return cmocka_run_group_tests(tests, NULL, NULL);
};
//result = cmocka_run_group_tests(twi_group, setup_gy521_instance, teardown_gy521_instance);
result = cmocka_run_group_tests(twi_group, NULL, NULL);
result = cmocka_run_group_tests(tests, NULL, NULL);
return result;
} }