I have tried to work with LIS3DSH sensor using SPI protocol. I applied procedures on STM32L0 Discovery kit LoRa. But it didn't work.
My problem is The LIS3DSH x-y-z-axis output value is invalid and the value is not changed. No matter which direction I rotate the device but I check ID of LIS3DSH is correct ( 0011 1111)
Component
STM32L0 Discovery kit LoRa
LIS3DSH (https://www.amazon.com/LIS3DSH-High-Resolution-Three-axis-Accelerometer-Triaxial/dp/B07QS5D9K9/ref=sr_1_4?dchild=1&keywords=LIS3DSH&qid=1615701212&sr=8-4)
init SPI2
...
void HW_SPI2_Init(void)
{
/*##-1- Configure the SPI2 peripheral */
/* Set the SPI parameters */
hspi2.Instance = SPI2;
hspi2.Init.BaudRatePrescaler = SpiFrequency(10000000);
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE; // CPHA = 1
// hspi2.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA = 0
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
// hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCPolynomial = 10;
SPI2_CLK_ENABLE(); // Enable clock for SPI 2
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/*##-2- Configure the SPI GPIOs */
HW_SPI2_IoInit();
}
...
initialization LIS3DSH
void HW_SPI2_IoInit(void)
{
GPIO_InitTypeDef initStruct = {0};
initStruct.Mode = GPIO_MODE_AF_PP;
initStruct.Pull = GPIO_PULLUP ;
initStruct.Speed = GPIO_SPEED_FAST;
initStruct.Alternate = SPI2_AF;
HW_GPIO_Init(LIS3DH_SCLK_PORT, LIS3DH_SCLK_PIN, &initStruct);
HW_GPIO_Init(LIS3DH_MISO_PORT, LIS3DH_MISO_PIN, &initStruct);
HW_GPIO_Init(LIS3DH_MOSI_PORT, LIS3DH_MOSI_PIN, &initStruct);
initStruct.Mode = GPIO_MODE_OUTPUT_PP;
initStruct.Pull = GPIO_NOPULL;
HW_GPIO_Init(LIS3DH_NSS_PORT, LIS3DH_NSS_PIN, &initStruct);
HW_GPIO_Write(LIS3DH_NSS_PORT, LIS3DH_NSS_PIN, 1);
}
CS ON or OFF
void HW_SPI2_CS_ON (void)
{
HW_GPIO_Write(LIS3DH_NSS_PORT, LIS3DH_NSS_PIN, 0);
}
void HW_SPI2_CS_OFF (void)
{
HW_GPIO_Write(LIS3DH_NSS_PORT, LIS3DH_NSS_PIN, 1);
}
Read ID
uint8_t LIS3DSH_DH_CHECK_ID (void)
{
uint8_t addr = LIS3Dx_WHO_AM_I_ADDR | LIS3Dx_READ;
HW_SPI2_CS_ON();
HW_SPI2_SPI_1byte_Write_and_Read(addr);
HW_SPI2_CS_OFF();
UsingTypeint_LIS3DSH_DH.ID = au8BufferRead_SPI2[0];
return UsingTypeint_LIS3DSH_DH.ID;
}
Read X axis ( only) (Incorrect)
void LIS3DSH_DH_GET_XYZ (void)
{
uint8_t addr = LIS3Dx_OUT_X_L_ADDR | LIS3Dx_READ ;
HW_SPI2_CS_ON();
HW_SPI2_SPI_1byte_Write_and_Read(addr);
HW_SPI2_CS_OFF();
addr = LIS3Dx_OUT_X_H_ADDR | LIS3Dx_READ ;
HW_SPI2_CS_ON();
HW_SPI2_SPI_1byte_Write_and_Read(addr);
HW_SPI2_CS_OFF();
}
HW_SPI2_1byte_Write_and_Read
bool HW_SPI2_SPI_1byte_Write_and_Read(uint8_t u8Address)
{
if(HAL_SPI_Transmit(&hspi2, (uint8_t *)&u8Address, 1, HAL_MAX_DELAY) == HAL_OK){
if(HAL_SPI_Receive(&hspi2, (uint8_t *)&au8BufferRead_SPI2[0], 1, HAL_MAX_DELAY) == HAL_OK){
return true;
}
}
return false;
}
Signal
output X L
output X H
Am not sure if you are using the state machine and register accesses of the LIS3DSH accurately.
As a reference see the register mapping in https://github.com/STMicroelectronics/stm32-lis3dsh/blob/main/lis3dsh.h (start at line 95) and the code in https://github.com/STMicroelectronics/stm32-lis3dsh/blob/main/lis3dsh.c
The function that actually writes and reads the SPI bus is void ACCELERO_IO_Write(uint8_t *pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) and void ACCELERO_IO_Read(uint8_t *pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead)
This is defined in https://documentation.help/STM32F4-Discovery-BSP/stm32f4__discovery_8c_source.html at lines 560 and 594
In void ACCELERO_IO_Write(uint8_t *pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) the actual SPI write is done at lines 574 to 582
00574 SPIx_WriteRead(WriteAddr);
00577 while(NumByteToWrite >= 0x01)
00578 {
00579 SPIx_WriteRead(*pBuffer);
00580 NumByteToWrite--;
00581 pBuffer++;
00582 }
(as always with SPI first activate register, then write data...)
In SPIx_WriteRead(WriteAddr); the WriteAddr is taken from the register mapping in https://github.com/STMicroelectronics/stm32-lis3dsh/blob/main/lis3dsh.h, start at line 9
(the above code by STM for the LIS3DSH is nice work, this implementation is very clean and structured)
The datasheet of the LIS3DSH is in https://www.st.com/resource/en/datasheet/lis3dsh.pdf, the reference manual is far more detailed it is in https://www.st.com/resource/en/application_note/dm00026768-lis3dsh-3axis-digital-output-accelerometer-stmicroelectronics.pdf it also includes explanation of the state machines, that have to be configured before the device works
Related
I am using a PIC 18F46K22 in SPI master mode to communicate with a Waveshare 1.54" ePaper Module. The FOSC frequency is 8Mhz internal and SPI configuration is FOSC/4. So when I check the output on logic-analyzer some output bits are differ from expected. And there is some deviation in SCL.
#include <xc.h>
#include "config.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "main.h"
//#define _XTAL_FREQ 8000000
#define SPI1_DUMMY_DATA 0x0
#define SPI_RX_IN_PROGRESS 0x0
#define MY_BUFFER_SIZE 25
extern UBYTE EPD_Init(const unsigned char* lut);
unsigned char myWriteBuffer[100]="Hi I'm master..";
uint8_t myReadBuffer[100];
uint8_t total;
uint8_t temp;
uint8_t my_data = 0x58;
void UART_Init(void)
{
//69
SPBRG2 = 69;
TXSTA2bits.BRGH = 1;
BAUDCON2bits.BRG16 = 1; // Divisor at 8 bit
TRISDbits.TRISD6 = 0;
TRISDbits.TRISD7 = 1;
RCSTA2bits.SPEN = 1; // Enable serial port
TXSTA2bits.SYNC = 0; // Async operation
TXSTA2bits.TX9 = 0; // No tx of 9th bit
RCSTA2bits.RX9 = 0; // No rx of 9th bit
TXSTA2bits.TXEN = 1; // Enable transmitter
RCSTA2bits.CREN = 1; // Enable receiver
}
void UART_Putch(unsigned char bt)
{
while (!PIR3bits.TX2IF); // hold the program till TX buffer is free
TXREG2 = bt; //Load the transmitter buffer with the received value
}
void UART_Print(unsigned const char *ptr)
{
while (*ptr != 0)
{
UART_Putch(*ptr++);
}
}
unsigned char UART_getch() {
unsigned char temp;
if (RCSTA2bits.OERR) // check for Error
{
RCSTA2bits.CREN = 0; //If error -> Reset
//__delay_ms(10);
RCSTA2bits.CREN = 1; //If error -> Reset
}
while (!PIR3bits.RC2IF); // hold the program till RX buffer is free
temp = RCREG2;
return temp; //receive the value and send it to main function
}
void main()
{
ANSELA = 0;
ANSELB = 0;
ANSELC = 0;
ANSELD = 0;
TRISBbits.TRISB0 = 0; //RST Pin OUTPUT
TRISBbits.TRISB1 = 0; //DC Pin OUTPUT
TRISBbits.TRISB2 = 0; //CS Pin OUTPUT
TRISBbits.RB3 = 1; //BUSY Pin INPUT
// int i;
TRISD =0;/* PORT initialize as output */
EPD_RST_PIN = 0;
EPD_DC_PIN = 0;
//OSCCON = 0x72; /* Use internal osc. frequency 16 MHz */
OSCCONbits.SCS = 0b10; //Frequency & PLL SETUP
OSCCONbits.IRCF = 0b110; //8 MHz
while (!OSCCONbits.HFIOFS);
OSCTUNEbits.PLLEN = 0; //PLL disable
UART_Init();
SPI_Init_Master(); /* Initialize SPI communication as a master */
if(EPD_Init(lut_full_update) != 0) {
UART_Print("e-Paper init failed\r\n");
while(1);
}
UART_Print("e-Paper init\r\n");
for(uint8_t i = 0; i < 10; i++){
__delay_ms(10);
}
EPD_Clear();
UART_Print("e-Paper cleared\r\n");
for(uint8_t i = 0; i < 10; i++){
__delay_ms(50);
}
while(1)
{
// total = 0;
// //do
// //{
// LATAbits.LATA5=0;
// //total = SPI1_Exchange8bitBuffer(SPI1_DUMMY_DATA, MY_BUFFER_SIZE, &myReadBuffer[total]);
// total = SPI1_Exchange8bit(my_data);
//
// LATAbits.LATA5=1;
// __delay_ms(500);
// __delay_ms(500);
// // Do something else...
//
// //} while(total < MY_BUFFER_SIZE);
// //while(1);
//
// EPD_Clear();
//
// __delay_ms(500);
}
}
void SPI_Init_Master()
{
/* PORT definition for SPI pins*/
TRISCbits.TRISC4 = 1; /* RB0 as input(SDI) */
TRISCbits.TRISC3 = 0; /* RB1 as output(SCK) */
// TRISBbits.TRISB2 = 0; /* RA5 as a output(SS') */
TRISCbits.TRISC5 = 0; /* RC7 as output(SDO) */
/* To initialize SPI Communication configure following Register*/
EPD_CS_PIN = 1;
SSP1STAT=0x00; /* Data change on rising edge of clk , BF=0*/
SSP1CON1=0x20; /* Slave mode,Serial enable, idle state high for clk */
PIR1bits.SSP1IF=0;
/* Disable the ADC channel which are on for multiplexed pin
when used as an input */
ADCON0=0; /* This is for de-multiplexed the SCL
and SDI from analog pins*/
ADCON1=0x0F; /* This makes all pins as digital I/O */
}
uint8_t SPI1_Exchange8bit(uint8_t data)
{
// Clear the Write Collision flag, to allow writing
SSP1CON1bits.WCOL = 0;
SSP1BUF = data;
while(SSP1STATbits.BF == SPI_RX_IN_PROGRESS)
{
}
return (SSP1BUF);
}
uint8_t SPI1_Exchange8bitBuffer(uint8_t *dataIn, uint8_t bufLen, uint8_t *dataOut)
{
uint8_t bytesWritten = 0;
if(bufLen != 0)
{
if(dataIn != NULL)
{
while(bytesWritten < bufLen)
{
if(dataOut == NULL)
{
SPI1_Exchange8bit(dataIn[bytesWritten]);
}
else
{
dataOut[bytesWritten] = SPI1_Exchange8bit(dataIn[bytesWritten]);
}
bytesWritten++;
}
}
else
{
if(dataOut != NULL)
{
while(bytesWritten < bufLen )
{
temp = SPI1_Exchange8bit(SPI1_DUMMY_DATA);
if(temp!=SPI1_DUMMY_DATA)
{
UART_Putch(temp); //uart print
dataOut[bytesWritten] = temp;
bytesWritten++;
}
__delay_ms(5);
}
}
}
}
return bytesWritten;
}
Compare your logic analyser SCK and MOSI timing with that specified for the part at https://www.waveshare.com/wiki/1.54inch_e-Paper_Module:
Note that the MOSI (SDIN) state must be stable on the rising edge of SCK (SCLK). In your case the MOSI transitions are synchronous with the rising edge, and you have a clock transition before the MOSI has the correct D7=0 state. SPI timing is defined by both clock polarity and clock phase - giving four possible clock modes. Compare the Waveshare timing diagram with the 18F46K22 datasheet:
The Waveshare diagram suggests that either CKP=1/CKE=0, or CKP=0/CKE=1 may be used, you have:
SSP1STAT=0x00 ;
SSP1CON1=0x20 ;
Which is CKP=0/CKE=0 (which correlates with your logic analyser trace).
You need on of either:
SSP1STAT=0x20 ; // CKE=1
SSP1CON1=0x20 ; // CKP=0
or
SSP1STAT=0x00 ; // CKE=0
SSP1CON1=0x30 ; // CKP=1
Since idle state (controlled by CKP) of SCK is a don't-care, I suggest leaving that as-is and using the first suggestion - that seems more intuitive somehow.
Note also that your logic analyser must also be set to the same phase/polarity clock mode in order for its presentation of the data to be correct.
I am developing uart communication on STMicroelectronics NUCLEO-G474RE Board(stm32G474RETX MCU, 64pins) using MDK-v5, but run into a perplexing problem these several days, make no progress on it and have to reach out to seek some help. If anyone could give me a right way to go, it would be appreciated greatly.
Code is not complicated, I use cubeMX to initialize the code base with usart2 configured, and use two threads based on cmsis v2 api for receiving occassinally and transmiting periodically(in 2 seconds).
The auto-generated usart2 init code
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_RTS;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
Interrupt call back code
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
printf("Interrupt error occured(0x%x)!!!\r\n", huart->ErrorCode);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart)
{
UNUSED(huart);
if(huart == &huart2 || huart == &huart3){
uint8_t aEnterLine[] = "\r\n";
//HAL_UART_Transmit(huart, aEnterLine, sizeof(aEnterLine), 0xFFFF);
//HAL_UART_Transmit(huart, gRxBuffer, 11, 0xFFFF);
//HAL_UART_Transmit(huart, aEnterLine, sizeof(aEnterLine), 0xFFFF);
//printf("%s\r\n", gRxBuffer);
if(g_cmdExecuting == 0)
osEventFlagsSet(evt_id, 0x00000001U);
}else if(huart == &huart1){
}else{
}
HAL_UART_Receive_IT(huart, (unsigned char*)gRxBuffer, 11);
}
int8_t app_usart_transmit(uint8_t* buf, uint8_t len)
{
return 0;
}
int8_t app_usart_init(UART_HandleTypeDef* huart)
{
if(huart == NULL)
return -1;
if(huart == &huart2 || huart == &huart3){
uint8_t aTxStartMessage[] = "\r\n****UART-Hyperterminal commmunication based on IT****"
"\r\nEnter 10 characters using keyboard: \r\n";
HAL_UART_Transmit(huart, aTxStartMessage, sizeof(aTxStartMessage), 0xFFFF);
}
clear_rx_buffer();
HAL_UART_Receive_IT(huart, (uint8_t*)gRxBuffer, 11);
return 0;
}
rx & tx worker code
extern osMutexId_t myMutex01Handle;
extern osEventFlagsId_t evt_id;
extern osEventFlagsId_t evt_id1;
osTimerId_t timer_id;
static osThreadId gTask1Handle;
static const osThreadAttr_t gTask1Attrbutes = {
.name = "Task1",
.priority = (osPriority_t) osPriorityAboveNormal,
.stack_size = 512
};
static osThreadId gTask2Handle;
static const osThreadAttr_t gTask2Attrbutes = {
.name = "Task2",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 512
};
uint8_t g_cmdExecuting = 0;
uint8_t g_SemExec = 0;
uint32_t timer_cnt;
void update_stat_timer(void* argument)
{
timer_cnt++;
//osMutexAcquire(myMutex01Handle, osWaitForever);
printf("update_stat_timer executes %u times, ====++++====++++\r\n", timer_cnt);
//osMutexRelease(myMutex01Handle);
}
void update_stat_task_run(void* argument)
{
uint32_t count;
for(;;){
osDelay(2000);
count++;
osMutexAcquire(myMutex01Handle, osWaitForever);
printf("update_stat_task_run executes %d times, ====++++====++++\r\n", count);
osMutexRelease(myMutex01Handle);
}
}
void exec_cmd_task_run(void* argument)
{
uint32_t flags;
for(;;){
flags = osEventFlagsWait(evt_id, 0x00000001U, osFlagsWaitAny, osWaitForever);
g_cmdExecuting = 1;
osMutexAcquire(myMutex01Handle, osWaitForever);
osDelay(1000);
printf("exec_cmd_task Print\r\n");
osMutexRelease(myMutex01Handle);
g_cmdExecuting = 0;
}
}
int8_t app_task_init()
{
osStatus_t status;
gTask1Handle = osThreadNew(exec_cmd_task_run, NULL, &gTask1Attrbutes);
gTask2Handle = osThreadNew(update_stat_task_run, NULL, &gTask2Attrbutes);
//uint32_t exec = 1;
//timer_id = osTimerNew(update_stat_timer, osTimerPeriodic, &exec, NULL);
//if(status != osOK){
//;
//}
//osTimerStart(timer_id, 2000U);
return 0;
}
My Question:
It could receive and transmit msg at very begining, but after receiving three or four hundred times, it couldn't receive the msg anymore while the transmitting is ok, and I also use the timer api to replace the transmitting thread, the result is the same, it couldn't receive any msg after hundreds times.
the testing result pic
You can perform the HAL status and error checks using uint32_t HAL_UART_GetError(UART_HandleTypeDef * huart) and HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart) (http://www.disca.upv.es/aperles/arm_cortex_m3/llibre/st/STM32F439xx_User_Manual/group__uart__exported__functions__group4.html)
The source code for HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) and HAL_UART_RxCplt_Callback() is in https://github.com/ARMmbed/mbed-hal-st-stm32cubef4/blob/master/source/stm32f4xx_hal_uart.c
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t tmp = 0;
tmp = huart->State;
if((tmp == HAL_UART_STATE_READY) || (tmp == HAL_UART_STATE_BUSY_TX))
{
if((pData == HAL_NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX)
{
huart->State = HAL_UART_STATE_BUSY_TX_RX;
}
else
{
huart->State = HAL_UART_STATE_BUSY_RX;
}
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
Note that the HAL mutually uses locking mechanisms (__HAL_LOCK, code line 107 in https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_def.h)
To get the UART status use i.e.
HAL_UART_StateTypeDef UARTStatus;
UARTStatus = HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
The definition of HAL_UART_StateTypeDef is
typedef enum
{
HAL_UART_STATE_RESET = 0x00, /*!< Peripheral is not yet Initialized */
HAL_UART_STATE_READY = 0x01, /*!< Peripheral Initialized and ready for use */
HAL_UART_STATE_BUSY = 0x02, /*!< an internal process is ongoing */
HAL_UART_STATE_BUSY_TX = 0x12, /*!< Data Transmission process is ongoing */
HAL_UART_STATE_BUSY_RX = 0x22, /*!< Data Reception process is ongoing */
HAL_UART_STATE_BUSY_TX_RX = 0x32, /*!< Data Transmission and Reception process is ongoing */
HAL_UART_STATE_TIMEOUT = 0x03, /*!< Timeout state */
HAL_UART_STATE_ERROR = 0x04 /*!< Error */
}HAL_UART_StateTypeDef;
(https://github.com/synthetos/Motate/blob/master/MotateProject/motate/cmsis/TARGET_STM/TARGET_DISCO_F407VG/stm32f4xx_hal_uart.h code line 97)
The definition of HAL_StatusTypeDef (general HAL status) is
typedef enum
{
HAL_OK = 0x00,
HAL_ERROR = 0x01,
HAL_BUSY = 0x02,
HAL_TIMEOUT = 0x03
} HAL_StatusTypeDef;
You can print the HAL status (i.e. with UART transmit or SWO ITM_SendChar((*ptr++));, see https://ralimtek.com/stm32_debugging/ )
The HAL has its own reference manual, the HAL status is described in the reference manual https://www.st.com/content/ccc/resource/technical/document/user_manual/2f/71/ba/b8/75/54/47/cf/DM00105879.pdf/files/DM00105879.pdf/jcr:content/translations/en.DM00105879.pdf on page 54 in chapter 2.9 HAL common resources.
Debugging techniques are described in https://www.st.com/content/ccc/resource/technical/document/application_note/group0/3d/a5/0e/30/76/51/45/58/DM00354244/files/DM00354244.pdf/jcr:content/translations/en.DM00354244.pdf, also for your programming adapter, that you can directly integrate in CubeMX (i use Atollic with integrated CubeMX because Atollic is based on Eclipse IDE and is almost identical to use)
Another debugging tutorial is https://ardupilot.org/dev/docs/debugging-with-gdb-on-stm32.html (if you are on linux).
This could also be a race condition either of the HAL or the OS. The problem that i have is that you do not post your full code, especially not concerning your OS and this is possibly critical here
So maybe first check the HAL status and then focus on the OS part (race conditions, buffer, stack or heap overflow, ... https://www.st.com/content/ccc/resource/technical/document/user_manual/2d/60/ff/15/8c/c9/43/77/DM00105262.pdf/files/DM00105262.pdf/jcr:content/translations/en.DM00105262.pdf)
Stack overflows are always critical on embedded systems so here is links https://barrgroup.com/embedded-systems/how-to/prevent-detect-stack-overflow , https://www.iar.com/support/resources/articles/detecting-and-avoiding-stack-overflow-in-embedded-systems/
Also see Uart dma receive interrupt stops receiving data after several minutes and UART receive interrupt stops triggering after several hours of successful receive
Many developers avoid using the HAL because it is a bit buggy and not very transparent, i personally would also not use it when i want full control over the system behavior
i'm monitorating heart rate by the MAX30100(https://img.filipeflop.com/files/download/Datasheet_MAX30100.pdf) using the MCU STM32F4. I'm trying read the IR and RED data from the FIFO, but all returns are ZERO. The method MAX30100_Get_Num_Samples() returns 8. I modeled the code using the pseudo code from datasheet of MAX30100. I tried several solutions for my problem but doesn't work. I dont know if i'm following the right way to get data from the FIFO. My code:
I2C_HandleTypeDef hi2c3; //i2c used
uint16_t RED[50] = {0}, IR[50] = {0}; //buffers for RED and IR
uint8_t buffOximeter[5];
char buffer[32];
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C3_Init();
MX_USB_DEVICE_Init();
MAX30100_Init();
while (1)
{
uint8_t numSamples = MAX30100_Get_Num_Samples();
MAX30100_Read_HeartBeat(numSamples);
for(int i = 0; i < numSamples; i++)
{
sprintf(buffer, "Amostra %d: %d / %d\n\r", i , IR[i], RED[i]);
CDC_Transmit_FS((char*)buffer, 50);
}
}
}
static void MX_I2C3_Init(void)
{
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();
}
}
void MAX30100_Init(void)
{
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x06,1,0x02,1,1000); //set heart rate mode
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x09,1,0xFF,1,1000); //i50 ledCurrent
uint8_t sr = 0x01, pw = 0x3;
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x07,1,(sr<<2)|pw,1,1000); //sr100, pw1600
HAL_Delay(50);
}
void MAX30100_Read_HeartBeat(uint8_t num)
{
for(int i=0;i<num;i++)
{
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x02,1,1000); //send fifo_wr_ptr
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAF,1,1000); //adress + read mode
uint8_t data;
HAL_I2C_Master_Receive(&hi2c3,0x57,&data,1,1000); //read fifo_wr_ptr
HAL_Delay(100); //STOP
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x05,1,1000); //send adress fifo data
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAF,1,1000); //adress + read mode
HAL_I2C_Mem_Read(&hi2c3,0x57,0x05,1,&buffOximeter,4,1000); //read fifo data
IR[i] = (buffOximeter[0] << 8) | buffOximeter[1];
RED[i] = (buffOximeter[2] << 8) | buffOximeter[3];
HAL_Delay(100); //STOP
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x04,1,1000); //send adress fifo_rd_ptr
HAL_Delay(100);
}
}
int MAX30100_Get_Num_Samples(void)
{
uint8_t wrPtr, rdPtr;
HAL_I2C_Mem_Read(&hi2c3,0x57,0x02,1,&wrPtr,1,1000);
HAL_Delay(50);
HAL_I2C_Mem_Read(&hi2c3,0x57,0x04,1,&rdPtr,1,1000);
HAL_Delay(50);
return (abs( 16 + wrPtr - rdPtr ) % 16);
}
connect the interrupt output pin (pin 13 active low) to an interrupt control pin on the cpu.
setup your cpu to be interrupted when the device indicates some event ready
Then, still in your interrupt handler function, read register 0 and determine which data is ready,.
the referenced datasheet, approx page 14 lists the order of communication events needed. Note that the posted code is NOT following the specified order of communication events
page 16, register 7, indicates how to initialize the SPO2 for the desired mode of operation
I'm trying to make a MPC2515 CAN controller works with an STML053R8 µC.
I'm using this driver/example https://github.com/eziya/STM32_SPI_MCP2515 with just the #include "stm32f1xx_hal.h" changed to #include "stm32l0xx_hal.h".
Here is my condensed code:
#define CAN_CS_Pin GPIO_PIN_5
#define CAN_CS_GPIO_Port GPIOA
extern SPI_HandleTypeDef hspi1;
#define SPI_CAN &hspi1
int CANSPI_Initialize(void)
{
// Some registers def
[...]
// This pass
if(MCP2515_Initialize() != 0)
return 1;
// This fails
if(MCP2515_SetConfigMode() != 0)
return 2;
[...]
}
int MCP2515_Initialize(void)
{
MCP2515_CS_HIGH();
uint8_t loop = 10;
do {
/* SPI Ready ? */
if(HAL_SPI_GetState(SPI_CAN) == HAL_SPI_STATE_READY)
return 0;
loop--;
} while(loop > 0);
return 1;
}
int MCP2515_SetConfigMode(void)
{
/* CANCTRL Register Configuration ?? ?? */
MCP2515_WriteByte(MCP2515_CANCTRL, 0x80);
uint8_t loop = 10;
do {
if((MCP2515_ReadByte(MCP2515_CANSTAT) & 0xE0) == 0x80)
return 0;
loop--;
} while(loop > 0);
return 1;
}
uint8_t MCP2515_ReadByte (uint8_t address)
{
uint8_t retVal;
MCP2515_CS_LOW();
SPI_Tx(MCP2515_READ);
SPI_Tx(address);
retVal = SPI_Rx();
MCP2515_CS_HIGH();
return retVal;
}
void MCP2515_WriteByte(uint8_t address, uint8_t data)
{
MCP2515_CS_LOW();
SPI_Tx(MCP2515_WRITE);
SPI_Tx(address);
SPI_Tx(data);
MCP2515_CS_HIGH();
}
My SPI configuration (generated by STM32 CubeMX):
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
and a link to the MPC2515 datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001801H.pdf
My problem here is in MCP2515_SetConfigMode(), as I set the CANCTRL register, I should read back
this value ('current mode is config mode') in CANSTAT register.
Also I tried to do the same thing for a LSM6DS3 IMU module yesterday and I got the same issue, unable
to read/write registers => unable to communicate with the device. I plugged it in I2C (this IMU got
2 interfaces) and I make it works like a charm.
The driver/example code I use seems very valid to me, I checked ALL the data & registers value from
the datasheet and everything looks OK to me. I suspect I might messed up something in my SPI configuration.
Any help will be gladly appreciated!
EDIT: I don't have a logical analyzer but I downloaded ones for Arduino and got this weird output. Not sure if this is the Arduino fault or my actual output:
I am trying to start learning programming and I am trying to get this code to work: https://github.com/espruino/Espruino/blob/master/targetlibs/stm32f4/lib/stm32f4xx_can.c
I am using Atollic TruStudio Lite.
I did the basic LED blinking code with Discovery, but other than that. I have no clue how to do this.
I don't get how am i supposed to simulate CAN message (and other things) via STM32.
I am a total beginner, so please, don't rip my throat out.
Thanks
Here's an example of a CAN loopback you can try. It's a CAN loopback test. It's in the stm32f4 examples that you can download from the STM website. This is for an STM32439 eval board but it should work on a discovery. (Both are STM32F4 chips).
#define USE_CAN1
/* Uncomment the line below if you will use the CAN 2 peripheral */
/* #define USE_CAN2 */
#ifdef USE_CAN1
#define CANx CAN1
#define CAN_CLK RCC_APB1Periph_CAN1
#else /* USE_CAN2 */
#define CANx CAN2
#define CAN_CLK (RCC_APB1Periph_CAN1 | RCC_APB1Periph_CAN2)
#endif /* USE_CAN1 */
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
__IO uint32_t ret = 0; /* for return of the interrupt handling */
volatile TestStatus TestRx;
TestStatus CAN_Polling(void);
int main(void) {
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
files (startup_stm32f40_41xxx.s/startup_stm32f427_437xx.s/startup_stm32f429_439xx.s)
before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f4xx.c file
*/
/* CANx Periph clock enable */
RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);
/* Initialize LEDs mounted on EVAL board */
STM_EVAL_LEDInit(LED1);
STM_EVAL_LEDInit(LED2);
/* CAN transmit at 125Kb/s and receive by polling in loopback mode */
TestRx = CAN_Polling();
if (TestRx != FAILED) { /* OK */
/* Turn on LED1 */
STM_EVAL_LEDOn(LED1);
} else { /* KO */
/* Turn on LED2 */
STM_EVAL_LEDOn(LED2);
}
/* Infinite loop */
while (1) { }
}
/**
* #brief Configures the CAN, transmit and receive by polling
* #param None
* #retval PASSED if the reception is well done, FAILED in other case
*/
TestStatus CAN_Polling(void) {
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
CanRxMsg RxMessage;
uint32_t uwCounter = 0;
uint8_t TransmitMailbox = 0;
/* CAN register init */
CAN_DeInit(CANx);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
/* CAN Baudrate = 175kbps (CAN clocked at 42 MHz) */
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;
CAN_InitStructure.CAN_Prescaler = 16;
CAN_Init(CANx, &CAN_InitStructure);
/* CAN filter init */
#ifdef USE_CAN1
CAN_FilterInitStructure.CAN_FilterNumber = 0;
#else /* USE_CAN2 */
CAN_FilterInitStructure.CAN_FilterNumber = 14;
#endif /* USE_CAN1 */
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* transmit */
TxMessage.StdId = 0x11;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.IDE = CAN_ID_STD;
TxMessage.DLC = 2;
TxMessage.Data[0] = 0xCA;
TxMessage.Data[1] = 0xFE;
TransmitMailbox = CAN_Transmit(CANx, &TxMessage);
uwCounter = 0;
while((CAN_TransmitStatus(CANx, TransmitMailbox) != CANTXOK) && (uwCounter != 0xFFFF)) {
uwCounter++;
}
uwCounter = 0;
while((CAN_MessagePending(CANx, CAN_FIFO0) < 1) && (uwCounter != 0xFFFF)) {
uwCounter++;
}
/* receive */
RxMessage.StdId = 0x00;
RxMessage.IDE = CAN_ID_STD;
RxMessage.DLC = 0;
RxMessage.Data[0] = 0x00;
RxMessage.Data[1] = 0x00;
CAN_Receive(CANx, CAN_FIFO0, &RxMessage);
if (RxMessage.StdId != 0x11) {
return FAILED;
}
if (RxMessage.IDE != CAN_ID_STD) {
return FAILED;
}
if (RxMessage.DLC != 2) {
return FAILED;
}
if ((RxMessage.Data[0]<<8|RxMessage.Data[1]) != 0xCAFE) {
return FAILED;
}
return PASSED; /* Test Passed */
}