dev #1
105
README.md
105
README.md
|
@ -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
|
||||
- Make it easy to setup a develoment enviroment
|
||||
- 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
|
||||
This driver doesn't make any assumptions about the platform it will run
|
||||
on beyond that it supports "stdlib.h" and "inttypes.h".
|
||||
|
||||
## 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.
|
||||
|
||||
```
|
||||
.
|
||||
├── 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
|
||||
## PINs
|
||||
|
||||
10 directories, 24 files
|
||||
```
|
||||
|
||||
The actual code is all stored inside the src direcotry with any libs/submodules
|
||||
stored inside their own subdirectories.
|
||||
|
||||
The code we use to test stuff gets stored inside the dir "tests" in the
|
||||
projects root.
|
||||
|
||||
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.
|
||||
- VCC: 3.3v
|
||||
- GND: ground or 0v
|
||||
- SCL: TWI clock
|
||||
- SDA: TWI data
|
||||
- XDA:
|
||||
- XCL:
|
||||
- AD0: changes the TWI address when pulled high.
|
||||
- INT: interrupt pin
|
||||
|
||||
|
||||
## Running and building the project
|
||||
The project uses CMake along with cmocka for unit testing.
|
||||
|
||||
```sh
|
||||
# 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
|
||||
|
||||
Things I want to add in the future:
|
||||
|
||||
- Automatic module creation
|
||||
- template generation
|
||||
- header --> mocking
|
||||
- report generation
|
||||
- embedded specific scripts for common hardware items
|
||||
|
||||
|
||||
|
||||
|
||||
##TDD stuff
|
||||
|
||||
- what does a struct typedef <name_struct> \*struct actually mean?
|
||||
- add in functions for interrupt driven updates.
|
||||
- add functions for configuration of module.
|
||||
- add functions to save configurations in memory.
|
||||
- add functions to load saved configs
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Author: Jake Goodwin
|
||||
* Date: 2023
|
||||
* Description:
|
||||
* Description: The implimentation file for the gy521 module, assumes TWI/I2c
|
||||
*/
|
||||
#include "gy521_driver.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__)
|
||||
#endif // UNIT_TESTING
|
||||
|
||||
#define NUM_ACCEL_REGS 6
|
||||
#define NUM_GYRO_REGS 6
|
||||
|
||||
/*
|
||||
* ############################
|
||||
* REGISTER MAP
|
||||
|
@ -25,6 +28,20 @@ extern void _test_free(void* const ptr, const char* file, const int line);
|
|||
*/
|
||||
|
||||
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,
|
||||
|
@ -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{
|
||||
uint8_t slave_address;
|
||||
|
@ -55,6 +62,30 @@ struct gy521_module{
|
|||
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 *) ®, data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ############################
|
||||
* Function Definitions
|
||||
|
@ -63,7 +94,6 @@ struct gy521_module{
|
|||
|
||||
gy521_module* gy521_new(void)
|
||||
{
|
||||
//gy521_module *m = malloc(sizeof(gy521_module));
|
||||
gy521_module *m = (gy521_module *)malloc(sizeof(gy521_module));
|
||||
m->slave_address = 0x0;
|
||||
return m;
|
||||
|
@ -75,7 +105,9 @@ void gy521_free(gy521_module *m)
|
|||
free(m);
|
||||
}
|
||||
|
||||
|
||||
_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)) {
|
||||
m->slave_address = 0x0;
|
||||
return 0;
|
||||
|
@ -84,10 +116,8 @@ _Bool gy521_init(gy521_module *m, uint8_t slave_address) {
|
|||
/*Set the slave address of the module*/
|
||||
m->slave_address = slave_address;
|
||||
|
||||
/*Try to talk with i2c module.*/
|
||||
uint8_t data = who_am_i;
|
||||
gy521_twi_tx(m->slave_address, &data, 1);
|
||||
gy521_twi_rx(m->slave_address, &data, 1);
|
||||
/*Make sure TWI is working and read the who_am_i register*/
|
||||
uint8_t data = read_register(m, who_am_i);
|
||||
|
||||
if(data != m->slave_address) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Author: Jake Goodwin
|
||||
* Date: 2023
|
||||
* Description:
|
||||
* Description: A Generic driver for gy521 IMU modules
|
||||
*/
|
||||
|
||||
#ifndef GY521_DRIVER_H
|
||||
|
@ -25,6 +25,18 @@
|
|||
* 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;
|
||||
|
||||
/*
|
||||
|
@ -42,9 +54,13 @@ 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);
|
||||
_Bool gy521_update_gyro(struct gy521_module* m);
|
||||
_Bool gy521_update_accel(struct gy521_module* m);
|
||||
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*/
|
||||
/*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 */
|
||||
|
|
|
@ -8,23 +8,46 @@
|
|||
|
||||
#define WRITE_BIT (1<<7)
|
||||
|
||||
uint8_t fake_twi_addr = 0x0;
|
||||
uint8_t fake_twi_data[16] = {0x0};
|
||||
typedef struct reg_addr{
|
||||
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*/
|
||||
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*/
|
||||
void fake_twi_rx(uint8_t slave_addr, uint8_t *data, uint8_t size)
|
||||
{
|
||||
fake_twi_addr = slave_addr;
|
||||
for(int i = 0; i < size; i++) {
|
||||
*data++ = fake_twi_data[i];
|
||||
/*Save the slave address passed*/
|
||||
reg_addr_arr[0].dev_addr = slave_addr;
|
||||
|
||||
/*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*/
|
||||
assert_false(gy521_init(m, TWI_GY521_ADDR1));
|
||||
|
||||
/*Now give it the correct response*/
|
||||
fake_twi_data[0] = TWI_GY521_ADDR1;
|
||||
/*Fill the address and the value registers with correct response value*/
|
||||
/*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(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(TWI_GY521_ADDR2 == fake_twi_addr);
|
||||
assert_true(TWI_GY521_ADDR2 == reg_addr_arr[0].dev_addr);
|
||||
|
||||
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)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue