├── MMC5983MA ├── README.md ├── mmc5983ma.h └── mmc5983ma.c ├── GUVC-S10GD ├── extra_doc │ ├── circuit-sample.png │ ├── GUVC-Circuit_Sample.png │ ├── tension_current_relation_1.png │ ├── tension_current_relation_2.png │ └── Dedution_of_De-conversion_Equation.pdf ├── guvcs10gd.h ├── guvcs10gd.c └── README.md ├── .gitmodules ├── BMP280 ├── README.md ├── bmp280.h └── bmp280.c ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── but_report.md │ ├── feature_request.md │ └── aplica--o-de-driver-.md └── workflows │ ├── hello.yml │ └── dynamic-readme.yaml ├── MPX5700 ├── mpx5700.h ├── README.md └── mpx5700.c ├── CartaoSD ├── README.md ├── sdcard.h └── sdcard.c ├── INA3221 ├── README.txt ├── INA3221.h └── INA3221.c ├── HDC1080 ├── README.md ├── hdc1080.h └── hdc1080.c ├── LICENSE ├── MS5607 ├── README.md ├── MS5607.h └── MS5607.c ├── W25Q ├── README.md ├── w25q_littlefs.h ├── w25q.h ├── w25q_littlefs.c └── w25q.c ├── INA219 ├── ina219.h ├── README.md └── ina219.c ├── NEO8M ├── ublox_neo8.h ├── README.md └── ublox_neo8.c ├── MAX31856 ├── max31856.h └── max31856.c ├── MPU6050 ├── README.md ├── MPU6050_MVD.h └── MPU6050_MVD.c ├── README.md └── CAN ├── CANZenTool.h ├── CUBEIDE-sample └── main.c ├── CANZenTool.c └── README.md /MMC5983MA/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /GUVC-S10GD/extra_doc/circuit-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenitheesc/Drivers/HEAD/GUVC-S10GD/extra_doc/circuit-sample.png -------------------------------------------------------------------------------- /GUVC-S10GD/extra_doc/GUVC-Circuit_Sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenitheesc/Drivers/HEAD/GUVC-S10GD/extra_doc/GUVC-Circuit_Sample.png -------------------------------------------------------------------------------- /GUVC-S10GD/extra_doc/tension_current_relation_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenitheesc/Drivers/HEAD/GUVC-S10GD/extra_doc/tension_current_relation_1.png -------------------------------------------------------------------------------- /GUVC-S10GD/extra_doc/tension_current_relation_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenitheesc/Drivers/HEAD/GUVC-S10GD/extra_doc/tension_current_relation_2.png -------------------------------------------------------------------------------- /GUVC-S10GD/extra_doc/Dedution_of_De-conversion_Equation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenitheesc/Drivers/HEAD/GUVC-S10GD/extra_doc/Dedution_of_De-conversion_Equation.pdf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SX127X-FSK-LoRa"] 2 | path = SX127X-FSK-LoRa 3 | url = https://github.com/JulioCalandrin/SX127X-FSK-LoRa.git 4 | [submodule "Platform-Lib"] 5 | path = Platform-Lib 6 | url = https://github.com/zenitheesc/Platform-Lib.git 7 | -------------------------------------------------------------------------------- /BMP280/README.md: -------------------------------------------------------------------------------- 1 | # BMP280 2 | 3 | ## Propósito 4 | Sensor de pressão e temperatura 5 | 6 | ## Exemplo 7 | Setup 8 | ```c' 9 | bmp280_t bmp = { .dev = { .address = 0x76, .i2c = &hi2c1 } }; 10 | bmp280_init(&bmp); 11 | 12 | ``` 13 | 14 | Medida: 15 | ```c 16 | bmp280_measurement_t measure = bmp280_measure(&bmp); 17 | printf("Presure: %.1f Temperature: %.2f", measure.pressure, measure.temperature); 18 | ``` 19 | 20 | ## Documentação 21 | 22 | ## Notas 23 | 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## What type of PR is this? (check all applicable) 5 | - [ ] ✨ Feature 6 | - [ ] 🐞 Bug Correction 7 | - [ ] 📝 Documentation Update 8 | - [ ] 🚩 Other 9 | 10 | # Changes: 11 | 12 | 13 | # Testing 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/but_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Error" 3 | about: Create an issue to help us to improve this project ✨ 4 | title: 'Error: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the Error 11 | 12 | 13 | 14 | ### Expected behavior 15 | 16 | 17 | 18 | ### Additional context 19 | 20 | 21 | -------------------------------------------------------------------------------- /MPX5700/mpx5700.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mpx5700.h 3 | * 4 | * Created on: Jul 21, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef INC_MPX5700_H_ 9 | #define INC_MPX5700_H_ 10 | #include "platform/platform.h" 11 | 12 | typedef struct { 13 | adc_t *adc; 14 | } mpx5700_t; 15 | 16 | error_t mpx5700_init(mpx5700_t mpx); 17 | result_uint16_t mpx5700_get_raw(mpx5700_t mpx); 18 | float mpx5700_get_pressure(mpx5700_t mpx); 19 | 20 | // Vout = A*pressao + B 21 | #define MPX_CONV_A (0.04f) 22 | #define MPX_CONV_B (0.0012858f) 23 | 24 | #endif /* INC_MPX5700_H_ */ 25 | -------------------------------------------------------------------------------- /CartaoSD/README.md: -------------------------------------------------------------------------------- 1 | # Cartao SD 2 | ## Requisitos 3 | - Configurar o SDMMc 4 | - Configurar o FatFS 5 | 6 | ## Como usar: 7 | Setup, mount 8 | ```c 9 | FIL logfile; 10 | sdcard_file_t log = { .file = &logfile, .filename = "MISSAO.TXT" }; 11 | if (sdcard_init(log)) { 12 | puts("Failed to INIT\r"); 13 | } 14 | ``` 15 | 16 | Adicionar uma linha ao log, append 17 | 18 | ```c 19 | uint8_t txt[] = "Status da Missao\r\n"; 20 | buffer_view_t line = { .data = txt, .size = sizeof(txt) }; 21 | result16_t x = sdcard_append(log, line); 22 | if (x.hasError) { 23 | puts("Failed to APPEND\r"); 24 | } 25 | 26 | ``` 27 | 28 | Limpeza, unmout 29 | 30 | ```c 31 | sdcard_destroy(); 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/hello.yml: -------------------------------------------------------------------------------- 1 | name: Auto message for PR's and Issues 2 | 3 | on: [pull_request, issues] 4 | jobs: 5 | build: 6 | name: Hello new contributor 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | issue-message: "Hey, thank you for opening your first Issue ! 🚀 Feel free to add assignees and tags. Also don't forget to read our README.md for contribution guidelines!\nWe'll address this issue soon ;)" 13 | pr-message: "Hey, thank you for opening your first Pull Request ! 🚀 Don't forget to read our README.md for contribution guidelines.\nWe'll be reviewing this PR soon ;)" 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: "Suggest an idea for this project \U0001F680" 4 | title: 'Feature: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your feature request related to a problem? Please describe. 11 | 12 | 13 | 14 | ### Describe the solution you'd like 15 | 16 | 17 | 18 | ### Describe alternatives you've considered 19 | 20 | 21 | 22 | ### Additional context 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/dynamic-readme.yaml: -------------------------------------------------------------------------------- 1 | name: Dynamic Template 2 | 3 | on: 4 | create: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | update_templates: 9 | name: "Update Templates" 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "📥 Fetching Repository Contents" 13 | uses: actions/checkout@main 14 | 15 | - name: "💾 Github Repository Metadata" 16 | uses: varunsridharan/action-repository-meta@main 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | - name: "💫 Dynamic Template Render" 21 | uses: varunsridharan/action-dynamic-readme@main 22 | with: 23 | GLOBAL_TEMPLATE_REPOSITORY: $GITHUB_REPOSITORY 24 | files: | 25 | README.md 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /MPX5700/README.md: -------------------------------------------------------------------------------- 1 | # MPX5700 2 | 3 | ## Propósito 4 | Sensor de pressão, usamos para medir a pressão externa em altitudes de sondas atmosféricas (>10km). 5 | 6 | ## Exemplo 7 | Setup 8 | ```c 9 | mpx5700_t mpx = {.adc = &hadc1}; 10 | mpx5700_init(mpx); 11 | ``` 12 | 13 | Medida: 14 | ```c 15 | float pressao = mpx5700_get_pressure(mpx); 16 | printf("pressao: %f\r\n", pressao); 17 | ``` 18 | 19 | ## Documentação 20 | Direta, usa a equação na Figure 2 do [datasheet](https://www.nxp.com/docs/en/data-sheet/MPX5700.pdf), invertida para 21 | nos dar `P` 22 | 23 | ``` 24 | Vout = VS*(0.0012858*P+0.04) ± Error 25 | ``` 26 | ### Referencia: 27 | [https://www.fernandok.com/2018/11/conheca-agora-um-sensor-importante.html](https://www.fernandok.com/2018/11/conheca-agora-um-sensor-importante.html) 28 | 29 | 30 | ## Notas 31 | - **Assume ADC em Modo Continuo no STM32** 32 | - Biblioteca não testada (ainda) 33 | - Assume Vs (alimentação), de 5V 34 | -------------------------------------------------------------------------------- /CartaoSD/sdcard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sdcard.h 3 | * 4 | * Created on: 1 de jul de 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef INC_STORAGE_SDCARD_H_ 9 | #define INC_STORAGE_SDCARD_H_ 10 | 11 | #include "fatfs.h" 12 | #include "bsp.h" 13 | 14 | typedef struct { 15 | FIL* file; // ponteiro para file 16 | char const * filename; 17 | } sdcard_file_t; 18 | 19 | /*** 20 | * Mounts SDCard and creates file if it doesn't exist 21 | */ 22 | error_t sdcard_init(sdcard_file_t file); 23 | /*** 24 | * Escreve dados do Buffer View no final do arquivo e retorna se 25 | * ocorreram erros e a quantidade de bytes escritos 26 | */ 27 | result16_t sdcard_append(sdcard_file_t file, buffer_view_t view); 28 | 29 | 30 | void sdcard_destroy(); 31 | /*** 32 | * Variaveis Globais definidas pelo fatfs.h 33 | */ 34 | extern FATFS SDFatFS; /* File system object for SD logical drive */ 35 | extern char SDPath[4]; /* SD logical drive path */ 36 | 37 | 38 | #endif /* INC_STORAGE_SDCARD_H_ */ 39 | -------------------------------------------------------------------------------- /BMP280/bmp280.h: -------------------------------------------------------------------------------- 1 | #include "platform/platform.h" 2 | 3 | typedef int32_t bmp280_temp_calib_t[4]; 4 | typedef int32_t bmp280_pres_calib_t[10]; 5 | 6 | typedef struct { 7 | i2c_device_t dev; 8 | bmp280_temp_calib_t temp_calib; 9 | bmp280_pres_calib_t pres_calib; 10 | } bmp280_t; 11 | 12 | typedef struct { 13 | float temperature; 14 | float pressure; 15 | error_t error; 16 | } bmp280_measurement_t; 17 | 18 | error_t bmp280_init(bmp280_t *bmp); 19 | 20 | bmp280_measurement_t bmp280_measure(bmp280_t *bmp); 21 | 22 | #define BMP_TEMP_XLSB 0xFC 23 | #define BMP_TEMP_LSB 0xFB 24 | #define BMP_TEMP_MSB 0xFA 25 | #define BMP_PRESS_XLSB 0xF9 26 | #define BMP_PRESS_LSB 0xF8 27 | #define BMP_PRESS_MSB 0xF7 28 | #define BMP_CONFIG 0xF5 29 | #define BMP_CTRL_MEAS 0xF4 30 | #define BMP_STATUS 0xF3 31 | #define BMP_RESET 0xE0 32 | #define BMP_ID 0xD0 33 | #define BMP_EXPECTED_ID 0x58 34 | #define BMP_CALIB25 0xA1 35 | #define BMP_CALIB_T1_MSB 0x88 36 | #define BMP_CALIB_T1_LSB 0x89 37 | 38 | #define BMP_CALIB_P1_MSB 0x8E 39 | 40 | #define BMP_CALIB_T1_MSB 0x88 41 | -------------------------------------------------------------------------------- /INA3221/README.txt: -------------------------------------------------------------------------------- 1 | Inicialização / Reset: 2 | 3 | ina3221_init(ina); 4 | 5 | A função "init" inicializa o dispositivo resetando o bit de configuração. Assim, 6 | o ina é configurado como: 7 | - 3 channels 8 | - avg mode: 1 sample 9 | - shunt/bus conversion time: 1.1ms (cada) 10 | - operating mode: shunt/bus continuous 11 | 12 | OBS: É necessário criar a struct "ina3221_config_t" para utilizar a função init. 13 | 14 | 15 | ina3221_alive(ina); 16 | 17 | Retorna falso se posssui um erro (Die ID) 18 | 19 | OBS: a função "alive" é utilizada dentro da "init". 20 | 21 | 22 | Setup: 23 | 24 | ina3221_config(ina); 25 | 26 | Configura o dispositivo de acordo com a struct referenciada. 27 | 28 | OBS: avg mode: a quantidade de samples é dada no tipo 4^(n). 29 | Onde "n" é o parâmetro em binário (3 algarismos). 30 | 31 | Ex: 010, 16 samples. 32 | 33 | 34 | Medida: 35 | 36 | ina3221_mensurement(ina, *values); 37 | 38 | Retorna os valores da medida parra a struct referenciada na função. 39 | 40 | Ex: printf("corrente ch1: %d", values.ch1_current); 41 | printf("potência ch1: %d", values.ch1.pot); -------------------------------------------------------------------------------- /HDC1080/README.md: -------------------------------------------------------------------------------- 1 | # HDC1080 2 | 3 | ## Propósito 4 | Sensor capaz de medir a umidade do ar e temperatura ambiente. 5 | 6 | ## Exemplo 7 | Setup 8 | ```c 9 | i2c_device_t sen = { .address = HDC1080_ADDR, .i2c = &hi2c1 }; 10 | hdc1080_init(sen); 11 | ``` 12 | 13 | Medida: 14 | ```c 15 | hdc1080_mesurement_t th = hdc1080_mesure(sen); 16 | printf("temp: %f\r\n", th.temperature); 17 | printf("humd: %f\r\n", th.humidity); 18 | ``` 19 | 20 | ## Documentação 21 | Quando é requisitado uma medição, a temperatura e umidade são medidas sequencialmente 22 | o sensor leva em torno de 7ms para medir cada uma então, no total , 14ms. 23 | 24 | Ordem de operações: 25 | - configura somente temperatura 26 | - le temperatura 27 | - configura somente umidade 28 | - le umidade 29 | 30 | ## Notas 31 | Os módulos mais comuns não tem pinos para alterar o endereço I2C (0x40) que é 32 | o mesmo que o padrão do INA219 33 | 34 | Em teoria é possivel medir ambas ao mesmo tempo e reduzir o intervalo, mas 35 | não obtive bons resultados. A umidade sempre corrompia. Então optei por 36 | fazer as medições em separado. 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Zenith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CartaoSD/sdcard.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sdcard.c 3 | * 4 | * Created on: 1 de jul de 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #include "Storage/sdcard.h" 9 | 10 | error_t sdcard_init(sdcard_file_t file) { 11 | int err = FR_OK; 12 | err = f_mount(&SDFatFS, "", 1); 13 | if (err) { 14 | return err; 15 | } 16 | // FA_OPEN_ALWAYS: Opens the file if it is existing. If not, a new file will be created. 17 | err = f_open(file.file, file.filename, FA_OPEN_ALWAYS); 18 | if (err) { 19 | return err; 20 | } 21 | f_close(file.file); 22 | return (error_t) err; 23 | } 24 | 25 | result16_t sdcard_append(sdcard_file_t file, buffer_view_t view) { 26 | int err = f_open(file.file, file.filename, FA_OPEN_APPEND | FA_WRITE); 27 | result16_t out = { .hasError = err, .value = 0 }; 28 | if (err) { 29 | return out; 30 | } 31 | 32 | unsigned int written_bytes = 0; // bytes escritos 33 | err = f_write(file.file, view.data, view.size, &written_bytes); 34 | 35 | f_close(file.file); 36 | out.hasError = err; 37 | // TODO: possivel truncamento da quantidade de bytes escritos. 38 | out.value = (uint16_t) written_bytes; 39 | return out; 40 | } 41 | 42 | 43 | void sdcard_destroy(){ 44 | f_mount(0, "", 0); 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /HDC1080/hdc1080.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hdc1080.h 3 | * 4 | * Created on: Jun 20, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef INC_APPLICATION_HDC1080_H_ 9 | #define INC_APPLICATION_HDC1080_H_ 10 | 11 | #include "bsp.h" 12 | 13 | typedef struct { 14 | float temperature; 15 | float humidity; 16 | } hdc1080_mesurement_t; 17 | 18 | error_t hdc1080_init(i2c_device_t device); 19 | 20 | result16_t hdc1080_get_humidity_raw(i2c_device_t device); 21 | result16_t hdc1080_get_temperature_raw(i2c_device_t device); 22 | 23 | hdc1080_mesurement_t hdc1080_mesure(i2c_device_t device); 24 | 25 | float hdc_1080_convert_temperature(uint16_t raw); 26 | float hdc_1080_convert_humidity(uint16_t raw); 27 | 28 | #define HDC1080_ADDR ((uint8_t)(0x40)) 29 | #define HDC1080_CONFIG ((uint8_t)(0x02)) 30 | #define HDC1080_DEVID ((uint8_t)(0xFF)) 31 | #define HDC1080_MEASURE ((uint8_t)(0x00)) 32 | #define HDC1080_HUMIDITY ((uint8_t)(0x01)) 33 | 34 | #define HDC1080_HEAT ((uint16_t)(1 << 13)) 35 | #define HDC1080_MESURE_BOTH ((uint16_t)(1 << 12)) 36 | #define HDC1080_MESURE_SINGLE ((uint16_t)(0 << 12)) 37 | #define HDC1080_TEMP_14 ((uint16_t)(0 << 10)) 38 | #define HDC1080_HUMD_14 ((uint16_t)((0 << 9) & (0 << 8))) 39 | #endif /* INC_APPLICATION_HDC1080_H_ */ 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/aplica--o-de-driver-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Aplicação de Driver ' 3 | about: Issue estruturado para criar tarefas e organizar discussão de um driver 4 | title: 'Driver para ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Aplicação: 11 | 12 | O responsável por esse issue deve: 13 | - **Pesquisar sobre essa aplicação** 14 | - Em qual contexto do projeto será utilizada? 15 | - Qual geralmente é o propósito dessa aplicação? 16 | - Existe algum dispositivo comum? 17 | - **Pesquisar sobre o dispositivo atual** 18 | - Por que estamos usando ele? 19 | - Tem algo que torna ele difícil de trabalhar? 20 | - Onde ele já foi utilizado? 21 | - **Implementar uma biblioteca do dispositivo.** 22 | - Qual informação está associada ao dispositivo? 23 | - Como se espera que ele seja utilizado? 24 | - Sua implementação compila sem Warnings? 25 | - **Documentar a Implementação** 26 | - O que cada função faz? 27 | - O que cada struct e seus campos significam? 28 | - Quais características do dispositivo afetam o código? 29 | - Quais partes do datasheet um usuário do driver deve ficar atento? 30 | - Existe alguma outra documentação externa de interesse para o usuário? 31 | 32 | ## Observações 33 | 34 | 35 | 36 | ## Tarefas 37 | - [ ] Pesquisa e Leitura do Datasheet 38 | - [ ] Selecionar Features 39 | - [ ] Rascunho da Interface 40 | - [ ] Implementação 41 | - [ ] Documentação 42 | - [ ] Revisão 43 | - [ ] Teste 44 | - [ ] Finalização 45 | -------------------------------------------------------------------------------- /MPX5700/mpx5700.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mpx5700.c 3 | * 4 | * Created on: Jul 21, 2021 5 | * Author: leocelente 6 | */ 7 | #include "mpx5700.h" 8 | 9 | error_t mpx5700_init(mpx5700_t mpx) { return adc_init(mpx.adc); } 10 | 11 | result_uint16_t mpx5700_get_raw(mpx5700_t mpx) { return adc_read(mpx.adc); } 12 | 13 | // Calulo do ganho de um divisor de tensão 14 | static float volt_div(float r1, float r2) { return (r2) / (r1 + r2); } 15 | 16 | /*** 17 | * Reverte a conversão do circuito entre o ADC e o Sensor 18 | * Nesse caso, um divisor de tensão 19 | * Entrada: 0 ... 3.3f (volts) 20 | * Saida: 0 ... 5.0f (volts) 21 | */ 22 | static float inv_adjustment_circuit(float adc_in) { 23 | float R1 = 1e3f; 24 | float R2 = 2e3f; 25 | float inv_volt_div = 1.f / (volt_div(R1, R2)); 26 | return inv_volt_div * adc_in; 27 | } 28 | 29 | /*** 30 | * Calcula a pressão baseado na tensão saindo do sensor 31 | * Supondo Vs = 5v 32 | * Entrada: 0 ... 5.0f (volts) 33 | * Saida: 0 ... 700.0f (kPa) 34 | */ 35 | static float conversion_function(float sensor_out) { 36 | float Vs = 5.f; 37 | float pressure = ((sensor_out / Vs) - MPX_CONV_A) / MPX_CONV_B; 38 | return pressure; 39 | } 40 | 41 | /*** 42 | * Lê o ADC e cacula a pressão 43 | */ 44 | float mpx5700_get_pressure(mpx5700_t mpx) { 45 | result_uint16_t raw = mpx5700_get_raw(mpx); 46 | if (raw.hasError) { 47 | return -99.f; 48 | } 49 | 50 | // Tensão que chega no ADC 51 | const float read_voltage = adc_raw_to_voltage(mpx.adc,raw.value); 52 | 53 | // Tensão saindo do Sensor 54 | const float sensor_out = inv_adjustment_circuit(read_voltage); 55 | 56 | return conversion_function(sensor_out); 57 | } 58 | -------------------------------------------------------------------------------- /MS5607/README.md: -------------------------------------------------------------------------------- 1 | # MS5706 Barometric Pressure Sensor 2 | 3 | ## Propósito: 4 | Sensor capaz de mensurar a pressão do ambiente 5 | 6 | ## Exemplo: 7 | 8 | ###### Setup: 9 | 10 | ```c 11 | //Build device struct 12 | prom_t prom = {0}; 13 | Dconst_t Dconst = {0}; 14 | 15 | i2c_device_t ms5607_dvc = { 16 | .i2c = &hi2c1, 17 | .address = I2C_ADR 18 | }; 19 | 20 | ms5607_t ms5607 = { 21 | .device = ms5607_dvc, 22 | .prom = prom, 23 | .Dconst = Dconst, 24 | .OSR_mode = 0 25 | }; 26 | 27 | //Initialize 28 | ms5607_init(ms5607, mode_2); 29 | ``` 30 | 31 | ###### Medida: 32 | ```c 33 | //Declare and get the pressure 34 | int32_t pressure = ms5607_getPressure(ms5607); 35 | printf("Pressão: %d mbar", pressure); 36 | ``` 37 | 38 | ## Documentação: 39 | 40 | ###### Funcionamento Geral: 41 | A função prototipada em formato I2C, quando requisitada, inicializa duas *structs* (*PROM* e *Dconst*) para o ajustar uma variável de temperatura(*TEMP*), que posteriormente será referência para calibrar o cálculo da pressão. Para isso é necessário definir a quantidade de amostras (*OSR_mode*) que serão utilizadas para a conversão ADC do sensor de temperatura e pressão. 42 | 43 | ###### OSR_mode: 44 | Representa a quantidade de amostras (iguais para D1 e D2) que serão convertidas em sinais digitais (ADC), logo seguem o padrão: 45 | 46 | - ```mode_1``` = 256 Samples 47 | - ```mode_2``` = 512 Samples 48 | - ```mode_3``` = 1024 Samples 49 | - ```mode_4``` = 2048 Samples 50 | - ```mode_5``` = 4096 Samples 51 | 52 | ## Referências: 53 | - MS5607 Datasheet:
54 | https://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Data+Sheet%7FMS5607-02BA03%7FB2%7Fpdf%7FEnglish%7FENG_DS_MS5607-02BA03_B2.pdf%7FCAT-BLPS0035 -------------------------------------------------------------------------------- /W25Q/README.md: -------------------------------------------------------------------------------- 1 | # W25Q FLASH Driver 2 | ## Propósito 3 | 4 | Driver da familia memórias flash W25Qxxx, compatível com os modelos: 5 | - W25Q16 6 | - W25Q32 7 | - W25Q64 8 | - W25Q128 9 | 10 | O driver fornece apenas a interface mínima para usar o sistema de arquivos 11 | [LittleFS](https://github.com/littlefs-project/littlefs). Então permite apenas 12 | a `read/write` de páginas de 256 bytes e `erase` de setores de 4kB. 13 | 14 | ## LittleFS 15 | 16 | Para usar o LittleFS inclua o `w25q_littlefs.h`. Segue o exemplo de um 17 | contador de boots: 18 | 19 | ```c 20 | puts("Start-----\r"); 21 | gpio_pin_t flash_cs = { .pin = FLASH_CS_Pin, .port = FLASH_CS_GPIO_Port }; 22 | spi_device_t spi_dev_flash = { .spi = &hspi1, .pin = flash_cs }; 23 | w25q_t flash = { .page_count = page_count_from_model(W25Q16), .dev = 24 | spi_dev_flash }; 25 | 26 | w25q_init(&flash, true); 27 | 28 | littlefs_init(flash); 29 | 30 | struct lfs_file file; 31 | 32 | uint32_t boot_count = 0; 33 | int e = lfs_file_open(&file_system, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); 34 | printf("Open Boot Count (%d)\r\n", e); 35 | e = lfs_file_read(&file_system, &file, &boot_count, sizeof(boot_count)); 36 | printf("Read Boot Count (%d), Value: %lu\r\n", e, boot_count); 37 | boot_count += 1; 38 | e = lfs_file_rewind(&file_system, &file); 39 | printf("Rewind (%d)\r\n", e); 40 | e = lfs_file_write(&file_system, &file, &boot_count, sizeof(boot_count)); 41 | printf("Write Boot Count (%d), Value: %lu\r\n", e, boot_count); 42 | 43 | e = lfs_file_close(&file_system, &file); 44 | printf("Close... %d\r\n", e); 45 | 46 | e = lfs_unmount(&file_system); 47 | printf("Unmount (%d)\r\n", e); 48 | printf("boot_count: %lu\r\n", boot_count); 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /GUVC-S10GD/guvcs10gd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * guvcs10gd.h 3 | * 4 | * Created on: Aug 18, 2021 5 | * Author: Carlos Craveiro 6 | */ 7 | 8 | #ifndef __GUVCS10GD_H__ 9 | #define __GUVCS10GD_H__ 10 | 11 | #include "bsp.h" 12 | 13 | 14 | 15 | /* TYPEDEFS */ 16 | 17 | typedef struct { 18 | 19 | float offset; 20 | float amplitude; 21 | 22 | } guvcs10gd_conv_param_t 23 | 24 | 25 | typedef struct { 26 | adc_t *adc; 27 | 28 | struct { 29 | 30 | guvcs10gd_conv_param_t tension; //unit => mili-volts 31 | guvcs10gd_conv_param_t current; //unit => pico-amperes 32 | uint16_t phase_difference; //unit => degrees 33 | 34 | } converter; 35 | 36 | } guvcs10gd_t; 37 | 38 | 39 | 40 | /* FUNCTIONS */ 41 | 42 | error_t guvcs10gd_init(guvcs10gd_t guvc); 43 | 44 | result16_t guvcs10gd_get_raw(guvcs10gd_t raw); 45 | 46 | result16_t guvcs10gd_conv_tension_to_current(guvcs10gd_t guvc, result16_t raw_data); 47 | 48 | float guvcs10gd_get_uvcpower(guvcs10gd_t guvc); 49 | 50 | 51 | // OPTIONAL FUNCTIONS THAT REQUIRES MORE PROCESSING POWER FROM THE BLUEPILL(STM32) 52 | double guvcs10gd_accurate_conv_degrees_to_radians(uint16_t deg); 53 | 54 | double guvcs10gd_accurate_conv_tension_to_current(guvcs10gd_t guvc, result16_t raw_data); 55 | 56 | double guvcs10gd_accurate_get_uvcpower(guvcs10gd_t guvc); 57 | 58 | 59 | 60 | /*CONVERSION VALUES GOT FROM DATASHEET*/ 61 | 62 | // CONV_A = (1 / 78) 63 | #define GUVC_CONV_A (float)(0.01282051282) 64 | 65 | 66 | 67 | /*CONVERSION BLUEPILL_ADC UNIT TO MILI-VOLTS*/ 68 | 69 | #define GUVCS10GD_ADC_CONVERTER(bpill_output) ((bpill_output * 3300) / 4096) 70 | 71 | 72 | 73 | /*ERRORS CODE-SHEET*/ 74 | 75 | //#define GUVCS10GD_INIT_ERROR 76 | #define GUVCS10GD_READ_ERROR ((float) -101); 77 | 78 | 79 | 80 | #endif /*__INC_APPLICATION_GUVCS10GD_H__*/ 81 | 82 | -------------------------------------------------------------------------------- /W25Q/w25q_littlefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * w25q_littlefs.h 3 | * 4 | * Created on: Sep 19, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef INC_STORAGE_W25Q_LITTLEFS_H_ 9 | #define INC_STORAGE_W25Q_LITTLEFS_H_ 10 | 11 | #include "Storage/w25q.h" 12 | #include "Storage/littlefs/lfs.h" 13 | 14 | /** 15 | * W25Q Flash chip in use by littlefs 16 | * gets set in `littlefs_init` 17 | */ 18 | w25q_t _littlefs_flash; 19 | 20 | /** 21 | * Global littlefs state 22 | */ 23 | lfs_t file_system; 24 | 25 | /** 26 | * Configuration 27 | * Note: References to it must be valid even after `lfs_mount` 28 | * is done, thats why it is a global in this case. 29 | */ 30 | struct lfs_config cfg; 31 | /** 32 | * Initialize and configure littlefs for use with 33 | * some FLASH 34 | */ 35 | error_t littlefs_init(w25q_t flash); 36 | 37 | /** 38 | * Wrapper that converts littlefs "blocks" to page 39 | * addresses before calling driver's `page_read` 40 | */ 41 | int block_device_read(const struct lfs_config *c, lfs_block_t block, 42 | lfs_off_t off, void *buffer, lfs_size_t size); 43 | /** 44 | * Wrapper that converts littlefs "blocks" to page 45 | * addresses before calling driver's `page_write` 46 | */ 47 | 48 | int block_device_prog(const struct lfs_config *c, lfs_block_t block, 49 | lfs_off_t off, const void *buffer, lfs_size_t size); 50 | /** 51 | * Wrapper that converts littlefs "blocks" to sector 52 | * addresses before calling driver's `sector_erase` 53 | */ 54 | int block_device_erase(const struct lfs_config *c, lfs_block_t block); 55 | /** 56 | * Does nothing, littlefs uses it check for device errors 57 | */ 58 | int block_device_sync(const struct lfs_config *c); 59 | 60 | /** 61 | * Returns a W25Qxxx compatible configuration 62 | */ 63 | struct lfs_config get_littlefs_config(w25q_t flash) ; 64 | 65 | #endif /* INC_STORAGE_W25Q_LITTLEFS_H_ */ 66 | -------------------------------------------------------------------------------- /MS5607/MS5607.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gusta on 14/11/2021. 3 | // 4 | // * I2C * 5 | 6 | #ifndef MS5607_H 7 | #define MS5607_H 8 | 9 | #include "platform/platform.h" 10 | #include 11 | 12 | enum OSR_samples {mode_1, mode_2, mode_3, mode_4, mode_5}; 13 | 14 | // OSR_Mode: (Same for D1 and D2) 15 | // mode_1 = 256 Samples 16 | // mode_2 = 512 Samples 17 | // mode_3 = 1024 Samples 18 | // mode_4 = 2048 Samples 19 | // mode_5 = 4096 Samples 20 | 21 | 22 | typedef struct { 23 | uint16_t C1; // Pressure sensitivity 24 | uint16_t C2; // Pressure offset 25 | uint16_t C3; // Temperature coef. of pressure sens. 26 | uint16_t C4; // Temperature coef. of pressure offset 27 | uint16_t C5; // Reference temperature 28 | uint16_t C6; // Temperature coef. of the temperature 29 | 30 | } prom_t; 31 | 32 | typedef struct { 33 | uint32_t D1; // Digital pressure value 34 | uint32_t D2; // Digital temperature value 35 | 36 | } DValue_t; 37 | 38 | typedef struct { 39 | i2c_device_t device; 40 | prom_t prom; 41 | DValue_t DValue; 42 | uint8_t OSR_mode; // Quantity of samples for conversion (ADC) 43 | 44 | } ms5607_t; 45 | 46 | 47 | error_t ms5607_init(ms5607_t* ms5607, enum OSR_samples mode); 48 | int32_t ms5607_getPressure(ms5607_t* ms5607); 49 | int32_t ms5607_getTemperature(ms5607_t* ms5607); 50 | 51 | 52 | #define QNT_PROM_CONST ((uint8_t) (6)) 53 | #define QNT_DVALUE ((uint8_t) (2)) 54 | 55 | // REGISTER_NAME ADDRESS(BIN) 56 | #define I2C_ADR ((uint8_t) (0b11101100)) 57 | #define I2S_ADR_ALT ((uint8_t) (0b11101111)) 58 | 59 | 60 | // REGISTER_NAME COMMAND(BIN) 61 | #define PROM_MASK ((uint8_t) (0b10100000)) // Bits 1, 2 e 3 represents the coef. 62 | 63 | #define D1_MASK ((uint8_t) (0b01000000)) // Bits 1, 2 e 3 represents the OSR_mode 64 | #define D2_MASK ((uint8_t) (0b01010000)) 65 | 66 | #define RESET_CMD ((uint8_t) (0b00011110)) 67 | 68 | 69 | #endif //MS5607_H 70 | -------------------------------------------------------------------------------- /INA3221/INA3221.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gustavo on 05/09/2021. 3 | // 4 | 5 | #ifndef OBJ_C_INA3221_H 6 | #define OBJ_C_INA3221_H 7 | 8 | #include "platform/platform.h" 9 | #include 10 | #include 11 | 12 | // REGISTER_NAME ADDRESS(HEX) 13 | #define CONFIG_ADR 0x00 14 | #define CH1_SHUNT_V 0x01 15 | #define CH1_BUS_V 0x02 16 | #define CH2_SHUNT_V 0x03 17 | #define CH2_BUS_V 0x04 18 | #define CH3_SHUNT_V 0x05 19 | #define CH3_BUS_V 0x06 20 | #define DIE_ADR 0xFF 21 | 22 | // CONST 23 | 24 | #define BIT_CH1 14 25 | #define BIT_CH2 13 26 | #define BIT_CH3 12 27 | #define BIT_AVG 9 28 | #define BIT_BUS_CT 6 29 | #define BIT_SH_CT 3 30 | #define BIT_MODE 0 31 | #define DIE_CODE 0x3220 32 | #define RESET_CONFIG (1 << 15) 33 | 34 | #define T_SH_CONV 40e-6f 35 | #define T_BUS_CONV 8e-3f 36 | 37 | typedef struct { 38 | 39 | /** 40 | * ativa os canais 41 | */ 42 | bool ch1; 43 | bool ch2; 44 | bool ch3; 45 | /** 46 | * ativa média móvel (N=) 47 | */ 48 | uint8_t avg_mode; 49 | /** 50 | * seta o tempo de conversão da medida do VBUS 51 | */ 52 | uint8_t v_bus_ct; 53 | /** 54 | * seta o tempo de conversão da medida do VSHUNT 55 | */ 56 | uint8_t v_sh_ct; 57 | /** 58 | * Medições continuas ou single shot 59 | */ 60 | uint8_t op_mode; 61 | 62 | float ch1_resistor; 63 | float ch2_resistor; 64 | float ch3_resistor; 65 | 66 | } ina3221_config_t; 67 | 68 | typedef struct { 69 | 70 | float ch1_sh_v; // sh_v = R * (i) 71 | float ch1_bus_v; 72 | float ch2_sh_v; 73 | float ch2_bus_v; 74 | float ch3_sh_v; 75 | float ch3_bus_v; 76 | 77 | float ch1_current; 78 | float ch2_current; 79 | float ch3_current; 80 | 81 | uint16_t ch1_resistor; 82 | uint16_t ch2_resistor; 83 | uint16_t ch3_resistor; 84 | 85 | float ch1_pot; 86 | float ch2_pot; 87 | float ch3_pot; 88 | 89 | } ina3221_values_t; 90 | 91 | typedef struct { 92 | 93 | i2c_device_t device; 94 | ina3221_config_t config; 95 | 96 | } ina3221_t; 97 | 98 | error_t ina3221_init(ina3221_t ina); 99 | error_t ina3221_config(ina3221_t ina); 100 | error_t ina3221_mensurement(ina3221_t ina, ina3221_values_t *values); 101 | bool ina3221_alive(ina3221_t ina); 102 | 103 | #endif // OBJ_C_INA3221_H 104 | -------------------------------------------------------------------------------- /W25Q/w25q.h: -------------------------------------------------------------------------------- 1 | /* 2 | * w25q.c 3 | * 4 | * Created on: Sep 18, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef INC_STORAGE_W25Q_C_ 9 | #define INC_STORAGE_W25Q_C_ 10 | #include "platform/platform.h" 11 | #include 12 | typedef struct { 13 | /** 14 | * Page Count on Chip 15 | */ 16 | uint32_t page_count; 17 | 18 | /** 19 | * SPI Connection 20 | */ 21 | spi_device_t dev; 22 | 23 | } w25q_t; 24 | 25 | /** 26 | * Identifies a single byte, inside a page 27 | */ 28 | typedef struct { 29 | // page address 30 | uint32_t sector_index; 31 | uint32_t page_index; 32 | uint32_t offset_bytes; 33 | } w25q_address_t; 34 | 35 | /** 36 | * Initialization, checks connection and chip model 37 | * capacity. If `check_capacity` then fails if capacity 38 | * read from chip is different from expected. 39 | */ 40 | error_t w25q_init(w25q_t *flash, bool check_capacity); 41 | 42 | /** 43 | * Writes up to a page (256 bytes) at some `PageAddress` 44 | */ 45 | error_t w25q_page_write(w25q_t flash, buffer_view_t tx_data, 46 | w25q_address_t address); 47 | 48 | /** 49 | * Reads up to a page (256 bytes) at some `PageAddress` 50 | */ 51 | error_t w25q_page_read(w25q_t flash, buffer_view_t rx_data, 52 | w25q_address_t address); 53 | 54 | /** 55 | * Erases all SECTOR_SIZE bytes of some sector 56 | */ 57 | error_t w25q_sector_erase(w25q_t flash, size_t sector_address); 58 | 59 | /** 60 | * Erase all data in the Chip 61 | */ 62 | error_t w25q_chip_erase(w25q_t flash); 63 | 64 | /** 65 | * Models by user-friendly "name" 66 | */ 67 | typedef enum { 68 | W25Q16, W25Q32, W25Q64, W25Q128 69 | } w25q_model_e; 70 | 71 | /** 72 | * Gets page count based on model 73 | */ 74 | uint32_t page_count_from_model(w25q_model_e model); 75 | 76 | /** 77 | * W25Qxxx fixed sizes 78 | */ 79 | #define W25Q_SECTOR_SIZE 4096 80 | #define W25Q_PAGE_SIZE 256 81 | 82 | /** 83 | * Instructions 84 | */ 85 | static const uint8_t W25Q_READ = 0x0B; 86 | static const uint8_t W25Q_WRITE = 0x02; 87 | static const uint8_t W25Q_ERASE = 0x20; 88 | static const uint8_t W25Q_WR_EN = 0x06; 89 | static const uint8_t W25Q_ID = 0x9F; 90 | static const uint8_t W25Q_BUSY_MSK = 0x01; 91 | static const uint8_t W25Q_RD_STATUS1 = 0x05; 92 | static const uint8_t W25Q_WR_STATUS1 = 0x01; 93 | static const uint8_t W25Q_CHIP_ERASE = 0xC7; 94 | 95 | #endif /* INC_STORAGE_W25Q_C_ */ 96 | -------------------------------------------------------------------------------- /W25Q/w25q_littlefs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * w25q_littlefs.h 3 | * 4 | * Created on: Sep 19, 2021 5 | * Author: leocelente 6 | */ 7 | #include "Storage/w25q_littlefs.h" 8 | 9 | int block_device_read(const struct lfs_config *c, lfs_block_t block, 10 | lfs_off_t off, void *buffer, lfs_size_t size) { 11 | 12 | uint32_t pageInBlock = off / W25Q_PAGE_SIZE; 13 | uint32_t offsetInPage = off % W25Q_PAGE_SIZE; 14 | 15 | w25q_address_t address = { .sector_index=block, .page_index=pageInBlock, .offset_bytes=offsetInPage}; 16 | buffer_view_t view = { .data = buffer, .size = size }; 17 | w25q_page_read(_littlefs_flash, view, address); 18 | return 0; 19 | } 20 | 21 | int block_device_prog(const struct lfs_config *c, lfs_block_t block, 22 | lfs_off_t off, const void *buffer, lfs_size_t size) { 23 | 24 | uint32_t pageInBlock = off / W25Q_PAGE_SIZE; 25 | uint32_t offsetInPage = off % W25Q_PAGE_SIZE; 26 | 27 | w25q_address_t address = { .sector_index=block, .page_index=pageInBlock, .offset_bytes=offsetInPage}; 28 | buffer_view_t view = { .data = (uint8_t* const ) buffer, .size = size }; 29 | w25q_page_write(_littlefs_flash, view, address); 30 | return 0; 31 | } 32 | 33 | int block_device_erase(const struct lfs_config *c, lfs_block_t block) { 34 | uint32_t sector_index = block; 35 | w25q_sector_erase(_littlefs_flash, sector_index); 36 | return 0; 37 | } 38 | 39 | int block_device_sync(const struct lfs_config *c) { 40 | return 0; 41 | } 42 | 43 | struct lfs_config get_littlefs_config(w25q_t flash) { 44 | const uint32_t block_count = (flash.page_count * W25Q_PAGE_SIZE) 45 | / W25Q_SECTOR_SIZE; 46 | struct lfs_config cfg = { // 47 | .read = block_device_read, // 48 | .prog = block_device_prog, // 49 | .erase = block_device_erase, // 50 | .sync = block_device_sync, // 51 | .read_size = W25Q_PAGE_SIZE, // 52 | .prog_size = W25Q_PAGE_SIZE, // 53 | .block_size = W25Q_SECTOR_SIZE, // 54 | .block_count = block_count, // 55 | .lookahead_size = block_count / 8, // 56 | .cache_size = W25Q_PAGE_SIZE, // 57 | .block_cycles = 500, // 58 | }; 59 | 60 | return cfg; 61 | } 62 | 63 | error_t littlefs_init(w25q_t flash) { 64 | _littlefs_flash = flash; 65 | cfg = get_littlefs_config(flash); 66 | 67 | int err = lfs_mount(&file_system, &cfg); 68 | if (err) { 69 | w25q_chip_erase(flash); 70 | lfs_format(&file_system, &cfg); 71 | err = lfs_mount(&file_system, &cfg); 72 | } 73 | return err; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /INA219/ina219.h: -------------------------------------------------------------------------------- 1 | /* 2 | * INA219_MVD.h 3 | * 4 | * Created on: Sep 8, 2021 5 | * Author: Murilo Henrique Pasini Trevisan 6 | */ 7 | 8 | #ifndef INC_INA219_H_ 9 | #define INC_INA219_H_ 10 | 11 | #include "platform/platform.h" 12 | 13 | /* REGISTRADORES */ 14 | // Nome Adrres(hex) 15 | 16 | #define INA_CONFIG_ADR (uint16_t)(0x00) 17 | #define INA_SHUNT_VOLT (uint16_t)(0x01) 18 | #define INA_BUS_VOLT (uint16_t)(0x02) 19 | //#define INA_POWER (uint16_t)(0x03) //Sem uso no MVD 20 | //#define INA_CURRENT (uint16_t)(0x04) //Depende da calibração 21 | //#define INA_CALIB (uint16_t)(0x05) //Sem uso no MVD 22 | 23 | /* CONFIGURAÇÕES */ 24 | 25 | // Reset sensor 26 | // Bit RST(15) 27 | #define INA_RST (uint16_t)(0x0001 << 15) 28 | 29 | // Bus voltage range 30 | // Bit BRNG(13) 31 | #define INA_BRANGE_16 (uint16_t)(0x0000 << 13) 32 | //#define INA_BUS_32 (uint16_t)(0x0001 << 13)// Sem uso no MVD 33 | 34 | // Shunt voltage range 35 | // Bit PG(11-12) 36 | #define INA_SRANGE_40 (uint16_t)(0x0000 << 11) 37 | //#define INA_SRANGE_80 (uint16_t)(0x0001 << 11) 38 | //#define INA_SRANGE_160 (uint16_t)(0x0002 << 11) 39 | //#define INA_SRANGE_320 (uint16_t)(0x0003 << 11) 40 | 41 | // Bus ADC resolution 42 | // Bit BADC(10-7) 43 | #define INA_BADC1_12bit (uint16_t)(0x0003 << 7) //DEFAULT 532us 44 | #define INA_BADC2_12bit (uint16_t)(0x0008 << 7) //CONFIG 532us 45 | 46 | //SHUNT ADC reslution 47 | // Bit SADC(6-3) 48 | #define INA_SADC1_12bit (uint16_t)(0x0003 << 7)//DEFAULT 532us 49 | #define INA_SADC2_12bit (uint16_t)(0x0008 << 7)//CONFIG 532 us 50 | 51 | // Operation mode 52 | // Bit MODE (2-0) 53 | #define INA_CONTINUOUS (uint16_t)(0x0007 << 0)//DEFAULT 54 | 55 | 56 | 57 | /* SENSOR STRUCTS */ 58 | 59 | typedef struct{ 60 | uint16_t BusVoltageRange; // BRNG 61 | uint16_t ShuntVoltageRange; // PG 62 | uint16_t BusADCResolution; // BADC 63 | uint16_t ShuntADCResolution; // SADC 64 | uint16_t OperationMode; 65 | } INA219_config_t ; 66 | 67 | typedef struct{ 68 | float Shunt_Voltage; 69 | float Bus_Voltage; 70 | float Shunt_Current; 71 | } INA219_values_t ; 72 | 73 | typedef struct{ 74 | i2c_device_t device; 75 | INA219_config_t config; 76 | } INA219_t ; 77 | 78 | error_t INA219_reset(INA219_t ina); 79 | error_t INA219_config(INA219_t ina); 80 | error_t INA219_measure(INA219_t ina, INA219_values_t *values); 81 | 82 | //confirmar o valor do resistor shunt, depende da calibração. 83 | #define INA_RESISTOR (float)(0.100) 84 | #define INA_BUS_MULTIPLY (float)(0.004) 85 | #define INA_SHUNT_MULTIPLY (float)(0.01) 86 | 87 | 88 | 89 | 90 | #endif /* INC_INA219_H_ */ 91 | -------------------------------------------------------------------------------- /MMC5983MA/mmc5983ma.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MMC5983MA.h 3 | * 4 | * Author: Thiago Henrique Vicentini (Anakin) 5 | */ 6 | 7 | #ifndef MMC5983MA_H_ 8 | #define MMC5983MA_H_ 9 | 10 | #include "bsp.h" 11 | 12 | //######################################################################################################################### 13 | //Register map for MMC5983MA 14 | 15 | #define MMC5983MA_ADDRESS 0x30 16 | 17 | #define MMC5983MA_XOUT_0 0x00 18 | #define MMC5983MA_XOUT_1 0x01 19 | #define MMC5983MA_YOUT_0 0x02 20 | #define MMC5983MA_YOUT_1 0x03 21 | #define MMC5983MA_ZOUT_0 0x04 22 | #define MMC5983MA_ZOUT_1 0x05 23 | #define MMC5983MA_XYZOUT_2 0x06 24 | #define MMC5983MA_TOUT 0x07 25 | #define MMC5983MA_STATUS 0x08 26 | #define MMC5983MA_CONTROL_0 0x09 27 | #define MMC5983MA_CONTROL_1 0x0A 28 | #define MMC5983MA_CONTROL_2 0x0B 29 | #define MMC5983MA_CONTROL_3 0x0C 30 | 31 | #define MMC5983MA_INT_meas_done_en 0x04 32 | #define MMC5983MA_Auto_SR_en 0x20 33 | 34 | // Sample rates 35 | #define MODR_ONESHOT 0x00 36 | #define MODR_1Hz 0x01 37 | #define MODR_10Hz 0x02 38 | #define MODR_20Hz 0x03 39 | #define MODR_50Hz 0x04 40 | #define MODR_100Hz 0x05 41 | #define MODR_200Hz 0x06 // BW = 0x01 only 42 | #define MODR_1000Hz 0x07 // BW = 0x11 only 43 | 44 | //Bandwidths 45 | #define MBW_100Hz 0x00 // 8 ms measurement time 46 | #define MBW_200Hz 0x01 // 4 ms 47 | #define MBW_400Hz 0x02 // 2 ms 48 | #define MBW_800Hz 0x03 // 0.5 ms 49 | 50 | 51 | // Set/Reset as a function of measurements 52 | #define MSET_1 0x00 // Set/Reset each data measurement 53 | #define MSET_25 0x01 // each 25 data measurements 54 | #define MSET_75 0x02 55 | #define MSET_100 0x03 56 | #define MSET_250 0x04 57 | #define MSET_500 0x05 58 | #define MSET_1000 0x06 59 | #define MSET_2000 0x07 60 | 61 | //######################################################################################################################### 62 | 63 | error_t mmc5983ma_init(i2c_device_t device, uint8_t measurementFrequency, uint8_t bandwidth); 64 | void mmc5983ma_readData(mmc5983ma_t *mmc, uint32_t * destination); 65 | void SET(i2c_device_t device); 66 | void RESET(i2c_device_t device); 67 | void mmc5983ma_selfTest(); 68 | 69 | error_t readTemperature(i2c_device_t device, uint8_t temp); 70 | error_t readStatus(i2c_device_t device, uint8_t _status); 71 | void mmc5983ma_clearInt(); 72 | void powerDown(i2c_device_t device); 73 | void powerUp(i2c_device_t device, uint8_t MODR); 74 | void mmc5983ma_offsetBias(mmc5983ma_t *mmc, float * dest1, float * dest2); 75 | 76 | #endif /* MMC5983MA_H_ */ -------------------------------------------------------------------------------- /NEO8M/ublox_neo8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ublox_neo8.h 3 | * 4 | * Created on: Jun 20, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #ifndef UBLOX_GPS_H_ 9 | #define UBLOX_GPS_H_ 10 | 11 | #include "platform/platform.h" 12 | 13 | #define UBLOX_I2C 14 | #if defined UBLOX_I2C 15 | typedef i2c_device_t connection_t; 16 | #define gps_transmit i2c_transmit 17 | #define gps_receive i2c_receive 18 | 19 | #define UBLOX_I2C_ADDR 0x42 20 | #define UBLOX_STREAM_REG 0xFF 21 | #define UBLOX_LEN_LSB_REG 0xFE 22 | #define UBLOX_LEN_HSB_REG 0xFD 23 | 24 | #else 25 | typedef uart_connection_t connection_t; 26 | #define gps_transmit uart_transmit 27 | #define gps_receive uart_receive 28 | #endif 29 | 30 | typedef struct { 31 | connection_t conn; 32 | } ublox_gps_t; 33 | 34 | typedef struct { 35 | uint32_t time; // GPS time of week of the navigation epoch. 36 | uint16_t year; // Year 37 | uint8_t month; // Month, range 38 | uint8_t day; // Day of month 39 | uint8_t hour; // Hour of day 40 | uint8_t minute; // Minute of hour 41 | uint8_t second; // Seconds of minute 42 | uint8_t valid; // Validity flags 43 | uint32_t tAcc; // Time accuracy estimate 44 | int32_t nano; // Fraction of second 45 | uint8_t fixType; // GNSSfix Type 46 | uint8_t flags; // Fix status flags 47 | uint8_t flags2; // Additional flags 48 | uint8_t sats; // Number of satellites used in Nav Solution 49 | int32_t lng; // Longitude 50 | int32_t lat; // Latitude 51 | int32_t height; // Height above ellipsoid 52 | int32_t hMSL; // Height above mean sea level 53 | uint32_t hAcc; // Horizontal accuracy estimate 54 | uint32_t vAcc; // Vertical accuracy estimate 55 | int32_t velN; // NED north velocity 56 | int32_t velE; // NED east velocity 57 | int32_t velD; // NED down velocity 58 | int32_t gSpeed; // Ground Speed (2-D) 59 | int32_t headMot; // Heading of motion (2-D) 60 | uint32_t sAcc; // Speed accuracy estimate 61 | uint32_t headAcc; // Heading accuracy estimate 62 | uint16_t pDOP; // Position DOP 63 | uint16_t flags3; // Additional flags 64 | uint8_t _reserved_; 65 | int32_t headVeh; // Heading of vehicle (2-D), this is only valid when headVehValid is set, otherwise the output is set to the heading of motion 66 | int16_t magDev; // Magnetic declination. Only supported in ADR 4.10 and later. 67 | uint16_t magAcc; // Magnetic declination accuracy. Only supported in ADR 4.10 and later. 68 | } __attribute__((packed)) ublox_pvt_t; 69 | 70 | typedef union { 71 | ublox_pvt_t values; 72 | uint8_t raw[sizeof(ublox_pvt_t)]; 73 | } ubx_pvt_parser_t; 74 | 75 | error_t ublox_get(ublox_gps_t gps, ublox_pvt_t *pvt); 76 | error_t ublox_init(ublox_gps_t gps); 77 | #endif /* UBLOX_GPS_H_ */ 78 | -------------------------------------------------------------------------------- /INA219/README.md: -------------------------------------------------------------------------------- 1 | # Driver_INA219_MVD 2 | Repositório para confecção do driver mínimo para funcionamento do sensor INA219 a ser utilizada nos projetos de Baixo Nível do zenith 3 | 4 | ## Função 5 | Sensor com função de medição da tensão e corrente em seus terminais. 6 | ## Exemplo 7 | 8 | SETUP: 9 | ~~~C 10 | //configurações a serem utilizadas no sensor 11 | INA219_config_t config = { 12 | .BusVoltageRange = INA_BRANGE_16, 13 | .ShuntVoltageRange = INA_SRANGE_40, 14 | .BusADCResolution = INA_BADC1_12bit, 15 | .ShuntADCResolution = INA_SADC1_12bit, 16 | .OperationMode = INA_CONTINUOUS 17 | } 18 | 19 | //configuração do i2c 20 | //configurar o endereço de acordo com o utilizado no hardware 21 | i2c_device_t dev = { 22 | .i2c = &hi2c1, 23 | .adress = 0x40 24 | } 25 | 26 | INA219_t ina = { 27 | .config = config, 28 | .device = dev 29 | } 30 | 31 | INA219_reset(ina); 32 | INA219_config(ina); 33 | 34 | INA219_values_t medida; 35 | 36 | ~~~ 37 | 38 | MEDIDA: 39 | 40 | ~~~C 41 | 42 | INA219_measure(ina, &medida); 43 | printf("Tensão: %f\r\n", medida.Bus_Voltage); 44 | printf("Corrente: %f\r\n", medida.Shunt_Current) 45 | 46 | ~~~ 47 | 48 | ## Funcionamento 49 | Este driver foi feito utilizando somente as funções mínimas necessárias para ser testado em tempo hábil, por este motivo não utiliza todas as funções disponíveis do sensor configuradas, utilizando assim as configurações default onde possível; para as configurações estão configuradas as configurações de faixa de valores de tensão no bus, para 16 V, e faixa de valores para o shunt de 40 mV, além disso a resolução do shunt e do bus como 12 bits, com tempo de 532 us. 50 | 51 | ## Funções 52 | As funções estão organizadas como mostradas no exemplo, com a função de reset, para reiniciar o sensor e suas configurações, config, para realizar as configurações estabelecidas na struct de configurações, no .h do driver há outras opções de valores que podem ser utilizados nas configurações, caso haja a necessidade de modificar algum modo de operação, como o modo de resolução dos ADCs, e as faixas de valores de operação, mais detalhes no .h do driver, as conversões das leituras feitas para o valor em tensão em corrente seguem como mencionados no [datasheet](https://www.ti.com/lit/ds/symlink/ina219.pdf), na seção 8.6.3. 53 | 54 | ### Operações 55 | Toda vez que utilizada a função `INA219_measure` são feitas as leituras nos bancos de registradores de cada seção (Shunt e Bus) e convertidos para valores de tensão e corrente, cada leitura ocorre, segundo o datasheet, a cada 532 us, como são feitas duas leituras (Shunt e Bus), é necessário o dobro do tempo para a atualização dos dados de tensão e corrente medidas. 56 | 57 | ## Notas 58 | Segundo o datasheet, para evitar conflitos com outros componentes i2c, há a possibilidade de mudar o endereço i2c do INA219, como segue na seção 8.5.5.1 tabela 1, com 16 endereços possíveis com a combinação dos pinos A0 e A1. -------------------------------------------------------------------------------- /HDC1080/hdc1080.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hdc1080.c 3 | * 4 | * Created on: Jun 20, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #include "Application/hdc1080.h" 9 | 10 | static const float div = 65536.0f; 11 | 12 | 13 | error_t hdc1080_init(i2c_device_t device) { 14 | delay_ms(1); 15 | result16_t id = i2c_read16(device, HDC1080_DEVID); 16 | if (id.hasError) { 17 | return id.hasError; 18 | } 19 | if (id.value != 0x1050) { 20 | return 1; // sensor not correct 21 | } 22 | delay_ms(14); // valor maximo necessario 23 | 24 | uint16_t config = 0x0000; 25 | config |= HDC1080_MESURE_SINGLE; 26 | config |= HDC1080_TEMP_14; 27 | config |= HDC1080_HUMD_14; 28 | 29 | return i2c_write16(device, HDC1080_CONFIG, config); 30 | } 31 | 32 | float hdc_1080_convert_temperature(uint16_t raw) { 33 | return raw / div * 165.0f - 40.0f; 34 | } 35 | 36 | float hdc_1080_convert_humidity(uint16_t raw) { 37 | return raw / div * 100.0f; 38 | } 39 | 40 | /*** 41 | * Depreciado, resultados inconsistentes na humidade 42 | */ 43 | static void hdc_1080_get_both_raw(i2c_device_t device, uint16_t *h_raw_out, 44 | uint16_t *t_raw_out) { 45 | 46 | // trigger mesurement 47 | uint8_t useless = 0x00; 48 | buffer_view_t view = { .data = &useless, .size = 1 }; 49 | i2c_transmit(device, view); 50 | 51 | delay_ms(15); 52 | 53 | uint8_t data[4] = { 0 }; 54 | buffer_view_t raw_view = {.data = data, .size = sizeof(data)}; 55 | i2c_receive(device, raw_view); 56 | 57 | *t_raw_out = data[0] << 8 | data[1]; 58 | *h_raw_out = data[2] << 8 | data[3]; 59 | 60 | } 61 | 62 | result16_t hdc1080_get_temperature_raw(i2c_device_t device){ 63 | result16_t out = {.value = 0, .hasError=1}; 64 | uint8_t address = 0x00; 65 | buffer_view_t view = { .data = &address, .size = 1 }; 66 | i2c_transmit(device, view); 67 | 68 | delay_ms(7); 69 | 70 | uint8_t data[4] = { 0 }; 71 | buffer_view_t raw_view = {.data = data, .size = sizeof(data)}; 72 | out.hasError = i2c_receive(device, raw_view); 73 | out.value = data[0] << 8 | data[1]; 74 | return out; 75 | } 76 | 77 | result16_t hdc1080_get_humidity_raw(i2c_device_t device){ 78 | result16_t out = {.value = 0, .hasError=1}; 79 | uint8_t address = 0x01; 80 | buffer_view_t view = { .data = &address, .size = 1 }; 81 | i2c_transmit(device, view); 82 | 83 | delay_ms(7); 84 | 85 | uint8_t data[4] = { 0 }; 86 | buffer_view_t raw_view = {.data = data, .size = sizeof(data)}; 87 | out.hasError = i2c_receive(device, raw_view); 88 | out.value = data[0] << 8 | data[1]; 89 | return out; 90 | } 91 | 92 | hdc1080_mesurement_t hdc1080_mesure(i2c_device_t device) { 93 | result16_t t = hdc1080_get_temperature_raw(device); 94 | result16_t h = hdc1080_get_humidity_raw(device); 95 | 96 | float humidity = hdc_1080_convert_humidity(h.value); 97 | float temperature = hdc_1080_convert_temperature(t.value); 98 | hdc1080_mesurement_t out = { .humidity = humidity, .temperature = 99 | temperature }; 100 | return out; 101 | } 102 | -------------------------------------------------------------------------------- /INA219/ina219.c: -------------------------------------------------------------------------------- 1 | /* 2 | * INA219_MVD.c 3 | * 4 | * Created on: Sep 8, 2021 5 | * Author: Murilo Henrique Pasini Trevisan 6 | */ 7 | 8 | #include "ina219.h" 9 | 10 | static result_uint16_t read(INA219_t ina, uint8_t address) { 11 | buffer_view_t addr = {.data=&address, .size=1}; 12 | error_t e = i2c_transmit(ina.device, addr); 13 | uint8_t value_buf[2] = {0}; 14 | buffer_view_t response = {.data=value_buf, .size=sizeof(value_buf)}; 15 | e |= i2c_receive(ina.device, response); 16 | uint16_t value = response.data[1] << 8 | response.data[0]; 17 | result_uint16_t out = {.value=value, .hasError=e}; 18 | return out; 19 | } 20 | 21 | static error_t write(INA219_t ina, uint8_t address, uint16_t value) { 22 | uint8_t transaction_buf[3] = {address, value >> 8, value}; 23 | buffer_view_t transaction = {.data=transaction_buf, .size=sizeof(transaction_buf)}; 24 | return i2c_transmit(ina.device, transaction); 25 | } 26 | 27 | error_t INA219_reset(INA219_t ina) { 28 | 29 | uint16_t config_rst = 0x0000; 30 | config_rst |= INA_RST; 31 | 32 | return write(ina, INA_CONFIG_ADR, config_rst); 33 | } 34 | 35 | error_t INA219_config(INA219_t ina) { 36 | 37 | // result_uint16_t config_raw = i2c_read16(ina.device, INA_CONFIG_ADR); 38 | // config_register = config_raw 39 | 40 | uint16_t config_register = 0x0000; 41 | 42 | config_register |= ina.config.BusVoltageRange; 43 | config_register |= ina.config.ShuntVoltageRange; 44 | config_register |= ina.config.BusADCResolution; 45 | config_register |= ina.config.ShuntADCResolution; 46 | config_register |= ina.config.OperationMode; 47 | 48 | return write(ina, INA_CONFIG_ADR, config_register); 49 | } 50 | 51 | error_t INA219_measure(INA219_t ina, INA219_values_t *medida) { 52 | 53 | //Shunt Voltage 54 | //Leitura do valor i2c no registrador do shunt 55 | result_uint16_t raw_s = read(ina, INA_SHUNT_VOLT); 56 | 57 | if (raw_s.hasError){ 58 | return raw_s.hasError; 59 | } 60 | 61 | //Complemento de 2 do valor lido do shunt 62 | int16_t val_s = raw_s.value; 63 | 64 | //Conversão para tensão 65 | float volt_s = ((float) val_s) * INA_SHUNT_MULTIPLY; 66 | 67 | 68 | // 69 | medida -> Shunt_Voltage = volt_s; 70 | 71 | 72 | //Bus Voltage 73 | //Leitura do valor i2c do registrador Bus 74 | result_uint16_t raw_b = read(ina, INA_BUS_VOLT); 75 | 76 | if (raw_b.hasError) { 77 | return raw_b.hasError; 78 | } 79 | 80 | //complemento de 2 do valor lido do Bus 81 | int16_t val_b = raw_b.value; 82 | 83 | //shift dos bits em 3 bits 84 | int16_t val_shift = val_b >> 3; 85 | 86 | //Conversão para tensão 87 | float volt_b = ((float) val_shift) * INA_BUS_MULTIPLY; 88 | 89 | // 90 | medida -> Bus_Voltage = volt_b; 91 | 92 | 93 | //Current 94 | //Calculo a partir da tensão no Shunt e do valor do resistor do datasheet 95 | float current_s = (volt_s)/(INA_RESISTOR); 96 | 97 | // 98 | medida -> Shunt_Current = ( current_s ); 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /MAX31856/max31856.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MAX31856.h 3 | * 4 | * Author: Thiago Henrique Vicentini (Anakin) 5 | */ 6 | 7 | #ifndef INC_MAX31856_H_ 8 | #define INC_MAX31856_H_ 9 | 10 | #include "..\bsp.h" 11 | 12 | //######################################################################################################################### 13 | #define MAX31856_CR0_READ 0x00 14 | #define MAX31856_CR0_WRITE 0x80 15 | #define MAX31856_CR1_READ 0x01 16 | #define MAX31856_CR1_WRITE 0x81 17 | #define MAX31856_MASK_READ 0x02 18 | #define MAX31856_MASK_WRITE 0x82 19 | #define MAX31856_CJHF_READ 0x03 20 | #define MAX31856_CJHF_WRITE 0x83 21 | #define MAX31856_CJLF_READ 0x04 22 | #define MAX31856_CJLF_WRITE 0x84 23 | #define MAX31856_LTHFTH_READ 0x05 24 | #define MAX31856_LTHFTH_WRITE 0x85 25 | #define MAX31856_LTHFTL_READ 0x06 26 | #define MAX31856_LTHFTL_WRITE 0x86 27 | #define MAX31856_LTLFTH_READ 0x07 28 | #define MAX31856_LTLFTH_WRITE 0x87 29 | #define MAX31856_LTLFTL_READ 0x08 30 | #define MAX31856_LTLFTL_WRITE 0x88 31 | #define MAX31856_CJTO_READ 0x09 32 | #define MAX31856_CJTO_WRITE 0x89 33 | #define MAX31856_CJTH_READ 0x0A 34 | #define MAX31856_CJTH_WRITE 0x8A 35 | #define MAX31856_CJTL_READ 0x0B 36 | #define MAX31856_CJTL_WRITE 0x8B 37 | 38 | #define MAX31856_LTCBH_READ 0x0C 39 | #define MAX31856_LTCBM_READ 0x0D 40 | #define MAX31856_LTCBL_READ 0x0E 41 | 42 | #define MAX31856_CONFIG_CMODE 0x80 43 | 44 | #define MAX31856_Temperature_Resolution 7.8125e-3 45 | //######################################################################################################################### 46 | 47 | typedef struct { 48 | spi_device_t* device; 49 | } max31856_t; 50 | 51 | typedef enum { 52 | MAX31856_1_SAMPLE = 0x00, 53 | MAX31856_2_SAMPLES = 0x01, 54 | MAX31856_4_SAMPLES = 0x02, 55 | MAX31856_8_SAMPLES = 0x03, 56 | MAX31856_16_SAMPLES = 0x04, 57 | } max31856_samples_t; 58 | 59 | typedef enum { 60 | MAX31856_TCTYPE_B = 0x00, 61 | MAX31856_TCTYPE_E = 0x01, 62 | MAX31856_TCTYPE_J = 0x02, 63 | MAX31856_TCTYPE_K = 0x03, 64 | MAX31856_TCTYPE_N = 0x04, 65 | MAX31856_TCTYPE_R = 0x05, 66 | MAX31856_TCTYPE_S = 0x06, 67 | MAX31856_TCTYPE_T = 0x07, 68 | MAX31856_VMODE_G8 = 0x08, 69 | MAX31856_VMODE_G32 = 0x0C, 70 | } max31856_thermocoupletype_t; 71 | 72 | error_t max31856_setConversionMode(max31856_t *max, uint8_t enable); 73 | error_t max31856_setAveragingMode(max31856_t *max, max31856_samples_t samples); 74 | error_t max31856_setThermocoupleType(max31856_t *max, max31856_thermocoupletype_t tc_type); 75 | 76 | error_t max31856_init(max31856_t *max, SPI_HandleTypeDef *spi, gpio_pin_t gpio); 77 | error_t max31856_readThermocoupleTemp(max31856_t *max, float *temp); 78 | 79 | #endif /* INC_MAX31856_H_ */ -------------------------------------------------------------------------------- /NEO8M/README.md: -------------------------------------------------------------------------------- 1 | # NEO-8M 2 | 3 | ## Propósito 4 | GPS capaz de funcionar na altitude das sondas 5 | 6 | ## Exemplo 7 | Setup 8 | ```c 9 | // uart_connection_t conn = { .uart = &huart3 }; 10 | i2c_device_t dev = { .i2c = &hi2c1, .address = UBLOX_I2C_ADDR }; 11 | ublox_gps_t gps = { .conn = dev }; 12 | ublox_pvt_t pvt = {0}; 13 | ublox_init(gps) 14 | ``` 15 | Leitura de PVT (_Position Velocity Time_) 16 | ```c 17 | error_t e = ublox_get(gps, &pvt); 18 | if (e) { 19 | continue; 20 | } 21 | printf("Hoje é dia: %.2u/%.2u/%.2u\r\n", pvt.day, pvt.month, pvt.year); 22 | float latitude = pvt.lat / 1e7; 23 | float longitude = pvt.lng / 1e7; 24 | printf("Sua posição é: (%f, %f)\r\n\r\n", latitude, longitude); 25 | ``` 26 | 27 | 28 | 29 | Lembrando que o projeto deve ser configurado para mandar `float` com printf 30 | e as syscalls também devem estar configuradas 31 | 32 | ## Documentação 33 | ### Apresentações e Justificativas 34 | Por padrão o GPS, assim que energizado manda mensagens NMEA via UART 35 | essas mensagens contem as informações que queremos, mas vem na forma de 36 | uma string de tamanho variado, o que implica uma carga e complexidade 37 | desnecessárias no microcontrolador. Para isso a u-blox (fabricante) disponibiliza 38 | o protocolo alternativo (e proprietário) UBX. Pelo UBX as mensagens vem como 39 | sequencia de bytes contendo os dados em sequência então é mais eficiente que o NMEA. 40 | 41 | Outra configuração que é opcional mas importante é a forma que fazemos uma medida 42 | ao invés de deixar o GPS decidir quando pegar um dado (por padrão 1Hz), configuramos 43 | para que ele nos de os dados em resposta a uma mensagem. 44 | 45 | Por ultimo, mas talvez mais interessante, temos a configuração do modelo 46 | físico. É uma configuração que otimiza o GPS para diversos casos de uso. Um 47 | desses casos é referenciado como High Altitude < 1G. Que significa um "veiculo" 48 | navegando em alta altitude sem acelerações bruscas, somente com a atuação da 49 | gravidade. O que é perfeito para sondas atmosféricas. 50 | 51 | ### Funcionamento da Biblioteca 52 | 53 | **Modifique o `#define UBLOX_I2C` para alterar a interface, 54 | remova para UART ou matenha para I2C** 55 | 56 | Toda biblioteca funciona ao redor das mensagens do protocolo UBX 57 | as funções são _wrappers_ para mensagens de interesse. 58 | 59 | Se começarmos pela `init` vemos a sequencia de configuração 60 | ```c 61 | disableNmea(gps); 62 | set_protocol(gps); 63 | set_model_mode(gps); 64 | ``` 65 | - Paramos o envio padrão de 1Hz 66 | - Desabilitamos o protocolo NMEA 67 | - Ativamos o protocolo UBX 68 | - Configuramos o Modelo de Sonda 69 | 70 | Cada uma dessas funções representa o envio de uma ou mais mensagens 71 | via UART conforme o padrão de mensagem do UBX 72 | 73 | ```c 74 | { 0xB5, 0x62, // sync (sempre presente) 75 | 0x01, 0x07, // class and id (identifica mensagem) 76 | 0x00, 0x00, // size little endian (tamanho do payload) 77 | 0xDE, 0xAD // payload (payload) 78 | 0x00, 0x00 // checksum (validador) 79 | }; 80 | ``` 81 | 82 | Para requisitar uma nova medição, é só usar a função `ublox_get` como no exemplo 83 | 84 | ## Notas 85 | Tive alguns problemas de sincronia com os dados chegando. Na maioria das vezes 86 | foi sem problemas, mas algumas vezes o inicio do pacote desalinha da struct 87 | e nesses casos acabei desconsiderando os pacotes. Seria bom no futuro usar um 88 | jeito mais robusto. 89 | -------------------------------------------------------------------------------- /GUVC-S10GD/guvcs10gd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * guvcs10gd.c 3 | * 4 | * Created on: Aug 18, 2021 5 | * Author: Carlos Craveiro 6 | */ 7 | 8 | #include "guvcs10gd.h" 9 | #include 10 | 11 | error_t guvcs10gd_init(guvcs10gd_t guvc) { 12 | adc_init(guvc.adc); 13 | //WHAT ADC_INIT DOES IF AN ERROR OCCURS? 14 | return 0; 15 | } 16 | 17 | 18 | result16_t guvcs10gd_get_raw(guvcs10gd_t guvc) { 19 | return adc_read(guvc.adc); 20 | } 21 | 22 | 23 | uint16_t guvcs10gd_conv_tension_to_current(guvcs10gd_t guvc, result16_t raw_data) { 24 | uint16_t output_tension = GUVCS10GD_ADC_CONVERTER(raw_data.value); 25 | 26 | uint16_t maximum_tension_value = guvc.converter.tension.offset + guvc.converter.tension.amplitude; 27 | uint16_t minimum_tension_value = guvc.converter.tension.offset - guvc.converter.tension.amplitude; 28 | 29 | 30 | if(output_tension > maximum_tension_value) { 31 | output_tension = maximum_tension_value; 32 | } 33 | if(output_tension < minimum_value) { 34 | output_tension = minimum_tension_value; 35 | } 36 | 37 | uint16_t converter_constant = guvc.converter.current.amplitude / guvc.converter.tension.amplitude; 38 | int phase_param = (guvc.converter.phase_difference < 90 || guvc.converter.phase_difference >= 270)? 1 : -1; 39 | uint16_t output_current = converter_constant * (output_tension - guvc.converter.tension.offset) * phase_param + guvc.converter.current.offset; 40 | 41 | return output_current; 42 | } 43 | 44 | 45 | float guvcs10gd_get_uvcpower(guvcs10gd_t guvc) { 46 | result16_t raw = guvcs10gd_get_raw(guvc); 47 | 48 | if(raw.hasError) { 49 | return (float) GUVCS10GD_READ_ERROR; 50 | } 51 | 52 | uint16_t sensors_current = guvcs10gd_conv_tension_to_current(guvc, raw); 53 | float uvc_radiation = (sensors_current / 1000.f) * GUVCS10GD_CONV_A; 54 | 55 | return uvc_radiation; 56 | 57 | } 58 | 59 | 60 | double guvcs10gd_accurate_conv_degrees_to_radians(uint16_t deg) { 61 | uint64_t pi = 3141592654; 62 | 63 | return ((double)(2 * pi * deg ) / (360 * 1000000000)); 64 | } 65 | 66 | 67 | double guvcs10gd_accurate_conv_tension_to_current(guvcs10gd_t guvc, result16_t raw_data) { 68 | uint16_t output_tension = GUVCS10GD_ADC_CONVERTER(raw_data.value); 69 | 70 | uint16_t maximum_tension_value = guvc.converter.tension.offset + guvc.converter.tension.amplitude; 71 | uint16_t minimum_tension_value = guvc.converter.tension.offset - guvc.converter.tension.amplitude; 72 | 73 | 74 | if(output_tension > maximum_tension_value) { 75 | output_tension = maximum_tension_value; 76 | } 77 | if(output_tension < minimum_value) { 78 | output_tension = minimum_tension_value; 79 | } 80 | 81 | double sine_x = (double)(output_tension - guvc.converter.tension.offset) / guvc.converter.tension.amplitude; 82 | double cosine_x = sqrt(1 - (double)((output_tension - guvc.converter.tension.offset) / guvc.converter.tension.amplitude) * ((output_tension - guvc.converter.tension.offset) / guvc.converter.tension.amplitude)); 83 | 84 | double output_current = guvc.converter.tension.amplitude * (sine_x * cos(guvc.converter.phase_difference) + cossine_x * sin(guvc.converter.phase_difference)) + guvc.converter.current.offset; 85 | return output_current; 86 | } 87 | 88 | 89 | double guvcs10gd_accurate_get_uvcpower(guvcs10gd_t guvc) { 90 | result16_t raw = guvcs10gd_get_raw(guvc); 91 | 92 | if(raw.hasError) { 93 | return (float) GUVCS10GD_READ_ERROR; 94 | } 95 | 96 | double sensors_current = guvcs10gd_accurate_conv_tension_to_current(guvc, raw); 97 | double uvc_radiation = (sensors_current / 1000) * GUVCS10GD_CONV_A; 98 | 99 | return uvc_radiation; 100 | 101 | } 102 | -------------------------------------------------------------------------------- /MPU6050/README.md: -------------------------------------------------------------------------------- 1 | # Driver_MPU6050_MVD 2 | Repositório para confecção do driver de funções mínimas para funcionamento da IMU MPU6050 a ser utilizada pelos projetos de baixo nível do Zenith. As funções utilizadas de configuração foram a taxa de atualização dos registradores internos, frequência de corte do filtro passa baixa das leituras, range máximo do giroscópio, e do acelerômetro. 3 | 4 | ## Exemplo 5 | 6 | ### SETUP: 7 | 8 | * sem leitura de sinais externos 9 | * filtro digital passa baixa de 260 Hz 10 | * atualização dos dados em 10 Hz 11 | * giroscópio com range máximo de 250°/s 12 | * acelerômetro com range máximo de 2g 13 | 14 | ~~~C 15 | //configurações a serem utilizadas no sensor 16 | MPU6050_config_t config = { 17 | .FSyncEnable = MPU_FSYNC_DISABLE, 18 | .LowPassFilter = MPU_DLPF_CG0, 19 | .Sample = MPU_SMPRT_10, 20 | .GyroRange = MPU_GFS_SEL_250, 21 | .AccelRange = MPU_AFS_SEL_2 22 | } 23 | 24 | //configuração do i2c 25 | //configurar o endereço de acordo com o utilizado no hardware 0x68 ou 0x69 26 | i2c_device_t dev = { 27 | .i2c = &hi2c1, 28 | .adress = 0x68 29 | } 30 | 31 | MPU6050_t mpu = { 32 | .config = config, 33 | .device = dev 34 | } 35 | 36 | MPU6050_init(mpu); 37 | 38 | MPU6050_values_t medida; 39 | ~~~ 40 | 41 | ### MEDIDA 42 | 43 | ~~~C 44 | MPU6050_measure(mpu, &medida); 45 | 46 | printf("Acelerômetro x: %f\r\n", medida.AccelX); 47 | printf("Acelerômetro y: %f\r\n", medida.AccelY); 48 | printf("Acelerômetro z: %f\r\n", medida.AccelZ); 49 | 50 | printf("Giroscópio x: %f\r\n", medida.GyroX); 51 | printf("Giroscópio y: %f\r\n", medida.GyroY); 52 | printf("Giroscópio z: %f\r\n", medida.GyroZ); 53 | 54 | printf("Temperatura ambiente: %f\r\n", medida.Temp); 55 | ~~~ 56 | 57 | ## Notas 58 | 59 | * O range do giroscópio e do acelerômetro alteram a precisão da medida, caso seja necessário aumentar o range há queda da precisão da leitura, não há indicação no datasheet de quanto é a alteração da precisão com o aumento do range, investigar caso seja necessário maior precisão ou maior range de leitura, há informações de precisão que devem ser estudadas na página 12 do datasheet. 60 | * O aumento da taxa de leitura ocasiona em maior consumo de energia no componente, no tópico 6 do datasheet há mais informações de consumo, porém, a variação de consumo do modo com menor taxa de atualização varia entre 10 uA até 150 uA. 61 | * De acordo com o datasheet, o valor de reset de todos os registradores é 0x00 com exceção dos registradores de PWR_MGMT e WHO_AM_I. 62 | * A partir do regustrador de PWR_MGMT é possível colocar a MPU em modo SLEEP e resetar a MPU, não está implementado neste driver, porém pode ser adicionado caso necessite dessas funções, além disso pode desabilitar o sensor de temperatura para redução de consumo caso já exista outra medição de temperatura externa. 63 | * o datasheet encontra-se em [Datasheet geral](https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf) e [mapa de registradores](https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf). 64 | 65 | ## Informações importantes 66 | 67 | Não é dito no datasheet, porém, a medição depende do fundo de escala selecionado, com isto, deve-se atentar a mudança no momento de leitura do valor do registrador de medidas, por este motivo, há uma constante dividindo os valores obtidos na measure, esta constante já está calculada para qualquer valor de escala escolhido na configuração do C.I., não exigindo alteração para uso, porém, caso seja utilizado este driver como referência para outro driver, atentar-se a esta fórmula e adaptar para a sua necessidade, os valores das constantes estão referenciados no header do driver, e servem para calcular o número de bits a serem utilizados para a escala, desta forma, a leitura se aproxima de um conversor A/D, e quanto maior a escala, maior o número de bits utilizado, por este motivo, deve-se dividir os valores obtidos. Em caso de dúvidas contactar os autores do driver. -------------------------------------------------------------------------------- /MAX31856/max31856.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MAX31856.c 3 | * 4 | * Author: Thiago Henrique Vicentini (Anakin) 5 | */ 6 | 7 | #include "MAX31856.h" 8 | 9 | error_t max31856_setConversionMode(max31856_t *max, uint8_t enable){ 10 | result8_t t = spi_read8(max->device, MAX31856_CR0_READ); 11 | if(t.hasError){ 12 | return t.hasError; 13 | } 14 | 15 | if(enable) 16 | t.value |= MAX31856_CONFIG_MODEAUTO; 17 | else 18 | t.value &= ~MAX31856_CONFIG_MODEAUTO; 19 | 20 | error_t error = spi_write8(max->device, MAX31856_CR0_READ, t.value); 21 | return error; 22 | } 23 | 24 | // The Thermocouple Voltage Conversion Averaging Mode settings should not be changed while conversions are taking place. 25 | error_t max31856_setAveragingMode(max31856_t *max, max31856_samples_t samples){ 26 | result8_t t = spi_read8(max->device, MAX31856_CR1_READ); 27 | if(t.hasError){ 28 | return t.hasError; 29 | } 30 | 31 | t.value |= (samples << 4); // bit 6:4 32 | 33 | error_t error = spi_write8(max->device, MAX31856_CR1_READ, t.value); 34 | return error; 35 | } 36 | 37 | error_t max31856_setThermocoupleType(max31856_t *max, max31856_thermocoupletype_t tc_type){ 38 | result8_t t = spi_read8(max->device, MAX31856_CR1_READ); 39 | if(t.hasError){ 40 | return t.hasError; 41 | } 42 | 43 | t.value |= tc_type// bit 3:0; 44 | 45 | error_t error = spi_write8(max->device, MAX31856_CR1_READ, t.value); 46 | return error; 47 | } 48 | 49 | error_t max31856_init(max31856_t *max, SPI_HandleTypeDef *spi, gpio_pin_t gpio){ 50 | max->device->spi = spi; 51 | max->device->pin = gpio; 52 | gpio_high(device->pin); 53 | 54 | error_t error; 55 | error = max31856_setConversionMode(max, 1); // NOMINAL 56 | if(error) 57 | return error; 58 | 59 | error = max31856_setAveragingMode(max, MAX31856_4_SAMPLES); 60 | if(error) 61 | return error; 62 | 63 | error = max31856_setThermocoupleType(max, MAX31856_TCTYPE_K); 64 | if(error) 65 | return error; 66 | 67 | result8_t fault_register = spi_read8(max->device, MAX31856_CR1_READ); 68 | if(fault_register.hasError){ 69 | return fault_register.hasError; 70 | } 71 | if(fault_register.value != 0x00){ 72 | if( (fault_register.value)&(0x80) == 0x80){ 73 | //The Cold-Junction temperature is outside of the normal operating range. 74 | }if( (fault_register.value)&(0x40) == 0x40){ 75 | //The Thermocouple Hot Junction temperature is outside of the normal operating range. 76 | }if( (fault_register.value)&(0x20) == 0x20){ 77 | //The Cold-Junction temperature is higher than the cold-junction temperature high threshold. The FAULT output is asserted unless masked. 78 | }if( (fault_register.value)&(0x10) == 0x10){ 79 | //The Cold-Junction temperature is lower than the cold-junction temperature low threshold. The FAULT output is asserted unless masked. 80 | }if( (fault_register.value)&(0x08) == 0x08){ 81 | //The Thermocouple Temperature is higher than the thermocouple temperature high threshold. The FAULT output is asserted unless masked. 82 | }if( (fault_register.value)&(0x04) == 0x04{ 83 | //Thermocouple temperature is lower than the thermocouple temperature low threshold. The FAULT output is asserted unless masked. 84 | }if( (fault_register.value)&(0x02) == 0x02){ 85 | //The input voltage is negative or greater than VDD. The FAULT output is asserted unless masked. 86 | }if( (fault_register.value)&(0x01) == 0x01){ 87 | //An open circuit such as broken thermocouple wires has been detected. The FAULT output is asserted unless masked. 88 | } 89 | return 1; 90 | } 91 | 92 | return 0; 93 | } 94 | 95 | error_t max31856_readThermocoupleTemp(max31856_t *max, float *temp){ 96 | 97 | uint8_t buffer[3] = { 0 }; 98 | buffer_view_t view = { .data=buffer, .size=sizeof(buffer) }; 99 | error_t error = spi_readN(max->device, MAX31856_LTCBH_READ, view); 100 | if(error){ 101 | return error; 102 | } 103 | 104 | uint32_t temp24 = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; 105 | 106 | if(temp24 & 0x800000) { 107 | temp24 |= 0xFF000000; // fix sign 108 | } 109 | 110 | (*temp) = (temp24 >> 5)*MAX31856_Temperature_Resolution; 111 | 112 | return 0; 113 | } -------------------------------------------------------------------------------- /INA3221/INA3221.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gusta on 05/09/2021. 3 | // 4 | 5 | #include "INA3221.h" 6 | 7 | static result_uint16_t read(ina3221_t ina, uint8_t addr) { 8 | uint8_t tx = addr; 9 | buffer_view_t tx_v = {.size = sizeof(tx), .data = &tx}; 10 | error_t e = i2c_transmit(ina.device, tx_v); 11 | 12 | uint8_t rx[2] = {0}; 13 | buffer_view_t rx_v = {.size = sizeof(rx), .data = rx}; 14 | e |= i2c_receive(ina.device, rx_v); 15 | 16 | uint16_t value = (rx[1] << 8) | (rx[0]); 17 | result_uint16_t res = {.hasError = e, .value = value}; 18 | return res; 19 | } 20 | 21 | static error_t write(ina3221_t ina, uint8_t addr, uint16_t value) { 22 | uint8_t tx[] = {addr, value << 8, value}; 23 | buffer_view_t tx_v = {.size = sizeof(tx), .data = tx}; 24 | return i2c_transmit(ina.device, tx_v); 25 | } 26 | 27 | static int16_t to_value(uint16_t raw_value) { 28 | 29 | raw_value >>= 3; 30 | if (raw_value & (1 << (15 - 3))) { 31 | raw_value |= 0xE000; 32 | } 33 | return (int16_t)raw_value; 34 | } 35 | 36 | error_t ina3221_init(ina3221_t ina) { 37 | if (!ina3221_alive(ina)) { 38 | return 1; 39 | } 40 | 41 | uint8_t address_config = CONFIG_ADR; 42 | uint16_t bit_config = 0x7127 | RESET_CONFIG; 43 | 44 | result_uint16_t id = read(ina, CONFIG_ADR); 45 | 46 | delay_ms(1); 47 | 48 | if (id.hasError) { 49 | return id.hasError; 50 | } 51 | 52 | delay_ms(10); 53 | 54 | return 0; 55 | return write(ina, address_config, bit_config); 56 | } 57 | 58 | error_t ina3221_config(ina3221_t ina) { 59 | 60 | uint16_t config_register = 0; 61 | uint8_t address_config = CONFIG_ADR; 62 | 63 | config_register |= (ina.config.ch1 << BIT_CH1); 64 | config_register |= (ina.config.ch2 << BIT_CH2); 65 | config_register |= (ina.config.ch3 << BIT_CH3); 66 | config_register |= (ina.config.avg_mode << BIT_AVG); 67 | config_register |= (ina.config.v_bus_ct << BIT_BUS_CT); 68 | config_register |= (ina.config.v_sh_ct << BIT_SH_CT); 69 | config_register |= (ina.config.op_mode << BIT_MODE); 70 | 71 | return 0; 72 | return write(ina, address_config, config_register); 73 | } 74 | 75 | error_t ina3221_mensurement(ina3221_t ina, ina3221_values_t *values) { 76 | float final_value; 77 | 78 | // Shunt Voltage values 79 | 80 | result_uint16_t raw = read(ina, CH1_SHUNT_V); 81 | if (raw.hasError) { 82 | return raw.hasError; 83 | } 84 | 85 | final_value = T_SH_CONV * to_value(raw.value); 86 | 87 | values->ch1_sh_v = final_value; 88 | 89 | raw = read(ina, CH2_SHUNT_V); 90 | if (raw.hasError) { 91 | return raw.hasError; 92 | } 93 | 94 | final_value = T_SH_CONV * to_value(raw.value); 95 | 96 | values->ch2_sh_v = final_value; 97 | 98 | raw = read(ina, CH3_SHUNT_V); 99 | if (raw.hasError) { 100 | return raw.hasError; 101 | } 102 | 103 | final_value = T_SH_CONV * to_value(raw.value); 104 | 105 | values->ch3_sh_v = final_value; 106 | 107 | // Bus Voltage Values 108 | 109 | raw = read(ina, CH1_BUS_V); 110 | if (raw.hasError) { 111 | return raw.hasError; 112 | } 113 | final_value = T_BUS_CONV * to_value(raw.value); 114 | 115 | values->ch1_bus_v = final_value; 116 | 117 | raw = read(ina, CH2_BUS_V); 118 | if (raw.hasError) { 119 | return raw.hasError; 120 | } 121 | final_value = T_BUS_CONV * to_value(raw.value); 122 | 123 | values->ch2_bus_v = final_value; 124 | 125 | raw = read(ina, CH3_BUS_V); 126 | if (raw.hasError) { 127 | return raw.hasError; 128 | } 129 | final_value = T_BUS_CONV * to_value(raw.value); 130 | 131 | values->ch3_bus_v = final_value; 132 | 133 | // Converted Current values U(shunt) = R * i 134 | 135 | float CH1_current = (values->ch1_sh_v) / (ina.config.ch1_resistor); 136 | float CH2_current = (values->ch2_sh_v) / (ina.config.ch2_resistor); 137 | float CH3_current = (values->ch3_sh_v) / (ina.config.ch3_resistor); 138 | 139 | values->ch1_current = CH1_current; 140 | values->ch2_current = CH2_current; 141 | values->ch3_current = CH3_current; 142 | 143 | // Power values (potência do bus - potência do shunt) 144 | 145 | float CH1_pot = ((values->ch1_bus_v) * (values->ch1_current)) - 146 | ((values->ch1_sh_v) * (values->ch1_current)); 147 | float CH2_pot = ((values->ch2_bus_v) * (values->ch2_current)) - 148 | ((values->ch2_sh_v) * (values->ch2_current)); 149 | float CH3_pot = ((values->ch3_bus_v) * (values->ch3_current)) - 150 | ((values->ch3_sh_v) * (values->ch3_current)); 151 | 152 | values->ch1_pot = CH1_pot; 153 | values->ch2_pot = CH2_pot; 154 | values->ch3_pot = CH3_pot; 155 | 156 | return 0; 157 | } 158 | 159 | bool ina3221_alive(ina3221_t ina) { 160 | result_uint16_t code = read(ina, DIE_ADR); 161 | if (code.value != DIE_CODE) { 162 | return false; 163 | } 164 | return true; 165 | } 166 | -------------------------------------------------------------------------------- /MS5607/MS5607.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gusta on 14/11/2021. 3 | // 4 | #include "MS5607.h" 5 | 6 | static error_t ms5607_reset(ms5607_t* ms5607) { 7 | //Create the reset command buffer 8 | uint8_t buffer_Rcmd[1] = { RESET_CMD }; 9 | 10 | buffer_view_t buffer_resetCmd = { .data = buffer_Rcmd, .size = 11 | sizeof(buffer_Rcmd) }; 12 | 13 | return i2c_transmit(ms5607->device, buffer_resetCmd); 14 | } 15 | 16 | static uint8_t getPROM(ms5607_t* ms5607) { 17 | 18 | uint16_t *prom_pt[] = { &ms5607->prom.C1, &ms5607->prom.C2, 19 | &ms5607->prom.C3, &ms5607->prom.C4, &ms5607->prom.C5, 20 | &ms5607->prom.C6 }; 21 | 22 | //Needs to start on 1 (Add 0 reserved for manufacturer) 23 | for (int i = 1; i <= QNT_PROM_CONST; i++) { 24 | 25 | //Found specific address to PROM 26 | uint8_t comman_PROM_i = PROM_MASK | (i << 1); 27 | 28 | //Create the array (command/response) 29 | uint8_t prom_response[2] = { 0 }; 30 | 31 | //Create the buffer 32 | buffer_view_t buffer_promCommand = { .data = &comman_PROM_i, .size = 1 }; 33 | buffer_view_t buffer_promResponse = { .data = prom_response, .size = 34 | sizeof(prom_response) }; 35 | 36 | //Transmit / Receive system 37 | i2c_transmit(ms5607->device, buffer_promCommand); 38 | i2c_receive(ms5607->device, buffer_promResponse); 39 | 40 | //Concatenate response ".data[1]+.data[0]" 41 | uint16_t response = (buffer_promResponse.data[1] << 8) | (buffer_promResponse.data[0]); 42 | 43 | //Write in "prom_t" struct 44 | *(prom_pt[i]) = response; 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | static uint8_t getDigitalValue(ms5607_t* ms5607) { 51 | 52 | uint8_t mode = ms5607->OSR_mode; 53 | uint32_t buffer_DValue[2] = {0}; 54 | uint8_t vec_mask[] = { D1_MASK, D2_MASK }; 55 | 56 | for (int i = 0; i < QNT_DVALUE; i++) { 57 | 58 | //Create the command according to OSR_mode 59 | uint8_t command_D = vec_mask[i] | (mode << 1); 60 | 61 | //Create the buffer cmd/resp 62 | uint8_t buffer_Dresp[3] = { 0 }; 63 | 64 | buffer_view_t buffer_Dcommand = { .data = &command_D, .size = 1}; 65 | buffer_view_t buffer_Dresponse = { .data = buffer_Dresp, .size = 66 | sizeof(buffer_Dresp) }; 67 | 68 | //Transmit / receive system 69 | i2c_transmit(ms5607->device, buffer_Dcommand); 70 | i2c_receive(ms5607->device, buffer_Dresponse); 71 | 72 | //Concatenate response values ".data[2]+.data[1]+.data[0]" 73 | uint32_t response = 0; 74 | for (int j = 2; j >= 0; j--) { 75 | response = response | (buffer_Dresponse.data[j] << (8 * j)); 76 | } 77 | 78 | //Attach values to DValue buffer 79 | buffer_DValue[i] = response; 80 | } 81 | 82 | //Attach Dvalue buffer to struct 83 | ms5607->DValue.D1 = buffer_DValue[0]; 84 | ms5607->DValue.D2 = buffer_DValue[1]; 85 | 86 | return 0; 87 | } 88 | 89 | error_t ms5607_init(ms5607_t* ms5607, enum OSR_samples mode) { 90 | 91 | //Reset 92 | error_t resetDevice = ms5607_reset(ms5607); 93 | 94 | if (resetDevice) { 95 | return resetDevice; 96 | } 97 | 98 | //Write OSR_mode 99 | ms5607->OSR_mode = mode; 100 | 101 | //read PROM (att PROM struct) 102 | getPROM(ms5607); 103 | 104 | delay_ms(10); 105 | 106 | return 0; 107 | } 108 | 109 | int32_t ms5607_getTemperature(ms5607_t* ms5607) { 110 | 111 | //Attach PROM values 112 | uint16_t C5 = ms5607->prom.C5; 113 | uint16_t C6 = ms5607->prom.C6; 114 | 115 | //Read D1 and D2 (att DValue struct) 116 | getDigitalValue(ms5607); 117 | 118 | //Attach D values 119 | uint32_t D2 = ms5607->DValue.D2; 120 | 121 | //Calculate Temperature 122 | int32_t dT = D2 - (C5 << 8); 123 | int32_t TEMP = 2000 + dT * (C6 >> 23); 124 | 125 | return TEMP; 126 | } 127 | 128 | int32_t ms5607_getPressure(ms5607_t* ms5607) { 129 | 130 | //Get Temperature 131 | int32_t TEMP = ms5607_getTemperature(ms5607); 132 | 133 | //Attach PROM values 134 | uint16_t C1 = ms5607->prom.C1; 135 | uint16_t C2 = ms5607->prom.C2; 136 | uint16_t C3 = ms5607->prom.C3; 137 | uint16_t C4 = ms5607->prom.C4; 138 | uint16_t C5 = ms5607->prom.C5; 139 | 140 | //Attach D values 141 | uint32_t D1 = ms5607->DValue.D1; 142 | uint32_t D2 = ms5607->DValue.D2; 143 | 144 | int32_t dT = D2 - (C5 << 8); 145 | int64_t OFF = (C2 << 17) + ((C4 * dT) >> 6); 146 | int64_t SENS = (C1 << 16) + ((C3 * dT) >> 7); 147 | 148 | //Second Order Temperature Compensation 149 | int64_t OFF2 = 0, SENS2 = 0; 150 | int32_t T2 = 0; 151 | 152 | if (TEMP < 20) { 153 | 154 | T2 = (dT * dT) >> 31; 155 | OFF2 = 61 * ((TEMP - 2000) * (TEMP - 2000)) >> 4; 156 | SENS2 = 2 * ((TEMP - 2000) * (TEMP - 2000)); 157 | } 158 | 159 | if (TEMP < -15) { 160 | 161 | OFF2 = OFF2 + (15 * (TEMP + 1500) * (TEMP + 1500)); 162 | SENS2 = SENS2 + (8 * (TEMP + 1500) * (TEMP + 1500)); 163 | } 164 | 165 | //Calculate compensated P and T 166 | TEMP = TEMP - T2; 167 | OFF = OFF - OFF2; 168 | SENS = SENS - SENS2; 169 | 170 | int32_t P = ((D1 * (SENS >> 21)) - OFF) >> 15; 171 | 172 | return P; 173 | } 174 | 175 | -------------------------------------------------------------------------------- /MPU6050/MPU6050_MVD.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MPU6050_MVD.h 3 | * 4 | * Created on: Oct 19, 2021 5 | * Author: Murilo Henrique Pasini Trevisan 6 | */ 7 | 8 | #ifndef INC_MPU6050_MVD_H_ 9 | #define INC_MPU6050_MVD_H_ 10 | 11 | #include "platform/platform.h" 12 | /* REGISTRADORES */ 13 | 14 | // Name //Adress(hex) 15 | 16 | // Registradores de configuração 17 | #define MPU_SMPRT_DIV (uint8_t)(0x19) 18 | #define MPU_CONFIG (uint8_t)(0x1A) 19 | #define MPU_GYRO_CONFIG (uint8_t)(0x1B) 20 | #define MPU_ACCEL_CONFIG (uint8_t)(0x1C) 21 | #define MPU_FIFO_EN (uint8_t)(0x23) //Default desabilitado 22 | #define MPU_INT_ENABLE (uint8_t)(0x38) //Default desabilitado 23 | #define MPU_INT_STATUS (uint8_t)(0x3A) //Read only 24 | // Registradores de leitura dos dados 25 | // Acelerômetro 26 | #define MPU_MEASURES (uint8_t)(0x3B) 27 | #define MPU_ACCEL_XOUT_H (uint8_t)(0x3B) 28 | #define MPU_ACCEL_XOUT_L (uint8_t)(0x3C) 29 | #define MPU_ACCEL_YOUT_H (uint8_t)(0x3D) 30 | #define MPU_ACCEL_YOUT_L (uint8_t)(0x3E) 31 | #define MPU_ACCEL_ZOUT_H (uint8_t)(0x3F) 32 | #define MPU_ACCEL_ZOUT_L (uint8_t)(0x40) 33 | 34 | //Temperatura 35 | #define MPU_TEMP_OUT_H (uint8_t)(0x41) 36 | #define MPU_TEMP_OUT_L (uint8_t)(0x42) 37 | 38 | // Giroscópio 39 | #define MPU_GYRO_XOUT_H (uint8_t)(0x43) 40 | #define MPU_GYRO_XOUT_L (uint8_t)(0x44) 41 | #define MPU_GYRO_YOUT_H (uint8_t)(0x45) 42 | #define MPU_GYRO_YOUT_L (uint8_t)(0x46) 43 | #define MPU_GYRO_ZOUT_H (uint8_t)(0x47) 44 | #define MPU_GYRO_ZOUT_L (uint8_t)(0x48) 45 | 46 | // Sensores externos conectados a IMU 47 | // Leitura dos endereços 0x49 até 0x60 48 | // Registradores de nomes EXT_SENS_DATA_00 até EXT_SENS_DATA_23 49 | 50 | /* Configurações */ 51 | 52 | //SMPRT_DIV 53 | //Sample rate = sensor output rate/(1+SMPLRT_DIV) 54 | //SMPRLT_DIV = (sensor output rate)/(sample rate) - 1 55 | //Utilizando filtros, SRmin=3,9 Hz e SRmáx=1kHz 56 | //Inserir um valor para realizar a divisão 57 | //Alguns valores base já calculados que podem ser usados 58 | #define MPU_SMPRT_1k (uint8_t)(0x00) //1 kHz 59 | #define MPU_SMPRT_4 (uint8_t)(0x00) //4 Hz 60 | #define MPU_SMPRT_10 (uint8_t)(0x63) //10 Hz 61 | #define MPU_SMPRT_100 (uint8_t)(0x09) //100 Hz 62 | 63 | //CONFIG 64 | // configuração da external frame synchronization 65 | #define MPU_FSYNC_DISABLE (uint8_t)(0x00 << 3) //desabilita a leitura de sinais externos 66 | // configuração do filtro digital passa baixa a ser utilizado 67 | #define MPU_DLPF_CG0 (uint8_t)(0x00 << 0) //A-260 G-256 Hz Digital low pass filter 68 | #define MPU_DLPF_CG1 (uint8_t)(0x01 << 0) //A-184 G-188 Hz 69 | #define MPU_DLPF_CG2 (uint8_t)(0x02 << 0) //A-94 G-98 Hz 70 | #define MPU_DLPF_CG3 (uint8_t)(0x03 << 0) //A-44 G-42 Hz 71 | #define MPU_DLPF_CG4 (uint8_t)(0x04 << 0) //A-21 G-20 Hz 72 | #define MPU_DLPF_CG5 (uint8_t)(0x05 << 0) //A-10 G-10 Hz 73 | #define MPU_DLPF_CG6 (uint8_t)(0x06 << 0) //A-05 G-05 Hz 74 | 75 | //GYRO_CONFIG 76 | //Quanto maior o range menor a precisão do sensor 77 | //Relação precisão e range explicada no datasheet na parte do acelerometro 78 | #define MPU_GFS_SEL_250 (uint8_t)(0x00 << 3) //+- 250°/s 79 | #define MPU_GFS_SEL_500 (uint8_t)(0x01 << 3) //+- 500°/s 80 | #define MPU_GFS_SEL_1000 (uint8_t)(0x02 << 3) //+- 1000°/s 81 | #define MPU_GFS_SEL_2000 (uint8_t)(0x03 << 3) //+- 2000°/s 82 | 83 | //ACCEL_CONFIG 84 | #define MPU_AFS_SEL_2 (uint8_t)(0x00 << 3) //+-2g 85 | #define MPU_AFS_SEL_4 (uint8_t)(0x01 << 3) //+-4g 86 | #define MPU_AFS_SEL_8 (uint8_t)(0x02 << 3) //+-8g 87 | #define MPU_AFS_SEL_16 (uint8_t)(0x03 << 3) //+-16g 88 | 89 | //FIFO_EN 90 | #define MPU_FIFO_DISABLE (uint8_t)(0x00) //Desabilita a FIFO 91 | 92 | //INT_EN 93 | #define MPU_INT_FIFO_DIS (uint8_t)(0x00 << 4) //desabilita interrupção da FIFO 94 | #define MPU_INT_DATA_READY (uint8_t)(0x00 << 0) //desabilita interrupção de dados prontos 95 | 96 | /* Structs de configuração */ 97 | 98 | typedef struct { 99 | //Config 100 | uint8_t FSyncEnable; //FSYNC 101 | uint8_t LowPassFilter; //DLPF 102 | //Sample config 103 | uint8_t Sample; 104 | //GyroRangeconfig 105 | uint8_t GyroRange; 106 | //AccelConfig 107 | uint8_t AccelRange; 108 | 109 | }MPU6050_config_t; 110 | 111 | 112 | typedef struct { 113 | i2c_device_t device; 114 | MPU6050_config_t config; 115 | }MPU6050_t; 116 | 117 | /* Structs de leitura dos valores */ 118 | 119 | typedef struct { 120 | float AccelX; 121 | float AccelY; 122 | float AccelZ; 123 | float Temp; 124 | float GyroX; 125 | float GyroY; 126 | float GyroZ; 127 | } MPU6050_values_t; 128 | 129 | //Funções de configuração 130 | error_t MPU6050_smprt(MPU6050_t mpu); 131 | error_t MPU6050_config(MPU6050_t mpu); 132 | error_t MPU6050_gyro_config(MPU6050_t mpu); 133 | error_t MPU6050_accel_config(MPU6050_t mpu); 134 | 135 | //Função de configuração geral, aplica todas as configs 136 | error_t MPU6050_init(MPU6050_t mpu); 137 | 138 | //Função para leitura burst-read 139 | error_t MPU6050_measure(MPU6050_t mpu, MPU6050_values_t *medida); 140 | 141 | 142 | #endif /* INC_MPU6050_MVD_H_ */ 143 | -------------------------------------------------------------------------------- /GUVC-S10GD/README.md: -------------------------------------------------------------------------------- 1 | # GUVC-S10GD 2 | 3 | ## Propósito 4 | Sensor de Radiação UV-C , que medirá a densidade de energia da radiação do tipo UV-C (`mW/cm²`) que incidir-se-á nas sondas que o contiverem. 5 | 6 | ## Ilustração 7 | Projeto onde o GUVC-S10GD foi utilizado (módulo de UV mais à direita) 8 | 9 | 10 | OBS: Diagramas de exemplo de aplicação do GUVC-S10GD estão disponíveis no diretório [extra_doc](https://github.com/CarlosCraveiro/Drivers/tree/GUVC-S10GD/GUVC-S10GD/extra_doc) desta bibliotca. 11 | 12 | ## Exemplo 13 | Setup 14 | ```c 15 | guvcs10gd_t guvc = { 16 | .adc = &hadc1, 17 | .converter.current.amplitude = 200000, //unit => pico-amperes 18 | .converter.current.offset = 200000, //unit => pico-amperes 19 | .converter.tension.amplitude = 1500, //unit => mili-volts 20 | .converter.tension.offset = 1500, //unit => mili-volts 21 | .converter.phase_difference = 0 //unit => degrees 22 | }; 23 | guvcs10gd_init(guvc); 24 | ``` 25 | 26 | Medida Simples 27 | ```c 28 | float uvc_radiation = guvcs10gd_get_uvcpower(guvc); 29 | printf("radiation in (mW/cm^2): %f\r\n", uvc_radiation); 30 | ``` 31 | 32 | Medida Acurata 33 | ```c 34 | double uvc_radiation = guvcs10gd_accurate_get_uvcpower(guvc); 35 | printf("radiation in (mW/cm^2): %lf\r\n", uvc_radiation); 36 | ``` 37 | ## Documentação 38 | ### Equação de conversão entre corrente e densidade de energia na radiação UV-C, apresentada no [datasheet](http://www.geni-uv.com/download/products/GUVC-S10GD.pdf): 39 | 40 | 41 | 42 | ### Equação de conversão genérica entre tensão de saída e corrente emitida pelo sensor: 43 | 44 | 45 | 46 | Essa equação, deduzida e explicada com maior riqueza de detalhes em um [documento]() a parte, é utilizada para converter a tensão de saída no ADC da Bluepill (STM32) na corrente de saída do `GUVC-S10GD`. Para esssa conversão, faz-se necessário inserir as informações da curva de calibração do `GUVC-S10GD` na configuração da struct do tipo `guvcs10gd_t` exemplificada na sessão `Setup` do tópico acima. 47 | 48 | ### Equação de conversão específica entre tensão de saída e corrente emitida pelo sensor: 49 | 50 | 51 | 52 | Essa equação pode ser utilizada para substituir a equação genérica em algumas situações. Como já foi abordado, a Bluepill (ou STM32) não foi projetada para trabalhar com números de "ponto flutuante". Assim, a fim de exigir menos poder de processamento da Bluepill em relação à expressão genérica, essa equação, implementada na função `guvcs10gd_get_uvcpower` desta biblioteca, apesar de retornar um `float`, abre mão de raizes quadras e funções seno ou cosseno, exigindo bem menos poder de processamento da Bluepill. Entretanto, para conseguir se livrar dessas funções da `math.h`, a equação só leva em consideração os casos onde os gráficos de tensão e corrente estão em perfeita oposição (`lambda = -1`) ou conjunção de fases (`lambda = 1`). Contudo, vale o adendo de que é possível utilizar essa função em situações "não ideais", porém os resultados não serão os mais precisos possíveis. 53 | 54 | ## Detalhes sobre os parâmetros do `.converter` do Setup de Exemplo 55 | Os parâmetros de `.converter`, como `.converter.current.offset`,`.converter.tension.amplitude`, etc, estão explicados e detalhados no pdf que descreve a dedução da [equação de desconversão](https://github.com/CarlosCraveiro/Drivers/blob/GUVC-S10GD/GUVC-S10GD/extra_doc/Dedution_of_De-conversion_Equation.pdf), apresentada neste README.md como "Equação de conversão genérica entre tensão de saída e corrente emitida pelo sensor" . 56 | 57 | ## Notas 58 | - Para resultados aproximados da radiação UVC, utilize a função `guvcs10gd_get_uvcpower`. 59 | - Para resultados mais precisos da radiação UVC, utilize a função `guvcs10gd_accurate_get_uvcpower`, mas saiba que ela exigirá contas utilizandon `double`. 60 | - Para mais informações sobre as equações de conversão de tensão em corrente [clique aqui](https://github.com/CarlosCraveiro/Drivers/blob/GUVC-S10GD/GUVC-S10GD/extra_doc/Dedution_of_De-conversion_Equation.pdf). 61 | -------------------------------------------------------------------------------- /MPU6050/MPU6050_MVD.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MPU6050_MVD.c 3 | * 4 | * Created on: Oct 19, 2021 5 | * Author: Murilo Henrique Pasini Trevisan 6 | */ 7 | 8 | 9 | #include "MPU6050_MVD.h" 10 | 11 | static result_uint8_t read(MPU6050_t mpu, uint8_t addr) { 12 | buffer_view_t tx_v = {.size=sizeof(addr), .data=&addr}; 13 | error_t e = i2c_transmit(mpu.device, tx_v); 14 | uint8_t rx = 0; 15 | buffer_view_t rx_v = {.size=sizeof(rx), .data=&rx}; 16 | e |= i2c_receive(mpu.device, rx_v); 17 | result_uint8_t res = {.hasError = e, .value = rx}; 18 | return res; 19 | } 20 | 21 | static error_t write(MPU6050_t mpu, uint8_t addr, uint8_t value){ 22 | uint8_t tx[] = {addr, value}; 23 | buffer_view_t tx_v = {.size=sizeof(tx), .data=tx}; 24 | return i2c_transmit(mpu.device, tx_v); 25 | } 26 | 27 | /* Configurações do sensor */ 28 | 29 | // Função para enviar via i2c o valor do samplerate 30 | error_t MPU6050_smprt(MPU6050_t mpu) { 31 | 32 | //Caso o valor de reset não seja o mesmo do datasheet usar esta parte comentada 33 | // result8_t config_raw = read(mpu, MPU_SMPRT_DIV); 34 | // config_reg = config_raw.value 35 | 36 | uint8_t smprt_reg = 0x00; 37 | 38 | smprt_reg |= mpu.config.Sample; 39 | 40 | return write(mpu, MPU_SMPRT_DIV, smprt_reg); 41 | } 42 | 43 | 44 | // Função para enviar via i2c o valor do registrador de configuração geral 45 | error_t MPU6050_config(MPU6050_t mpu) { 46 | 47 | //Caso o valor de reset não seja o mesmo do datasheet usar esta parte comentada 48 | // result8_t config_raw = read(mpu, MPU_CONFIG); 49 | // config_reg = config_raw.value 50 | 51 | uint8_t config_reg = 0x00; 52 | 53 | config_reg |= mpu.config.FSyncEnable; 54 | config_reg |= mpu.config.LowPassFilter; 55 | 56 | return write(mpu, MPU_CONFIG, config_reg); 57 | } 58 | 59 | // Função para enviar via i2c o valor do registrador de configuração do giroscópio 60 | error_t MPU6050_gyro_config(MPU6050_t mpu) { 61 | 62 | //Caso o valor de reset não seja o mesmo do datasheet usar esta parte comentada 63 | //result8_t config_raw = read(mpu, MPU_GYRO_CONFIG); 64 | //gyro_config_reg = config_raw.value 65 | 66 | uint8_t gyro_config_reg = 0x00; 67 | 68 | gyro_config_reg |= mpu.config.GyroRange; 69 | 70 | return write(mpu, MPU_GYRO_CONFIG, gyro_config_reg); 71 | } 72 | 73 | // Função para enviar via i2c o valor do registrador de configuração do acelerômetro 74 | error_t MPU6050_accel_config(MPU6050_t mpu) { 75 | 76 | //Caso o valor de reset não seja o mesmo do datasheet usar esta parte comentada 77 | //result8_t config_raw = read(mpu, MPU_ACCEL_CONFIG); 78 | //accel_config_reg = config_raw.value 79 | 80 | uint8_t accel_config_reg = 0x00; 81 | 82 | accel_config_reg |= mpu.config.AccelRange; 83 | 84 | return write(mpu, MPU_ACCEL_CONFIG, accel_config_reg); 85 | } 86 | 87 | error_t MPU6050_init(MPU6050_t mpu) { 88 | 89 | //Realiza todas as configurações em uma única função 90 | //para facilitar a execução do programa na main 91 | 92 | write(mpu, 0x6B, 0); 93 | MPU6050_smprt(mpu); 94 | 95 | MPU6050_config(mpu); 96 | MPU6050_accel_config(mpu); 97 | MPU6050_gyro_config(mpu); 98 | 99 | return 0; 100 | } 101 | 102 | /* Leituras do sensor */ 103 | 104 | error_t MPU6050_measure(MPU6050_t mpu, MPU6050_values_t *medida) { 105 | 106 | //Realiza a leitura dos registradores dos resultados 107 | //Separa os resultados em cada uma das leituras 108 | //Converte para as medidas necessárias 109 | //Armazena na struct de resultados 110 | 111 | //Criação do buffer para leitura burst-read 112 | uint8_t buffer[14] = { 0xFF, [10] = 0xFF }; 113 | buffer_view_t buffer_view = { .data = buffer, .size = sizeof(buffer) }; 114 | 115 | //Leitura de todos os registradores de resultados e save no buffer 116 | uint8_t addr = MPU_MEASURES; // envia endereço do registrador primeiro 117 | buffer_view_t addr_v = {.size=sizeof(addr), .data=&addr}; 118 | i2c_transmit(mpu.device, addr_v); 119 | error_t error = i2c_receive(mpu.device, buffer_view); 120 | 121 | //Tratamento de erro da HAL 122 | if (error) { 123 | return error; 124 | } 125 | 126 | //Separação das componentes da leitura 127 | //Acelerômetro 128 | int16_t accel_x = (buffer[0] << 8) | buffer[1]; 129 | int16_t accel_y = (buffer[2] << 8) | buffer[3]; 130 | int16_t accel_z = (buffer[4] << 8) | buffer[5]; 131 | //Temperatura 132 | int16_t temp = (buffer[6] << 8) | buffer[7]; 133 | //Giroscópio 134 | int16_t gyro_x = (buffer[8] << 8) | buffer[9]; 135 | int16_t gyro_y = (buffer[10] << 8) | buffer[11]; 136 | int16_t gyro_z = (buffer[12] << 8) | buffer[13]; 137 | 138 | //Alocação na struct de resultados 139 | //Acelerômetro 140 | int const scaleAcc = 1 << ((3 - (mpu.config.AccelRange / (1 << 3))) + 11); 141 | medida -> AccelX = ((float)accel_x) / scaleAcc; 142 | medida -> AccelY = ((float)accel_y) / scaleAcc; 143 | medida -> AccelZ = ((float)accel_z) / scaleAcc; 144 | //Temperatura 145 | medida -> Temp = (((float)temp) / 340.0f) + 36.53f; 146 | //Giroscópio 147 | int const scaleGyr = (1 << ( 17 - (mpu.config.GyroRange >> 3))) / 1000.f; 148 | 149 | medida -> GyroX = (float)gyro_x / scaleGyr; 150 | medida -> GyroY = (float)gyro_y / scaleGyr; 151 | medida -> GyroZ = (float)gyro_z / scaleGyr; 152 | 153 | return 0; 154 | } 155 | 156 | -------------------------------------------------------------------------------- /NEO8M/ublox_neo8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ublox_neo8.c 3 | * 4 | * Created on: Jun 20, 2021 5 | * Author: leocelente 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include "ublox_neo8.h" 12 | #include 13 | 14 | // Calculate checksum over the packet buffer excluding sync (first two) and checksum chars (last two). 15 | static void ubx_checksum(int len, uint8_t packet[len]) { 16 | packet[len - 2] = 0x00; 17 | packet[len - 1] = 0x00; 18 | 19 | for (uint8_t j = 0; j < len - 4; j++) { 20 | packet[len - 2] += packet[2 + j]; 21 | packet[len - 1] += packet[len - 2]; 22 | } 23 | } 24 | 25 | /*** 26 | * Seleciona UBX como protocolo principal 27 | */ 28 | static error_t set_protocol(ublox_gps_t gps) { 29 | static uint8_t message[] = { 0xB5, 0x62, // sync 30 | 0x06, 0x2, // class and id 31 | 0x01, 0x00, // size 32 | // payload 33 | 0x00, 0x00 // checksum 34 | }; 35 | 36 | ubx_checksum(sizeof(message), message); 37 | buffer_view_t bv = { .data = message, .size = sizeof(message) }; 38 | 39 | 40 | return gps_transmit(gps.conn, bv); 41 | } 42 | 43 | /*** 44 | * Seleciona Modelo de calculo para High Altitude 1G 45 | * ideal para sondas 46 | */ 47 | static error_t set_model_mode(ublox_gps_t gps) { 48 | static const uint8_t message[] = { 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 49 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 50 | 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x16, 0xDC }; 53 | buffer_view_t bv = { .data = message, .size = sizeof(message) }; 54 | return gps_transmit(gps.conn, bv); 55 | } 56 | 57 | static void disableNmea(ublox_gps_t gps) { 58 | // Array of two bytes for CFG-MSG packets payload. 59 | static const uint8_t messages[][2] = { { 0xF0, 0x0A }, { 0xF0, 0x09 }, 60 | { 0xF0, 0x00 }, { 0xF0, 0x01 }, { 0xF0, 0x0D }, { 0xF0, 0x06 }, { 61 | 0xF0, 0x02 }, { 0xF0, 0x07 }, { 0xF0, 0x03 }, 62 | { 0xF0, 0x04 }, { 0xF0, 0x0E }, { 0xF0, 0x0F }, { 0xF0, 0x05 }, { 63 | 0xF0, 0x08 }, { 0xF1, 0x00 }, { 0xF1, 0x01 }, 64 | { 0xF1, 0x03 }, { 0xF1, 0x04 }, { 0xF1, 0x05 }, { 0xF1, 0x06 }, }; 65 | 66 | // CFG-MSG packet buffer. 67 | static uint8_t packet[] = { 0xB5, 0x62, // sync 68 | 0x06, 0x01, // class and id 69 | 0x03, 0x00, // length 70 | 0x00, 0x00, 0x00, // payload (not changed in the case) 71 | 0x00, 0x00, // checksum 72 | }; 73 | int packetSize = sizeof(packet); 74 | int messageSize = sizeof(messages[0]); 75 | int messageCount = sizeof(messages) / messageSize; 76 | int payloadOffset = 6; 77 | 78 | // Iterate over the messages array. 79 | for (int i = 0; i < messageCount; i++) { 80 | for (int j = 0; j < messageSize; j++) { 81 | packet[payloadOffset + j] = messages[i][j]; 82 | } 83 | ubx_checksum(sizeof(packet), packet); 84 | buffer_view_t bv = { .data = packet, .size = packetSize }; 85 | 86 | 87 | gps_transmit(gps.conn, bv); 88 | 89 | 90 | delay_ms(1); 91 | } 92 | } 93 | 94 | /*** 95 | * Requisita um pacote PVT 96 | */ 97 | static error_t trigger(ublox_gps_t gps) { 98 | static uint8_t message[] = { 0xB5, 0x62, // sync 99 | 0x01, 0x07, // class and id 100 | 0x00, 0x00, // size little endian 101 | // payload 102 | 0x00, 0x00 // checksum (to be defined) 103 | }; 104 | ubx_checksum(sizeof(message), message); 105 | buffer_view_t bv = { .data = message, .size = sizeof(message) }; 106 | return gps_transmit(gps.conn, bv); 107 | } 108 | 109 | 110 | static uint8_t get(ublox_gps_t gps){ 111 | uint8_t x = 0; 112 | buffer_view_t data_v = {.data=&x, sizeof(x)}; 113 | gps_receive(gps.conn, data_v); 114 | return x; 115 | } 116 | 117 | static uint8_t wait_for(ublox_gps_t gps, uint8_t data, uint32_t attempts){ 118 | while (attempts--) { 119 | uint8_t x = get(gps); 120 | if(x == data) return x; 121 | // delay_ms(1); 122 | } 123 | return attempts; 124 | } 125 | 126 | /*** 127 | * Pede pacote e decodifica 128 | */ 129 | error_t ublox_get(ublox_gps_t gps, ublox_pvt_t *pvt) { 130 | trigger(gps); 131 | #if defined UBLOX_I2C 132 | uint8_t stream = UBLOX_STREAM_REG; 133 | error_t e = i2c_transmit(gps.conn, (buffer_view_t){.data=&stream, .size=1}); 134 | if(e){ 135 | return e; 136 | } 137 | 138 | #endif 139 | wait_for(gps, 0xB5, 150); 140 | wait_for(gps, 0x62, 1); 141 | wait_for(gps, 0x01, 1); 142 | wait_for(gps, 0x07, 1); 143 | wait_for(gps, 0x5C, 1); 144 | wait_for(gps, 0x00, 1); 145 | 146 | static uint8_t message[92+2] = {0}; 147 | for(int i = 0; i < sizeof(message); ++i){ 148 | uint8_t received = get(gps); 149 | message[i] = received; 150 | } 151 | 152 | static ubx_pvt_parser_t data = {0}; 153 | memcpy(data.raw, message, sizeof(data.raw)); 154 | *pvt = data.values; 155 | 156 | if((pvt->valid & 0x07) != 0x7){ 157 | return 2; 158 | } 159 | 160 | if(fabs(pvt->lat) < 1e-9 || fabs(pvt->lng) < 1e-9){ 161 | return 2; 162 | } 163 | return 0; 164 | } 165 | 166 | /*** 167 | * Inicializa GPS ublox com o protocolo binario UBX 168 | */ 169 | error_t ublox_init(ublox_gps_t gps) { 170 | disableNmea(gps); 171 | set_protocol(gps); 172 | set_model_mode(gps); 173 | return 0; 174 | } 175 | 176 | -------------------------------------------------------------------------------- /BMP280/bmp280.c: -------------------------------------------------------------------------------- 1 | #include "bmp280.h" 2 | #include 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 |
  1. Find the best Id and Mask for the Filter (CANZenTool_IdNdMaskBuilder_t)
  2. 12 |
  3. Configure the filter (CANZenTool_setFilter)
  4. 13 |
  5. Write a standard can frame (CANZenTool_writeStdCanFrame)
  6. 14 |
  7. Send a standard can frame (CANZenTool_sendCanFrameMsg)
  8. 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 | --------------------------------------------------------------------------------