STM32 Interface with EEPROM using SPI and having issue with address for writing and reading - stm32

With an STM32 bit microcontroller, I am working on an EEPROM interface. I'm using STMCUBEIDE and proteus software to simulate hardware for the same purpose. The following is my code:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
uart_buf_len = sprintf(uart_buf, "SPI Test Begin\r\n");
HAL_UART_Transmit(&huart1, (uint8_t *)uart_buf, uart_buf_len, 100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&EEPROM_WREN, 1, 100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
spi_buf[0] = 0x01;
spi_buf[1] = 0x02;
spi_buf[2] = 0x03;
addr = 0x05;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&EEPROM_WRITE, 1, 100);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&addr, 1, 100);
HAL_SPI_Transmit(&hspi1, (uint8_t *)spi_buf, 3, 100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
spi_buf[0] = 0;
spi_buf[1] = 0;
spi_buf[2] = 0;
wip = 1;
while (wip)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&EEPROM_RDSR, 1, 100);
HAL_SPI_Receive(&hspi1, (uint8_t *)spi_buf, 1, 100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
wip = spi_buf[0] & 0b00000001;
}
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&EEPROM_READ, 1, 100);
HAL_SPI_Transmit(&hspi1, (uint8_t *)&addr, 1, 100);
HAL_SPI_Receive(&hspi1, (uint8_t *)spi_buf, 3, 100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
uart_buf_len = sprintf(uart_buf,
"0x%02x 0x%02x 0x%02x\r\n",
(unsigned int)spi_buf[0],
(unsigned int)spi_buf[1],
(unsigned int)spi_buf[2]);
HAL_UART_Transmit(&huart1, (uint8_t *)uart_buf, uart_buf_len, 100);
I am reusing code given in
https://www.digikey.in/en/maker/projects/getting-started-with-stm32-how-to-use-spi/09eab3dfe74c4d0391aaaa99b0a8ee17
I am facing following issue:
If I start at address 0x05 and write three bytes with the data sequence 01, 02, 03, and then retrieve the data from that address, I get 02, 03, and ff. However, if I read data from address 0x04, I can read the exact sequence that I just wrote in the writing portion.

Related

STM32L4 I2C LED Driver

I am trying to control LEDs using IS31FL3236A LED driver from LUMISSIL Microsystem which communicates with I2C.
LED driver datasheet
I use the HAL communication protocol of the STM32, I am able to turn on all the LEDs but I don't understand why I can't control the LEDs one by one or choose the intensity of the LEDs.
Here is my code.
IS31FL3236A.h to configure the address of all channels and the device address.
#define Single 0
#define RGB 1
#define Addr_GND 0x78 // P7 datasheet 0x78 = 1111000 en bit (addr 1)
#define PWM 0x04
#define All_EN 0x4A
#define IS3236_REG_PWM_OUT1 0x01 //LED PWM
#define IS3236_REG_PWM_OUT2 0x02
#define IS3236_REG_PWM_OUT3 0x03
#define IS3236_REG_PWM_OUT4 0x04
#define IS3236_REG_PWM_OUT5 0x05
#define IS3236_REG_PWM_OUT6 0x06
#define IS3236_REG_PWM_OUT7 0x07
#define IS3236_REG_PWM_OUT8 0x08
#define IS3236_REG_PWM_OUT9 0x09
#define IS3236_REG_PWM_OUT10 0x0A
#define IS3236_REG_PWM_OUT11 0x0B
#define IS3236_REG_PWM_OUT12 0x0C
#define IS3236_REG_PWM_OUT13 0x0D
#define IS3236_REG_PWM_OUT14 0x0E
#define IS3236_REG_PWM_OUT15 0x0F
#define IS3236_REG_PWM_OUT16 0x10
#define IS3236_REG_PWM_OUT17 0x11
#define IS3236_REG_PWM_OUT18 0x12
#define IS3236_REG_PWM_OUT19 0x13
#define IS3236_REG_PWM_OUT20 0x14
#define IS3236_REG_PWM_OUT21 0x15
#define IS3236_REG_PWM_OUT22 0x16
#define IS3236_REG_PWM_OUT23 0x17
#define IS3236_REG_PWM_OUT24 0x18
#define IS3236_REG_PWM_OUT25 0x19
#define IS3236_REG_PWM_OUT26 0x1A
#define IS3236_REG_PWM_OUT27 0x1B
#define IS3236_REG_PWM_OUT28 0x1C
#define IS3236_REG_PWM_OUT29 0x1D
#define IS3236_REG_PWM_OUT30 0x1E
#define IS3236_REG_PWM_OUT31 0x1F
#define IS3236_REG_PWM_OUT32 0x20
#define IS3236_REG_PWM_OUT33 0x21
#define IS3236_REG_PWM_OUT34 0x22
#define IS3236_REG_PWM_OUT35 0x23
#define IS3236_REG_PWM_OUT36 0x24
#define IS3236_REG_CTRL_OUT1 0x26 //LED ONOFF
#define IS3236_REG_CTRL_OUT2 0x27
#define IS3236_REG_CTRL_OUT3 0x28
#define IS3236_REG_CTRL_OUT4 0x29
#define IS3236_REG_CTRL_OUT5 0x2A
#define IS3236_REG_CTRL_OUT6 0x2B
#define IS3236_REG_CTRL_OUT7 0x2C
#define IS3236_REG_CTRL_OUT8 0x2D
#define IS3236_REG_CTRL_OUT9 0x2E
#define IS3236_REG_CTRL_OUT10 0x2F
#define IS3236_REG_CTRL_OUT11 0x30
#define IS3236_REG_CTRL_OUT12 0x31
#define IS3236_REG_CTRL_OUT13 0x32
#define IS3236_REG_CTRL_OUT14 0x33
#define IS3236_REG_CTRL_OUT15 0x34
#define IS3236_REG_CTRL_OUT16 0x35
#define IS3236_REG_CTRL_OUT17 0x36
#define IS3236_REG_CTRL_OUT18 0x37
#define IS3236_REG_CTRL_OUT19 0x38
#define IS3236_REG_CTRL_OUT20 0x39
#define IS3236_REG_CTRL_OUT21 0x3A
#define IS3236_REG_CTRL_OUT22 0x3B
#define IS3236_REG_CTRL_OUT23 0x3C
#define IS3236_REG_CTRL_OUT24 0x3D
#define IS3236_REG_CTRL_OUT25 0x3E
#define IS3236_REG_CTRL_OUT26 0x3F
#define IS3236_REG_CTRL_OUT27 0x40
#define IS3236_REG_CTRL_OUT28 0x41
#define IS3236_REG_CTRL_OUT29 0x42
#define IS3236_REG_CTRL_OUT30 0x43
#define IS3236_REG_CTRL_OUT31 0x44
#define IS3236_REG_CTRL_OUT32 0x45
#define IS3236_REG_CTRL_OUT33 0x46
#define IS3236_REG_CTRL_OUT34 0x47
#define IS3236_REG_CTRL_OUT35 0x48
#define IS3236_REG_CTRL_OUT36 0x49
#define Imax 0x11
#define update 0x25
#define freq 0x4B
#define mode 0x01
main.c to turn on all LEDs
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 */
//I2C_GPIO_Init();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
//I2C_GPIO_Init();
IS31FL3236A_Init();
static const uint16_t LED19 = 0x13;
static const uint16_t LED19PWM = 0x38;
static const uint16_t deviceAddr = 0x78;
static const uint16_t Update = 0x25;
static const uint16_t Freq = 0x4B;
static const uint16_t intensityPWM = 0x02;
static const uint16_t CurrentMax = 0x00;
static const uint16_t Mode = 0x01;
uint8_t pwm[bufSize];
uint8_t current[bufSize];
uint8_t mode1[bufSize];
pwm[0] = 0x7B;
current[0] = 0x00;
mode1[0] = 0x01;
uint8_t i=0;
/*for(i=0;i<=0x49;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, I2C_MEMADD_SIZE_16BIT, (uint8_t*)PWM, 1, HAL_MAX_DELAY);
}
for(i=0;i<=0x24;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)Imax, sizeof(Imax), 10);
}*/
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19PWM, I2C_MEMADD_SIZE_8BIT, pwm, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Freq, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Mode, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Update, I2C_MEMADD_SIZE_8BIT, mode1, bufSize, 10);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/*uint8_t i=0;
for(i=0;i<=0x49;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)PWM, sizeof(PWM), 10);
}
for(i=0;i<=0x24;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)Imax, sizeof(Imax), 10);
}
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, freq, sizeof(freq), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, mode, sizeof(mode), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, RESET, sizeof(RESET), (uint8_t*)mode, sizeof(mode), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);*/
//Swith();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
To select LED number 19 for example, I just removed the two for loops and added this but the LED does not light up.
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_PWM_OUT19, sizeof(IS3236_REG_PWM_OUT19), (uint8_t*)PWM, sizeof(PWM), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_CTRL_OUT19, sizeof(IS3236_REG_CTRL_OUT19), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);
I2C configuration
/**
* #brief I2C1 Initialization Function
* #param None
* #retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00702991;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
stm32l4xx_hal_msp.c
/**
* #brief I2C MSP Initialization
* This function configures the hardware resources used in this example
* #param hi2c: I2C handle pointer
* #retval None
*/
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hi2c->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**I2C1 GPIO Configuration
PA9 ------> I2C1_SCL
PA10 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
}
/**
* #brief I2C MSP De-Initialization
* This function freeze the hardware resources used in this example
* #param hi2c: I2C handle pointer
* #retval None
*/
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
{
if(hi2c->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspDeInit 0 */
/* USER CODE END I2C1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_I2C1_CLK_DISABLE();
/**I2C1 GPIO Configuration
PA9 ------> I2C1_SCL
PA10 ------> I2C1_SDA
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_10);
/* USER CODE BEGIN I2C1_MspDeInit 1 */
/* USER CODE END I2C1_MspDeInit 1 */
}
}
Here is what STM32 expects
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c,
uint16_t DevAddress,
uint16_t MemAddress,
uint16_t MemAddSize,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
Here is what you give:
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_PWM_OUT19, sizeof(IS3236_REG_PWM_OUT19), (uint8_t*)PWM, sizeof(PWM), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_CTRL_OUT19, sizeof(IS3236_REG_CTRL_OUT19), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);
Do not take a sizeof(#define)
Rebind register #defines into static const. Or #define the MemAddSize if desired
Don't use a pointer to a #define
Change your MemAddSize to either: I2C_MEMADD_SIZE_16BIT, I2C_MEMADD_SIZE_8BIT
My blind guess as to why the system is able to turn the leds all on or off and at max brightness is due to address auto increment.
Try using the I2C_MEM_WRITE() using magic numbers and local variables, buffers, and pointers. If this works then it is likely your usage of #defines within your driver file.
Check the return type of HAL_StatusTypeDef between each call. Make sure that it is OK.
Here are two example for stm32 i2c:
Example using mem_write()
Intro to stm32 i2c
------------ Reply 1 ------------
Dont use I2C_MEMADD_SIZE_16BIT for the Size param. You need to specify the num of of the buffer you are pointing to. Go to your local source code and look at the instructions for how to use I2C_mem_write(). Based on the data sheet, I believe you should also be using I2C_MEMADD_SIZE_8BIT instead of 16.
Suppose you have a buffer of size one. You want to only read one byte then.
#define bufSize 1
uint8_t myBuf[bufSize];
myBuf[0] = someDataVal;
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19, I2C_MEMADD_SIZE_8BIT, myBuf, bufSize , 10);
------------ Reply 2 ------------
Is bufSize defined? If so, what number is it?
You need to be assigning to the first index of each buf if you are only writing one byte from each buf. pwm[0], current[0] (not 1), mode1[0] (not 2).
uint8_t pwm[bufSize];
uint8_t current[bufSize];
uint8_t mode1[bufSize];
pwm[0] = 0x7B;
current[1] = 0x00;
mode1[2] = 0x01;
------------ Reply 3 ------------
// First lets discuss device address
// What is your AD pin connected to (PG 7)
// Currently you have it configured to 0x78. Bit shift it for STM32
// 0x78 << 1
uint8_t myBuf[1];
// Turn on the device by writing to register 0. Shutdown register
myBuf[0] = 0x01;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x00, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Then lets assign the pwm to the first LED (OUT1). Register 1. Reuse prior buffer.
myBuf[0] = 0x80;
// Lets toggle LED 19
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x01, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Enable OUT1 via Control register 26
myBuf[0] = 0x01; // This would have maximum current settings (Pg. 9)
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x26, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Tell system to update the registers. Register 25
myBuf[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x25, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
I always would encourage you to look at the HAL_STATUS of each of these returns. Can you tell me if it is OK?
The offset changes the device address, you should not do the offset, it works like this:
// First lets discuss device address
// What is your AD pin connected to (PG 7)
uint8_t myBuf[1];
// First lets turn on the device by writing to register 0. Shutdown register
myBuf[0] = 0x01;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x00, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Then lets assign the pwm to the first LED (OUT1). Register 1. Reuse prior buffer.
myBuf[0] = 0xff;
// Lets toggle LED 19
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x13, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Enable OUT1 via Control register 26
myBuf[0] = 0x01; // This would have maximum current settings (Pg. 9)
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x38, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Tell system to update the registers. Register 25
myBuf[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x25, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
Thanks for your help #Frebreeze now it works.

STM32H747 Register implemented ADC with DMA

I'm using the H747 on a Arduino Portenta H7. I'm trying to implement the ADC and DMA together.
The ADC should run in continous mode and scan over the 2 channels.
void ADC_Init (void){
/******Enable ADC Voltage regulator******/
ADC1->CR&= ~(1<<29); //DEEPPWD set to zero to set adc not deep power down
ADC1->CR|= ADC_CR_ADVREGEN; //Turn on voltage regulator ADC |=(1<<28)
digitalWrite(LEDR,LOW);
//while (!(ADC1->ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Enable ADC CLOCK******/
RCC->AHB1ENR|=(1<<5);//ADC peripheral clock enable
//RCC->D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
ADC1->CR|=(3<<8);
/******Set the prescalar******/
ADC12_COMMON->CCR &= ~(15<<18);// PRESC[3:0]: ADC prescaler 0000: input ADC clock not divided
ADC12_COMMON->CCR &= ~(3<<16);// CKMODE[3:0]: ADC clock mode
/******Set Scan Mode Data Management and resolution******/
ADC1->CFGR|=(6<<2); //RES[2:0]: Data resolution 110=12bits
ADC1->CFGR|=(3<<0); //DMNGT[1:0]: Data Management configuration 11: DMA Circular Mode selected
//ADC regular sequence register 1
ADC1->SQR1|=(1<<0); // L[3:0]: Regular channel sequence length 1=2 conversions
ADC1->SQR1&= ~(0<<6);// SQ1[4:0]: 1st conversion in regular sequence channel number =0
ADC1->SQR1&= ~(1<<12);// SQ1[4:0]: 2st conversion in regular sequence channel number =1
/******Set the Continuous Conversion, ******/
ADC1->CFGR|=(1<<13); //CONT: Single / continuous conversion mode for regular conversions
/******Set the Sampling Time for the channels in ADC_SMPRx******/
ADC1->SMPR1&= ~(7<<0);//SMP0[2:0]: Channel 0 sampling time selection 000: 1.5 ADC clock cycles
ADC1->SMPR1&= ~(7<<3);//SMP1[2:0]: Channel 1 sampling time selection 000: 1.5 ADC clock cycles
ADC1->PCSEL|= (1<<0);//PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
ADC1->PCSEL|= (1<<1);//PCSEL[19:0] :Channel 1 (VINP[i]) pre selection
/******Set singleEnded Input******/
ADC1->DIFSEL&= ~(1<<0); //DIFSEL[19:0]: Differential mode for channels 19 to 0
ADC1->DIFSEL&= ~(1<<1); //DIFSEL[19:0]: Differential mode for channels 19 to 0
/******Enable ADC******/
ADC1->ISR|=(1<<0); // Reset ADC ready flag
ADC1->CR|= ADC_CR_ADEN; //Enable ADC
while (!(ADC1->ISR & (1<<0))); //Wait for ready flag
ADC1->ISR|=(1<<0);}
The DMA should catch the output from the adc and move them into memory
void DMA_Init (void){
RCC->AHB1ENR |= (1<<0);
DMA1_Stream0->CR &= ~(0x1 << 0);
while (DMA1_Stream0->CR & 0x1);
DMA1_Stream0->CR |= (0x3 << 16);
DMA1_Stream0->CR &= ~(3<<6); //Bits 7:6 DIR[1:0]: data transfer direction
DMA1_Stream0->CR |= (1<<8); //CIRC: circular mode
DMA1_Stream0->CR |= (1<<10); // MINC = 1;
DMA1_Stream0->CR |= (1<<11)|(1<<13); // MSIZE[1:0]: memory data size PSIZE[1:0]: peripheral data size
DMA1_Stream0->CR &= ~(7<<25); // Channel 0 selected
DMAMUX1_Channel0->CCR |=(9<<0);
DMA1_Stream0->CR |= (0x1 << 4);
DMA1_Stream0->CR |= (0x1 << 3);
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t s){
DMA1_Stream0->NDTR = s; // Set the size of the transfer
DMA1_Stream0->PAR = srcAdd; // Source address is peripheral address
DMA1_Stream0->M0AR = destAdd; // Destination Address is memory address
// Enable the DMA Stream
DMA1_Stream0->CR |= (1<<0); // EN =1
}
When running the code it gets stuck in ADCStart() at ADC1->CR|= ADC_CR_ADON;
void setup() {
SystemClock_Config();
ADC_Init();
DMA_Init();
DMA_Config ((uint32_t ) &ADC1->DR, (uint32_t) RxData, 3);
ADC_start();
digitalWrite(LEDB,LOW);
Serial.begin(9600); //for debuging
}
void ADC_start (void){
while ((ADC1->CR & (1<<1)));
ADC1->CR|= ADC_CR_ADON;
}
Is my procedure wrong?
configure adc
configure dma
start adc
I'm still new to stm32 so I used https://controllerstech.com/dma-with-adc-using-registers-in-stm32/ as guidance and the reference manual of stm32h7
Not sure wether you still need an answer to your question.
Here is what I did with your code :
(I don't have any library installed for the portenta so I declared every registers above, I am french so some of my comments are written in frenglish I apologize for that...).
#include <Arduino.h>
#include <string.h>
#define HWREG(x) (*((volatile uint32_t *)(x)))
//REGISTRE DEF---------------------------------------------------
//REG DMA1---------------------------------------------
volatile uint32_t* const DMA_LISR = (uint32_t *) 0x40020000;
volatile uint32_t* const DMA_HISR = (uint32_t *) 0x40020004;
volatile uint32_t* const DMA_LIFCR = (uint32_t *) 0x40020008;
volatile uint32_t* const DMA_HIFCR = (uint32_t *) 0x4002000C;
volatile uint32_t* const DMA_S1CR = (uint32_t *) 0x40020028;
volatile uint32_t* const DMA_S1NDTR = (uint32_t *) 0x4002002C;
volatile uint32_t* const DMA_S1PAR = (uint32_t *) 0x40020030;
volatile uint32_t* const DMA_S1M0AR = (uint32_t *) 0x40020034;//Mem 1 no use since no double buffer in mem2mem
volatile uint32_t* const DMA_S1FCR = (uint32_t *) 0x4002003C;
uint32_t S1CR = *DMA_S1CR;
uint32_t S1FCR = *DMA_S1FCR;
uint32_t S1PAR = *DMA_S1PAR;
uint32_t S1M0AR = *DMA_S1M0AR;
uint32_t S1NDTR = *DMA_S1NDTR;
//REG DMAMUX-------------------------------------------
volatile uint32_t* const DMAMUX1_C1CR = (uint32_t *) 0x40020804;//adresse dmamux1_chan1
uint32_t C1CR = *DMAMUX1_C1CR;
//REG ADC----------------------------------------------
//--------------------ADC REGISTRES-------------------//
volatile uint32_t* const ADC_ISR = (uint32_t *) 0x40022000;//Interupt and status register
uint32_t ISR = *ADC_ISR;
volatile uint32_t* const ADC_IER = (uint32_t *) 0x40022004;//Interupt enable register
uint32_t IER = *ADC_IER;
volatile uint32_t* const ADC_CR = (uint32_t *) 0x40022008;//Control register
uint32_t CR = *ADC_CR;
volatile uint32_t* const ADC_CFGR = (uint32_t *) 0x4002200C;//COnfiguration register
uint32_t CFGR = *ADC_CFGR;
volatile uint32_t* const ADC_CFGR2 = (uint32_t *) 0x40022010;//2eme conf regrister
uint32_t CFGR2 = *ADC_CFGR2;
volatile uint32_t* const ADC_SMPR1 = (uint32_t *) 0x40022014; //Sample time reg (directement lié au temps de calc de l'ADC)
uint32_t SMPR1 = *ADC_SMPR1;
volatile uint32_t* const ADC_PCSEL = (uint32_t*) 0x4002201C;//channel preselection register on choisis un chan pour la conv
uint32_t PCSEL = *ADC_PCSEL;
volatile uint32_t* const ADC_DR = (uint32_t *) 0x40022040;//Registre où l'on stocke le resultat des conv
uint32_t DR = *ADC_DR;
volatile uint32_t* const ADC_DIFSEL = (uint32_t *) 0x400220C0;
uint32_t DIFSEL = *ADC_DIFSEL;
volatile uint32_t* const ADC_SQR1 = (uint32_t *) 0x40022030;
uint32_t SQR1 = *ADC_SQR1;
volatile uint32_t* const ADC1_CSR = (uint32_t *) 0x40022300;//ADC1 common status register
uint32_t CSR = *ADC1_CSR;
volatile uint32_t* const ADC1_CCR = (uint32_t *)0x40022308; //ADC1 Common Control Register
uint32_t CCR = *ADC1_CCR;
//REG RCC-----------------------------------------------
volatile uint32_t* const RCC_APB4ENR = (uint32_t *) 0x580244F4;//to enable sysconf
volatile uint32_t* const RCC_AHB4ENR =(uint32_t *) 0x580244E0;
volatile uint32_t* const RCC_AHB1ENR =(uint32_t *) 0x580244D8;
volatile uint32_t* const RCC_CR = (uint32_t *) 0x58024400;
volatile uint32_t* const RCC_CFGR = (uint32_t *) 0x58024410;
volatile uint32_t* const RCC_D1CFGR = (uint32_t *) 0x58024418;
volatile uint32_t* const RCC_D2CFGR = (uint32_t *) 0x5802441C;
volatile uint32_t* const RCC_D3CFGR = (uint32_t *) 0x58024420;
volatile uint32_t* const RCC_PLLCKSELR = (uint32_t *) 0x58024428;
volatile uint32_t* const RCC_PLLCFGR = (uint32_t *) 0x5802442C;
volatile uint32_t* const RCC_PLL1DIVR = (uint32_t *) 0x58024430;
volatile uint32_t* const RCC_PLL1FRACR = (uint32_t *) 0x58024434;
volatile uint32_t* const RCC_PLL2DIVR = (uint32_t *) 0x58024438;
volatile uint32_t* const RCC_PLL2FRACR = (uint32_t *) 0x5802443C;
volatile uint32_t* const RCC_PLL3DIVR = (uint32_t *) 0x58024440;
volatile uint32_t* const RCC_PLL3FRACR = (uint32_t *) 0x58024444;
volatile uint32_t* const RCC_CIER = (uint32_t *) 0x58024460;
//setup---------------------------------------------
int dataFromRegister = 0;
void SystemClock_Config();
void ADC_start();
void ADC_Init();
void DMA_Init();
void DMA_Config();
void setup(){
// put your setup code here, to run once:
Serial.begin(9600); //for debuging
digitalWrite(LEDR, LOW);
//Serial.println("Début");
SystemClock_Config();
//Serial.println("ClockConf");
ADC_Init();
//Serial.println("Init_ADC");
DMA_Init();
//Serial.println("Init_DMA");
DMA_Config ();
//Serial.println("Config_DMA");
ADC_start();
//Serial.println("ADC_Start");
digitalWrite(LEDB,LOW);
}
void loop() {
// put your main code here, to run repeatedly:
//
}
void ADC_start (void){
CR&=~(1 << 30);
*ADC_CR=CR;
//On a deja ADCALDIF en single ended inputs mode par nos initialisations précédentes
CR=(1<<16);//ADCALLIN calibration linéaire ET offset
*ADC_CR=CR;
//CR=0x90010001;
*ADC_CR|=(1<<31);//Lancer une calibration ADCAL=1
//dataFromRegister=*ADC_CR;
while(*ADC_CR & 0x80000000!=0x00000000){
digitalWrite(LEDR,HIGH);
delay(1000);
digitalWrite(LEDR,LOW);
delay(1000);
}digitalWrite(LEDR,HIGH);
//On attends que la calibration soit complète par un reset de ADCAL
//Processus de calibration terminé (Serial.println(/*dataFromRegister,BIN*/"calibration");)
/******Enable ADC******/
*ADC_ISR |= (1 << 0); // Reset ADC ready flag
*ADC_CR |=(1 << 0); //Enable ADC
while (!(*ADC_ISR & (1 << 0))); //Wait for ready flag
*ADC_ISR |= (1 << 0);
*ADC_CR |= (3 << 8);//11 boost if ADC Clock entre 25 et 50MHz
//ADSTART
CR|=(1<<2);
*ADC_CR=CR;
}
void ADC_Init (void) {
/******Enable ADC Voltage regulator******/
CR=0x00000000;//Fin du deep power down
*ADC_CR=CR;
CR=0x10000000;//ADC voltage regulator Enable
*ADC_CR=CR;
while(*ADC_CR & 0x00001000!=0x00001000){}//check volatage enable (peut etre remplacé par un :
//delayMicroseconds(5); mais c'est moins safe)
//Petit interlude Differentiel ou single ended (a faire avant ADEN)---------
DIFSEL=0x00000000;
*ADC_DIFSEL=DIFSEL;
//digitalWrite(LEDR, LOW);
//while (!(ADC1_ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Calibrate ADC*********/
/******Enable ADC CLOCK******/
*RCC_AHB1ENR |= (1 << 5); //ADC peripheral clock enable
//RCC_D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
digitalWrite(LEDB,LOW);
delay(2000);
digitalWrite(LEDB,HIGH);
delay(2000);
digitalWrite(LEDB,LOW);
delay(2000);
digitalWrite(LEDB,HIGH);
/******Set the prescalar******/
CCR=0x000F0000;
*ADC1_CCR=CCR;
/******Set Scan Mode Data Management and resolution******/
CFGR |= (6 << 2); //RES[2:0]: Data resolution 110=12bits
CFGR |= (3 << 0); //DMNGT[1:0]: Data Management configuration 11: DMA circular mode
*ADC_CFGR=CFGR;
//ADC regular sequence register 1
SQR1=0x00000000;//1st conv correspond au chan 0 (00000) et on réalise une conv par sequence de 1 conv(0000)
*ADC_SQR1=SQR1;
/******Set the Continuous Conversion, ******/
CFGR |= (1 << 13); //CONT: Single / continuous conversion mode for regular conversions
*ADC_CFGR=CFGR;
/******Set the Sampling Time for the channels in ADC_SMPRx******/
SMPR1 &= ~(7 << 0); //SMP0[2:0]: Channel 0 sampling time selection 000: 1.5 ADC clock cycles
*ADC_SMPR1=SMPR1;
PCSEL |= (1 << 0); //PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
*ADC_PCSEL=PCSEL;
/******Set singleEnded Input******/
DIFSEL &= ~(1 << 0); //DIFSEL[19:0]: single ended mode for channel 0
*ADC_DIFSEL=DIFSEL;
}
void DMA_Init (void){
*RCC_AHB1ENR |= (1<<0);
*DMA_S1CR &= ~(1 << 0);
while (*DMA_S1CR & 0x1);
*DMA_S1CR |= (3 << 16);
*DMA_S1CR &= ~(3<<6); //Bits 7:6 DIR[1:0]: data transfer direction
*DMA_S1CR |= (1<<8); //CIRC: circular mode
*DMA_S1CR |= (1<<10); // MINC = 1;
*DMA_S1CR |= (1<<11)|(1<<13); // MSIZE[1:0]: memory data size PSIZE[1:0]: peripheral data size
*DMA_S1CR &= ~(7<<25); // Channel 0 selected
*DMAMUX1_C1CR |=(9<<0);
//*DMA_S1CR |= (1 << 4);//IT TC
//*DMA_S1CR |= (1 << 3);//IT HTC
//NVIC_EnableIRQ(DMA1_IRQn);
}
void DMA_Config (void){
*DMA_S1NDTR = 0x00000003; // Set the size of the transfer
*DMA_S1PAR = 0x40022040; // Source address is peripheral address
*DMA_S1M0AR = 0x30000000; // Destination Address is memory address
// Enable the DMA Stream
*DMA_S1CR |= (1<<0); // EN =1
}
void SystemClock_Config(void) {
/* Enable the floating-point unit. Any configuration of the
floating-point unit must be done here prior to it being enabled */
HWREG(0xE000ED88) = ((HWREG(0xE000ED88) & ~0x00F00000) | 0x00F00000);
/*Var reg declaration*/
uint32_t CR=*RCC_CR;
uint32_t CFGR=*RCC_CFGR;
uint32_t D1CFGR=*RCC_D1CFGR;
uint32_t D2CFGR=*RCC_D2CFGR;
uint32_t D3CFGR=*RCC_D3CFGR;
uint32_t PLLCKSELR=*RCC_PLLCKSELR;
uint32_t PLLCFGR=*RCC_PLLCFGR;
uint32_t PLL1DIVR=*RCC_PLL1DIVR;
uint32_t PLL1FRACR=*RCC_PLL1FRACR;
uint32_t PLL2DIVR=*RCC_PLL2DIVR;
uint32_t PLL2FRACR=*RCC_PLL2FRACR;
uint32_t PLL3DIVR=*RCC_PLL3DIVR;
uint32_t PLL3FRACR=*RCC_PLL3FRACR;
uint32_t CIER=*RCC_CIER;
/*------- Reset the RCC clock configuration to the default reset state -------*/
/* Set HSION bit */
CR = 0x00000001;
*RCC_CR = CR;
/* Reset CFGR register */
CFGR = 0x00000000;
*RCC_CFGR = CFGR;
/* Reset HSEON, CSSON , CSION,RC48ON, CSIKERON PLL1ON, PLL2ON and PLL3ON bits */
CR &= (uint32_t)0xEAF6ED7F;
*RCC_CR = CR;
/* Reset D1CFGR register */
D1CFGR = 0x00000000;
*RCC_D1CFGR = D1CFGR;
/* Reset D2CFGR register */
D2CFGR = 0x00000000;
*RCC_D2CFGR = D2CFGR;
/* Reset D3CFGR register */
D3CFGR = 0x00000000;
*RCC_D3CFGR = D3CFGR;
/* Reset PLLCKSELR register */
PLLCKSELR = 0x00000000;
*RCC_PLLCKSELR = PLLCKSELR;
/* Reset PLLCFGR register */
PLLCFGR = 0x00000000;
*RCC_PLLCFGR = PLLCFGR;
/* Reset PLL1DIVR register */
PLL1DIVR = 0x00000000;
*RCC_PLL1DIVR = PLL1DIVR;
/* Reset PLL1FRACR register */
PLL1FRACR = 0x00000000;
*RCC_PLL1FRACR = PLL1FRACR;
/* Reset PLL2DIVR register */
PLL2DIVR = 0x00000000;
*RCC_PLL2DIVR = PLL2DIVR;
/* Reset PLL2FRACR register */
PLL2FRACR = 0x00000000;
*RCC_PLL2FRACR = PLL2FRACR;
/* Reset PLL3DIVR register */
PLL3DIVR = 0x00000000;
*RCC_PLL3DIVR = PLL3DIVR;
/* Reset PLL3FRACR register */
PLL3FRACR = 0x00000000;
*RCC_PLL3FRACR = PLL3FRACR;
/* Reset HSEBYP bit */
CR &= (uint32_t)0xFFFBFFFF;
*RCC_CR = CR;
/* Disable all interrupts */
CIER = 0x00000000;
*RCC_CIER = CIER;
/* Change the switch matrix read issuing capability to 1 for the AXI SRAM target (Target 7) */
HWREG(0x51008108) = 0x000000001;
}
What I did differently from you is mainly calibrate my ADC and add an ADSTART (my code is for single ended on only one chan of the ADC1). I sincerely think the issue comes from the fact that in ADC_Start() you check the ADDIS bit instead of working on the ADSTART bit in the ADC_CR register.
Thanks anyway you helped me get a better grasp of DMA config using register,
Just a noob :)

MPU9250 with STM32 - Magnetometer readings are constant

Trying to use MPU9250 IMU sensor with my STM32G431RB NUCLEO board. The IMU's accelerometer and gyroscope are working fine. However, magnetometer gives constant values.
I think magnetometers power mode is set to read-once or off. However, I did not understand what the problem was.
On GitHub, I tried to implement the MPU9250 library that kriswiner wrote for Arduino. But I don't know whether I succeeded or failed.
The original code I implemented:
https://github.com/kriswiner/MPU9250/blob/master/MPU9250BasicAHRS.ino
The CubeIDE code I wrote for STM32, to configure and read magnetometer:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* 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_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | Mmode;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
float MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
float MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
float MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
UART tab for readings:
MPU9250 and Mag.'s WHOAMI readings are fine.
When I studied the code a little more, I found that I made some critical mistakes.
After I post this question, I saw an article about reading data from MPU9250.
https://longnight975551865.wordpress.com/2018/02/11/how-to-read-data-from-mpu9250/
Although it is an article written for Arduino, it explained very well how register operations should be done in MPU9250 and especially how power management should be done. I saw that I had configured the AK8963 (magnetometer) incorrectly, and I realized that I was trying to read the data from the AK8963 incorrectly.
I will explain step by step my mistakes and solutions.
1)
After FUSE ROM access, I should have reset AK8963 but I continued without resetting.
Before writing the data resolution and sample ODR values, we need to reset the power mode by typing 0x00 into the AK8963_CNTL register.
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
Also, in the code that I shared, I was trying to read data as soon as I init the magnetometer, but this was also an inaccurate method because the way of reading the data is different compared to the accelerometer or gyroscope. That's why I deleted this part as well.
3)
The magnetometer's data reading process must be accurate. At first, it is necessary to read the register AK8963_ST1 and enable data updating.
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
New, updated code:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
#define DATA_READY_MASK 0x01
#define MAGIC_OVERFLOW_MASK 0x8
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
float MagX;
float MagY;
float MagZ;
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* 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_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | 0x02;
//writeData = 0x16;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/*
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
*/
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
It's a simple mistake because I didn't read the datasheet truly. I hope it will be a good resource for people experiencing the same error. :)
Read ST2 register from the magnetometer to enable data updating after each reading of the measurement registers.
From the datasheet:
When any of measurement data register (HXL ~ HZH) or ST2 register is
read, AK8963 judges that data reading is started.
...
When ST2 register is read, AK8963 judges that data reading is
finished. Stored measurement data is protected during data reading and
data is not updated.
To avoid receiving incomplete or duplicate data you should implement the correct reading sequence as specified in the above datasheet, part 6.4.3.2. Normal Read Sequence

2 stm32 adress does not match in proteus

I am trying to communicate 2 STM32 with I2C.My configuration is as followed:
7-bit addressing mode (no dual address, only OAR1)
100khz speed
ACK enabled (on slave)
ACK disabled (on master, since only 1 byte is transferred between master/slave at any time)
on both master/slave, datasheet says GPIOB (PB6) as SCL as AF and GPIOB (PB7) as SDA as AF but PB8 and PB9 becomes logic 1 in Proteus so I use PB8 and PB9.Adress does not match.Could it be because of Proteus?
Master code:
#include "stm32f10x.h" // Device header
#include "delay.h"
void pinConfig(void);
void i2c_Master_Config(void);
void sendAdress();
int main() {
pinConfig();
i2c_Master_Config();
sendAdress();
while(1) {}
}
void pinConfig() {
RCC->APB1ENR |= 1<<21;//Enable I2C 1 clock
RCC->APB2ENR |= 1<<2;//Enable GPIOA clock
RCC->APB2ENR |= 1<<3;//Enable GPIOB clock
RCC->APB2ENR |= 1<<0;//Enable AFIO clock
GPIOB->CRH |= 0x000000FF; //SCL ve SDA AF Open Drain SCL => PB8
SDA =>PB9
}
void i2c_Master_Config() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock Enable.
I2C1->CR1 |= (1 << 15);
I2C1->CR1 &= ~(1 << 15);
I2C1->CR2 |= 0x08; //36 Mhz peripheral clock.
I2C1->CCR = 0x28; //100 khz clock
I2C1->TRISE = 0x09; //1/8MHZ= 125 ns => 1000ns/125ns =8 => 8+1=9
I2C1->CR1 |= (1<<0); //Peripheral enable.
}
void sendAdress() {
volatile int temp;
I2C1->CR1 |= 1<<8; //START bit.
while(!(I2C1->SR1 & (1<<0))); //wait until start flag is set
I2C1->DR = 0x0B; //7 bit adress.
while(!(I2C1->SR1 & (1<<1))); //wait until addr flag is set
temp = I2C1->SR2; //clear addr flag.
}
Slave code:
#include "stm32f10x.h" // Device header
void pinConfig(void);
void i2c_Slave_Config(void);
uint8_t data;
void I2C1_EV_IRQHandler() {
volatile int temp;
if(I2C1->SR1 &(1<<1)) { //wait until addr flag is set
temp = I2C1->SR1; //clear addr
temp = I2C1->SR2; //clear addr
GPIOA->BRR |= 1<<3;
}
}
int main() {
pinConfig();
i2c_Slave_Config();
while(1) {}
}
void pinConfig() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock enable.
RCC->APB2ENR |= 1<<2; //Enable GPIOA clock
RCC->APB2ENR |= 1<<3; //Enable GPIOB clock
RCC->APB2ENR |= 1<<0; //Enable AFIO clock
GPIOA->CRL |= 0x00002000; //PA3 led.
GPIOB->CRH |= 0x000000FF; //SCL ve SDA AF Open Drain SCL => PB8 SDA =>PB9
GPIOA->BSRR |= 1<<3; //Turn off the led.
}
void i2c_Slave_Config() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock Enable.
I2C1->CR1 |= (1 << 15);
I2C1->CR1 &= ~(1 << 15);
I2C1->CR2 |= 0x08;
I2C1->CCR = 0x28; //100 khz clock
I2C1->OAR1 &= ~(1<<15); //7-bit slave adress.
I2C1->CR2 |= 1<<9; //Interrupt enable.
NVIC->ISER[0] |= 1<<31; //i2c1 interrupt enable.
I2C1->OAR1 = (0x05 << 1); //Slave adress
I2C1->CR1 |= (1<<0); //Peripheral enable.
I2C1->CR1 |= 1<<10; //ACK bit.
}

STM32F030F4 does not start I2C1 correctly in Bare Metal

STM32F030F4 does not start I2C1 correctly.
this is my code:
//Clocks------------------------------------------------
RCC->AHBENR = 0x200000;
RCC->APB1ENR = 0x4040 ;
//------------------------------------------------------
//GPIO--------------------------------------------------
//Configure Alternate Fucntion GPIO to I2C1
GPIOA->AFR[1] = 0x440 //(0b0100)<<4 | (0b0100)<<8 //AF4;
//Set GPIOA Pin 9-10 as Alternate Function
GPIOA->MODER = 0x280000;
//Set GPIOA pin 9-10 as speed as High
GPIOA->OSPEEDR = 0x3c0000;
//Set GPIOA pin 9-10 as open drain
GPIOA->OTYPER = 0x600;
//Set GPIOA pin 9-10 as no Pull-up Pull-down
GPIOA->PUPDR = 0x0;
//------------------------------------------------------
//I2C Resgisters
I2C1->TIMINGR = 0x00201D2D;
I2C1->CR1 = 0x1;
I2C1->CR2 = 0x307001c;
I2C1->TXDR = 0x111;
//Start I2C
I2C1->CR2 |= (0b1) << 13;
But output is not true.
It is like this:
SDA port is not working.
This line seems to be problematic.
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1;
You should set AFSEL9 and AFSEL10 value if I2C pins are 9 and 10. But you set AFSEL8 (which is the lowest in AFR[1]), and clear others.
This code should do the thing:
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1 * 0x00000110;
Problem Solved..
Clocks are problematic:
changes:(
//Enable PORTA clocks
RCC->AHBENR |= 0x20000;
//Enable I2C1 clocks
RCC->APB1ENR |= 0x200000 ;
)

Categories