Introdução
Neste artigo, você aprenderá a configurar a Franzininho C0 para trabalhar com protocolo de comunicação SPI e realizar leituras do acelerômetro LIS3DH.
Materiais necessários:
- 1 Franzininho C0
- 1 Módulo LIS3DH
Esquema de montagem:
- PA6 – SD0 (MISO)
- PA1 – SCL (SCK)
- PA2 – SDA (MOSI)
- P4 – CS
Protocolo SPI
SPI é um protocolo de comunicação serial síncrono , full -duplex , baseado em mestre-escravo. Foi implementado pela primeira vez em 1970 pela Motorola.
O protocolo de comunicação SPI (Serial Peripheral Interface) utiliza quatro fios principais: SCK (Clock Serial), MISO (Master-In Slave-Out), MOSI (Master-Out Slave-In) e CS (Chip Select). Diferente do protocolo I2C, que utiliza endereços para comunicação entre dispositivos, o SPI utiliza o pino CS para selecionar o escravo com o qual o mestre deseja se comunicar. Quando o mestre precisa iniciar uma comunicação com um escravo específico, ele ativa o pino CS desse escravo, levando-o para o nível lógico baixo (0). Isso estabelece o canal de comunicação exclusivo entre o mestre e o escravo selecionado.
Além disso, é preciso configurar corretamente os parâmetros CPOL (Clock Polarity) e CPHA (Clock Phase). O CPOL define o estado inativo do clock, enquanto o CPHA determina em qual borda do clock os dados serão amostrados, garantindo que a sincronização dos sinais entre mestre e escravo.
Para mais informações detalhadas deste protocolo recomendo a leitura de https://embarcados.com.br/spi-parte-1/ e https://embarcados.com.br/comunicacao-spi-parte-2/
LIS3DH
O LIS3DH é um acelerômetro linear de três eixos, de alta performance e ultra baixa potência, com interface digital I²C/SPI. Oferece modos de ultra baixa potência, funções inteligentes e escalas selecionáveis de ±2g/±4g/±8g/±16g, medindo acelerações de 1 Hz a 5,3 kHz. Possui auto-teste, geradores de interrupção configuráveis, e um buffer FIFO de 32 níveis para otimizar a eficiência.
Características:
- Tensão de alimentação: 1,71 V a 3,6 V
- Consumo em ultra baixa potência: até 2 μA
- Escala selecionável: ±2g/±4g/±8g/±16g
- Interface comunicação I²C/SPI
- Saída de dados de 16 bits
- 2 geradores de interrupção programável (queda livre e movimento)
- Detecção de orientação 6D/4D, queda livre e movimento
- Sensor de temperatura e auto-teste embutidos
- FIFO de 32 níveis, alta resistência a choques (até 10000 g)
Aplicações:
- Funções por movimento, detecção de queda livre
- Pedômetros, orientação de display
- Dispositivos para jogos e realidade virtual
- Economia de energia inteligente para portáteis
- Reconhecimento e registro de impactos, monitoramento de vibrações
Acesse o datasheet em: https://www.st.com/resource/en/datasheet/lis3dh.pdf
Configuração CubeMX
- Abra STM32Cube, crie um novo projeto e selecione o microcontrolador “STM32C011F6P6”.
- Vá para a página de configurações de relógio e em HCLK digite 48 MHz para a frequência de saída desejada do sistema. Depois volte para a página de configuração dos pinos, selecione “Trace and Debug” e habilite “Serial Wire.
- Para a configuração do SPI, neste exemplo, vá em “Connectivity’ > “SPI”, habilite o modo “FullDuplex Master” e mude o “Clock Parameter” > “Prescaler (Baud Rate)” para 32. Os demais parâmetros de configuração não será necessário modificar.
- Perceba que ao habilitar o SPI, o CubeMx ativa automaticamente os pinos de SCK, MOSI e MISO. O pino de CS é necessário configurar manualmente, para isso, selecione o PA4 como “GPIO_Output”.
- Para configurar a UART, selecione o pino PA10 como “USART1_RX” e PA9 como “USART1_TX”. Em “Connectivity” selecione a opção “USART1” e em Mode escolha o “Asynchronous”. As demais configurações deixe padrão.
- Por fim, gere o código em “Project” > “Generate Code”.
Implementação Código
- Inicialmente, criaremos dois arquivos novos. Em “Inc” crie “lis3dh.h” e em “Src” crie “lis3dh.c”.

- Após criar esses novos arquivos, acesse o arquivo lis3dh.h. Neste arquivo, vamos adicionar algumas definições (#define) dos registradores do acelerômetro LIS3DH, assim como os protótipos das funções que implementaremos em lis3dh.c.
#include <stdint.h>
// Definições dos registradores do acelerômetro LIS3DH
#define LIS3DH_CTRL_REG1 0x20
#define LIS3DH_CTRL_REG4 0x23
#define LIS3DH_WHO_AM_I 0x0F
#define LIS3DH_OUT_X_L 0x28
#define LIS3DH_OUT_X_H 0x29
#define LIS3DH_OUT_Y_L 0x2A
#define LIS3DH_OUT_Y_H 0x2B
#define LIS3DH_OUT_Z_L 0x2C
#define LIS3DH_OUT_Z_H 0x2D
// Protótipos das funções para manipulação do acelerômetro
void LIS3DH_Init(void);
uint8_t LIS3DH_ReadReg(uint8_t address);
void LIS3DH_WriteReg(uint8_t address, uint8_t data);
void LIS3DH_ReadAccel(int16_t *acceleration);- Acesse o arquivo lis3dh.c, inclua as bibliotecas necessárias e faça referência às interfaces SPI e UART que foram configuradas previamente.
#include "stm32c0xx.h"
#include "lis3dh.h"
#include "stdio.h"
#include "string.h"
extern SPI_HandleTypeDef hspi1;
extern UART_HandleTypeDef huart1;- Ainda em lis3dh.c, criaremos as funções de leitura e escrita na SPI. Em ambas as funções, é necessário ativar e desativar o CS (Chip Select) entre as leituras ou escritas SPI, conforme explicado ao longo do artigo.
/** @brief Reads given amount of data from sensor
* @param reg: Register address to read from
* @retval data receive
*/
uint8_t LIS3DH_ReadReg(uint8_t address) {
uint8_t txData[2];
uint8_t rxData[2];
txData[0] = address | 0x80 ; // Set read bit (MSB = 1)
txData[1] = 0x00; // Dummy byte
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS low
// delay to ensure CS is correctly registered
HAL_Delay(1);
if (HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, HAL_MAX_DELAY) != HAL_OK) {
char error_msg[] = "SPI Transmit/Receive Error\r\n";
HAL_UART_Transmit(&huart1, (uint8_t *)error_msg, strlen(error_msg), 1000);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS high
return rxData[1]; // The second byte received is the actual data
}
/** @brief Writes given amount of data to sensor
* @param reg: Register address to write to
* @param value: Number of bytes to be written
* @retval Zero on success, -1 otherwise
*/
void LIS3DH_WriteReg(uint8_t address, uint8_t data) {
uint8_t txData[2];
txData[0] = address;
txData[1] = data;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS low
// delay to ensure CS is correctly registered
HAL_Delay(1);
if (HAL_SPI_Transmit(&hspi1, txData, 2, HAL_MAX_DELAY) != HAL_OK) {
char error_msg[] = "SPI Write Error\r\n";
HAL_UART_Transmit(&huart1, (uint8_t *)error_msg, strlen(error_msg), 1000);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS high
}
- Mantenha-se em lis3dh.c, adicione a função de inicialização do acelerômetro, nela vamos configurar os registradores LIS3DH_CTRL_REG1 e LIS3DH_CTRL_REG4. No LIS3DH_CTRL_REG1, ajustamos a taxa de dados e habilitamos os eixos. No LIS3DH_CTRL_REG4, configuramos o modo de comunicação (3 ou 4 fios), a escala e a resolução, conforme mostrado nas tabelas abaixo.
/** @brief Initializes LIS3DH device with following configuration:
* 100Hz sampling rate
* Enable all axis
* @retval None
*/
void LIS3DH_Init(void) {
// delay to ensure LIS3DH is ready after power-up
HAL_Delay(10);
// Write to CTRL_REG1 to set data rate and enable all axes
LIS3DH_WriteReg(LIS3DH_CTRL_REG1, 0x57); // 100Hz, enable X, Y, Z
// Write to CTRL_REG4 to set scale default 2g and high-resolution mode
LIS3DH_WriteReg(LIS3DH_CTRL_REG4, 0xC8);
uint8_t who_am_i = LIS3DH_ReadReg(LIS3DH_WHO_AM_I);
char status[50];
sprintf(status, "WHO_AM_I: 0x%02X\r\n", who_am_i);
HAL_UART_Transmit(&huart1, (uint8_t *)status, strlen(status), 1000);
}
valor configurado no código: 0x57 = 01010111
valor configurado no código: 0xC8 = 11001000
- Por fim, no arquivo lis3dh.c, adicione a função de leitura de dados. Esta função lerá os valores de cada eixo do acelerômetro (vertical e horizontal) e os armazenará no array fornecido. A função faz a leitura dos registradores de saída de dados do acelerômetro e combina os bytes alto e baixo para formar os valores de 16 bits de cada eixo.
void LIS3DH_ReadAccel(int16_t *acceleration) {
uint8_t x_l = LIS3DH_ReadReg(LIS3DH_OUT_X_L);
uint8_t x_h = LIS3DH_ReadReg(LIS3DH_OUT_X_H);
uint8_t y_l = LIS3DH_ReadReg(LIS3DH_OUT_Y_L);
uint8_t y_h = LIS3DH_ReadReg(LIS3DH_OUT_Y_H);
uint8_t z_l = LIS3DH_ReadReg(LIS3DH_OUT_Z_L);
uint8_t z_h = LIS3DH_ReadReg(LIS3DH_OUT_Z_H);
acceleration[0] = ((int16_t)x_h << 8) | x_l; // X-axis
acceleration[1] = ((int16_t)y_h << 8) | y_l; // Y-axis
acceleration[2] = ((int16_t)z_h << 8) | z_l; // Z-axis
}
- Veja na imagem abaixo o código como todo do arquivo lis3dh.c
- Entre no arquivo main.c, faça a inclusão das seguintes bibliotecas e variáveis.
#include "lis3dh.h"
#include "stdio.h"
#include "string.h"
char uart_buf[50];
char acel_buf[50];- Depois, modifique a função principal int main de main.c com o seguinte:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* Initialize LIS3DH device */
// CS pin should default high
// teste uart
sprintf(uart_buf, "SPI Test\r\n");
HAL_UART_Transmit(&huart1, (uint8_t *)uart_buf, strlen(uart_buf), 1000);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_Delay(500);
LIS3DH_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
int16_t accel[3];
LIS3DH_ReadAccel(accel);
// Print values via UART
sprintf(acel_buf, "Leitura Bruta - X: %i, Y: %i, Z: %i\r\n", accel[0], accel[1], accel[2]);
HAL_UART_Transmit(&huart1, (uint8_t *)acel_buf, strlen(acel_buf), 1000);
HAL_Delay(1000);
// Conversão para mg (miligravidade)
int32_t accel_x_mg = (int32_t)accel[0] * 1000 / 16384;
int32_t accel_y_mg = (int32_t)accel[1] * 1000 / 16384;
int32_t accel_z_mg = (int32_t)accel[2] * 1000 / 16384;
sprintf(acel_buf, "Aceleracao (mg) - X: %ld, Y: %ld, Z: %ld\r\n", accel_x_mg, accel_y_mg, accel_z_mg);
HAL_UART_Transmit(&huart1, (uint8_t *)acel_buf, strlen(acel_buf), 1000);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
Neste código, estamos inicializando o acelerômetro LIS3DH. No loop principal, lemos os valores brutos do acelerômetro, os convertendo em valores em miligramas (mg) e, em seguida, imprimimos essas informações pela interface serial. Abaixo temos a tabela de conversão que explica o motivo pelo qual foi utilizado 16384 na conversão da aceleração sabendo que a sensibilidade foi configurada em 2g.
- A imagem abaixo mostra o código main.c modificado.
Gravação
Ao finalizar o código, partiremos para gravação. Nessa etapa você pode utilizar o ST-Link seguindo as conexões da imagem abaixo e clicando em “run” no STM32CubeIde.
Ou você pode optar por utilizar um cabo usb e gravar conforme explicado no seguinte tutorial: gravar-franzininho-c0-via-stm32cubeprogrammer
Funcionamento

Conclusão
Neste artigo, demonstramos como configurar e utilizar o acelerômetro LIS3DH com a Franzininho C0 via SPI. Detalhamos a configuração do SPI e a implementação do código para inicializar o acelerômetro, ler os valores dos eixos X, Y e Z, e converter esses valores para unidades de miligravidade (mg).





