IMU über SPI steigt nach wenigen Sekunden aus
Hallo zusammen,
ich habe ein Nucleo-64 Board (STM32F411). Ich möchte (irgendwann mal eine Reihe von) Sensoren über SPI ansprechen. Dazu habe ich mir eine Platine gebaut, die die drei SPI-Bahnen SCLK, MOSI und MISO vom STM32 aufteilt (jede hat noch 100 Ohm in Reihe), und über die ich jeweils mit einem Flachbandkabel jeden Sensor nach belieben dran stecken kann. Jeder Sensor hat natürlich seinen eigenen SS-Pin.
Jetzt "scheitere" ich an: Sensor 1!
Zur Info, was vorher geschah: ich habe mir ein selbst zusammengebauten Controller (nennen wir ihn auch mal "Sensor") als SPI-Slave konfiguriert und kann erfolgreich nach Belieben alle Daten hin und her schieben. Beliebig oft und lange.
SPI funktioniert also erstmal über den STM32.
Jetzt klemme ich den ersten echten Sensor an: einen LSM6DS3, Breakout-Board von Sparkfun (Link). Davon habe ich ca. 3 Stk hier funktionsfähig, und auch schon teilweise erfolgreich in einer anderen Konfiguration eingesetzt. Bei allen drei Sensoren ist das Problem das gleiche.
Was jetzt passiert: ich frage das WHO_AM_I-Register ab, um zu überprüfen, ob er da ist.
Jedes Mal, wenn ich den Sensor anstecke, wird das Register erfolgreich ausgelesen. Nach mal 1s, mal 20s verweigert der LSM allerdings die Antwort. Dann aber auch für immer. Erst wenn ich den Sensor abziehe und wieder auf mein Board stecke, wird er wieder aktiv - erneut für 1-20s. Danach steigt er wieder aus.
Ich habe mir am Oszi alle Kanäle angeschaut: SS, SCLK und MOSI sind im erfolgreichen Betrieb identisch, wie in dem Fall, wenn der Sensor aussteigt - keine Qualitätseinbußen. Ich habe jetzt mal SCLK bei 500kHz und 1MHz getestet - das Verhalten ist bei beiden Taktraten identisch.
Diese Register frage ich im Full Duplex-Mode ab:
1) WHO_AM_I
2) Gyro_x_low-Byte
3) Gyro_x_high-Byte
4) Gyro_y_low-Byte
5) Gyro_y_high-Byte
6) Gyro_z_low-Byte
7) Gyro_z_high-Byte
8)Acc_x_low-Byte
9) Acc_x_high-Byte
...
13) Acc_z_high-Byte
14) 0x00 - dient nur zum Lesen des letzten Registers
Wenn der Sensor aussteigt, passiert es lediglich eher zufällig (gefühlt einer von 5 Abfrage-Sätzen, dass der MISO vom Sensor während Befehl 1 (WHO_AM_I) auf komplett low bleibt, genau ab dem Beginn von Befehl 2 (also mit dem ersten clk auf SCLK) komplett auf high geht und dort bleibt, bis der SS wieder deaktiviert wird).
Zugegeben, ich weiß nichtmal, was euch weiterhelfen könnte, um mir weiterzuhelfen.
Hat jemand von euch schonmal so ein Problem gehabt? Oder wenn ihr sagt, in welche Richtung ich schauen sollte, dann gebt mal Bescheid - dann stelle ich dann noch ein paar mehr Infos zur Verfügung.
Grüß, NRicola
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Chris und Moppi,
danke für euer Reindenken. Hier die benötigten Informationen:
@Chris:
Der STM32 läuft mit 16MHz. Das reicht erstmal aus, um auch bei einer SCLK-Rate von 1MHz noch den einen Sensor erfolgreich auszulesen - wenn nichts anderes angeschlossen ist. Wie gesagt: neuerdings auf 500kHz reduziert.
Der Code ist unten. Ich habe ihn mal auf die SPI- und IMU-relevanten Sachen beschnitten. Dann müsst ihr nicht die ganzen 900 Zeilen Code durchschauen.
Ich gestalte lediglich die SS-Initialisierung mit:
SS auf high (Zeile 103)
Die anderen Leitungen stimmen zumindest zwischen den einzelnen SPI-Übertragung in der Polarität.
Wenn der Sensor positiv mit WHO_AM_I antwortet, aber noch nicht richtig konfiguriert ist, dann wird er erneut konfiguriert (Zeile 196). Dass das ab und zu passiert, sehe ich an einer LED, die in dann immer schalten lasse.
@Moppi:
Den Schaltplan habe ich angehangen. Auch reduziert auf's Wesentliche. Der Schaltplan vom IMU-Breakout-Board ist hier:https://cdn.sparkfun.com/datasheets/...eakout_v10.pdf
Ohne 100Ohm in Serie hatte ich es am Anfang des Jahres probiert. Diese Wiederstände bügeln etwas die steigenden und fallenden Flanken. Gerade bei langen Leitungen bekommt man die übertragenen Bits wieder etwas den Rechteck-Verläufen angenähert. Ansonsten kommt es zu teils recht starken Über- und Unterschwingern auf den Zielspannungsniveaus. Dann muss man allerdings mit der SCLK-Frequenz aufpassen, dass die nicht zu hoch gewählt wird, da sonst die Spannungsniveaus nicht mehr erreicht werden. Bei 500kHz ist das Verhalten aber ohnehin vernachlässigbar. Aber durchaus gängige Praxis, siehe
hier: https://www.first-sensor.com/cms/upl...CE_D_11156.pdf (S. 10)
oder hier: https://www.edaboard.com/threads/spi-serial-resistor.345440/
Wenn ihr noch etwas braucht, gebt Bescheid!
Vielen Dank euch!
Grüß, NRicola
Code:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdbool.h"
#include "math.h"
#include "../Peripherals/LSM6DS3.h" // Funktionen sind hier angehangen (siehe unten)
#include "../Peripherals/OLED_128x32.h"
/* 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 ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c3;
SPI_HandleTypeDef hspi2;
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
/* USER CODE BEGIN PV */
const uint8_t IMU_datlength = 8; // die letzten x Messwerte werden mitgespeichert
struct IMU_Struct IMU1; // besteht aus IMU1.status, sowie 6 Arrays [0:IMU_datlength-1] für acc und gyr in x,y,z
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_SPI2_Init(void);
static void MX_I2C3_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 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_DMA_Init();
MX_USART2_UART_Init();
MX_SPI2_Init();
MX_I2C3_Init();
/* USER CODE BEGIN 2 */
IMU1.status=0;
for(uint8_t i=0;i<IMU_datlength-1;i++){
IMU1.acc_x[i] = 0;
IMU1.acc_y[i] = 0;
IMU1.acc_z[i] = 0;
IMU1.gyr_x[i] = 0;
IMU1.gyr_y[i] = 0;
IMU1.gyr_z[i] = 0;
}
//--------------------------------
IMU1_SS_Disable(); // SS auf high
HAL_Delay(20); // 20ms start-up time für IMUs
// OLED-Display
uint16_t OLED_refresh_cntr=0;
uint16_t OLED_refresh_max=5;
OLED_Init();
OLED_Clear_Screen();
OLED_IMU1_vars();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
IMU1 = IMU1_Read_Values();
if(OLED_refresh_cntr>=OLED_refresh_max){ OLED_refresh_cntr=0; OLED_IMU1_vars(); // Messwerte des IMU1 anzeigen
}else{ OLED_refresh_cntr++;
}
}
/* USER CODE END 3 */
}
/**
* @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_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI2_Init 2 */
/* USER CODE END SPI2_Init 2 */
}
//-------------------------------------------------------------------------------
//---------- Inhalt LSM6DS3.c ---------------------------------------------------
//-------------------------------------------------------------------------------
void IMU1_config(void){
const uint8_t CTRL1_XL_addr = 0x10;
const uint8_t CTRL1_XL_write = 0x3B; // 0011_10_11
const uint8_t CTRL2_G_addr = 0x11;
const uint8_t CTRL2_G_write = 0x34; // 0011_01_0_0
Send_2Bytes_IMU1(CTRL1_XL_addr,CTRL1_XL_write);
Send_2Bytes_IMU1(CTRL2_G_addr,CTRL2_G_write);
}
struct IMU_Struct IMU1_Read_Values(void){
const uint8_t WHO_AM_I = 0x69; // ist die korrekte Antwort, falls der Sensor da ist
const uint8_t addr_WHO_AM_I_read = 0x8F;
const uint8_t addr_CTRL1_XL_read = 0x90;
const uint8_t addr_acc_x_l_read = 0xA8;
const uint8_t addr_acc_x_h_read = 0xA9;
//------------------------------
//--------------------------------------------------
if (Read_1Byte_IMU1(addr_WHO_AM_I_read) == WHO_AM_I){ // Wenn IMU1 auf WHO_AM_I reagiert,
IMU1.status=1; //
if (Read_1Byte_IMU1(addr_CTRL1_XL_read) == 0){ IMU1_config(); // Falls der Accelerometer im Sensor nicht korrekt konfiguriert ist, konfiguriere nochmal neu
HAL_GPIO_TogglePin(LED_GN_GPIO_Port, LED_GN_Pin);
}
}else{
IMU1.status=0; // Status: Sensor ist nicht angeschlossen
}
if(IMU1.status==1){ // Wenn IMU1 erkannt,
IMU1_Status_SET(); // dann Status-LED anschalten
}else{
IMU1_Status_RESET(); // ansonsten Status-LED ausschalten
}
IMU1.acc_x[IMU_datlength-1]=Read_1Byte_IMU1(addr_acc_x_l_read); // einen Messwert lesen
return IMU1;
}
void Send_2Bytes_IMU1(uint8_t Byte1,uint8_t Byte2){
const uint8_t timeout = 10;
uint8_t TxBuffer[2];
TxBuffer[0]=Byte1;
TxBuffer[1]=Byte2;
IMU1_SS_Enable(); // SS auf low
HAL_SPI_Transmit(&hspi2, TxBuffer, 2, timeout);
IMU1_SS_Disable(); // SS auf high
}
uint8_t Read_1Byte_IMU1(uint8_t Byte){
const uint8_t timeout = 10;
uint8_t TxBuffer[2];
TxBuffer[0]=Byte; // Adresse des Read-Bytes
TxBuffer[1]=0;
uint8_t RxBuffer[2];
IMU1_SS_Enable(); // SS auf low
HAL_SPI_TransmitReceive(&hspi2, TxBuffer, RxBuffer, 2, timeout);
IMU1_SS_Disable(); // SS auf high
return RxBuffer[1];
}
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Chris,
dann also hier die fehlenden Funktionen im Code und der Schaltplan des Nucleo-Shields. Sollte dir noch etwas fehlen, gib Bescheid!
Grüß, NRicola
Code:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdbool.h"
#include "math.h"
#include "../Peripherals/LSM6DS3.h" // Funktionen sind hier angehangen (siehe unten)
#include "../Peripherals/OLED_128x32.h"
/* 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 ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c3;
SPI_HandleTypeDef hspi2;
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
/* USER CODE BEGIN PV */
const uint8_t IMU_datlength = 8; // die letzten x Messwerte werden mitgespeichert
struct IMU_Struct IMU1; // besteht aus IMU1.status, sowie 6 Arrays [0:IMU_datlength-1] für acc und gyr in x,y,z
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_SPI2_Init(void);
static void MX_I2C3_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 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_DMA_Init();
MX_USART2_UART_Init();
MX_SPI2_Init();
MX_I2C3_Init();
/* USER CODE BEGIN 2 */
IMU1.status=0;
for(uint8_t i=0;i<IMU_datlength-1;i++){
IMU1.acc_x[i] = 0;
IMU1.acc_y[i] = 0;
IMU1.acc_z[i] = 0;
IMU1.gyr_x[i] = 0;
IMU1.gyr_y[i] = 0;
IMU1.gyr_z[i] = 0;
}
//--------------------------------
Mot001_SS_Disable(); // SS auf high
IMU1_SS_Disable(); // SS auf high
HAL_Delay(20); // 20ms start-up time für IMUs
// OLED-Display
uint16_t OLED_refresh_cntr=0;
uint16_t OLED_refresh_max=5;
OLED_Init();
OLED_Clear_Screen();
OLED_IMU1_vars();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
IMU1 = IMU1_Read_Values();
if(OLED_refresh_cntr>=OLED_refresh_max){ OLED_refresh_cntr=0; OLED_IMU1_vars(); // Messwerte des IMU1 anzeigen
}else{ OLED_refresh_cntr++;
}
}
/* USER CODE END 3 */
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** 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_NONE;
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_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C3 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C3_Init(void)
{
/* USER CODE BEGIN I2C3_Init 0 */
/* USER CODE END I2C3_Init 0 */
/* USER CODE BEGIN I2C3_Init 1 */
/* USER CODE END I2C3_Init 1 */
hi2c3.Instance = I2C3;
hi2c3.Init.ClockSpeed = 400000;
hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C3_Init 2 */
/* USER CODE END I2C3_Init 2 */
}
/**
* @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_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
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;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, LED_GN_Pin|LED_RD_Pin|Mot003_Status_Pin|Mot002_Status_Pin
|IMU2_SS_Pin|IMU3_SS_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, IMU1_Status_Pin|IMU2_Status_Pin|Mot001_Status_Pin|Mot003_SS_Pin
|IMU3_Status_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|IMU1_SS_Pin|Mot001_SS_Pin|HDMode_LED_Pin
|Mot002_SS_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : Reset_SW_Pin Hall_Detection_Button_PC14_Pin */
GPIO_InitStruct.Pin = Reset_SW_Pin|Hall_Detection_Button_PC14_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : LED_GN_Pin LED_RD_Pin Mot003_Status_Pin Mot002_Status_Pin
IMU2_SS_Pin IMU3_SS_Pin */
GPIO_InitStruct.Pin = LED_GN_Pin|LED_RD_Pin|Mot003_Status_Pin|Mot002_Status_Pin
|IMU2_SS_Pin|IMU3_SS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : IMU1_Status_Pin IMU2_Status_Pin Mot001_Status_Pin Mot003_SS_Pin
IMU3_Status_Pin */
GPIO_InitStruct.Pin = IMU1_Status_Pin|IMU2_Status_Pin|Mot001_Status_Pin|Mot003_SS_Pin
|IMU3_Status_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 IMU1_SS_Pin Mot001_SS_Pin HDMode_LED_Pin
Mot002_SS_Pin */
GPIO_InitStruct.Pin = GPIO_PIN_0|IMU1_SS_Pin|Mot001_SS_Pin|HDMode_LED_Pin
|Mot002_SS_Pin;
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);
/*Configure GPIO pins : Mot002_Error_Pin_Pin Mot003_Error_Pin_Pin Mot001_Error_Pin_Pin HDMode_SW_Pin */
GPIO_InitStruct.Pin = Mot002_Error_Pin_Pin|Mot003_Error_Pin_Pin|Mot001_Error_Pin_Pin|HDMode_SW_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* 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 */
/* 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,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
//-------------------------------------------------------------------------------
//---------- Inhalt LSM6DS3.c ---------------------------------------------------
//-------------------------------------------------------------------------------
void IMU1_config(void){
const uint8_t CTRL1_XL_addr = 0x10;
const uint8_t CTRL1_XL_write = 0x3B; // 0011_10_11
const uint8_t CTRL2_G_addr = 0x11;
const uint8_t CTRL2_G_write = 0x34; // 0011_01_0_0
Send_2Bytes_IMU1(CTRL1_XL_addr,CTRL1_XL_write);
Send_2Bytes_IMU1(CTRL2_G_addr,CTRL2_G_write);
}
struct IMU_Struct IMU1_Read_Values(void){
const uint8_t WHO_AM_I = 0x69; // ist die korrekte Antwort, falls der Sensor da ist
const uint8_t addr_WHO_AM_I_read = 0x8F;
const uint8_t addr_CTRL1_XL_read = 0x90;
const uint8_t addr_acc_x_l_read = 0xA8;
const uint8_t addr_acc_x_h_read = 0xA9;
//------------------------------
//--------------------------------------------------
if (Read_1Byte_IMU1(addr_WHO_AM_I_read) == WHO_AM_I){ // Wenn IMU1 auf WHO_AM_I reagiert,
IMU1.status=1; //
if (Read_1Byte_IMU1(addr_CTRL1_XL_read) == 0){ IMU1_config(); // Falls der Accelerometer im Sensor nicht korrekt konfiguriert ist, konfiguriere nochmal neu
HAL_GPIO_TogglePin(LED_GN_GPIO_Port, LED_GN_Pin);
}
}else{
IMU1.status=0; // Status: Sensor ist nicht angeschlossen
}
if(IMU1.status==1){ // Wenn IMU1 erkannt,
IMU1_Status_SET(); // dann Status-LED anschalten
}else{
IMU1_Status_RESET(); // ansonsten Status-LED ausschalten
}
IMU1.acc_x[IMU_datlength-1]=Read_1Byte_IMU1(addr_acc_x_l_read); // einen Messwert lesen
return IMU1;
}
void Send_2Bytes_IMU1(uint8_t Byte1,uint8_t Byte2){
const uint8_t timeout = 10;
uint8_t TxBuffer[2];
TxBuffer[0]=Byte1;
TxBuffer[1]=Byte2;
IMU1_SS_Enable(); // SS auf low
HAL_SPI_Transmit(&hspi2, TxBuffer, 2, timeout);
IMU1_SS_Disable(); // SS auf high
}
uint8_t Read_1Byte_IMU1(uint8_t Byte){
const uint8_t timeout = 10;
uint8_t TxBuffer[2];
TxBuffer[0]=Byte; // Adresse des Read-Bytes
TxBuffer[1]=0;
uint8_t RxBuffer[2];
IMU1_SS_Enable(); // SS auf low
HAL_SPI_TransmitReceive(&hspi2, TxBuffer, RxBuffer, 2, timeout);
IMU1_SS_Disable(); // SS auf high
return RxBuffer[1];
}
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo zusammen, Hallo Chris,
ich bitte um Entschuldigung für die späte Rückmeldung. Ich war in der Zeit nicht untätig. Chris, vielen Dank für deine Hilfe. Tatsächlich gab es ein Init-Problem: zwar war das angesprochene IMU korrekt initialisiert. Allerdings haben die SS-Initialisierungen der anderen IMUs gefehlt. Immer wenn ich einen der anderen IMUs angeschlossen habe, haben die sich natürlich auch angesprochen gefühlt (SS noch low/aktiv gewesen). Das hat dann auf MISO ein großes Chaos verursacht.
Da ich mehr in der Hardware das Problem vermutet habe, habe ich da mal etwas beleuchten können. Und ich behaupte mal den Übeltäter gefunden zu haben:
Die Spannung! Welche Spannung? Alle!
Der LSM6DS3 möchte eine Versorgungsspannung von 1,7...3,6V haben.
"Absolute maximum Ratings": -0,3...4,8V
An den Signalleitungen: 0,3...Vcc+0,3V
Klingt an sich erstmal im Limit, wenn ich eine Versorgungs- und SPI-Nennspannung von 3,3V anlege. Die langen Leitungen sind hier aber nicht gut. Lang bezieht sich schon auf 0,2-0,4m pro Sensor (Summiert sich über alle Sensoren des gleichen Busses). Wie oben schon geschrieben, nutze ich in Reihe geschaltete Widerstände, um Überschwinger an den Flanken abzumildern. Mein bisheriger Aufbau:
MCU ____________________________________ IMU
MOSI -|100Ohm|-------------------------- MISO
SCLK -|100Ohm|-------------------------- SCLK
MISO --------------------------|100Ohm|- MISO
SS ------------------------------------- SS
Das reicht allerdings nicht. Ich habe mal die Spannung (Vcc für IMU und Signale) ein bisschen zurückgenommen: bei ca. 2V funktioniert alles.
Als es funktionierte, konnte ich endlich auch mal mit dem Oszi ran - siehe Bild unten.
Durch die Leitungen kam es teilweise zu erheblichen Über- und Unterschwingern. Vor allem die negativen Spannungen mögen die IMUs nicht. Ab ca. -1V auf irgendeiner der Signalleitungen steigen sie aus.
Ich habe dazu mal eine kleine Variation durchgeführt. Siehe zweites Bild im Anhang. Aufgelistet ist der Spannungsbereich, bis zu dem der IMU noch sauber im Betrieb war (gefühlt ca. 95% aller Signale vom Sensor i.O.). Im Durchschnitt ist er 0,02V unter dem min-Wert bzw. 0,15V über dem max-Wert permanent ausgestiegen. Auf den Wegen dorthin hat der Anteil der fehlerhaften Übertragungen zugenommen.
Beim Eintrag "Diode" wird eine Diode von GND ----|>|---- SCLK gespannt. Beim blau Geschriebenen war es eine 1N4148, beim schwarz geschriebenen eine 1N4005. Wie man sieht, funktionierte die 1N4005 besser.
Man sieht, dass es einen großen Unterschied machen kann, wo man diese Widerstände platziert - auf der IMU-Seite oder auf der µC-Seite. Wenn man auf die 3,3V kommen möchte, ist wohl wichtig, dass auf beiden Seiten Widerstände vorhanden sind.
Fazit für mich:
Ich werde jetzt nur noch mit 2-2,2V unterwegs sein und damit hoffentlich auch mit meinen Ziel-Leitungslängen von 1m klarkommen.
Ich hoffe, dass jemand anderes von dieser Erkenntnis hier auch profitieren kann!
Grüß, NRicola