dev #1

Merged
jakegoodwin merged 11 commits from dev into main 2023-09-01 01:22:30 +00:00
4 changed files with 245 additions and 121 deletions

105
README.md
View File

@ -1,68 +1,30 @@
# CMOCKA/CMAKE template # gy-521 Driver
## Purporse The gy-521 module is usally interfaced with via TWI/I2C. The actual chip that
is used in the module is the mpu6050 ic.
- Streamline the setup of new C projects This driver doesn't make any assumptions about the platform it will run
- Make it easy to setup a develoment enviroment on beyond that it supports "stdlib.h" and "inttypes.h".
- Allow TDD for embedded systems
- Provide easy LSP usage via compile_commands.json
- correctly use the CTEST command.
- Avoid having to setup ruby/ceedling with it's dependency hell
## Organization The interface handles all the register and bitshift operations in the
the backgorund. The module uses getters and setters to abstract the
actual data. This prevents the end user from trying to directly use the
structures data when it is volatile and may be setup to update in a ISR.
``` ## PINs
.
├── CMakeLists.txt
├── LICENSE
├── README.md
├── build
├── cmake
│   └── cmocka
│   ├── AddCCompilerFlag.cmake
│   ├── AddCMockaTest.cmake
│   ├── COPYING-CMAKE-SCRIPTS
│   ├── CheckCCompilerFlagSSP.cmake
│   ├── DefineCMakeDefaults.cmake
│   ├── DefineCompilerFlags.cmake
│   ├── DefinePlatformDefaults.cmake
│   ├── FindNSIS.cmake
│   └── MacroEnsureOutOfSourceBuild.cmake
├── compile_commands.json -> ./build/compile_commands.json
├── git_modules
├── src
│   ├── CMakeLists.txt
│   ├── led_driver
│   │   ├── CMakeLists.txt
│   │   ├── led_driver.c
│   │   └── led_driver.h
│   ├── main.c
│   └── main.h
└── tests
├── CMakeLists.txt
├── led_driver
│   ├── CMakeLists.txt
│   └── test_led_driver.c
└── simple_test
├── CMakeLists.txt
└── simple_test.c
10 directories, 24 files - VCC: 3.3v
``` - GND: ground or 0v
- SCL: TWI clock
The actual code is all stored inside the src direcotry with any libs/submodules - SDA: TWI data
stored inside their own subdirectories. - XDA:
- XCL:
The code we use to test stuff gets stored inside the dir "tests" in the - AD0: changes the TWI address when pulled high.
projects root. - INT: interrupt pin
The compile_commands.json file in the project root is actaully a symlink to
the generated compile_commands.json file inside the build directory. So if
you notice your LSP isn't working correctly it probably means that you haven't
ran cmake yet.
## Running and building the project ## Running and building the project
The project uses CMake along with cmocka for unit testing.
```sh ```sh
# change into the build dir # change into the build dir
@ -76,33 +38,12 @@ ctest
``` ```
# Adding tests
To actually add tests to the project create a new directory inside the
"tests" dir. From there it will need a source.c and CMAKELists.txt file.
You will also need to include it inside the tests/CMakeLists.txt file as well.
```cmake
add_subdirectory(new_test)
```
## RoadMap ## RoadMap
Things I want to add in the future: - add in functions for interrupt driven updates.
- add functions for configuration of module.
- Automatic module creation - add functions to save configurations in memory.
- template generation - add functions to load saved configs
- header --> mocking
- report generation
- embedded specific scripts for common hardware items
##TDD stuff
- what does a struct typedef <name_struct> \*struct actually mean?

View File

@ -1,7 +1,7 @@
/* /*
* Author: Jake Goodwin * Author: Jake Goodwin
* Date: 2023 * Date: 2023
* Description: * Description: The implimentation file for the gy521 module, assumes TWI/I2c
*/ */
#include "gy521_driver.h" #include "gy521_driver.h"
#include <stdint.h> #include <stdint.h>
@ -18,6 +18,9 @@ extern void _test_free(void* const ptr, const char* file, const int line);
#define free(ptr) _test_free(ptr, __FILE__, __LINE__) #define free(ptr) _test_free(ptr, __FILE__, __LINE__)
#endif // UNIT_TESTING #endif // UNIT_TESTING
#define NUM_ACCEL_REGS 6
#define NUM_GYRO_REGS 6
/* /*
* ############################ * ############################
* REGISTER MAP * REGISTER MAP
@ -25,6 +28,20 @@ extern void _test_free(void* const ptr, const char* file, const int line);
*/ */
enum gy521_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_couth = 0x72,
fifo_contl, fifo_contl,
fifo_r_w, fifo_r_w,
@ -37,17 +54,7 @@ enum gy521_map {
* ############################ * ############################
*/ */
typedef struct{
uint16_t x;
uint16_t y;
uint16_t z;
}gyro_values_struct;
typedef struct{
uint16_t x;
uint16_t y;
uint16_t z;
}accel_values_struct;
struct gy521_module{ struct gy521_module{
uint8_t slave_address; uint8_t slave_address;
@ -55,6 +62,30 @@ struct gy521_module{
accel_values_struct accel; accel_values_struct accel;
}; };
/*
* ############################
* Helper/Private Functions
* ############################
*/
uint8_t read_register(gy521_module *m, enum gy521_map reg)
{
/*Load the data var with athe address of the register to read*/
uint8_t data = reg;
/*Indicate we want to read a value from the slave.*/
gy521_twi_rx(m->slave_address, &data, 1);
/*The value should now be in the data var*/
return data;
}
void write_register(gy521_module *m, enum gy521_map reg, uint8_t data)
{
gy521_twi_tx(m->slave_address, (uint8_t *) &reg, data);
}
/* /*
* ############################ * ############################
* Function Definitions * Function Definitions
@ -63,7 +94,6 @@ struct gy521_module{
gy521_module* gy521_new(void) gy521_module* gy521_new(void)
{ {
//gy521_module *m = malloc(sizeof(gy521_module));
gy521_module *m = (gy521_module *)malloc(sizeof(gy521_module)); gy521_module *m = (gy521_module *)malloc(sizeof(gy521_module));
m->slave_address = 0x0; m->slave_address = 0x0;
return m; return m;
@ -75,7 +105,9 @@ void gy521_free(gy521_module *m)
free(m); free(m);
} }
_Bool gy521_init(gy521_module *m, uint8_t slave_address) { _Bool gy521_init(gy521_module *m, uint8_t slave_address) {
/*Check to make sure it's a valid address for this module*/
if((slave_address != TWI_GY521_ADDR1) && (slave_address != TWI_GY521_ADDR2)) { if((slave_address != TWI_GY521_ADDR1) && (slave_address != TWI_GY521_ADDR2)) {
m->slave_address = 0x0; m->slave_address = 0x0;
return 0; return 0;
@ -84,10 +116,8 @@ _Bool gy521_init(gy521_module *m, uint8_t slave_address) {
/*Set the slave address of the module*/ /*Set the slave address of the module*/
m->slave_address = slave_address; m->slave_address = slave_address;
/*Try to talk with i2c module.*/ /*Make sure TWI is working and read the who_am_i register*/
uint8_t data = who_am_i; uint8_t data = read_register(m, who_am_i);
gy521_twi_tx(m->slave_address, &data, 1);
gy521_twi_rx(m->slave_address, &data, 1);
if(data != m->slave_address) { if(data != m->slave_address) {
return 0; return 0;
@ -97,12 +127,44 @@ _Bool gy521_init(gy521_module *m, uint8_t slave_address) {
} }
_Bool gy521_update_gyro(gy521_module *m) void gy521_update_accel(gy521_module *m)
{ {
return 0; /*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_xoutl);
m->accel.y = read_register(m, accel_youth) <<8;
m->accel.y |= read_register(m, accel_youtl);
m->accel.z = read_register(m, accel_zouth) <<8;
m->accel.z |= read_register(m, accel_zoutl);
} }
_Bool gy521_update_accel(gy521_module *m) void gy521_update_gyro(gy521_module *m)
{ {
return 0; /*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_xoutl);
m->gyro.y = read_register(m, gyro_youth) <<8;
m->gyro.y |= read_register(m, gyro_youtl);
m->gyro.z = read_register(m, gyro_zouth) <<8;
m->gyro.z |= read_register(m, gyro_zoutl);
}
struct accel_values gy521_get_accel(struct gy521_module* m)
{
struct accel_values s;
s.x = m->accel.x;
s.y = m->accel.y;
s.z = m->accel.z;
return s;
}
struct gyro_values gy521_get_gyro(struct gy521_module* m)
{
struct gyro_values s;
s.x = m->gyro.x;
s.y = m->gyro.y;
s.z = m->gyro.z;
return s;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Author: Jake Goodwin * Author: Jake Goodwin
* Date: 2023 * Date: 2023
* Description: * Description: A Generic driver for gy521 IMU modules
*/ */
#ifndef GY521_DRIVER_H #ifndef GY521_DRIVER_H
@ -24,7 +24,19 @@
* ############################ * ############################
* Types/Structures * Types/Structures
* ############################ * ############################
*/ */
typedef struct gyro_values{
uint16_t x;
uint16_t y;
uint16_t z;
}gyro_values_struct;
typedef struct accel_values{
uint16_t x;
uint16_t y;
uint16_t z;
}accel_values_struct;
typedef struct gy521_module gy521_module; typedef struct gy521_module gy521_module;
/* /*
@ -42,9 +54,13 @@ extern void (*gy521_twi_rx)(uint8_t, uint8_t*, uint8_t);
struct gy521_module* gy521_new(void); struct gy521_module* gy521_new(void);
_Bool gy521_init(struct gy521_module *m, uint8_t slave_address); _Bool gy521_init(struct gy521_module *m, uint8_t slave_address);
_Bool gy521_update_gyro(struct gy521_module* m); void gy521_update_gyro(struct gy521_module* m);
_Bool gy521_update_accel(struct gy521_module* m); void gy521_update_accel(struct gy521_module* m);
void gy521_free(struct gy521_module *m); void gy521_free(struct gy521_module *m);
/*These are used instead of direct access; assuming interrupt driven*/
/*updates to the gy521 then it protects the user from using volatile values*/
struct accel_values gy521_get_accel(struct gy521_module* m);
struct gyro_values gy521_get_gyro(struct gy521_module* m);
#endif /* GY521_DRIVER_H */ #endif /* GY521_DRIVER_H */

View File

@ -8,23 +8,46 @@
#define WRITE_BIT (1<<7) #define WRITE_BIT (1<<7)
uint8_t fake_twi_addr = 0x0; typedef struct reg_addr{
uint8_t fake_twi_data[16] = {0x0}; uint8_t dev_addr;
uint8_t addr;
uint8_t value;
}reg_addr;
reg_addr reg_addr_arr[16] = {{0x0, 0x0, 0x0}};
uint8_t idx = 0;
/* 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)
{ {
fake_twi_addr = slave_addr; /*Save the slave address passed*/
reg_addr_arr[0].dev_addr = slave_addr;
/*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*/
idx++;
} }
/* 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)
{ {
fake_twi_addr = slave_addr; /*Save the slave address passed*/
for(int i = 0; i < size; i++) { reg_addr_arr[0].dev_addr = slave_addr;
*data++ = fake_twi_data[i];
/*Read the registers requested*/
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*/
idx++;
} }
@ -43,33 +66,115 @@ static void test_gy521_init(void **state) {
/*Check for it's confirmation of the right twi device*/ /*Check for it's confirmation of the right twi device*/
assert_false(gy521_init(m, TWI_GY521_ADDR1)); assert_false(gy521_init(m, TWI_GY521_ADDR1));
/*Now give it the correct response*/ /*Fill the address and the value registers with correct response value*/
fake_twi_data[0] = TWI_GY521_ADDR1; /*The zero element get's read on init of the module*/
reg_addr_arr[0].addr = TWI_GY521_ADDR1;
reg_addr_arr[0].value = TWI_GY521_ADDR1;
idx = 0;
/*Check that it worked*/
assert_true(gy521_init(m, TWI_GY521_ADDR1)); assert_true(gy521_init(m, TWI_GY521_ADDR1));
assert_true(TWI_GY521_ADDR1 == fake_twi_addr); assert_true(TWI_GY521_ADDR1 == reg_addr_arr[0].dev_addr);
fake_twi_data[0] = TWI_GY521_ADDR2; reg_addr_arr[0].addr = TWI_GY521_ADDR2;
reg_addr_arr[0].value = TWI_GY521_ADDR2;
idx = 0;
assert_true(gy521_init(m, TWI_GY521_ADDR2)); assert_true(gy521_init(m, TWI_GY521_ADDR2));
assert_true(TWI_GY521_ADDR2 == fake_twi_addr); assert_true(TWI_GY521_ADDR2 == reg_addr_arr[0].dev_addr);
gy521_free(m); gy521_free(m);
} }
static void test_gy521_update(void **sate) static void test_gy521_update_accel(void **sate)
{ {
assert_false(1); /*check it reads the accel registers*/
gy521_module *m = gy521_new();
reg_addr_arr[0].addr = TWI_GY521_ADDR1;
gy521_init(m, TWI_GY521_ADDR1);
/*Setup the fake accel values.*/
for(uint8_t i = 0; i < 6; i++) {
reg_addr_arr[i].value = i * 8;
}
/*Zero the global index for the TWI*/
idx = 0;
gy521_update_accel(m);
/*Ensure the correct registers are read*/
_Bool is_correct = 1;
assert_true(is_correct);
for(uint8_t i = 0; i < 6; i++){
/*The starting address of the registers is 59 and goes to 64*/
if((59 + i) != reg_addr_arr[i].addr) {
is_correct = 0;
}
}
assert_true(is_correct);
/*Check that the values are assembled correctly*/
struct accel_values accel = gy521_get_accel(m);
/*Make sure to bitshift by 8 and recomine the two u8 into a single u16*/
assert_true(accel.x == ((reg_addr_arr[0].value<<8) | reg_addr_arr[1].value));
assert_true(accel.y == ((reg_addr_arr[2].value<<8) | reg_addr_arr[3].value));
assert_true(accel.z == ((reg_addr_arr[4].value<<8) | reg_addr_arr[5].value));
gy521_free(m);
} }
static void test_gy521_update_gyro(void **sate)
{
/*check it reads the gyro registers*/
gy521_module *m = gy521_new();
reg_addr_arr[0].addr = TWI_GY521_ADDR1;
gy521_init(m, TWI_GY521_ADDR1);
/*Setup the fake gyro values.*/
for(uint8_t i = 0; i < 6; i++) {
reg_addr_arr[i].value = i * 8;
}
/*Zero the global index for the TWI*/
idx = 0;
gy521_update_gyro(m);
/*Ensure the correct registers are read*/
_Bool is_correct = 1;
assert_true(is_correct);
for(uint8_t i = 0; i < 6; i++){
/*The starting address of the registers is 67 and goes to 72*/
if((67 + i) != reg_addr_arr[i].addr) {
is_correct = 0;
}
}
assert_true(is_correct);
/*Check that the values are assembled correctly*/
struct gyro_values gyro = gy521_get_gyro(m);
/*Make sure to bitshift by 8 and recomine the two u8 into a single u16*/
assert_true(gyro.x == ((reg_addr_arr[0].value<<8) | reg_addr_arr[1].value));
assert_true(gyro.y == ((reg_addr_arr[2].value<<8) | reg_addr_arr[3].value));
assert_true(gyro.z == ((reg_addr_arr[4].value<<8) | reg_addr_arr[5].value));
gy521_free(m);
}
int main(void) int main(void)
{ {
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), cmocka_unit_test(test_gy521_update_accel),
cmocka_unit_test(test_gy521_update_gyro),
}; };
return cmocka_run_group_tests(tests, NULL, NULL); return cmocka_run_group_tests(tests, NULL, NULL);
} }