3 |
4 | static result_uint8_t read(bmp280_t *bmp, uint8_t addr) {
5 | error_t e = i2c_transmit(bmp->dev, (buffer_view_t){.data = &addr, .size = 1});
6 | uint8_t value = 0xFF;
7 | buffer_view_t value_v = {.data = &value, .size = sizeof(value)};
8 | e |= i2c_receive(bmp->dev, value_v);
9 | return (result_uint8_t){.hasError = e, .value = value};
10 | }
11 |
12 | static error_t write(bmp280_t *bmp, uint8_t addr, uint8_t value) {
13 | uint8_t write[] = {addr, value};
14 | buffer_view_t write_v = {.data = write, .size = sizeof(write)};
15 | return i2c_transmit(bmp->dev, write_v);
16 | }
17 |
18 | static result_int read_temp(bmp280_t *bmp) {
19 | error_t e = 0x00;
20 | uint8_t start_addr = BMP_TEMP_MSB;
21 | e |= i2c_transmit(bmp->dev, (buffer_view_t){.data = &start_addr, .size = 1});
22 |
23 | uint8_t temp_buf[3] = {0};
24 | buffer_view_t temp_buf_v = {.data = temp_buf, .size = sizeof(temp_buf)};
25 | e |= i2c_receive(bmp->dev, temp_buf_v);
26 | int32_t temp = temp_buf[0] << 16 | temp_buf[1] << 8 | temp_buf[0];
27 | temp >>= 4;
28 | return (result_int){.value = temp, .hasError = e};
29 | }
30 |
31 | static result_int read_pres(bmp280_t *bmp) {
32 | error_t e = 0x00;
33 | uint8_t start_addr = BMP_PRESS_MSB;
34 | e |= i2c_transmit(bmp->dev, (buffer_view_t){.data = &start_addr, .size = 1});
35 |
36 | uint8_t pres_buf[3] = {0};
37 | buffer_view_t temp_buf_v = {.data = pres_buf, .size = sizeof(pres_buf)};
38 | e |= i2c_receive(bmp->dev, temp_buf_v);
39 | int32_t pres = pres_buf[0] << 16 | pres_buf[1] << 8 | pres_buf[0];
40 | pres >>= 4;
41 | return (result_int){.value = pres, .hasError = e};
42 | }
43 |
44 | static void reset(bmp280_t *bmp) { write(bmp, BMP_RESET, 0xB6); }
45 |
46 | static error_t read_temp_calibration(bmp280_t *bmp) {
47 | error_t e = 0;
48 | // We skip index 0 to keep datasheet indexing (start from 1)
49 | for (int i = 0; i < 3 * 2; i += 2) {
50 | result_uint8_t lsb = read(bmp, BMP_CALIB_T1_MSB + i);
51 | result_uint8_t msb = read(bmp, BMP_CALIB_T1_MSB + i + 1);
52 | bmp->temp_calib[(i / 2) + 1] = (int16_t)(msb.value << 8 | lsb.value);
53 | e |= msb.hasError | lsb.hasError;
54 | }
55 | bmp->temp_calib[1] &= ~0xFFFF0000;
56 | return e;
57 | }
58 |
59 | static error_t read_pres_calibration(bmp280_t *bmp) {
60 | error_t e = 0;
61 | // We skip index 0 to keep datasheet indexing (start from 1)
62 | for (int i = 0; i < 9 * 2; i += 2) {
63 | result_uint8_t lsb = read(bmp, BMP_CALIB_P1_MSB + i);
64 | result_uint8_t msb = read(bmp, BMP_CALIB_P1_MSB + i + 1);
65 | bmp->pres_calib[(i / 2) + 1] = (int16_t)(msb.value << 8 | lsb.value);
66 | e |= msb.hasError | lsb.hasError;
67 | }
68 | bmp->pres_calib[1] &= ~0xFFFF0000;
69 | return e;
70 | }
71 |
72 | static error_t config(bmp280_t *bmp) {
73 | uint8_t ctrl_meas = 0x00;
74 | ctrl_meas |= (0b010 << 5); // osrs_t = x2 oversampling
75 | ctrl_meas |= (0b001 << 2); // osrs_p = x1 oversampling
76 | ctrl_meas |= (0b00 << 0); // mode = sleep
77 | error_t e = write(bmp, BMP_CTRL_MEAS, ctrl_meas);
78 |
79 | uint8_t config = 0x00;
80 | config |= (0b000 << 5); // t_sb =
81 | config |= (0b000 << 2); // filter =
82 | config |= (0b0 << 0); // spi3w_en =
83 | e |= write(bmp, BMP_CONFIG, config);
84 | return e;
85 | }
86 |
87 | static error_t force_measure(bmp280_t * bmp){
88 | result_uint8_t ctrl_meas = read(bmp, BMP_CTRL_MEAS);
89 | ctrl_meas.value |= (0b01 << 0); // mode = forced
90 | error_t e = write(bmp, BMP_CTRL_MEAS, ctrl_meas.value);
91 | return e;
92 |
93 | }
94 |
95 |
96 | error_t bmp280_init(bmp280_t *bmp) {
97 | result_uint8_t id = read(bmp, BMP_ID);
98 | error_t e = id.hasError;
99 | if (id.value != BMP_EXPECTED_ID) {
100 | // return e;
101 | }
102 | e |= read_pres_calibration(bmp);
103 | e |= read_temp_calibration(bmp);
104 | e |= config(bmp);
105 | return e;
106 | }
107 |
108 | bmp280_measurement_t bmp280_measure(bmp280_t *bmp) {
109 | bmp280_measurement_t out = {.temperature = 0.f, .pressure = 0.f, .error = 0};
110 | force_measure(bmp);
111 | result_int temp = read_temp(bmp);
112 |
113 | int64_t var1 = (temp.value >> 3) - (bmp->temp_calib[1] << 1);
114 | var1 *= bmp->temp_calib[2];
115 | var1 >>= 11;
116 |
117 | int64_t var2 = (temp.value >> 4) - bmp->temp_calib[1];
118 | var2 = var2 * var2;
119 | var2 >>= 12;
120 | var2 *= bmp->temp_calib[3];
121 | var2 >>= 14;
122 |
123 | int64_t t_fine = var1 + var2;
124 |
125 | float T = (t_fine * 5 + 128) >> 8;
126 | T /= 100.f;
127 |
128 | result_int pres = read_pres(bmp);
129 |
130 | var1 = ((int64_t)t_fine) - 128000;
131 | var2 = var1 * var1 * (int64_t)bmp->pres_calib[6];
132 | var2 = var2 + ((var1 * (int64_t)bmp->pres_calib[5]) << 17);
133 | var2 = var2 + (((int64_t)bmp->pres_calib[4]) << 35);
134 | var1 = ((var1 * var1 * (int64_t)bmp->pres_calib[3]) >> 8) +
135 | ((var1 * (int64_t)bmp->pres_calib[2]) << 12);
136 | var1 = (((((int64_t) 1) << 47) + var1))
137 | * ((int64_t) bmp->pres_calib[1]) >> 33;
138 |
139 | if (var1 == 0) {
140 | out.error = 1;
141 | return out; // avoid exception caused by division by zero
142 | }
143 |
144 | int64_t p = 1048576 - pres.value;
145 | p = (((p << 31) - var2) * 3125) / var1;
146 | var1 = (((int64_t) bmp->pres_calib[9]) * (p >> 13)
147 | * (p >> 13)) >> 25;
148 | var2 = (((int64_t) bmp->pres_calib[8]) * p) >> 19;
149 |
150 | p = ((p + var1 + var2) >> 8)
151 | + (((int64_t) bmp->pres_calib[7]) << 4);
152 | float P = p / 256.f;
153 |
154 | out.pressure = P;
155 | out.temperature = T;
156 | out.error = 0;
157 | return out;
158 | }
159 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Drivers
2 | Centralized repository for the low level drivers developed by Zenith Aerospace
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Objective •
36 | How to use a Driver •
37 | Environment •
38 | Adding a Driver
39 |
40 |
41 | ## Objective
42 |
43 | Store and manage developed drivers.
44 |
45 | ## How to use a Driver
46 | Each driver is organized in its own folder, where you'll find the `.c/.h` pair. Just add the files to your project, for example in a STM32CubeIDE project add the `.c` file to the `Core/Src` folder and the `.h` to the `Core/Inc`. However all drivers use the `platform` "framework", so you also need to copy the whole `platform` folder to your project as well, in STM32CubeIDE add it to the `Core/Inc` folder. All drivers should have an example of how to use it.
47 |
48 | ### Platform
49 | The platform framework is a set of macros and header-only libraries that
50 | drivers use instead of platform specific functions. For example, an I2C transaction in a STM32 project calls the ST HAL library, that means that the driver (as it is) can only be used in STM32 microcontrollers. The job of the platform is to detect in which microcontroller it is being compiled to and then choose the appropriate library, so that a driver developed to the platform can be used in other microcontrollers/processors.
51 |
52 | #### How it works?
53 | First the `platform.h` file sets up the common interface to the driver, common `structs` and function signatures. Then a bunch of preprocessor directives try to guess what type of project this is and include the correct platform implementation (header-only library) in the `arch` folder.
54 |
55 | FYI, if platform can't find an implementation it defaults to a PC debug mode, where an implementation that just prints to stdout is used.
56 |
57 | #### Where are platform archives?
58 | To find platform archives, go to the [`Platform_Lib`](https://github.com/zenitheesc/Platform-Lib/tree/main) repository. Then go to the platform folder with library archives, such as `platform.h`, and another to the arch folder.
59 |
60 | ## Environment
61 |
62 | Realistically, we mostly use the STM32CubeIDE as most of the projects we do are based on that platform. But with the platform framework you should just need a C compiler.
63 |
64 | ## Adding a Driver
65 | Guidelines for drivers:
66 | - You should use C unless otherwise necessary
67 | - Why: to maximize compatibility (yes there are still uCs that can only use C)
68 | - You should add some documentation with an example
69 | - Why: Most users just want to copy and paste correct code
70 | - You should use platform functions to access hardware resources, unless necessary
71 | - Why: maximize compatibility, however platform doesn't cover everything so ,for now, you can add a driver that is platform specific.
72 |
73 | Steps in a list:
74 | 1. Clone this repository
75 | 2. Create a folder with the Driver name
76 | 3. Add the `.c` and `.h`
77 | 4. Add a `README.md` file to the Driver folder
78 | 1. On the README add the Author, Date, and purpose of the library
79 | 2. Add Documentation: Parameters and purpose of each function
80 | 3. Add a Notes Section for any extra information of the IC
81 | 5. (Optional) Add a folder called `Docs`
82 | 1. Add any External Documentation including Datasheets, Application Notes, and Register Maps
83 | 6. If there is a GitHub Issue for the Driver add a message or close it.
84 |
85 | ---
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | zenith.eesc@gmail.com
101 |
102 |
--------------------------------------------------------------------------------
/W25Q/w25q.c:
--------------------------------------------------------------------------------
1 | /*
2 | * w25q.c
3 | *
4 | * Created on: Sep 18, 2021
5 | * Author: leocelente
6 | */
7 |
8 | #include "Storage/w25q.h"
9 |
10 | /**
11 | * Sends WRITE_ENABLE instruction
12 | */
13 | static void enable_write(w25q_t flash) {
14 | buffer_view_t _ = { .data = &W25Q_WR_EN, .size = 1 };
15 | gpio_low(flash.dev.pin);
16 | spi_transmit(flash.dev, _);
17 | gpio_high(flash.dev.pin);
18 | }
19 |
20 | error_t w25q_chip_erase(w25q_t flash) {
21 | enable_write(flash);
22 | buffer_view_t _ = { .data = &W25Q_CHIP_ERASE, .size = 1 };
23 | gpio_low(flash.dev.pin);
24 | error_t e = spi_transmit(flash.dev, _);
25 | gpio_high(flash.dev.pin);
26 | return e;
27 | }
28 | /**
29 | * Gets last byte of model ID, indicating capacity
30 | */
31 | static result_uint8_t get_capacity_id(w25q_t flash) {
32 | uint8_t chipid[3] = { 0 };
33 | buffer_view_t buffer = { .data = chipid, .size = sizeof(chipid) };
34 | buffer_view_t _ = { .data = &W25Q_ID, .size = 1 };
35 | gpio_low(flash.dev.pin);
36 | spi_transmit(flash.dev, _);
37 | error_t e = spi_receive(flash.dev, buffer);
38 | gpio_high(flash.dev.pin);
39 | result_uint8_t out = { .hasError = e, .value = chipid[2] };
40 | return out;
41 | }
42 |
43 | /**
44 | * Polls until we can write again
45 | */
46 | static void wait_busy(w25q_t flash) {
47 | uint8_t status = 0xFF;
48 | buffer_view_t data = { .data = &status, .size = 1 };
49 | buffer_view_t instr = { .data = (uint8_t*) &W25Q_RD_STATUS1, .size = 1 };
50 | gpio_low(flash.dev.pin);
51 | spi_transmit(flash.dev, instr);
52 | do {
53 | spi_receive(flash.dev, data);
54 | delay_ms(1);
55 | } while ((status & 0x01) == 0x01);
56 | gpio_high(flash.dev.pin);
57 | }
58 |
59 | static uint32_t byte_address(w25q_address_t address) {
60 | return (address.sector_index * W25Q_SECTOR_SIZE)
61 | + (address.page_index * W25Q_PAGE_SIZE) + address.offset_bytes;
62 | }
63 |
64 | static uint32_t page_count_from_block_count(uint32_t blocks) {
65 | const int bytes_per_sector = W25Q_SECTOR_SIZE;
66 | const int sectors_per_block = 16;
67 | uint32_t bytes = (blocks * sectors_per_block * bytes_per_sector);
68 | uint32_t pages = bytes / W25Q_PAGE_SIZE;
69 | return pages;
70 | }
71 |
72 | static uint32_t page_count_from_id(uint8_t chipid) {
73 | int blocks = 0;
74 | switch (chipid) {
75 | case 0x18: // w25q128
76 | blocks = 256;
77 | break;
78 | case 0x17: // w25q64
79 | blocks = 128;
80 | break;
81 | case 0x16: // w25q32
82 | blocks = 64;
83 | break;
84 | default:
85 | case 0x15: // w25q16
86 | blocks = 32;
87 | break;
88 | }
89 |
90 | return page_count_from_block_count(blocks);
91 | }
92 |
93 | uint32_t page_count_from_model(w25q_model_e model) {
94 |
95 | int blocks = 0;
96 | switch (model) {
97 | case W25Q128:
98 | blocks = 256;
99 | break;
100 | case W25Q64:
101 | blocks = 128;
102 | break;
103 | case W25Q32:
104 | blocks = 64;
105 | break;
106 | default: // deve dar warning pq todos estão no enum
107 | case W25Q16:
108 | blocks = 32;
109 | break;
110 | }
111 |
112 | return page_count_from_block_count(blocks);
113 | }
114 |
115 | error_t w25q_init(w25q_t *flash, bool check_capacity) {
116 | gpio_high(flash->dev.pin);
117 |
118 | result_uint8_t res = get_capacity_id(*flash);
119 | if (res.hasError) {
120 | return res.hasError;
121 | }
122 |
123 | // signs of bad connection
124 | if (res.value == 0xFF || res.value == 0x00) {
125 | return 1;
126 | }
127 |
128 | uint32_t real_count = page_count_from_id(res.value);
129 | if (real_count != flash->page_count) {
130 | if (check_capacity) {
131 | return 1;
132 | }
133 | // override count from chip
134 | flash->page_count = real_count;
135 | }
136 |
137 | uint8_t status_default[] = {W25Q_WR_STATUS1, 0x00};
138 | buffer_view_t status_reg = { .data = status_default, .size = sizeof(status_default) };
139 |
140 | gpio_low(flash->dev.pin);
141 | error_t e = spi_transmit(flash->dev, status_reg);
142 | gpio_high(flash->dev.pin);
143 |
144 | wait_busy(*flash); // just in case
145 | return e;
146 | }
147 |
148 | error_t w25q_page_write(w25q_t flash, buffer_view_t tx_data,
149 | w25q_address_t address) {
150 | if ((address.offset_bytes + tx_data.size > W25Q_PAGE_SIZE)
151 | || (tx_data.size == 0)) {
152 | tx_data.size = W25Q_PAGE_SIZE - address.offset_bytes;
153 | }
154 |
155 | uint32_t byte_addr = byte_address(address);
156 | uint8_t addr_vec[3] = { byte_addr >> 16, byte_addr >> 8, byte_addr };
157 | buffer_view_t addr = { .data = addr_vec, .size = sizeof(addr_vec) };
158 |
159 | buffer_view_t instr = { .data = (uint8_t*) &W25Q_WRITE, .size = 1 };
160 |
161 | error_t error = 0;
162 |
163 | wait_busy(flash);
164 | enable_write(flash);
165 | gpio_low(flash.dev.pin);
166 | error |= spi_transmit(flash.dev, instr);
167 | error |= spi_transmit(flash.dev, addr);
168 | error |= spi_transmit(flash.dev, tx_data);
169 | gpio_high(flash.dev.pin);
170 | wait_busy(flash);
171 |
172 | return error;
173 | }
174 |
175 | error_t w25q_page_read(w25q_t flash, buffer_view_t rx_data,
176 | w25q_address_t address) {
177 | if ((rx_data.size > W25Q_PAGE_SIZE) || (rx_data.size == 0)) {
178 | rx_data.size = W25Q_PAGE_SIZE;
179 | }
180 |
181 | uint32_t byte_addr = byte_address(address);
182 | uint8_t addr_vec[4] = { byte_addr >> 16, byte_addr >> 8, byte_addr, 0 };
183 | buffer_view_t addr = { .data = addr_vec, .size = sizeof(addr_vec) };
184 | buffer_view_t instr = { .data = (uint8_t*) &W25Q_READ, .size = 1 };
185 | error_t error = 0;
186 |
187 | gpio_low(flash.dev.pin);
188 | error |= spi_transmit(flash.dev, instr);
189 | error |= spi_transmit(flash.dev, addr);
190 | error |= spi_receive(flash.dev, rx_data);
191 | gpio_high(flash.dev.pin);
192 | return error;
193 | }
194 |
195 | error_t w25q_sector_erase(w25q_t flash, size_t sector_address) {
196 | wait_busy(flash);
197 | uint32_t byte_addr = sector_address * W25Q_SECTOR_SIZE;
198 | uint8_t addr_vec[3] = { byte_addr >> 16, byte_addr >> 8, byte_addr };
199 | buffer_view_t addr = { .data = addr_vec, .size = sizeof(addr_vec) };
200 |
201 | buffer_view_t instr = { .data = (uint8_t*) &W25Q_ERASE, .size = 1 };
202 | error_t error = 0;
203 |
204 | enable_write(flash);
205 | gpio_low(flash.dev.pin);
206 | error |= spi_transmit(flash.dev, instr);
207 | error |= spi_transmit(flash.dev, addr);
208 | gpio_high(flash.dev.pin);
209 | wait_busy(flash);
210 | return error;
211 | }
212 |
213 |
--------------------------------------------------------------------------------
/CAN/CANZenTool.h:
--------------------------------------------------------------------------------
1 | /**
2 | * CANZenTool.h
3 | *
4 | * Author: Carlos Craveiro (@CarlosCraveiro)
5 | * Created On: September 25, 2021
6 | *
7 | * Brief Description: A header for the library to abstract some over complicated functionalities
8 | * of "stm32f1xx_hal_can.h" to the standard projects developed by the
9 | * Zenith's Low Level Programming Team.
10 | * */
11 |
12 | /**
13 | * This refers to the "main" created by CUBEIDE where all HAL Libraries are included and where
14 | * the Error_Handler is defined.
15 | */
16 | #include "main.h"
17 |
18 | #include
19 | #include
20 |
21 | /* Declaration of the Structure acts as a builder for the Mask and Id for @ref CANZenTool_setFilter*/
22 | typedef struct CANZenTool_IdNdMaskBuilder_t CANZenTool_IdNdMaskBuilder_t;
23 |
24 | /* Definition of the Structure (Proto-Class) acts as a builder for the Mask and Id for @ref CANZenTool_setFilter*/
25 | struct CANZenTool_IdNdMaskBuilder_t {
26 |
27 | unsigned int m_rawIds;
28 |
29 | uint32_t m_idBuffer;
30 | uint32_t m_maskBuffer;
31 |
32 | void (*addId)(CANZenTool_IdNdMaskBuilder_t*, uint32_t);
33 | uint32_t (*getResultId)(CANZenTool_IdNdMaskBuilder_t*);
34 | uint32_t (*getResultMask)(CANZenTool_IdNdMaskBuilder_t*);
35 | void (*addIdList)(CANZenTool_IdNdMaskBuilder_t*, uint32_t , uint32_t[]);
36 | };
37 |
38 |
39 | /**
40 | * @brief "adds" a standard CAN Id to the "builder" refered as "this". The addition "re-calculates"
41 | * the new Id and Mask inside the "builder" structure based in the income Id.
42 | * Note: this function can be both used as a "method" of the "builder" structure
43 | * or as a stand-alone function.
44 | *
45 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
46 | *
47 | * @param newId unsigned integer of 32 bits that represents a standard CAN Id following a Big-endian convention.
48 | */
49 | void CANZenTool_addId(CANZenTool_IdNdMaskBuilder_t* this, uint32_t newId);
50 |
51 |
52 | /**
53 | * @brief "adds" a list of standard CAN Ids to the "builder" refered as "this".
54 | * The addition "re-calculates" the new Id and Mask inside the "builder" structure
55 | * based in the income Ids. Note: this function can be both used as a "method" of
56 | * the "builder" structure or as a stand-alone function.
57 | *
58 | * @param idListLength unsigned integer of 32 bits that represents the length of the income idList.
59 | *
60 | * @param idList (pointer to uint32_t) array of standard CAN Ids following the Big-endian convention.
61 | */
62 | void CANZenTool_addIdList(CANZenTool_IdNdMaskBuilder_t* this, uint32_t idListLength, uint32_t idList[]);
63 |
64 |
65 | /**
66 | * @brief gets the result Mask constructed in the "builder" structure (proto-class).
67 | *
68 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
69 | *
70 | * @return the result Mask (unsigned int of 32 bits) constructed in the "builder" structure.
71 | */
72 | uint32_t CANZenTool_getResultMask(CANZenTool_IdNdMaskBuilder_t* this);
73 |
74 |
75 | /**
76 | * @brief gets the result Id constructed in the "builder" structure (proto-class).
77 | *
78 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
79 | *
80 | * @return the result Id (unsigned int of 32 bits) constructed in the "builder" structure.
81 | */
82 | uint32_t CANZenTool_getResultId(CANZenTool_IdNdMaskBuilder_t* this);
83 |
84 |
85 | /**
86 | * @brief creates a new "builder" structure (proto-class) of type @ref CANZenTool_IdNdMaskBuilder_t.
87 | *
88 | * @return a new "builder" structure (proto-class) @ref CANZenTool_IdNdMaskBuilder_t.
89 | */
90 | CANZenTool_IdNdMaskBuilder_t CANZenTool_newIdNdMaskBuilder();
91 |
92 |
93 | /**
94 | * @brief Sets the configurations parameters passed in the function call
95 | * and other standard settings in a CAN_FilterTypeDef
96 | * named canFilterConfigs.
97 | *
98 | * @param hcan pointer to a CAN_HandleTypeDef structure that contains
99 | * the configuration information for the specified CAN.
100 | *
101 | * @param canFilterConfig pointer to a CAN_FilterTypeDef structure that
102 | * is going to receive the filter configurations set in
103 | * this function.
104 | *
105 | * @param isActive bool that specifies either the Filter will be active
106 | * or not.
107 | *
108 | * @param filterBank unsigned int of 32 bits that specifies the filter
109 | * bank which will be initialized.For single CAN instance
110 | * (14 dedicated filter banks), this parameter must be a number
111 | * between Min_Data = 0 and Max_Data = 13.
112 | *
113 | * @param filterId specifies the filter identification number.
114 | * This parameter must be a number between
115 | * Min_Data = 0x000 and Max_Data = 0x7FF.
116 | *
117 | * @param filterMaskId specifies the filter mask number.
118 | * This parameter must be a number between
119 | * Min_Data = 0x000 and Max_Data = 0x7FF.
120 | */
121 | void CANZenTool_setFilter(CAN_HandleTypeDef* hcan, CAN_FilterTypeDef* canFilterConfig , bool isActive, uint32_t filterBank, uint32_t filterId, uint32_t filterMaskId);
122 |
123 |
124 | /**
125 | * @brief Writes a CAN Standard Frame with the parameters passed in the function call.
126 | *
127 | * @param dlc specifies the length of the frame that will be transmitted.
128 | * This parameter must be a number between Min_Data = 0 and Max_Data = 8.
129 | *
130 | * @param id specifies the standard identifier. This parameter must be
131 | * a number between Min_Data = 0 and Max_Data = 0x7FF.
132 | *
133 | * @param isData bool that specifies either the CanFrame will be
134 | * a data frame or not (remote frame).
135 | *
136 | * @return The writed CAN Frame.
137 | */
138 | CAN_TxHeaderTypeDef CANZenTool_writeStdCanFrame(uint32_t dlc, uint32_t id, bool isData);
139 |
140 |
141 | /**
142 | * @brief Sends the CANFrame specified in the function call (value of pointer TxHeader)
143 | * and call Error_Handler if some error occurred during the transmission.
144 | *
145 | * @param hcan pointer to an CAN_HandleTypeDef structure that contains
146 | * the configuration information for the specified CAN.
147 | *
148 | * @param TxHeader pointer to a CAN_TxHeaderTypeDef structure.
149 | *
150 | * @param TxData array containing the payload of the Tx frame.
151 | *
152 | * @param pTxMailbox pointer to a variable where the function will
153 | * return the TxMailbox used to store the Tx message.
154 | */
155 | void CANZenTool_sendCanFrameMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *TxHeader, uint8_t TxData[], uint32_t *pTxMailbox);
156 |
157 |
--------------------------------------------------------------------------------
/CAN/CUBEIDE-sample/main.c:
--------------------------------------------------------------------------------
1 | /* USER CODE BEGIN Header */
2 | /**
3 | ******************************************************************************
4 | * @file : main.c
5 | * @brief : Main program body
6 | ******************************************************************************
7 | * @attention
8 | *
9 | * © Copyright (c) 2021 STMicroelectronics.
10 | * All rights reserved.
11 | *
12 | * This software component is licensed by ST under BSD 3-Clause license,
13 | * the "License"; You may not use this file except in compliance with the
14 | * License. You may obtain a copy of the License at:
15 | * opensource.org/licenses/BSD-3-Clause
16 | *
17 | ******************************************************************************
18 | */
19 | /* USER CODE END Header */
20 | /* Includes ------------------------------------------------------------------*/
21 | #include "main.h"
22 | #include "can.h"
23 | #include "usart.h"
24 | #include "gpio.h"
25 |
26 | /* Private includes ----------------------------------------------------------*/
27 | /* USER CODE BEGIN Includes */
28 | #include "CANZenTool.h"
29 | #include
30 | /* USER CODE END Includes */
31 |
32 | /* Private typedef -----------------------------------------------------------*/
33 | /* USER CODE BEGIN PTD */
34 |
35 | /* USER CODE END PTD */
36 |
37 | /* Private define ------------------------------------------------------------*/
38 | /* USER CODE BEGIN PD */
39 | /* USER CODE END PD */
40 |
41 | /* Private macro -------------------------------------------------------------*/
42 | /* USER CODE BEGIN PM */
43 |
44 | /* USER CODE END PM */
45 |
46 | /* Private variables ---------------------------------------------------------*/
47 |
48 | /* USER CODE BEGIN PV */
49 |
50 | /* USER CODE END PV */
51 |
52 | /* Private function prototypes -----------------------------------------------*/
53 | void SystemClock_Config(void);
54 | /* USER CODE BEGIN PFP */
55 |
56 | /* USER CODE END PFP */
57 |
58 | /* Private user code ---------------------------------------------------------*/
59 | /* USER CODE BEGIN 0 */
60 | uint32_t mailbox;
61 |
62 | void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
63 | CAN_RxHeaderTypeDef header;
64 | uint8_t buffer[8] = { 0 };
65 | HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &header, buffer);
66 | printf("Message Received From: %.3lX\r\n", header.StdId);
67 | printf("\tContent: %.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X\r\n", buffer[0],
68 | buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
69 | buffer[7]);
70 | }
71 |
72 | int __io_putchar(int ch) {
73 | return !HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 10);
74 | }
75 |
76 | /* USER CODE END 0 */
77 |
78 | /**
79 | * @brief The application entry point.
80 | * @retval int
81 | */
82 | int main(void) {
83 | /* USER CODE BEGIN 1 */
84 |
85 | /* USER CODE END 1 */
86 |
87 | /* MCU Configuration--------------------------------------------------------*/
88 |
89 | /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
90 | HAL_Init();
91 |
92 | /* USER CODE BEGIN Init */
93 |
94 | /* USER CODE END Init */
95 |
96 | /* Configure the system clock */
97 | SystemClock_Config();
98 |
99 | /* USER CODE BEGIN SysInit */
100 |
101 | /* USER CODE END SysInit */
102 |
103 | /* Initialize all configured peripherals */
104 | MX_GPIO_Init();
105 | MX_CAN_Init();
106 | MX_USART1_UART_Init();
107 | /* USER CODE BEGIN 2 */
108 |
109 | // CAN_HandleTypeDef hcan;
110 | // HAL_CAN_Init(hcan);
111 | bool wholeFilterIsActive = true;
112 |
113 | /* Filter Configs */
114 |
115 | CAN_FilterTypeDef canFilterConfig;
116 |
117 | uint32_t filterBank = 0;
118 |
119 | CANZenTool_IdNdMaskBuilder_t builder = CANZenTool_newIdNdMaskBuilder();
120 |
121 | uint32_t ids[] = { 0xF2, 0x55 };
122 | builder.addIdList(&builder, 2, ids);
123 | uint32_t filterId = builder.getResultId(&builder);
124 | //
125 | uint32_t filterMaskId = builder.getResultId(&builder);
126 |
127 | /* The Function */
128 |
129 | CANZenTool_setFilter(&hcan, &canFilterConfig, wholeFilterIsActive,
130 | filterBank, filterId, filterMaskId);
131 | HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
132 | CAN_TxHeaderTypeDef header = CANZenTool_writeStdCanFrame(8, 0xE1, true);
133 | uint8_t buffer[8] = { 0, [3]=0xFA };
134 | uint8_t counter = 0;
135 | HAL_StatusTypeDef e = HAL_CAN_Start(&hcan);
136 | if (e) {
137 | printf("Start failed\r\n");
138 | }
139 | /* USER CODE END 2 */
140 |
141 | /* Infinite loop */
142 | /* USER CODE BEGIN WHILE */
143 | while (1) {
144 | HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
145 | counter++;
146 | // if (counter % 2) {
147 | // header.StdId = 0x55;
148 | // } else {
149 | // header.StdId = 0xF2;
150 | // }
151 | buffer[sizeof(buffer) - 1] = counter;
152 | printf("Sent message from %.3lX\r\n", header.StdId);
153 | printf("\tContent: %.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X\r\n", buffer[0],
154 | buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
155 | buffer[6], buffer[7]);
156 | HAL_Delay(2);
157 | CANZenTool_sendCanFrameMsg(&hcan, &header, buffer, &mailbox);
158 | HAL_Delay(500);
159 | /* USER CODE END WHILE */
160 |
161 | /* USER CODE BEGIN 3 */
162 | }
163 | /* USER CODE END 3 */
164 | }
165 |
166 | /**
167 | * @brief System Clock Configuration
168 | * @retval None
169 | */
170 | void SystemClock_Config(void) {
171 | RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
172 | RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
173 |
174 | /** Initializes the RCC Oscillators according to the specified parameters
175 | * in the RCC_OscInitTypeDef structure.
176 | */
177 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
178 | RCC_OscInitStruct.HSEState = RCC_HSE_ON;
179 | RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
180 | RCC_OscInitStruct.HSIState = RCC_HSI_ON;
181 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
182 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
183 | RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
184 | if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
185 | Error_Handler();
186 | }
187 | /** Initializes the CPU, AHB and APB buses clocks
188 | */
189 | RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
190 | | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
191 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
192 | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
193 | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
194 | RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
195 |
196 | if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
197 | Error_Handler();
198 | }
199 | /** Enables the Clock Security System
200 | */
201 | HAL_RCC_EnableCSS();
202 | }
203 |
204 | /* USER CODE BEGIN 4 */
205 |
206 | /* USER CODE END 4 */
207 |
208 | /**
209 | * @brief This function is executed in case of error occurrence.
210 | * @retval None
211 | */
212 | void Error_Handler(void) {
213 | /* USER CODE BEGIN Error_Handler_Debug */
214 | /* User can add his own implementation to report the HAL error return state */
215 | __disable_irq();
216 | while (1) {
217 | }
218 | /* USER CODE END Error_Handler_Debug */
219 | }
220 |
221 | #ifdef USE_FULL_ASSERT
222 | /**
223 | * @brief Reports the name of the source file and the source line number
224 | * where the assert_param error has occurred.
225 | * @param file: pointer to the source file name
226 | * @param line: assert_param error line source number
227 | * @retval None
228 | */
229 | void assert_failed(uint8_t *file, uint32_t line)
230 | {
231 | /* USER CODE BEGIN 6 */
232 | /* User can add his own implementation to report the file name and line number,
233 | ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
234 | /* USER CODE END 6 */
235 | }
236 | #endif /* USE_FULL_ASSERT */
237 |
238 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
239 |
--------------------------------------------------------------------------------
/CAN/CANZenTool.c:
--------------------------------------------------------------------------------
1 | /**
2 | * CANZenTool.c
3 | *
4 | * Author: Carlos Craveiro (@CarlosCraveiro)
5 | * Created On: September 25, 2021
6 | *
7 | * Brief Description: The code of the library to abstract some overcomplicated functionalities
8 | * of "stm32f1xx_hal_can.h" to the standard projects developed by the
9 | * Zenith's Low Level Programming Team.
10 | */
11 |
12 | #include "CANZenTool.h"
13 |
14 | /**
15 | * @brief Sets the configurations parameters passed in the function call
16 | * and other standard settings in a CAN_FilterTypeDef
17 | * named canFilterConfigs.
18 | *
19 | * @param hcan pointer to a CAN_HandleTypeDef structure that contains
20 | * the configuration information for the specified CAN.
21 | *
22 | * @param canFilterConfig pointer to a CAN_FilterTypeDef structure that
23 | * is going to recieve the filter configurations setted in
24 | * this function.
25 | *
26 | * @param isActive bool that specifies either the Filter will be active
27 | * or not.
28 | *
29 | * @param filterBank unsigned int of 32 bits that specifies the filter
30 | * bank which will be initialized.For single CAN instance
31 | * (14 dedicated filter banks), this parameter must be a number
32 | * between Min_Data = 0 and Max_Data = 13.
33 | *
34 | * @param filterId specifies the filter identification number.
35 | * This parameter must be a number between
36 | * Min_Data = 0x000 and Max_Data = 0x7FF.
37 | *
38 | * @param filterMaskId specifies the filter mask number.
39 | * This parameter must be a number between
40 | * Min_Data = 0x000 and Max_Data = 0x7FF.
41 | */
42 | void CANZenTool_setFilter(CAN_HandleTypeDef* hcan, CAN_FilterTypeDef* canFilterConfig , bool isActive, uint32_t filterBank, uint32_t filterId, uint32_t filterMaskId) {
43 |
44 | if(isActive) {
45 |
46 | canFilterConfig->FilterActivation = CAN_FILTER_ENABLE;
47 | } else {
48 |
49 | canFilterConfig->FilterActivation = CAN_FILTER_DISABLE;
50 | }
51 | canFilterConfig->FilterBank = filterBank;
52 | canFilterConfig->FilterFIFOAssignment = CAN_RX_FIFO0;
53 | canFilterConfig->FilterIdHigh = filterId << 5;
54 | canFilterConfig->FilterIdLow = 0x0000;
55 | canFilterConfig->FilterMaskIdHigh = filterMaskId << 5;
56 | canFilterConfig->FilterMaskIdLow = 0x0000;
57 | canFilterConfig->FilterMode = CAN_FILTERMODE_IDMASK;
58 | canFilterConfig->FilterScale = CAN_FILTERSCALE_32BIT;
59 | canFilterConfig->SlaveStartFilterBank = 0;
60 |
61 | HAL_CAN_ConfigFilter(hcan, canFilterConfig);
62 | }
63 |
64 |
65 | /**
66 | * @brief "adds" a standard CAN Id to the "builder" refered as "this". The addition "re-calculates"
67 | * the new Id and Mask inside the "builder" structure based in the income Id.
68 | * Note: this function can be both used as a "method" of the "builder" structure
69 | * or as a stand-alone function.
70 | *
71 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
72 | *
73 | * @param newId unsigned integer of 32 bits that represents a standard CAN Id following a Big-endian convention.
74 | */
75 | void CANZenTool_addId(CANZenTool_IdNdMaskBuilder_t* this, uint32_t newId) {
76 |
77 | this->m_idBuffer &= newId;
78 |
79 | /* If is the first Id added */
80 | if(this->m_rawIds == 0) {
81 | this->m_maskBuffer = this->m_maskBuffer;
82 |
83 | } else {
84 | uint32_t incomeMaskBuffer = ~(this->m_idBuffer ^ newId);
85 | this->m_maskBuffer = this->m_maskBuffer & incomeMaskBuffer;
86 | }
87 |
88 | this->m_rawIds++;
89 |
90 | }
91 |
92 |
93 | /**
94 | * @brief "adds" a list of standard CAN Ids to the "builder" refered as "this".
95 | * The addition "re-calculates" the new Id and Mask inside the "builder" structure
96 | * based in the income Ids. Note: this function can be both used as a "method" of
97 | * the "builder" structure or as a stand-alone function.
98 | *
99 | * @param idListLength unsigned integer of 32 bits that represents the length of the income idList.
100 | *
101 | * @param idList (pointer to uint32_t) array of standard CAN Ids following the Big-endian convention.
102 | */
103 | void CANZenTool_addIdList(CANZenTool_IdNdMaskBuilder_t* this, uint32_t idListLength, uint32_t idList[]) {
104 |
105 | for(int i = 0; i < idListLength; i++) {
106 |
107 | CANZenTool_addId(this, idList[i]);
108 | }
109 | }
110 |
111 |
112 | /**
113 | * @brief gets the result Mask constructed in the "builder" structure (proto-class).
114 | *
115 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
116 | *
117 | * @return the result Mask (unsigned int of 32 bits) constructed in the "builder" structure.
118 | */
119 | uint32_t CANZenTool_getResultMask(CANZenTool_IdNdMaskBuilder_t* this) {
120 |
121 | return this->m_maskBuffer;
122 | }
123 |
124 |
125 | /**
126 | * @brief gets the result Id constructed in the "builder" structure (proto-class).
127 | *
128 | * @param this pointer to a CANZenTool_IdNdMaskBuilder_t structure (proto-class).
129 | *
130 | * @return the result Id (unsigned int of 32 bits) constructed in the "builder" structure.
131 | */
132 | uint32_t CANZenTool_getResultId(CANZenTool_IdNdMaskBuilder_t* this) {
133 |
134 | return this->m_idBuffer;
135 | }
136 |
137 |
138 | /**
139 | * @brief creates a new "builder" structure (proto-class) of type @ref CANZenTool_IdNdMaskBuilder_t.
140 | *
141 | * @return a new "builder" structure (proto-class) @ref CANZenTool_IdNdMaskBuilder_t.
142 | */
143 | CANZenTool_IdNdMaskBuilder_t CANZenTool_newIdNdMaskBuilder() {
144 |
145 | CANZenTool_IdNdMaskBuilder_t newBuilder;
146 | newBuilder.addId = &CANZenTool_addId;
147 | newBuilder.addIdList = &CANZenTool_addIdList;
148 | newBuilder.getResultId = &CANZenTool_getResultId;
149 | newBuilder.getResultMask = &CANZenTool_getResultMask;
150 | newBuilder.m_maskBuffer = 0xFFFFFFFF;
151 | newBuilder.m_idBuffer = 0xFFFFFFFF;
152 | newBuilder.m_rawIds = 0;
153 |
154 | return newBuilder;
155 | }
156 |
157 |
158 | /**
159 | * @brief Writes a CAN Standard Frame with the parameters passed in the function call.
160 | *
161 | * @param dlc specifies the length of the frame that will be transmitted.
162 | * This parameter must be a number between Min_Data = 0 and Max_Data = 8.
163 | *
164 | * @param id specifies the standard identifier. This parameter must be
165 | * a number between Min_Data = 0 and Max_Data = 0x7FF.
166 | *
167 | * @param isData bool that specifies either the CanFrame will be
168 | * a data frame or not (remote frame).
169 | *
170 | * @return The writed CAN Frame.
171 | */
172 | CAN_TxHeaderTypeDef CANZenTool_writeStdCanFrame(uint32_t dlc, uint32_t id, bool isData) {
173 |
174 | CAN_TxHeaderTypeDef TxHeader;
175 |
176 | TxHeader.DLC = dlc;
177 | TxHeader.ExtId = 0;
178 | TxHeader.IDE = CAN_ID_STD;
179 |
180 | if (isData) {
181 |
182 | TxHeader.RTR = CAN_RTR_DATA;
183 | } else {
184 |
185 | TxHeader.RTR = CAN_RTR_REMOTE;
186 | }
187 |
188 | TxHeader.StdId = id;
189 | TxHeader.TransmitGlobalTime = DISABLE;
190 |
191 | return TxHeader;
192 | }
193 |
194 |
195 | /**
196 | * @brief Sends the CANFrame specified in the function call (value of pointer TxHeader)
197 | * and call Error_Handler if some error occurred during the transmission.
198 | *
199 | * @param hcan pointer to an CAN_HandleTypeDef structure that contains
200 | * the configuration information for the specified CAN.
201 | *
202 | * @param TxHeader pointer to a CAN_TxHeaderTypeDef structure.
203 | *
204 | * @param TxData array containing the payload of the Tx frame.
205 | *
206 | * @param pTxMailbox pointer to a variable where the function will
207 | * return the TxMailbox used to store the Tx message.
208 | */
209 | void CANZenTool_sendCanFrameMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *TxHeader, uint8_t TxData[], uint32_t *pTxMailbox) {
210 |
211 | if(HAL_CAN_AddTxMessage(hcan, TxHeader, TxData, pTxMailbox) != HAL_OK) {
212 | Error_Handler();
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/MMC5983MA/mmc5983ma.c:
--------------------------------------------------------------------------------
1 | /*
2 | * MMC5983MA.c
3 | *
4 | * Author: Thiago Henrique Vicentini (Anakin)
5 | */
6 |
7 | #include "mmc5983ma.h"
8 |
9 | error_t mmc5983ma_init(i2c_device_t device, uint8_t measurementFrequency, uint8_t bandwidth){
10 | device.addr = MMC5983MA_ADDRESS;
11 |
12 | result8_t result;
13 | error_t error;
14 |
15 | // Start configuration registers
16 | //--------------------------------------------------------------------------------
17 |
18 | result = i2c_read8(device, MMC5983MA_CONTROL_0);
19 | if(result.hasError)
20 | return result.hasError;
21 |
22 | // -> enable the interrupt for completed measurements (bit 2 == 1)
23 | // -> enable auto set/reset (bit 5 == 1)
24 | result.value |= (MMC5983MA_Auto_SR_en | MMC5983MA_INT_meas_done_en);
25 | if(error = i2c_write8(device, MMC5983MA_CONTROL_0, result.value))
26 | return error;
27 |
28 | //--------------------------------------------------------------------------------
29 |
30 | result = i2c_read8(device, MMC5983MA_CONTROL_1);
31 | if(result.hasError)
32 | return result.hasError;
33 |
34 | // -> set magnetometer bandwidth
35 | result.value |= bandwidth;
36 | if(error = i2c_write8(device, MMC5983MA_CONTROL_1, result.value));
37 | return error;
38 |
39 | //--------------------------------------------------------------------------------
40 |
41 | result = i2c_read8(device, MMC5983MA_CONTROL_2);
42 | if(result.hasError)
43 | return result.hasError;
44 |
45 | // -> enable continuous measurement mode (bit 3 == 1)
46 | // -> enable the feature of periodic set (bit 7 == 1)
47 | result.value |= (0x08 | measurementFrequency); // 0x80 | (setFrequency << 4) | 0x08 | measurementFrequency
48 | if(error = i2c_write8(device, MMC5983MA_CONTROL_2, result.value));
49 | return error;
50 |
51 | //--------------------------------------------------------------------------------
52 | // End configuration registers
53 |
54 | return 0;
55 | }
56 |
57 | error_t readData(i2c_device_t device, uint32_t* destination){
58 | uint8_t rawData[7] = { 0 }; // x/y/z mag register data stored here
59 | buffer_view_t view = { .data = rawData, .size = sizeof(rawData) };
60 | error_t error = i2c_readN(device, MMC5983MA_XOUT_0, view);
61 | if(error) return error;
62 |
63 | destination[0] = (uint32_t)(rawData[0] << 10 | rawData[1] << 2 | (rawData[6] & 0xC0) >> 6); // Turn the 18 bits into a unsigned 32-bit value
64 | destination[1] = (uint32_t)(rawData[2] << 10 | rawData[3] << 2 | (rawData[6] & 0x30) >> 4); // Turn the 18 bits into a unsigned 32-bit value
65 | destination[2] = (uint32_t)(rawData[4] << 10 | rawData[5] << 2 | (rawData[6] & 0x0C) >> 2); // Turn the 18 bits into a unsigned 32-bit value
66 |
67 | return 0;
68 | }
69 |
70 | void SET(i2c_device_t device){
71 | result8_t result = i2c_read8(device, MMC5983MA_CONTROL_0);
72 | if(result.hasError)
73 | return result.hasError;
74 |
75 | result.value |= 0x08;
76 | i2c_write8(device, MMC5983MA_CONTROL_0, result.value);
77 | delay_ms(1); // self clearing after 500 us
78 | }
79 |
80 | void RESET(i2c_device_t device){
81 | result8_t result = i2c_read8(device, MMC5983MA_CONTROL_0);
82 | if(result.hasError)
83 | return result.hasError;
84 |
85 | result.value |= 0x10;
86 | i2c_write8(device, MMC5983MA_CONTROL_0, result.value);
87 | delay_ms(1); // self clearing after 500 us
88 | }
89 |
90 | void selfTest(i2c_device_t device){
91 | uint8_t rawData[6] = {0}; // x/y/z mag register data stored here
92 | uint16_t data_set[3] = {0}, data_reset[3] = {0};
93 | uint32_t delta_data[3] = {0};
94 |
95 | // Clear control registers
96 | i2c_write8(device, MMC5983MA_CONTROL_0, 0x00);
97 | i2c_write8(device, MMC5983MA_CONTROL_1, 0x00);
98 | i2c_write8(device, MMC5983MA_CONTROL_2, 0x00);
99 |
100 | SET(); // Enable set current
101 | i2c_write8(device, MMC5983MA_CONTROL_0, 0x01); // Enable one-time mag measurement
102 | delay_ms(10);
103 |
104 | i2c_read8(device, MMC5983MA_XOUT_0, 6, &rawData[0]); // Read the 6 raw data registers into data array
105 | data_set[0] = (uint16_t) (((uint16_t) rawData[0] << 8) | rawData[1]); // x-axis
106 | data_set[1] = (uint16_t) (((uint16_t) rawData[2] << 8) | rawData[3]); // y-axis
107 | data_set[2] = (uint16_t) (((uint16_t) rawData[4] << 8) | rawData[5]); // z-axis
108 |
109 | RESET(); // Enable reset current
110 | i2c_write8(device, MMC5983MA_CONTROL_0, 0x01); // Enable one-time mag measurement
111 | delay_ms(10);
112 |
113 | i2c_read8(device, MMC5983MA_XOUT_0, 6, &rawData[0]); // Read the 6 raw data registers into data array
114 | data_reset[0] = (uint16_t) (((uint16_t) rawData[0] << 8) | rawData[1]); // x-axis
115 | data_reset[1] = (uint16_t) (((uint16_t) rawData[2] << 8) | rawData[3]); // y-axis
116 | data_reset[2] = (uint16_t) (((uint16_t) rawData[4] << 8) | rawData[5]); // z-axis
117 |
118 | for(uint8_t i = 0; i < 3; i++){
119 | if(data_set[i] > data_reset[i])
120 | delta_data[i] = data_set[i] - data_reset[i];
121 | else
122 | delta_data[i] = data_reset[i] - data_set[i];
123 | }
124 | }
125 |
126 | //--------------------------------------------------------------------------------
127 |
128 | error_t readTemperature(i2c_device_t device, uint8_t temp){
129 | result8_t result = i2c_read8(device, MMC5983MA_TOUT); // Read temperature register
130 | if(result.hasError)
131 | return 1;
132 |
133 | (temp*) = result.value;
134 |
135 | return 0;
136 | }
137 |
138 | error_t readStatus(i2c_device_t device, uint8_t _status){
139 | result8_t result = i2c_read8(device, MMC5983MA_STATUS); // Read status register
140 | if(result.hasError)
141 | return 1;
142 |
143 | (_status*) = result.value;
144 |
145 | return 0;
146 | }
147 |
148 | void clearInt(i2c_device_t device){
149 | uint8_t temp = i2c_read8(device, MMC5983MA_STATUS);
150 | temp &= 0x01; // Clear data ready interrupts
151 | i2c_write8(device, MMC5983MA_STATUS, temp);
152 | }
153 |
154 | void powerDown(i2c_device_t device){
155 | uint8_t temp = i2c_read8(device, MMC5983MA_CONTROL_2);
156 | temp &= ~(0x07); // Clear lowest four bits
157 | i2c_write8(device, MMC5983MA_CONTROL_2, temp);
158 | delay_ms(20); // Make sure to finish the lest measurement
159 | }
160 |
161 | void powerUp(i2c_device_t device, uint8_t MODR){
162 | uint8_t temp = i2c_read8(device, MMC5983MA_CONTROL_2);
163 | i2c_write8(device, MMC5983MA_CONTROL_2, temp | MODR); // Start continuous mode
164 | }
165 |
166 | void offsetBias(i2c_device_t device, float* dest1, float* dest2){
167 | int32_t mag_bias[3] = {0, 0, 0}, mag_scale[3] = {0, 0, 0};
168 | int32_t mag_max[3] = {-262143, -262143, -262143}, mag_min[3] = {262143, 262143, 262143};
169 | uint32_t mag_temp[3] = {0, 0, 0}, magOffset = 131072;
170 | float _mRes = 1.0f/16384.0f; // mag sensitivity if using 18 bit data
171 | delay_ms(4000);
172 |
173 | for (int i=0; i<4000; i++){
174 | readData(device, mag_temp);
175 |
176 | for (int j=0; j<3; j++){
177 | if((int32_t)(mag_temp[j] - magOffset) > mag_max[j])
178 | mag_max[j] = (int32_t)(mag_temp[j] - magOffset);
179 |
180 | if((int32_t)(mag_temp[j] - magOffset) < mag_min[j])
181 | mag_min[j] = (int32_t)(mag_temp[j] - magOffset);
182 | }
183 |
184 | delay_ms(12);
185 | }
186 |
187 | // Get hard iron correction
188 | mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in counts
189 | mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in counts
190 | mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in counts
191 |
192 | dest1[0] = (float) (mag_bias[0]) * _mRes; // Save mag biases in G for main program
193 | dest1[1] = (float) (mag_bias[1]) * _mRes;
194 | dest1[2] = (float) (mag_bias[2]) * _mRes;
195 |
196 | // Get soft iron correction estimate
197 | mag_scale[0] = (mag_max[0] - mag_min[0])/2; // get average x axis max chord length in counts
198 | mag_scale[1] = (mag_max[1] - mag_min[1])/2; // get average y axis max chord length in counts
199 | mag_scale[2] = (mag_max[2] - mag_min[2])/2; // get average z axis max chord length in counts
200 |
201 | float avg_rad = (mag_scale[0] + mag_scale[1] + mag_scale[2])/3.0f;
202 |
203 | dest2[0] = avg_rad/((float)mag_scale[0]);
204 | dest2[1] = avg_rad/((float)mag_scale[1]);
205 | dest2[2] = avg_rad/((float)mag_scale[2]);
206 | }
--------------------------------------------------------------------------------
/CAN/README.md:
--------------------------------------------------------------------------------
1 | # CANZenTool
2 |
3 | ## Objective
4 | This library was designed to help Zenith's Members to use/deal with `stm32f1xx_hal_can.h`, the HAL's `Hardware Abstraction Layer` "sub library" to deal with CAN `Controlled Area Network` Protocol.
5 |
6 | ## Resume
7 |
8 | This library provides functions to help the user to:
9 |
10 |
11 | - Find the best Id and Mask for the Filter (CANZenTool_IdNdMaskBuilder_t)
12 | - Configure the filter (CANZenTool_setFilter)
13 | - Write a standard can frame (CANZenTool_writeStdCanFrame)
14 | - Send a standard can frame (CANZenTool_sendCanFrameMsg)
15 |
16 |
17 |
18 | ### Important - 1
19 |
20 | This library is not over yet!! It is an ongoing project that will evolve as long we (Zenith's Members) continue to work with CAN. So the idea is to look for our bare necessities, which means, when new opportunities to avoid code duplication and reduce painful and unnecessary journeys to H.A.L and BluePill documentation pop up, then we will come up with new features. So forget about your worries and your strife with code, when new bare necessities arise, the new features will come to you. They'll come to you!!
21 |
22 | ### Important - 2
23 |
24 | Maybe it's obvious, but this library depends on the libraries `"stm32f1xx_hal_can.h"` and `"stm32f1xx_hal_def.h"`. So you will need them to use this library.
25 |
26 | ## Function `CANZenTool_setFilter`
27 |
28 | This function sets the configuration parameters passed in the function call and other standard settings in a `CAN_FilterTypeDef` named `canFilterConfigs`. The "other standard settings" refers to no required parameters in the function call, but that does not vary between Zenith's projects, so they are defaulted set inside the function.
29 |
30 |
31 | ### Sample
32 |
33 | ```c
34 | /* Filter Configs */
35 |
36 | bool wholeFilterIsActive = true;
37 |
38 | CAN_FilterTypeDef canFilterConfig;
39 |
40 | uint32_t filterBank = 0;
41 |
42 | CANZenTool_IdNdMaskBuilder_t builder = CANZenTool_newIdNdMaskBuilder();
43 |
44 | uint32_t ids[] = { 0xF2, 0x55 };
45 | builder.addIdList(&builder, 2, ids);
46 | uint32_t filterId = builder.getResultId(&builder);
47 |
48 | uint32_t filterMaskId = builder.getResultId(&builder);
49 |
50 | /* The Function */
51 | CANZenTool_setFilter(&hcan, &canFilterConfig, wholeFilterIsActive, filterBank, filterId, filterMaskId);
52 |
53 | /* An Important Interrupt */
54 | HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); //Remember to activate this Interrupt!!
55 | ```
56 | ## Function `CANZenTool_writeStdCanFrame`
57 |
58 | This function pre-configures a standard CAN Frame with the specifications inserted in the function call, such as the length of the CAN Frame (DLC - the first parameter), the ID of the frame (second parameter) and if it is a data frame or not (last parameter).
59 |
60 | ### Sample
61 | ```c
62 | uint32_t mailbox;
63 |
64 | /*... some code ...*/
65 |
66 | CAN_TxHeaderTypeDef header = CANZenTool_writeStdCanFrame(8, 0xE1, true);
67 | uint8_t buffer[8] = { 0, [3]=0xFA };
68 |
69 | /*... some code ...*/
70 |
71 | /*... CAN Start ...*/ //CAN also can be started before, but not after any CANZenTool_sendFrameMsg() call
72 |
73 | CANZenTool_sendCanFrameMsg(&hcan, &header, buffer, &mailbox);
74 | ```
75 |
76 | ## Function `CANZenTool_sendFrameMsg`
77 |
78 | This function in few words helps the user to send a Standard CAN Frame, already calling the ``Error_Handler`` in failure case.
79 |
80 | ### Sample
81 | ```c
82 | uint32_t mailbox;
83 |
84 | /*... some code ...*/
85 |
86 | CAN_TxHeaderTypeDef header = CANZenTool_writeStdCanFrame(8, 0xE1, true);
87 | uint8_t buffer[8] = { 0, [3]=0xFA };
88 |
89 | /*... some code ...*/
90 |
91 | /*... CAN Start ...*/ //CAN also can be started before, but not after any CANZenTool_sendFrameMsg() call
92 |
93 | CANZenTool_sendCanFrameMsg(&hcan, &header, buffer, &mailbox);
94 | ```
95 |
96 | ## Struct `CANZenTool_IdNdMaskBuilder_t`
97 |
98 | This structure and the functions below from `CANZenTool_addId` to `CANZenTool_newFilterBuilder`, together try to follow the **[Builder Design Pattern]**.
99 |
100 | The idea is providing for the user a tool to create the **FilterId** and the **FiltertMask** (required by `CANZenTool_setFilter`) based on the Standard CAN Ids that the user wants to allow pass the filter.
101 |
102 | ## Function `CANZenTool_newIdNdMaskBuilder`
103 |
104 | ### Sample
105 | ```c
106 | CANZenTool_IdMask_t builder = newIdNdMaskBuilder();
107 | ```
108 |
109 |
110 | ## Function `CANZenTool_addId`
111 |
112 | This function "adds" a **standard CAN Id** to the "builder" referred as "this". The addition "re-calculates" the new Id and Mask inside the "builder" structure based in the income Id.
113 |
114 | Note: this function can be both used as a "method" of the "builder" structure or as a stand-alone function.
115 |
116 |
117 | ### Sample - Stand-alone
118 | ```c
119 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
120 |
121 | CANZenTool_addId(&builder, 0x0D0);
122 |
123 | CANZenTool_addId(&builder, 0x0D1);
124 |
125 | CANZenTool_addId(&builder, 0x0D2);
126 |
127 | CANZenTool_addId(&builder, 0x0D3);
128 |
129 | CANZenTool_addId(&builder, 0x0D4);
130 | ```
131 |
132 |
133 | ### Sample - Builder-method
134 |
135 | ```c
136 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
137 |
138 | builder.addId(&builder, 0x0D0);
139 |
140 | builder.addId(&builder, 0x0D1);
141 |
142 | builder.addId(&builder, 0x0D2);
143 |
144 | builder.addId(&builder, 0x0D3);
145 |
146 | builder.addId(&builder, 0x0D4);
147 | ```
148 |
149 |
150 |
151 | ## Function `CANZenTool_addIdList`
152 |
153 | This function "adds" a list of **standard CAN Ids** to the "builder" referred as "this". The addition "re-calculates" the new Id and Mask inside the "builder" structure based in the income Ids.
154 |
155 | Note: this function can be both used as a "method" of the "builder" structure or as a stand-alone function.
156 |
157 |
158 | ### Sample - Stand-alone
159 | ```c
160 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
161 |
162 |
163 | uint32_t list[] = {0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7
164 | ,0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4
165 | ,0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7};
166 |
167 | CANZenTool_addIdList(&builder, 21, list);
168 | ```
169 |
170 |
171 | ### Sample - Builder-method
172 |
173 | ```c
174 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
175 |
176 |
177 | uint32_t list[] = {0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7
178 | ,0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4
179 | ,0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7};
180 |
181 | builder.addIdList(&builder, 21, list);
182 | ```
183 |
184 |
185 | ## Function `CANZenTool_getResultMask`
186 |
187 | This function is a "getter" for the result ``filterMask``.
188 |
189 | Note: this function can be both used as a "method" of the "builder" structure or as a stand-alone function.
190 |
191 | ### Sample - Stand-alone
192 | ```c
193 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
194 |
195 | CANZenTool_addId(&builder, 0x0D0);
196 |
197 | CANZenTool_addId(&builder, 0x0D1);
198 |
199 | uint32_t filterMask = CANZenTool_getResultMask();
200 | ```
201 |
202 |
203 | ### Sample - Builder-method
204 |
205 | ```c
206 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
207 |
208 | builder.addId(&builder, 0x0D0);
209 |
210 | builder.addId(&builder, 0x0D1);
211 |
212 | uint32_t filterMask = builder.getResultMask();
213 | ```
214 |
215 |
216 | ## Function `CANZenTool_getResultId`
217 |
218 | This function is a "getter" for the result ``filterId``.
219 |
220 | Note: this function can be both used as a "method" of the "builder" structure or as a stand-alone function.
221 |
222 | ### Sample - Stand-alone
223 | ```c
224 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
225 |
226 | CANZenTool_addId(&builder, 0x0D0);
227 |
228 | CANZenTool_addId(&builder, 0x0D1);
229 |
230 | uint32_t filterId = CANZenTool_getResultId();
231 | ```
232 |
233 |
234 | ### Sample - Builder-method
235 |
236 | ```c
237 | CANZenTool_IdNdMaskBuilder_t builder = newIdNdMaskBuilder();
238 |
239 | builder.addId(&builder, 0x0D0);
240 |
241 | builder.addId(&builder, 0x0D1);
242 |
243 | uint32_t filterId = builder.getResultId();
244 | ```
245 |
246 | ## Full Sample
247 |
248 | There is one tested-working sample (created with the **"STM-CUBEIDE"**) ``main.c`` in a folder named as "CUBEIDE-sample", if you are having problems with some of the provided tools, you should check the code and see if it clarifies something.
249 |
250 |
251 |
252 | [Builder Design Pattern]: https://refactoring.guru/design-patterns/builder "Refactoring Guru - Builder Design Pattern"
253 |
--------------------------------------------------------------------------------