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.
Related
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 :)
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
I am trying to connect to the ADXL345, which is on another board with GPIOs connected to it (PA5, PA6, PA7, PA12). Also, i am using PulseViewer from [sigrok][1]
The Software shows, that the Sensor is NOT being enabled, yet it seems the functionality is correct. Is there something I'm missing?
#include "stm32l0xx.h"
#include "pmi_stddefs.h"
int32_t spi_init_adxl345(void)
{
/* enable SPI1 clock */
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/* Enable clock for port A */
//RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
/* CPHA at 1, CPOL at 1 */
SPI1->CR1 |= SPI_CR1_CPOL;
SPI1->CR1 |= SPI_CR1_CPHA;
/* enable master mode */
SPI1->CR1 |= SPI_CR1_MSTR;
/* baud rate Maximum (5MHZ ADXL = 011) */
// SPI1->CR1 &= ~SPI_CR1_BR_0;
// SPI1->CR1 |= SPI_CR1_BR_1;
// SPI1->CR1 |= SPI_CR1_BR_2;
SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1);
/* Internal Slave Select */
/* soft. slave management */
SPI1->CR1 |= SPI_CR1_SSM;
/* select slave */
SPI1->CR1 |= SPI_CR1_SSI;
/* Frame Format MSB */
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
/* Receive only mode enable (0 = full-duplex (Transmit and receive)) */
SPI1->CR1 &= ~SPI_CR1_RXONLY;
/* Data frame format 8 */
SPI1->CR1 &= ~SPI_CR1_DFF;
//SPI1->CR2 = SPI_CR2_SSOE | SPI_CR2_RXNEIE;
SPI1->CR2 = 0;
/* Set AF,OUTPUT modes */
/* MISO AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE6_0;
GPIOA->MODER |= GPIO_MODER_MODE6_1;
/* MOSI AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE7_0;
GPIOA->MODER |= GPIO_MODER_MODE7_1;
/* SCLK AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE5_0;
GPIOA->MODER |= GPIO_MODER_MODE5_1;
// TODO: PUPDR for PA4
// /* CS OUTPUT*/
GPIOA->MODER |= GPIO_MODER_MODE12_0;
GPIOA->MODER &= ~GPIO_MODER_MODE12_1;
/* Set I/O output speed (11=very high speed) */
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED5;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED6;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED7;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED12;
/* Alternate Function Low Register 9.3.2*/
/* Pin 5,6,7 (in AFRL) */
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7;
return RC_SUCC;
}
int32_t spi_txrx (uint8_t *buf, uint32_t size)
{
GPIOA->BSRR |= GPIO_BSRR_BR_12;
/* TXE == 1 : the buffer is empty */
while (!(SPI1->SR & SPI_SR_TXE))
{
/* wait untill TXE is empty */
}
for (uint8_t i = 0; i < size; i++)
{
SPI1->DR = buf[i];
}
while (!(SPI1->SR & SPI_SR_TXE))
{
/* mandatory wait until TXE is set */
}
/* detect the end of a transfer */
while ((SPI1->SR & SPI_SR_BSY) != 0)
{
/* 1:busy, 0:not busy */
}
/* random noise */
uint8_t temp = SPI1->DR;
temp = SPI1->SR;
/* Disable sensor */
GPIOA->BSRR |= GPIO_BSRR_BS_12;
/* Diable the SPI <-- No need (Master in full-duplex or transmit
only mode can finish any transaction when it stops providing data
for transmission. In this case, the clock stops after the last
data transaction.*/
return RC_SUCC;
}
Im stuck on this for a reasonable amount of time, yet i didn't find an answer for this issue. Any ideas? Thanks in advance!
[1]: https://www.sigrok.org/wiki/PulseView
SPI nCS lines are typicaly active-low, so PA12 must be set during board startup. In your code ADXL345 is always selected.
This GPIOA->BSRR |= GPIO_BSRR_BR_12; is reset (BR = Bit Reset, BS = Bit Set). Also, there is no need to use the |= operator, because BSRR is write-only register, designed to change port state without use of the read-modify-write sequence.
The SPE bit in SPI_CR1 regisger is not set, SPI is disabled and not transmitting anything. spi_txrx would stuck in the second SPI_SR_TXE while-loop.
Not related to the question, but still - spi_txrx looks FIFO-oriened, yet STM32L0-series SPI have only shifter and DR register, and exchange must be implemented on the byte-by-byte basis, or with DMA. Perhaps this code would work for a 1- or 2-byte long transfer, and you'll even get a correct RX result if only last the byte is valuable. But generaly, it's a code smell.
To me it looks like you are choosing the wrong alternate functions for PA5, PA6 and PA7. Notice that GPIO_AFRL_AFSEL5 is defined as (0xFUL << GPIO_AFRL_AFSEL5_Pos), so when you write it in the AFR[0] register you are choosing alternate function 15 (which is not available). However what you want to choose for SPI1 is alternate function 1.
You could try something like
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL5_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL6_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL7_Pos);
I've made a project based on ATSAMG55J19 MCU, programmed with Atmel Studio and ASF 3
Now i'm trying to add an external RTC clock, because internal SAMg55 rtc does not have a backup battery.
The module will be used to read current time after power failure, then i'll use internal RTC, so i need only basic communication. No need to write specific data in EEPROM or setting alarms.
I have a MCP79411, connected via i2c, but there aren't any library suitable for this MCU that uses ASF TWI library.
There are many Arduino implementation, but they uses Wire.h library, and i can't port it.
I made an attempt porting this simple "driver": https://www.ccsinfo.com/forum/viewtopic.php?t=54105
Here is some code
static void i2c_start(void){
static twi_options_t ext3_twi_options;
flexcom_enable(FLEXCOM4);
flexcom_set_opmode(FLEXCOM4, FLEXCOM_TWI);
ext3_twi_options.master_clk = sysclk_get_cpu_hz();
ext3_twi_options.speed = 100000;
ext3_twi_options.smbus = 0;
twi_master_init(TWI4, &ext3_twi_options);
}
// Init Real Time Clock
void rtc_Init(void)
{
uint8_t seconds = 0;
i2c_start();
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, ADDR_RTCC_READ); // RD from RTC
seconds = bcd2bin(i2c_read(0)); // Read current "seconds" in rtc
//i2c_stop();
//seconds &= 0x7F;
seconds |= 0x80; //set to 1 bit 7 of seconds(ST) enabling oscillator
delay_us(3);
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, bin2bcd(seconds) | 0x80); // Start oscillator with current "seconds value
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, 0x07); // Control Register
twi_write_byte(TWI4, 0x80); // Disable squarewave output pin
//i2c_stop();
}
Then i tried rtc_set_date_time(uint8_t day, uint8_t mth, uint8_t year, uint8_t dow, uint8_t hr, uint8_t min, uint8_t sec)
and
void rtc_get_time(uint8_t &hr, uint8_t &min, uint8_t &sec)
{
twi_write_byte(TWI4, ADDR_RTCC_WRITE);
twi_write_byte(TWI4, 0x00);
twi_write_byte(TWI4, ADDR_RTCC_READ);
sec = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f b01111111
min = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f
hr = bcd2bin(twi_read_byte(TWI4) & 0x3f); //0x3f b00111111
//i2c_stop();
}
But i always get "0" bytes.
i could not understand the correct way to open communication and read bytes from i2c.
The only reference i found is http://asf.atmel.com/docs/latest/sam.drivers.twi.twi_eeprom_example.samg53_xplained_pro/html/index.html but it seems to be a very different type of communication.
What is the correct way to send and receive that bytes via i2c?
I managed to get and set data. I post a draft of library:
#include "asf.h"
#include "conf_board_3in4out.h"
#include "external_rtc.h"
#include <time.h>
//#include <time_utils.h>
twi_packet_t packet_tx, packet_rx;
// helper functions to manipulate BCD and binary to integers
static int bcd2dec(char r_char)
{
MSN = (r_char & 0xF0)/16;
LSN = r_char & 0x0F;
return(10*MSN + LSN);
}
static char msn(char tim)
{
return (tim & 0xF0)/16;
}
static char lsn(char tim)
{
return (tim & 0x0F);
}
#define RTC_ADDR 0x6F // 7 bits
char config_t[10];
char config_2[8];
char tim_read[8];
#define ADDR_SEC 0x00 // address of SECONDS register
#define ADDR_MIN 0x01 // address of MINUTES register
#define ADDR_HOUR 0x02 // address of HOURS register
#define ADDR_DAY 0x03 // address of DAY OF WEEK register
#define ADDR_STAT 0x03 // address of STATUS register
#define ADDR_DATE 0x04 // address of DATE register
#define ADDR_MNTH 0x05 // address of MONTH register
#define ADDR_YEAR 0x06 // address of YEAR register
#define ADDR_CTRL 0x07 // address of CONTROL register
#define ADDR_CAL 0x08 // address of CALIB register
#define ADDR_ULID 0x09 // address of UNLOCK ID register
#define ADDR_SAVtoBAT_MIN 0x18 // address of T_SAVER MIN(VDD->BAT)
#define ADDR_SAVtoBAT_HR 0x19 // address of T_SAVER HR (VDD->BAT)
#define ADDR_SAVtoBAT_DAT 0x1a // address of T_SAVER DAT(VDD->BAT)
#define ADDR_SAVtoBAT_MTH 0x1b // address of T_SAVER MTH(VDD->BAT)
#define START_32KHZ 0x80 // start crystal: ST = b7 (ADDR_SEC)
#define OSCON 0x20 // state of the oscillator(running or not)
#define VBATEN 0x08 // enable battery for back-up
static uint8_t bin2bcd(uint8_t binary_value)
{
uint8_t temp;
uint8_t retval;
temp = binary_value;
retval = 0;
if(temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else
{
retval += temp;
//break;
}
return(retval);
}
static uint8_t bcd2bin(uint8_t bcd_value)
{
uint8_t temp;
temp = bcd_value;
temp >>= 1;
temp &= 0x78;
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
static void setConfig(void){
config_2[0] = tim_read[0] | START_32KHZ; // bitwise OR sets Start osc bit = 1
config_2[1] = tim_read[1];
config_2[2] = tim_read[2];
//0x03h – Contains the BCD day. The range is 1-7.
//Bit 3 is the VBATEN bit. If this bit is set, the
//internal circuitry is connected to the VBAT pin
//when VCC fails. If this bit is ‘0’ then the VBAT pin is
//disconnected and the only current drain on the
//external battery is the VBAT pin leakage.
config_2[3] = tim_read[3] | VBATEN;
config_2[4] = tim_read[4];
config_2[5] = tim_read[5];
config_2[6] = tim_read[6];
config_2[7] = 0x00; // control b3 - extosc = 0
}
static void initialize(void){
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
// read stored time data
config_t[0] = 0x00; //reset pointer reg to '00'
// Set up config to read the time and set the control bits
//i2c.write(addr, config_t, 1); // write address 00
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_t;
packet_tx.length = 1;
twi_master_write(TWI4, &packet_tx);
delay_ms(250);
//
//i2c.read(addr, tim_read, 7); //read time ss mm hh from r1, r2, r3
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = tim_read;
packet_rx.length = sizeof(tim_read);
twi_master_read(TWI4, &packet_rx);
delay_ms(250);
setConfig(); //puts RTCC data into config array from tim_read array
// write the config data
//i2c.write(addr, config_t, 9); // write the config data back to the RTCC module
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void write_time(void){
// re-calculate mins
mins = 8; //ORE 10:08
mins = mins%60;
ch_mins = 16*(mins/10) + mins%10;
MSN = msn(ch_mins);
LSN = lsn(ch_mins);
tim_read[1] = ch_mins;
inc_mins = 0;
//write the data back to RTCC
setConfig();
//i2c.write(addr, config_t, 9);
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
//Display and set hours
//hrs = bcd2dec(tim_read[2]);
// re-calculate hrs
hrs = 10; //ORE 10:08
hrs = hrs%24;
ch_hrs = 16*(hrs/10) + hrs%10;
MSN = msn(ch_hrs);
LSN = lsn(ch_hrs);
tim_read[2] = ch_hrs;
inc_hr = 0;
//write the data back to RTCC
setConfig();
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void read_time(void){
//config_t[0] = 0x00; //reset pointer reg to '00'
//// First Get the time
////i2c.write(addr, config_t, 1); // write address 00
//packet_tx.chip = RTC_ADDR;
//packet_tx.addr[0] = 0; // RTCSEC
//packet_tx.addr_length = 1;
//packet_tx.buffer = config_t;
//packet_tx.length = 1;
//
//twi_master_write(TWI4, &packet_tx);
delay_ms(250);
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
/* Configure the data packet to be received */
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = buf;
packet_rx.length = sizeof(buf);
twi_master_read(TWI4, &packet_rx);
for(uint8_t i = 0; i < sizeof(buf); i++){
tim_read[i] = buf[i];
}
}
void example_print_time(void){
//initialize();
delay_ms(1000);
//write_time(); //commented to see if time is permanent
delay_ms(1000);
read_time();
while(1){
printf("Reading time\n");
printf("%d:%d:%d\n", bcd2dec(tim_read[2]), bcd2dec(tim_read[1]), bcd2dec(tim_read[0] ^ START_32KHZ));
delay_ms(1000);
read_time();
}
}
I need to monitorate the acelleration of a object. I'm using the MPU6050(accelerometer and gyroscope) and the controller STM32F401RBT6. The code below is the solution that i'm using for this.
#define MPU6050_ADDR 0xD0
#define SMPLRT_DIV_REG 0x19
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_XOUT_H_REG 0x43
#define PWR_MGMT_1_REG 0x6B
#define WHO_AM_I_REG 0X75
uint16_t Accel_X_RAW,Accel_Y_RAW,Accel_Z_RAW;
uint16_t Ax,Ay,Az;
char buffer[10];
void MPU6050_Init(void)
{
uint8_t check, data;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,WHO_AM_I_REG,1,&check,1,100);
if(check == 104)
{
data = 0x07;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,SMPLRT_DIV_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,ACCEL_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,GYRO_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,PWR_MGMT_1_REG,1,&data,1,50);
HAL_Delay(50);
}
}
void MPU6050_Read_Accel(void)
{
uint8_t recData[6];
for(int i=0;i<6;i++) recData[i] = 0;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,ACCEL_XOUT_H_REG,I2C_MEMADD_SIZE_8BIT,recData,6,100);
HAL_Delay(50);
uint16_t dataConvert1,dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[0]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[1]);
Accel_X_RAW = dataConvert1 | dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[2]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[3]);
Accel_Y_RAW = dataConvert1 | dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[4]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[5]);
Accel_Z_RAW = dataConvert1 | dataConvert2;
Ax = (uint16_t)(Accel_X_RAW / 16384);
Ay = (uint16_t)(Accel_Y_RAW / 16384);
Az = (uint16_t)(Accel_Z_RAW / 16384);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C3_Init();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
MPU6050_Init();
while (1)
{
MPU6050_Read_Accel();
sprintf(buffer, "%d / ", Accel_X_RAW);
CDC_Transmit_FS((char*)buffer,10);
}
}
I already did it on ATMEL Controler (Arduino) and it worked, but not on STM32.
I am trying to read the value of X Axis and show it using the USB CDC. This code sets a value for the `` `Accel_X_RAW```` variable between 0 and 65535. In Arduino, the reference value was 32768 when the object was stopped, but reading with STM32 remains at the maximum value (65535) if don't have movement. I don't know what's wrong with this code, I tried many options, but it still doesn't work. Can you help me please.
According to the MPU6050 datasheet, the 16-bit values for acceleration and gyroscope are returned in the signed 2's complement form (it detects acceleration values in the range +-g). As you are receiving signed data in the unsigned variables, the result is not what you expect. Therefore, replace all uint16_t datatypes with int16_t.
The reason why you are getting 65535 value; the hex value of -1 in signed int16_t form is 0xFFFF. However, if you store it in the uint16_t variable, it will be read as 65535. I am assuming that the default acceleration value at rest is -1g.
#include <stdlib.h> /* For using memset */
#define MPU6050_ADDR 0xD0
#define SMPLRT_DIV_REG 0x19
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_XOUT_H_REG 0x43
#define PWR_MGMT_1_REG 0x6B
#define WHO_AM_I_REG 0X75
int16_t Accel_X_RAW,Accel_Y_RAW,Accel_Z_RAW;
int16_t Ax,Ay,Az;
char buffer[10];
void MPU6050_Init(void)
{
uint8_t check, data;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,WHO_AM_I_REG,1,&check,1,100);
if(check == 104)
{
data = 0x07;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,SMPLRT_DIV_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,ACCEL_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,GYRO_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,PWR_MGMT_1_REG,1,&data,1,50);
HAL_Delay(50);
}
}
void MPU6050_Read_Accel(void)
{
uint8_t recData[6];
//for(int i=0;i<6;i++) recData[i] = 0;
memset(recData, 0, sizeof(recData));
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,ACCEL_XOUT_H_REG,I2C_MEMADD_SIZE_8BIT,recData,6,100);
HAL_Delay(50);
Accel_X_RAW = (int16_t)(recData[0] << 8 | recData[1]);
Accel_Y_RAW = (int16_t)(recData[2] << 8 | recData[3]);
Accel_Z_RAW = (int16_t)(recData[4] << 8 | recData[5]);
Ax = (int16_t)(Accel_X_RAW / 16384);
Ay = (int16_t)(Accel_Y_RAW / 16384);
Az = (int16_t)(Accel_Z_RAW / 16384);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C3_Init();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
MPU6050_Init();
while (1)
{
MPU6050_Read_Accel();
sprintf(buffer, "%d / ", Accel_X_RAW);
CDC_Transmit_FS((char*)buffer,10);
}
}