I'm trying to configure and read data from a LIS2DE12 Acc using SPI Comm. The master of the SPI is an EM9304 uC. First, I'm trying to read the "WHO AM I" register, and compare with the ID provided in the datasheet of the LIS2DE12. The module does respond, but using the oscilloscope I've found out that it keeps sending it's ID through MISO at every transaction.
For example, if I write some bytes to LIS2DE12, while the MOSI is sending the byte, the MISO is sending the LIS2DE12 ID. If I try to read another register with a known value, It stills responds with the LIS2DE12 ID and does not send the register content.
I am new to SPI Comm, but I dont think that its a normal behavior.
This is the struct used to pass the bytes.
typedef struct{
struct{
uint8_t address : 6; //6bit address
uint8_t ms : 1; //If 0, keep the address unchanged, else, it increments the address in case of writing/reading multiple bytes
uint8_t mode : 1; //Read/Write
}control;
uint8_t data; //8bit data
}SPIM_Transaction_t;
Some prints of what is going on.
Clock and MOSI signals
Clock and MISO signals
In the MOSI image, the first 2 Bytes represents a write attempt.
First Byte:
00100011 byte:
0 - Write/ 0 - Dont increment address/ 100011 - 0x0F - The register address
Second Byte:
00110000 byte: 0x30 - The value I want to load in the register.
The next 2 Bytes represents an attempt to read this same register: 10100011
1 - Read 0 - Dont increment address 100011 - (0x23) - Register Address
And 8bits of zero, expecting the sensor to answer with an 8bit data.
In the Clock and MISO image, we can see that the sensor answers with 0x33 whenever there's a clock signal. Because of the symmetry it could be just a random or an error signal.
I am using the correct polarity and phase, other combinations doesnt get any result. The slave CS is grounded to be always activated.
I am using an SPI library and using this function to write bytes:
/**
* #brief Performs one or more write operations.
*
* This is a non-blocking operation, the input buffer cannot be modified until
* the transaction is complete.
*
* #note If the buffer is on the stack, you must ensure that the transaction
* completes before the function exits.
*
* #param chipSelectGpio Number of the GPIO to use for the chip select.
* #param[in] pWriteBuffer An array of data to write to the device.
* #param bytes The number of entries in the source array.
* #param[in] callbackFunction The function to call after the transaction is
* complete, the return status is passed as an argument.
* #param[in] pUserData The data to pass to the callback function.
* #param frequency Maximum clock frequency to send the transaction at or 0 to
* not change the clock frequency (previous frequency is used).
* #returns true if the transaction was scheduled, false otherwise.
*/
//lint -function( fopen(1), SPIM_WriteBytes(2) ) // arg cannot be null
bool SPIM_WriteBytes(uint8_t chipSelectGpio, const uint8_t *pWriteBuffer,
uint8_t bytes, Driver_Callback_t callbackFunction, void *pUserData,
uint32_t frequency);
Related
I'm using an efm32lg230f256 microcontroller and in its code there is a line which uses USART_Rx and it returns:
1 2 3 4
but when I look inside of it I can't see how it retuns 1 2 3 4. I tried to look in the data sheet but there are no such names.
What is the logic in this function? And why does it do this?
c = USART_Rx(uart);
uint8_t USART_Rx(USART_TypeDef *usart)
{
while (!(usart->STATUS & USART_STATUS_RXDATAV))
;
return (uint8_t)usart->RXDATA;
}
"1 2 3 4" is simply the device password
This is a HAL (Hardware Abstraction Layer).
It is probably defined something like this:
typedef struct {
//other uart registers
uint8_t STATUS
//other uart registers
uint8_t RXDATA
} USART_TypeDef;
#define USART_STATUS_RXDATAV 0b00001000 // or equivalent hex value
This struct is a way of interfacing with the memory mapped to the USART peripheral. As such the USART_TypeDef *usart pointer points to a piece of memory which is the location of an USART. When it reads usart->STATUS, it reads the data in the location pointed to by usart, which is the usart STATUS register.
The USART_STATUS_RXDATAV is a bitmask, this is used to extract only the value of RXDATAV which stands for RX DATa AVailable. As such the operation usart->STATUS & USART_STATUS_RXDATAV will return a true value only if the bit at position USART_STATUS_RXDATAV is 1. Otherwise this is false. (True and false are 1 and 0 in C generally.)
(uint8_t)usart->RXDATA
Is reading the data in the RXDATA register of that uart. As such it reads the currently received value.
To insure that RXDATA contains usefull new data,
while (!(usart->STATUS & USART_STATUS_RXDATAV))
;
is executed. This waits for the UART to have received data available.
The datasheet is often quite useful but if more detailed information is needed then the reference manual is the next step. Page 447 is the one about the USART. Page 474 shows the memory layout of the USART peripheral.
I'm trying to set communication between esp32 (master) and stm32 (slave) over SPI. esp32 is running under micropython and sends four bytes, for example
spi.write_readinto(b'\x31\x32\x33\x34', buf)
stm32' code is here (instead of this i use SPI_InitDef.SPI_NSS = SPI_NSS_Soft;)
void SPI_Init(void) {
...
// initialize SPI slave
// for slave, no need to define SPI_BaudRatePrescaler
SPI_InitDef.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitDef.SPI_Mode = SPI_Mode_Slave;
SPI_InitDef.SPI_DataSize = SPI_DataSize_8b; // 8-bit transactions
SPI_InitDef.SPI_FirstBit = SPI_FirstBit_MSB; // MSB first
SPI_InitDef.SPI_CPOL = SPI_CPOL_Low; // CPOL = 0, clock idle low
SPI_InitDef.SPI_CPHA = SPI_CPHA_2Edge; // CPHA = 1
SPI_InitDef.SPI_NSS = SPI_NSS_Hard; // use hardware SS
SPI_InitDef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // APB2 72/64 = 1.125 MHz
SPI_InitDef.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitDef);
SPI_Cmd(SPI1, ENABLE);
NVIC_EnableIRQ(SPI1_IRQn);
//Тут мы разрешаем прерывание по приему
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
}
void main() {
/* Setup SysTick Timer for 10ms interrupts */
if (SysTick_Config(SystemCoreClock / 100))
{
/* Capture error */
while (1);
}
/* Configure the SysTick handler priority */
NVIC_SetPriority(SysTick_IRQn, 0x0);
SPI_Init();
while(1) {
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
for (u8 i=0; i<4; i++) {
printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));
}
printf("\r\n");
}
}
But when I send four bytes 0x31 0x32 0x33 0x34 (analyzer confirms bytes were sent) and my stm gets only 0x31 0x32 0x31 0x32
UPD
I use std periph library and SPI_I2S_ReceiveData is a native method to read byte from SPI.
uint16_t SPI_I2S_ReceiveData ( SPI_TypeDef * SPIx )
Returns the most recent received data by the SPIx/I2Sx peripheral.
Parameters:
SPIx,: To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The value of the received data.
uint16_t SPI_I2S_ReceiveData ( SPI_TypeDef * SPIx )
Returns the most recent received data by the SPIx/I2Sx peripheral.
Parameters:
SPIx,: To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The value of the received data.
But maybe I exit out from IRQ before all data are read. I found to run the while loop until the transmission of the last byte is complete
I think the following code is not correct (but I don't know what the function SPI_I2S_ReceiveData is doing):
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
for (u8 i=0; i<4; i++) {
printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));
}
You exit from the while as soon as one byte is ready to be read. I assume SPI_I2S_ReceiveData is only reading the SPI FIFO. in that case you try to read 4 bytes when possibly only one or two has been received.
You didn't precise the kind of STM32 you're using so I am describing the SPI of STM32H7 (as far as I know it should be pretty similar in other STM32).
To setup a reception in slave mode you should define in particular these 3 parameters:
the length of the socalled "frame" (number of bytes to be read/written at once). This is the field SPI_DataSize` in the HAL data structure, here 8 bits.
the number of transfer (TSIZE) which specifies when the End Of Transmission event is generated. It is expressed in number of "frames". This parameter must be set through register SPI.CR2 before each reception (provided you know the number of bytes to be received of course).
the "FIFO threshold". It specifies at which frequency an event RXP or TXP is generated. You can change this parameter to decrease the workload on the software but to receive only 4 bytes it has no impact.
In your case I think you should setup a transfer size of 4 (4 bytes) and wait for EOT flag to be set. When it is set you only have to read 4 bytes from SPI Receive Register (you can read all 4 bytes at once by the way).
I suggest you do not use the HAL but write your own SPI reception / transmission routines by reading / writing registers. It is not a very complex peripheral (so it will not cost you a lot of time) and you will understand precisely how it works (instead of digging into the HAL).
I am trying to use an lcd screen on my stm32F3discovery.
The screen is made of an lcd 16 characters on 2 lines and an I2C module.
Here is the link of the product:
https://www.aliexpress.com/item/32763867041.html?spm=a2g0s.9042311.0.0.27424c4dsV7dLS
On the back of the screen I can see written: QAPASS 1602A
On the chip of the I2C module I can see written: PCF8574T
Here is the datasheet of the chip:
https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
I tried to follow this tutoriel (the closest from what I am trying to do):
https://www.youtube.com/watch?v=1COFk1M2tak
I use the HAL library, the main function to send data is "HAL_I2C_Master_Transmit".
Here is the description of the function in "HAL_I2C_Master_Transmit":
#brief Transmits in master mode an amount of data in blocking mode.
#param hi2c Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
#param DevAddress Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
#param pData Pointer to data buffer
#param Size Amount of data to be sent
#param Timeout Timeout duration
#retval HAL status
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
//I initialise a buffer
//I use a for loop to find the address of my slave: 0x3D (even tho on the //datasheet it's 0x3F, looks like A1 is bridged :O )
//I use the HAL_I2C_Master_Transmit function
//I move the address one bit to the left
//I reuse the HAL_I2C_Master_Transmit
//Nothing happens on the screen
//Here is my code (I tried to remove the useless comments):
#include "main.h"
I2C_HandleTypeDef hi2c1; // Init generated bu CubeMX
SPI_HandleTypeDef hspi1; // Init generated bu CubeMX
PCD_HandleTypeDef hpcd_USB_FS; // Init generated bu CubeMX
uint16_t adresseLCD; // the variable I put the slave address on
uint8_t buffer[]="123"; // The buffer I wanna see on the screen
void SystemClock_Config(void); // Generated by CubeMX
static void MX_GPIO_Init(void); // Generated by CubeMX
static void MX_I2C1_Init(void); // Generated by CubeMX
static void MX_SPI1_Init(void); // Generated by CubeMX
static void MX_USB_PCD_Init(void); // Generated by CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
adresseLCD=0x3D;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
adresseLCD=adresseLCD*2; // Yes I could have used "adresseLCD<<1" but I
//am not used to that
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
while(1)
{
}
}
I expected something to show on the screen (even random values) but nothing appears (it lights up though).
I get no error (only warnings because I use "1" instead of "Pin_ON" when I WritePIn)
You expect it wrong.
First of allб the I2C module, which is soldered on back, it is just simple serial-to parallel convertor. When you write data on I2C bus, it sets its 8 outputs according to 8 received bits in data bytes. When you read data bytes, it switches into input mode and read logical levels on 8 pins and transmits it over serial wire. More detailed description you can read in the datasheet on PCF8574 you have provided.
I.e. this part does not perform any "magic" which will take characters on input and output them on display. That means the outputting of random data is futile.
You need to know two things more:
how this module is connected to the display driver
what kind of display driver is used and how to utilize it.
Answer to the first question may be found in the Internet:
(taken from here)
You can see the display is connected in 4-bit mode, outputs P4-P7 (i.e. what you transmit in four most significant bits of data bytes) are connected to data lines of the display, while outputs P0-P2 are connected to control lines RS, R/W, EH and P3 is used to control the backlight.
Knowing this we came to the second question. I can only suggest, but more likely your display module have Hitachi HD44780 IC on it. In the datasheet on it you can found the information what data should be output on the control lines.
You can find pin description at page 8:
bit RS selects whatever it be a command (0) or a data (1)
bit R/W chooses write opertion (0) or read (1)
and bit E is actually a strobe. On the falling edge, i.e. when it changes from 1 to 0, the display driver reads data from data lines. That means to pass 4 bits of data you should perform 2 write operations: first one with bit 2 is set high, second with all other bits are the same, but the bit 2 is zero.
Now you can read list of instructions at page 25 of datasheet. And initialization sequence for the 4-bit mode at page 46 (Figure 24). Note for each line of bits there you actually send 2 data bytes: with bit 2 high and then with bit 2 low.
Note, in 4-bit mode all commands and data consist of two write phases: first - top half, then bottom half of a byte. Each phase is 2 data writes to I2C module, with E bit high and low, i.e. you'll need to send 4 bytes to output 1 byte of data.
So basically in order to transmit data from you STM32 to the LCD display driver (HD44780) you need to emulate the latter's interface signalling via the I2C interface chip (PCF8574).
In other words, MCU will send I2C commands that will toggle the I2C "backpack" chip such that it should emulate the right signaling for the LCD driver.
This happens somehow easy whne you are using the HAL_I2C_Master_*() methods. In the buffer array you specify the state of the pins on the LCD as you want them and in the order [0], [1], [2]... etc. For example, let's say that we have DB[7:4] connected to the upper 4 bits of the PCF I2C expander. We can setup the following:
buffer[0] = 0xD0 ; // 0b11010000;
buffer[1] = 0xA0 ; // 0b10100000;
buffer[2] = 0xF0 ; // 0b11110000;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 3, 10); // Note 3 bytes are sent
Once the buffer is prepared, the HAL_I2C_Master_Transmit() call will send the tree bytes consecutively, thus toggling the DB pins as you have mentioned:
DB7: 1 -> 1 -> 1
DB6: 1 -> 0 -> 1
DB5: 0 -> 1 -> 1
DB4: 1 -> 0 -> 1
The same can be applied to all 8 pins on the PCF chip. It is also worth noticing that this sequential IO update also creates a bit less of I2C communication overhead as it only addresses the I2C slave chip once :) .
By the way, I am currently working on a LCD over I2C Library for STM32F1xx (probably it will fit other STM32F series). You can check it out on github (still WIP):
https://github.com/meteowrite/stm32_i2cLcd
Cheers
The IDE I use is 'Keil µVision5'.
I own a STM32F429ZIT-Discovery Board (it has a LCD display on board), to which I connected a Bosch Sensortec BNO055 9-Axis IMU. I want them to communicate via I²C to each other, thus I already made the required connections as follows:
Vin (Sensor) -> 5V (Discovery Board)
GND (Sensor) -> GND (Discovery
Board)
SDA (Sensor) -> PB7 (Discovery Board)
SCL (Sensor) -> PB6
(Discovery Board)
I downloaded the BNO055_driver, which contains the file 'bno055_support.c' (it includes code examples on how to get data from the sensor).
I find this very confusing. I don't know exactly which lines of code I need to include in my project.
I tried it, and I also included some lines that should be relevant for I²C.
I must say I am very inexperienced in programming and microcontrollers in general, clearly. And it's just like a bunch of pieces of code that I scrambled together, which probably don't make any sense. Please bear with me. Here you can see the code I tried.
The mess I've got so far:
#include "stm32f4xx.h"
#include "system_stm32f4xx.h"
#include "stm32f4xx_i2c.h"
#include "stm32f4xx_gpio.h"
#include <stdint.h>
#include "bno055.h"
#include "stm32f4xx_rcc.h"
#define SLAVE_ADDRESS 0x29 // BNO055 Slave Address
void init_I2C1(void){
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// enable APB1 peripheral clock for I2C1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// enable clock for SCL and SDA pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* setup SCL and SDA pins
* You can connect I2C1 to two different
* pairs of pins:
* 1. SCL on PB6 and SDA on PB7
* 2. SCL on PB8 and SDA on PB9
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // we are going to use PB6 and PB7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // set pins to alternate function
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // set GPIO speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // set output to open drain --> the line has to be only pulled low, not driven high
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // enable pull up resistors
GPIO_Init(GPIOB, &GPIO_InitStruct); // init GPIOB
// Connect I2C1 pins to AF
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
// configure I2C1
I2C_InitStruct.I2C_ClockSpeed = 100000; // 100kHz
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; // I2C mode
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 50% duty cycle --> standard
I2C_InitStruct.I2C_OwnAddress1 = 0x00; // own address, not relevant in master mode
I2C_InitStruct.I2C_Ack = I2C_Ack_Disable; // disable acknowledge when reading (can be changed later on)
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // set address length to 7 bit addresses
I2C_Init(I2C1, &I2C_InitStruct); // init I2C1
// enable I2C1
I2C_Cmd(I2C1, ENABLE);
}
/* This function issues a start condition and
* transmits the slave address + R/W bit
*
* Parameters:
* I2Cx --> the I2C peripheral e.g. I2C1
* address --> the 7 bit slave address
* direction --> the tranmission direction can be:
* I2C_Direction_Tranmitter for Master transmitter mode
* I2C_Direction_Receiver for Master receiver
*/
void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
// wait until I2C1 is not busy anymore
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
// Send I2C1 START condition
I2C_GenerateSTART(I2Cx, ENABLE);
// wait for I2C1 EV5 --> Slave has acknowledged start condition
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
// Send slave Address for write
I2C_Send7bitAddress(I2Cx, address, direction);
/* wait for I2C1 EV6, check if
* either Slave has acknowledged Master transmitter or
* Master receiver mode, depending on the transmission
* direction
*/
if(direction == I2C_Direction_Transmitter){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
}
else if(direction == I2C_Direction_Receiver){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
}
}
/* This function transmits one byte to the slave device
* Parameters:
* I2Cx --> the I2C peripheral e.g. I2C1
* data --> the data byte to be transmitted
*/
void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
I2C_SendData(I2Cx, data);
// wait for I2C1 EV8_2 --> byte has been transmitted
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
/* This function reads one byte from the slave device
* and acknowledges the byte (requests another byte)
*/
uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
// enable acknowledge of recieved data
I2C_AcknowledgeConfig(I2Cx, ENABLE);
// wait until one byte has been received
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
// read data from I2C data register and return data byte
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}
/* This function reads one byte from the slave device
* and doesn't acknowledge the recieved data
*/
uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
// disabe acknowledge of received data
// nack also generates stop condition after last byte received
// see reference manual for more info
I2C_AcknowledgeConfig(I2Cx, DISABLE);
I2C_GenerateSTOP(I2Cx, ENABLE);
// wait until one byte has been received
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
// read data from I2C data register and return data byte
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}
/* This funtion issues a stop condition and therefore
* releases the bus
*/
void I2C_stop(I2C_TypeDef* I2Cx){
// Send I2C1 STOP Condition
I2C_GenerateSTOP(I2Cx, ENABLE);
}
int main(void){
init_I2C1(); // initialize I2C peripheral
uint8_t received_data[2];
while(1){
I2C_start(I2C1, SLAVE_ADDRESS<<1, I2C_Direction_Transmitter); // start a transmission in Master transmitter mode
I2C_write(I2C1, 0x20); // write one byte to the slave
I2C_write(I2C1, 0x03); // write another byte to the slave
I2C_stop(I2C1); // stop the transmission
I2C_start(I2C1, SLAVE_ADDRESS<<1, I2C_Direction_Receiver); // start a transmission in Master receiver mode
received_data[0] = I2C_read_ack(I2C1); // read one byte and request another byte
received_data[1] = I2C_read_nack(I2C1); // read one byte and don't request another byte, stop transmission
}
}
/*----------------------------------------------------------------------------*
* The following functions are used for reading and writing of
* sensor data using I2C communication
*----------------------------------------------------------------------------*/
#ifdef BNO055_API
/* \Brief: The function is used as I2C bus read
* \Return : Status of the I2C read
* \param dev_addr : The device address of the sensor
* \param reg_addr : Address of the first register, will data is going to be read
* \param reg_data : This data read from the sensor, which is hold in an array
* \param cnt : The no of byte of data to be read
*/
s8 BNO055_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt);
/* \Brief: The function is used as SPI bus write
* \Return : Status of the SPI write
* \param dev_addr : The device address of the sensor
* \param reg_addr : Address of the first register, will data is going to be written
* \param reg_data : It is a value hold in the array,
* will be used for write the value into the register
* \param cnt : The no of byte of data to be write
*/
s8 BNO055_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt);
/*
* \Brief: I2C init routine
*/
s8 I2C_routine(void);
#endif
/********************End of I2C function declarations***********************/
s32 bno055_data_readout_template(void)
{
/*---------------------------------------------------------------------------*
*********************** START INITIALIZATION ************************
*--------------------------------------------------------------------------*/
#ifdef BNO055_API
/* Based on the user need configure I2C interface.
* It is example code to explain how to use the bno055 API*/
I2C_routine();
#endif
/*--------------------------------------------------------------------------*
* This function used to assign the value/reference of
* the following parameters
* I2C address
* Bus Write
* Bus read
* Chip id
* Page id
* Accel revision id
* Mag revision id
* Gyro revision id
* Boot loader revision id
* Software revision id
*-------------------------------------------------------------------------*/
comres = bno055_init(&bno055);
/* For initializing the BNO sensor it is required to the operation mode
of the sensor as NORMAL
Normal mode can set from the register
Page - page0
register - 0x3E
bit positions - 0 and 1*/
power_mode = POWER_MODE_NORMAL; /* set the power mode as NORMAL*/
comres += bno055_set_power_mode(power_mode);
/*--------------------------------------------------------------------------*
************************* END INITIALIZATION *************************
/************************* START READ RAW SENSOR DATA****************/
/* Using BNO055 sensor we can read the following sensor data and
virtual sensor data
Sensor data:
Accel
Mag
Gyro
Virtual sensor data
Euler
Quaternion
Linear acceleration
Gravity sensor */
/* For reading sensor raw data it is required to set the
operation modes of the sensor
operation mode can set from the register
page - page0
register - 0x3D
bit - 0 to 3
for sensor data read following operation mode have to set
* SENSOR MODE
*0x01 - OPERATION_MODE_ACCONLY
*0x02 - OPERATION_MODE_MAGONLY
*0x03 - OPERATION_MODE_GYRONLY
*0x04 - OPERATION_MODE_ACCMAG
*0x05 - OPERATION_MODE_ACCGYRO
*0x06 - OPERATION_MODE_MAGGYRO
*0x07 - OPERATION_MODE_AMG
based on the user need configure the operation mode*/
comres += bno055_set_operation_mode(OPERATION_MODE_ACCONLY);
/******************START READ CONVERTED SENSOR DATA****************/
/* API used to read Linear acceleration data output as m/s2
float functions also available in the BNO055 API */
comres += bno055_convert_double_linear_accel_x_msq(&d_linear_accel_datax);
comres += bno055_convert_double_linear_accel_y_msq(&d_linear_accel_datay);
comres += bno055_convert_double_linear_accel_z_msq(&d_linear_accel_dataz);
comres += bno055_convert_double_linear_accel_xyz_msq(&d_linear_accel_xyz);
/*-----------------------------------------------------------------------*
************************* START DE-INITIALIZATION ***********************
*-------------------------------------------------------------------------*/
/* For de - initializing the BNO sensor it is required to the operation mode
of the sensor as SUSPEND
Suspend mode can set from the register
Page - page0
register - 0x3E
bit positions - 0 and 1*/
power_mode = POWER_MODE_SUSPEND; /* set the power mode as SUSPEND*/
comres += bno055_set_power_mode(power_mode);
/*---------------------------------------------------------------------*
************************* END DE-INITIALIZATION **********************
*---------------------------------------------------------------------*/
return comres;
}
#ifdef BNO055_API
/*--------------------------------------------------------------------------*
* The following function is used to map the I2C bus read, write, delay and
* device address with global structure bno055_t
*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*
* By using bno055 the following structure parameter can be accessed
* Bus write function pointer: BNO055_WR_FUNC_PTR
* Bus read function pointer: BNO055_RD_FUNC_PTR
* Delay function pointer: delay_msec
* I2C address: dev_addr
*--------------------------------------------------------------------------*/
s8 I2C_routine(void) {
bno055.bus_write = BNO055_I2C_bus_write;
bno055.bus_read = BNO055_I2C_bus_read;
bno055.delay_msec = BNO055_delay_msek;
bno055.dev_addr = BNO055_I2C_ADDR2;
return BNO055_ZERO_U8X;
}
/************** I2C buffer length******/
#define I2C_BUFFER_LEN 8
#define I2C0 5
/*-------------------------------------------------------------------*
*
* This is a sample code for read and write the data by using I2C
* Use either I2C based on your need
* The device address defined in the bno055.h file
*
*-----------------------------------------------------------------------*/
/* \Brief: The function is used as I2C bus write
* \Return : Status of the I2C write
* \param dev_addr : The device address of the sensor
* \param reg_addr : Address of the first register, will data is going to be written
* \param reg_data : It is a value hold in the array,
* will be used for write the value into the register
* \param cnt : The no of byte of data to be write
*/
s8 BNO055_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
s32 iError = BNO055_ZERO_U8X;
u8 array[I2C_BUFFER_LEN];
u8 stringpos = BNO055_ZERO_U8X;
array[BNO055_ZERO_U8X;] = reg_addr;
for (stringpos = BNO055_ZERO_U8X; stringpos < cnt; stringpos++) {
array[stringpos + BNO055_ONE_U8X] = *(reg_data + stringpos);
}
/*
* Please take the below function as your reference for
* write the data using I2C communication
* "IERROR = I2C_WRITE_STRING(DEV_ADDR, ARRAY, CNT+1)"
* add your I2C write function here
* iError is an return value of I2C read function
* Please select your valid return value
* In the driver SUCCESS defined as 0
* and FAILURE defined as -1
* Note :
* This is a full duplex operation,
* The first read data is discarded, for that extra write operation
* have to be initiated. For that cnt+1 operation done in the I2C write string function
* For more information please refer data sheet SPI communication:
*/
return (s8)iError;
}
/* \Brief: The function is used as I2C bus read
* \Return : Status of the I2C read
* \param dev_addr : The device address of the sensor
* \param reg_addr : Address of the first register, will data is going to be read
* \param reg_data : This data read from the sensor, which is hold in an array
* \param cnt : The no of byte of data to be read
*/
s8 BNO055_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
s32 iError = BNO055_ZERO_U8X;
u8 array[I2C_BUFFER_LEN] = {BNO055_ZERO_U8X;};
u8 stringpos = BNO055_ZERO_U8X;
array[BNO055_ZERO_U8X;] = reg_addr;
/* Please take the below function as your reference
* for read the data using I2C communication
* add your I2C rad function here.
* "IERROR = I2C_WRITE_READ_STRING(DEV_ADDR, ARRAY, ARRAY, 1, CNT)"
* iError is an return value of SPI write function
* Please select your valid return value
* In the driver SUCCESS defined as 0
* and FAILURE defined as -1
*/
for (stringpos = BNO055_ZERO_U8X; stringpos < cnt; stringpos++) {
*(reg_data + stringpos) = array[stringpos];
}
return (s8)iError;
}
#endif
Of course I get error messages when I try to compile:
main.c(108): error: #268: declaration may not appear after executable statement in block uint8_t data = I2C_ReceiveData(I2Cx);
main.c(124): error: #268: declaration may not appear after executable
statement in block uint8_t data = I2C_ReceiveData(I2Cx);
main.c(140): error: #268: declaration may not appear after executable
statement in block uint8_t received_data(2);
main.c(140): warning: #550-D: variable "received_data" was set but
never used uint8_t received_data(2);
main.c(207): error: #20: identifier "comres" is undefined comres =
bno055_init(&bno055);
main.c(207): error: #20: identifier "bno055" is undefined comres =
bno055_init(&bno055);
main.c(215): error: #20: identifier "power_mode" is undefined
power_mode = POWER_MODE_NORMAL; /* set the power mode as NORMAL*/
main.c(220): warning: #9-D: nested comment is not allowed
/************************* START READ RAW SENSOR DATA****************/
main.c(255): error: #20: identifier "d_linear_accel_datax" is
undefined comres +=
bno055_convert_double_linear_accel_x_msq(&d_linear_accel_datax);
main.c(256): error: #20: identifier "d_linear_accel_datay" is
undefined comres +=
bno055_convert_double_linear_accel_y_msq(&d_linear_accel_datay);
main.c(257): error: #20: identifier "d_linear_accel_dataz" is
undefined comres +=
bno055_convert_double_linear_accel_z_msq(&d_linear_accel_dataz);
main.c(258): error: #20: identifier "d_linear_accel_xyz" is undefined
comres +=
bno055_convert_double_linear_accel_xyz_msq(&d_linear_accel_xyz);
main.c(375): warning: #1-D: last line of file ends without a newline
endif
The 'BNO055 sensor' has buit in sensor fusion algorithms (blends accelerometer, magnetometer and gyroscope data into stable three-axis orientation output). So it can output the data that I need. I would like to receive 'X, Y and Z Linear acceleration data as m/s2' and print it out on the Computer screen. I don't know if it is even possible to print out the data on my PC screen, since I connected the Discovery Board via USB to my computer. If it is not possible, then maybe it's possible to print it out on the Discovery Board's built in LCD Display.
Any help to clarify my concern would be greatly welcomed.
The first error is complaining that the function call
init_I2C1();
preceeds the declaration
uint8_t received_data[2];
Some compilers allow this, others (such as yours) must have all variables declared before any executable code in a function.
The second error is of the same type
uint8_t data = I2C_ReceiveData(I2Cx);
but is more complicated because you are defining the variable and setting its value with executable code. You should separate the two
uint8_t data;
// ... more code
data = I2C_ReceiveData(I2Cx);
I won't go through them all, most are self-explanatory like "nested comments not allowed".
I'm planning on writing a STM32 Discovery port for this IMU in the coming weeks. Until then, I recommend starting with the Arduino example provided by Adafruit and start replacing the Arduino I2C calls with the appropriate STM32 Discovery I2C calls. On the other hand, if you don't need to use the Discovery board and just want the IMU working with a micro-controller, I'd recommend just using the Arduino. Sample projects and libraries already exist here: https://github.com/adafruit/Adafruit_BNO055
The below IRQ handler drains the USART3 of incoming 32 byes of data. The first IRQ TC event reads the first 6 bytes, then reprograms the DMA engine to read in the last 24 bytes. The second TC repograms it to read the header again This method will allow for variable length messages using DMA. However I cannot seem to be able the change the DMA start address. I'd like to be able to store each message in a seperate buffer, however it just over writes the first buffer on upon recieving each message. What am I doing wrong? The microcontroller is a STM32F103ze (Cortex-M3).
void DMA1_Channel3_IRQHandler(void)
{
uint32_t tmpreg = 0;
/* Test on DMA1 Channel3 Transfer Complete interrupt */
if(DMA_GetITStatus(DMA1_IT_TC3))
{
if (rx3_dma_state == RECIEVE_HEADER )
{
DMA_Cmd(DMA1_Channel3, DISABLE);/* Disable DMA1_Channel2 transfer*/
// Now that have received the header. Configure the DMA engine to receive
// the data portion of the message
DMA_SetCurrDataCounter(DMA1_Channel3,26);
DMA_Cmd(DMA1_Channel3, ENABLE);
} else if ( rx3_dma_state == RECIEVE_MSG )
{
//CurrDataCounterEnd3 = DMA_GetCurrDataCounter(DMA1_Channel3);
DMA_Cmd(DMA1_Channel3, DISABLE);
/* Get Current Data Counter value after complete transfer */
USART3_Rx_DMA_Channel_Struct->CMAR = (uint32_t) RxBuffer3LookUp[rx_buffer_index];
DMA_SetCurrDataCounter(DMA1_Channel3, 6);
DMA_Cmd(DMA1_Channel3,ENABLE);
// Set up DMA to write into the next buffer slot (1 of 8)
++rx_buffer_index;
rx_buffer_index %= 8;
}
/* Clear DMA1 Channel6 Half Transfer, Transfer Complete and Global interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_GL3);
}
// Update the state to read fake header of 6 bytes
// or to read the fake data section of the message 26 bytes
++rx3_dma_state;
rx3_dma_state %= 2;
isr_sem_send(dma_usart3_rx_sem);
}
You say:
...reprograms the DMA engine to read in the last 24 bytes.
but your code says:
DMA_SetCurrDataCounter(DMA1_Channel3,26);
Is that right? Is it 24 or 26?