在 stm32f407g 使用 lis302dl 三軸加速規和 external interrupt

Posted by blueskyson on April 29, 2022

環境: wnidows 10, STM32CubeIDE 1.8.0

在這個教學將透過 PA0PA1 腳位來使用 uart4。

專案初始化

首先在 CubeIDE 新增 stm32f407g 的專案,將專案命名為 lis302dl_test。

依據在 stm32f407g 使用 uart 設定好 uart4。

接下來按照下圖將 PE3 設為 GPIO_Output、PE0 設為 GPIO_EXT0。

設定 PA5PA6PA7,開啟 SPI1。

啟用 EXTI line0 interrupt,並設定其 priority:

接下來按下 Ctrl+S 自動產生程式碼。

檢查 WHO_AM_I_ADDR

LIS302DL 有許多用來讀寫的 register,具體的編號以及功能可以參閱官方 datasheet 或是參閱 lis302dl.h,如果要直接 include lis302dl.h,請手動把缺失的相依函式庫補齊或刪除。

這裡列出本教學會使用的 register,可以將其定義在 /* USER CODE BEGIN PM */ 下方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Private macro -------------------------------*/
/* USER CODE BEGIN PM */
#define LIS302DL_WHO_AM_I_ADDR               0x0F
#define LIS302DL_CTRL_REG1_ADDR              0x20
#define LIS302DL_CTRL_REG2_ADDR              0x21
#define LIS302DL_CTRL_REG3_ADDR              0x22
#define LIS302DL_STATUS_REG_ADDR             0x27
#define LIS302DL_OUT_X_ADDR                  0x29
#define LIS302DL_OUT_Y_ADDR                  0x2B
#define LIS302DL_OUT_Z_ADDR                  0x2D
#define LIS302DL_FF_WU_CFG1_REG_ADDR         0x30
#define LIS302DL_FF_WU_SRC1_REG_ADDR         0x31
#define LIS302DL_FF_WU_THS1_REG_ADDR         0x32
#define LIS302DL_FF_WU_DURATION1_REG_ADDR    0x33
/* USER CODE END PM */

接下來在 /* USER CODE BEGIN 0 */ 實作用來讀寫 SPI1 的函式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Private user code --------------------------*/
/* USER CODE BEGIN 0 */
void MEMS_Write(uint8_t address, uint8_t data){
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,&address,1,10);
	HAL_SPI_Transmit(&hspi1,&data,1,10);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
}

void MEMS_Read(uint8_t address, uint8_t *data){
    address |= 0x80;
	HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,&address,1,10);
	HAL_SPI_Receive(&hspi1,data,1,10);
	HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3, GPIO_PIN_SET);
}
/* USER CODE END 0 */

然後在專案的 main 函式的 while 迴圈改寫如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
  uint8_t data;
  MEMS_Read(LIS302DL_WHO_AM_I_ADDR, &data);
  if(data == 0x3B)
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
  HAL_Delay(500);
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

如果三軸加速規型號為 LIS302DL,綠色 LED 燈會持續閃爍。

測試輸出 X、Y、Z 字串

參考 datasheet:

設定 CTRL_REG1,output rate 設為 400 Hz,X、Y、Z 全都啟用:

1
2
3
/* USER CODE BEGIN 2 */
MEMS_Write(LIS302DL_CTRL_REG1_ADDR, 0x47);
/* USER CODE END 2 */

CTRL_REG2 不需要更動。

CTRL_REG3 以及之後的 register 在稍後使用 external interrupt 時才需要用到。

接下來改寫 while 迴圈,透過 uart4 持續每隔 0.1 秒印出 X、Y、Z 三軸的加速度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
  uint8_t x, y, z;
  MEMS_Read(LIS302DL_OUT_X_ADDR, &x);
  MEMS_Read(LIS302DL_OUT_Y_ADDR, &y);
  MEMS_Read(LIS302DL_OUT_Z_ADDR, &z);
  
  char Monitor_data[100];
  memset(Monitor_data,'\0',sizeof(Monitor_data));
  sprintf(Monitor_data, "%3d %3d %3d\n\r", x, y, z);
  HAL_UART_Transmit(&huart4, (uint8_t *)Monitor_data, strlen(Monitor_data), HAL_MAX_DELAY);
  
  HAL_Delay(100);
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

Demo:

測試使用 External Interrupt

一樣參考 datasheet,設置以下 register:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* USER CODE BEGIN 2 */
MEMS_Write(LIS302DL_CTRL_REG1_ADDR, 0x47);
MEMS_Write(LIS302DL_CTRL_REG2_ADDR, 0x00);

// use FF_WU_1 to trigger interrupt on INT1
MEMS_Write(LIS302DL_CTRL_REG3_ADDR, 0x01);

// enable interrupt on X high, Y high
MEMS_Write(LIS302DL_FF_WU_CFG1_REG_ADDR, 0x0A);

// free-fall/wake-up threshold (7 bit)
MEMS_Write(LIS302DL_FF_WU_THS1_REG_ADDR, 0x55);

// free-fall/wake-up duration (8 bit)
// Step 2.5 msec, from 0 to 637.5 msec if ODR=400Hz,
// else step 10 msec, from 0 to 2.55 sec when ODR=100Hz.
MEMS_Write(LIS302DL_FF_WU_DURATION1_REG_ADDR, 0x04);
/* USER CODE END 2 */

在 main.c 實作 HAL_GPIO_EXTI_Callback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* USER CODE BEGIN 0 */

// ...

int is_handling = 0, count = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	if (is_handling) {
		return;
	}
	is_handling = 1;

	char Monitor_data[100];
	memset(Monitor_data,'\0', sizeof(Monitor_data));
	sprintf(Monitor_data, "interrupt! %d\n\r", count++);
	HAL_UART_Transmit(&huart4, (uint8_t *)Monitor_data, strlen(Monitor_data), HAL_MAX_DELAY);

	is_handling = 0;
}
/* USER CODE END 0 */

執行成功後,預期每一次左右搖晃,uart4 都會印出 “interrupt!” 和當前 interrupt 次數: