はじめに
ドローンの姿勢制御などで必須パーツである加速度センサを動かしてみました。加速度センサの使用経験は初めてでしたので仕様書を読みながら進めました。
今回はアナログ・デバイセズ株式会社のADXL345を使いました。選んだ理由としてはアナログ出力ではなくSPI通信でコントロールできるので今回使用しました。
評価ボードはST MicroelectronicsのNUCLEO-L476RGが手元にあったので、これを使用します。ADXL345加速度センサは作業が楽な基板タイプをAmazonで購入しました。
動作環境

NUCLEO-L476ZG評価ボード

STM32CubeIDEを起動
STM32CubeIDEを起動して、File - New - STM32 Projectをクリック。
Board Selectorをクリック。
Command Part Numberの欄でNUCLEO-L476RGを選択しNextをクリック

Project Nameはtest_adxl345(何でもいいです)と入力してFinishをクリック。

"Initialize all peripherals with their default Mode?"はYesをクリック。

下の画面(test_adxl345.ioc)が表示されます。

SPI2を使用します。またCHとしてGPIOを使用します。ピンの部分をクリックして下のように変更します。
- PB15 SPI2_MOSI
- PB14 SPI2_MISO
- PB13 SPI2_sck
- PB12 GPIO_Output

Connctivity -> SPI2をクリック。下のように設定。
- Frame Format
Motorola - Mode
Full-Duplex Master - Data Size
16 Bits - First Bit
MSB First - Prescaler
32(Baud Rateは2.5MBits/sとなる) - Clock Polarity
High - Clock Phase
2 Edge

File -> Save allをクリック。下のウィンドウが表示され、"Do you want generate Code?"と聞かれるのでYesをクリック。

"Open Associated Perspective?"というウィンドウが表示さっるので、Yesをクリック。これで先ほど設定した内容のコードが生成されることになります。

main.cを下に示します。エレガントなプログラムからは程遠いですが、とりあえず動きます。
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi2;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_SPI2_Init(void);
/* USER CODE BEGIN PFP */
uint8_t aRxBuffer[10];
uint8_t aTxBuffer[] = {0, 0};
uint8_t vallower, valupper;
int16_t valx, valy, valz;
uint16_t uvalx;
float capturedata;
uint8_t bdata = 0x12;
uint8_t hexdataAscii[10];
char msgG[] = "G\n\r";
char s[32];
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t *HexToAscii(uint8_t hexdata);
uint8_t *HexToAscii2byte(uint8_t hexdata1, uint8_t hexdata2);
void OperateSpi(uint8_t *data);
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
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_USART2_UART_Init();
MX_SPI2_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(1000);
HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin); ⓵
uint8_t msg[] = "Capture data\n\r";
HAL_UART_Transmit(&huart2, msg, strlen((char*)msg), 100);
uint8_t msgID[] = "Read ID\n\r"; ⓶
HAL_UART_Transmit(&huart2, msgID, strlen((char*)msgID), 100);
aTxBuffer[1] = 0x80;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
uint8_t msgFullRes[] = "Write full res\n\r"; ⓷
HAL_UART_Transmit(&huart2, msgFullRes, strlen((char*)msgFullRes), 100);
aTxBuffer[0] = 0x08;
aTxBuffer[1] = 0x31; // Write
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
uint8_t msgStart[] = "Write measure start\n\r";
HAL_UART_Transmit(&huart2, msgStart, strlen((char*)msgStart), 100);
aTxBuffer[0] = 0x08;
aTxBuffer[1] = 0x2d; // Write
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
uint8_t msgXLower[] = "Read X lower\n\r"; ⓸
HAL_UART_Transmit(&huart2, msgXLower, strlen((char*)msgXLower), 100);
aTxBuffer[1] = 0x80 + 0x32;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
vallower = aRxBuffer[0];
uint8_t msgXUpper[] = "Read X upper\n\r";
HAL_UART_Transmit(&huart2, msgXUpper, strlen((char*)msgXUpper), 100);
aTxBuffer[1] = 0x80 + 0x33;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
valupper = aRxBuffer[0];
uint8_t msgXHex[] = "X = 0x";
HAL_UART_Transmit(&huart2, msgXHex, strlen((char*)msgXHex), 100);
HAL_UART_Transmit(&huart2, HexToAscii2byte(valupper, vallower), strlen(HexToAscii2byte(valupper, vallower)), 100);
uint8_t msgXFloat[] = "X = ";
HAL_UART_Transmit(&huart2, msgXFloat, strlen((char*)msgXFloat), 100);
valx = valupper;
valx = (valx << 8) + vallower;
capturedata = valx * 0.004;
snprintf (s, sizeof(s), "%lf", capturedata);
HAL_UART_Transmit(&huart2, s, strlen((char*)s), 100);
HAL_UART_Transmit(&huart2, msgG, strlen((char*)msgG), 100);
uint8_t msgYLower[] = "Read Y lower\n\r"; ⓹
HAL_UART_Transmit(&huart2, msgYLower, strlen((char*)msgYLower), 100);
aTxBuffer[1] = 0x80 + 0x34;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
vallower = aRxBuffer[0];
uint8_t msgYUpper[] = "Read Y upper\n\r";
HAL_UART_Transmit(&huart2, msgYUpper, strlen((char*)msgYUpper), 100);
aTxBuffer[1] = 0x80 + 0x35;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
valupper = aRxBuffer[0];
uint8_t msgYHex[] = "Y = 0x";
HAL_UART_Transmit(&huart2, msgYHex, strlen((char*)msgYHex), 100);
HAL_UART_Transmit(&huart2, HexToAscii2byte(valupper, vallower), strlen(HexToAscii2byte(valupper, vallower)), 100);
uint8_t msgYFloat[] = "Y = ";
HAL_UART_Transmit(&huart2, msgYFloat, strlen((char*)msgYFloat), 100);
valx = valupper;
valx = (valx << 8) + vallower;
capturedata = valx * 0.004;
snprintf (s, sizeof(s), "%lf", capturedata);
HAL_UART_Transmit(&huart2, s, strlen((char*)s), 100);
HAL_UART_Transmit(&huart2, msgG, strlen((char*)msgG), 100);
uint8_t msgZLower[] = "Read Z lower\n\r"; ⓺
HAL_UART_Transmit(&huart2, msgZLower, strlen((char*)msgZLower), 100);
aTxBuffer[1] = 0x80 + 0x36;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
vallower = aRxBuffer[0];
uint8_t msgZUpper[] = "Read Z upper\n\r";
HAL_UART_Transmit(&huart2, msgZUpper, strlen((char*)msgZUpper), 100);
aTxBuffer[1] = 0x80 + 0x37;
OperateSpi(aTxBuffer);
HAL_UART_Transmit(&huart2, HexToAscii(aRxBuffer[0]), strlen(HexToAscii(aRxBuffer[0])), 100);
valupper = aRxBuffer[0];
uint8_t msgZHex[] = "Z = 0x";
HAL_UART_Transmit(&huart2, msgZHex, strlen((char*)msgZHex), 100);
HAL_UART_Transmit(&huart2, HexToAscii2byte(valupper, vallower), strlen(HexToAscii2byte(valupper, vallower)), 100);
uint8_t msgZFloat[] = "Z = ";
HAL_UART_Transmit(&huart2, msgZFloat, strlen((char*)msgZFloat), 100);
valx = valupper;
valx = (valx << 8) + vallower;
capturedata = valx * 0.004;
snprintf (s, sizeof(s), "%lf", capturedata);
HAL_UART_Transmit(&huart2, s, strlen((char*)s), 100);
HAL_UART_Transmit(&huart2, msgG, strlen((char*)msgG), 100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief SPI2 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI2_Init(void)
{
/* USER CODE BEGIN SPI2_Init 0 */
/* USER CODE END SPI2_Init 0 */
/* USER CODE BEGIN SPI2_Init 1 */
/* USER CODE END SPI2_Init 1 */
/* SPI2 parameter configuration*/
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 7;
hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI2_Init 2 */
/* USER CODE END SPI2_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PB12 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
uint8_t *HexToAscii(uint8_t hexdata)
{
//uint8_t hexdataAscii[10];
bdata = hexdata;
bdata = bdata >> 4;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[0] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[0] = bdata;
}
bdata = 0;
bdata = hexdata & 0x0f;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[1] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[1] = bdata;
}
hexdataAscii[2] = 0xd;
hexdataAscii[3] = 0xa;
hexdataAscii[4] = 0;
return &hexdataAscii[0];
}
uint8_t *HexToAscii2byte(uint8_t hexdata1, uint8_t hexdata2)
{
bdata = hexdata1;
bdata = bdata >> 4;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[0] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[0] = bdata;
}
bdata = 0;
bdata = hexdata1 & 0x0f;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[1] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[1] = bdata;
}
bdata = hexdata2;
bdata = bdata >> 4;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[2] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[2] = bdata;
}
bdata = 0;
bdata = hexdata2 & 0x0f;
if (bdata <=9){
bdata = bdata + 0x30;
hexdataAscii[3] = bdata;
}
else {
bdata = bdata + 0x57;
hexdataAscii[3] = bdata;
}
hexdataAscii[4] = 0xd;
hexdataAscii[5] = 0xa;
hexdataAscii[6] = 0;
return &hexdataAscii[0];
}
void OperateSpi(uint8_t *txdata)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,0);
HAL_SPI_TransmitReceive(&hspi2,txdata,(uint8_t*)aRxBuffer,1,10);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,1);
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
いくつか簡単に説明します。
まず今回のプログラムでは以下のエラーが発生します。
Problem description: The float formatting support is not enabled, check your MCU Settings from "Project Properties > C/C++ Build > Settings > Tool Settings", or add manually "-u _printf_float" in linker flags.
なので下のように"Use float with printf from newlib-nano (-u_print_float)"にチェックを入れApply and Closeをクリックします。これでエラーが消えます。

動作概要としては、加速度センサーのX軸、Y軸、Z軸(それぞれUppsr, Lowerバイトの16ビットデータ)を繰り返し取得して、そのHEXデータおよびGに変換したデータをTera termで表示させます。シリアルのスピードは115200bpsです。
⓵はLEDのON/OFFをさせます。プログラムが動いているかチェックしているだけです。
⓶は加速度センサADXL345のIDを読み出しています。ADKL345との通信が正常に行われているかの確認です。IDの値は0xe5であり、正常に読み出されています。
⓷はFULL_RESビットを1に設定しています。これによりGの分解能は4mg/LSBになるということです。
⓸、⓹、⓺はそれぞれX軸、Y軸、Z軸(それぞれUppsr, Lowerバイトの16ビットデータ)を繰り返し取得して、そのHEXデータおよびGに変換したデータを生成しています。
ADXL345とNUCLEO-L476RGとの接続
ADXL345加速度センサ基板とNUCLEO-L476RGのピンアサインを以下にしまします。
また各信号の接続表をその下に示します。


ADXL345加速度センサ基板 | NUCLEO-L476RG |
VCC | CN7-16 |
GND | CN10-20 |
CS | CN10-16 |
SCL | CN10-30 |
MISO | CN10-26 |
MOSI | CN10-28 |

動作確認
PCとNUCLEO-L476RGとをUSBケーブルで接続しTera termを起動してシリアルをSTMicroelectronics STLinkに設定します。その後設定 -> シリアルポートでスピードを115200に変更し、現在の接続を再設定ボタンをクリック。

プロジェクトtest_adxl345の上で右クリックしDebug As -> Debug Configulationsをクリック。
ここではNameをtest_adxl345とし、Project及びelfファイルを生成したプロジェクト及びファイルに設定してApply及びDebugをクリック。

左上の

をクリック。これによりプログラムが動きます。LEDが点滅しているので一応プログラムは動いているように思います。
またTera termでは下のようにデータが表示されました。バラツキはありますが、ADXL345をだいたい水平にしておりZ軸方向が1G程度と表示されます。X軸方向及びY軸方向に向きを変えたところ、それぞれの方向に約1Gと表示されたので一応正しく動いていると思います。

この加速度センサは他にもいろいろと設定できるようですが今回はここまでにします。