I'm doing a project involving STM32 and an accelerometer sensor (LIS2DE) I have to read every second the register from the sensor and send it via UART to arduino IDE but, idk why, the other two HAL_I2C_Mem_Read_DMA won't work
I tried to delete the first Mem_Read and only the second would work, so I checked the return value of the second call and it gives me HAL_BUSY. Using HAL_I2C_Master_Transmit_DMA and HAL_I2C_Master_Receive_DMA works correctly, but I wanted to experiment this way because professors suggested us to use this function to do automatically Transmit+Receive. Is better to use Transmit and Receive separately or this one in your opinion? And why?
Furthermore, is there a function to get all 3-axis value? I inspected the datasheet and I saw an auto increment feature, but I dind't find any example with that
I'll leave my code, obviously I set a timer of 1 second and I set up DMA for UART_TX, I2C_TX and I2C_RX, enabled interrupts from UART, TIM and I2C event
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_X_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[0], 1);
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Y_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[1], 1);
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Z_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&acc[2], 1);
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c){
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
if(count==2){ //because I'll receive a single interrupt for each Mem_Read, so after the third MemRxCpltCallback will transmit thanks UART values
count = 0;
len = snprintf(string, sizeof(string), "X: %+.3fg Y: %+.3fg Z: %+.3fg\n",
acc[0] / 256.0 * 4.0,
acc[1] / 256.0 * 4.0,
acc[2] / 256.0 * 4.0);
HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
}
else
count++;
}
Thanks for any help, if you need more information or there is something not good in the question, tell me and I'll update the question. I can post a link for the zip file of my code too if someone needs it
--- UPDATE ---
Now I just start the first one with the TIM IT, then other ones are triggered by previous one and last one triggers the print, just using a global variable to keep track of which call is executed. Do you think it's a quite decent solution? It works but seems very dirty to me, but it's mandatory to use I2C with DMA Btw thanks again for every contribution
#if VERSION == 2
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_X_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
count++;
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c){
switch(count){
case 1:
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Y_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
count++;
break;
case 2:
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, OUT_Z_ADD, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+count), 1);
count++;
break;
case 3:
count=0;
len = snprintf(string, sizeof(string), "X: %+.3fg Y: %+.3fg Z: %+.3fg, (Tot:%+.3fg | %+.3fm/s²)\n",
acc[0] / 256.0 * 4.0,
acc[1] / 256.0 * 4.0,
acc[2] / 256.0 * 4.0,
sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0,
sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0 * 9.80665);
HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
}
}
#endif
--- UPDATE2 --- Here there is my main, other code is generated automatically plus the two callbacks that @pmacfarlane suggested
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_DMA_Init();
MX_USART2_UART_Init();
MX_I2C1_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
if (HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG1, sizeof(CTRL_REG1)) == HAL_OK)
len = snprintf(string, sizeof(string), "LIS2DE found!\n");
else{
ACCEL_ADD = LIS2DE12_ADD;
if (HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG1, sizeof(CTRL_REG1)) == HAL_OK)
len = snprintf(string, sizeof(string), "LIS2DE12 found!\n");
else
len = snprintf(string, sizeof(string), "Accelerator error!\n");
}
HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG2, sizeof(CTRL_REG2));
HAL_I2C_Master_Transmit_DMA(&hi2c1, ACCEL_ADD, (uint8_t*)CTRL_REG4, sizeof(CTRL_REG4));
TIM2->SR &= ~TIM_SR_UIF; //or __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); or HAL_Delay(500);
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
#if VERSION == 3
while (!timer_elapsed){/*do nothing*/}
for (int i=0; i<3; i++){
dma_complete = false;
HAL_I2C_Mem_Read_DMA(&hi2c1, ACCEL_ADD, regs[i], I2C_MEMADD_SIZE_8BIT, (uint8_t*)(acc+i), 1);
while (!dma_complete){/*do nothing*/}
}
len = snprintf(string, sizeof(string), "X: %+.3fg Y: %+.3fg Z: %+.3fg (Tot:%+.3fg | %+.3fm/s²)\n",
acc[0] / 256.0 * 4.0,
acc[1] / 256.0 * 4.0,
acc[2] / 256.0 * 4.0,
sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0,
sqrt(pow(acc[0],2)+pow(acc[1],2)+pow(acc[2],2))/64.0 * 9.80665);
HAL_UART_Transmit_DMA(&huart2, (unsigned char*)string, len);
timer_elapsed = false;
#endif
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
A few points:
HAL_Delay()
will not work in the timer interrupt, because the sysTick is not incrementing.With those points in mind, you could have a design something like this. (Untested, incomplete, but hopefully shows the intent.)
#include <stdbool.h>
static volatile bool timer_elapsed = false;
static volatile bool dma_complete = false;
void main(void)
{
// Configure all peripherals etc.
for (;;)
{
// Wait for timer to elapse
while (!timer_elapsed)
{
// You could be doing other things while you wait...
}
// Read the three registers
for (int i = 0; i < 3; ++i)
{
static const uint8_t regs[] = {OUT_X_ADD, OUT_Y_ADD, OUT_Z_ADD};
dma_complete = false;
do_i2c_dma(regs[i]); // Call the HAL DMA function
// Wait for DMA to complete
while (!dma_complete)
{
// You could be doing things here too...
}
}
print_results(); // To be implemented
timer_elapsed = false;
}
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c)
{
dma_complete = true;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
timer_elapsed = true;
}
The flags are volatile because they are modified in the ISRs, otherwise the main code might assume they never change.