Setup I2C reading and writing in c language - c

First I'm noob in this stuff, but learning and really want to get this working. I bought a raspberrypi and a bno055 bosch accelerometer. It comes with a bno055.c, bno055.h and a bno055_support.c file. After getting into programming and c and studying/trying out it seems somehow I need to define how to do I2C read and write. It needs to be setup so you can define the amount of bytes read/written. Below you can find the two functions as predefined :
/* \Brief: The API 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 BNO055_iERROR = BNO055_INIT_VALUE;
u8 array[I2C_BUFFER_LEN];
u8 stringpos = BNO055_INIT_VALUE;
array[BNO055_INIT_VALUE] = reg_addr;
for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
array[stringpos + BNO055_I2C_BUS_WRITE_ARRAY_INDEX] =
*(reg_data + stringpos);
}
/*
* Please take the below APIs as your reference for
* write the data using I2C communication
* "BNO055_iERROR = I2C_WRITE_STRING(DEV_ADDR, ARRAY, CNT+1)"
* add your I2C write APIs here
* BNO055_iERROR is an return value of I2C read API
* Please select your valid return value
* In the driver BNO055_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)BNO055_iERROR;
}
/* \Brief: The API 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 BNO055_iERROR = BNO055_INIT_VALUE;
u8 array[I2C_BUFFER_LEN] = {BNO055_INIT_VALUE};
u8 stringpos = BNO055_INIT_VALUE;
array[BNO055_INIT_VALUE] = reg_addr;
/* Please take the below API as your reference
* for read the data using I2C communication
* add your I2C read API here.
* "BNO055_iERROR = I2C_WRITE_READ_STRING(DEV_ADDR,
* ARRAY, ARRAY, 1, CNT)"
* BNO055_iERROR is an return value of SPI write API
* Please select your valid return value
* In the driver BNO055_SUCCESS defined as 0
* and FAILURE defined as -1
*/
for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
*(reg_data + stringpos) = array[stringpos];
return (s8)BNO055_iERROR;
}
My question is there somebody out, that can coach me through this challenge?
I'm learning about, https://www.kernel.org/doc/Documentation/i2c/dev-interface, but stuck here for the moment. Thx in advance for reading / replying.

I recently wrote a library similar to what you're describing for the MMA8451 i2c accelerometer.
Essentially i2c controllers in Linux get assigned a device node (e.g. /dev/i2c-1). You'll open this device node as a file like this:
int file = open(path, O_RDWR); //path = /dev/i2c-1
Once you have your file handle you can read and write i2c registers using ioctl's. The i2c kernel module supports the I2C_RDWR ioctl which lets you interact with i2c registers.
To read a register you do something like this:
int mma8451_get_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char *val) {
unsigned char inbuf, outbuf;
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
outbuf = reg;
messages[0].addr = addr;
messages[0].flags = 0;
messages[0].len = sizeof(outbuf);
messages[0].buf = &outbuf;
messages[1].addr = addr;
messages[1].flags = I2C_M_RD;
messages[1].len = sizeof(inbuf);
messages[1].buf = &inbuf;
packets.msgs = messages;
packets.nmsgs = 2;
if(ioctl(file, I2C_RDWR, &packets) < 0) {
return 0;
}
*val = inbuf;
return 1;
}
To write a register you do something like this:
int mma8451_set_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char value) {
unsigned char outbuf[2];
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[1];
messages[0].addr = addr;
messages[0].flags = 0;
messages[0].len = sizeof(outbuf);
messages[0].buf = outbuf;
outbuf[0] = reg;
outbuf[1] = value;
packets.msgs = messages;
packets.nmsgs = 1;
if(ioctl(file, I2C_RDWR, &packets) < 0) {
return 0;
}
return 1;
}
Edit: The I2C_RDWR ioctl takes a i2c_rdwr_ioctl_data structure as an argument. It's described like this:
Another common data structure is struct i2c_rdwr_ioctl_data
This is the structure as used in the I2C_RDWR ioctl call
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};
(Defined in linux/i2c-dev.h)
This structure points to the array of i2c_msg to process and defines the number of i2c_msg in the array.
Usage:
If the program is to write one byte (example - the index byte), followed by
reading one byte, two struct i2c_msg data structures will be needed.
One for the write, and another for the read. These two data structures should be declared as an array of two i2c_msg data structures. They will be processed in the order they appear in the array.
The i2c_rdwr_ioctl_data structure contains a pointer to an array of i2c_msg structures. These structures contain the actual messages you want to send or receive. For for example my accelerometer in order to read a register I first needed to write the register I wanted to read to the device and then I could read it (hence why there's two i2c_msg's in my read function). If I was simply writing a register I only needed one.
You'll want to refer to the data sheet for your BNO055 to figure out exactly which registers do what.
As for your example, it looks like it comes from bno055_support.c. It looks like this is just a set of stubs you're meant to implement. It looks like it's basically a mock for a real interface. So what's important is the interface, not the actual code (so don't worry about cnt). The important bits are here:
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_ADDR1;
return BNO055_INIT_VALUE;
}
This sets the function pointers on your device structure to the write functions you're going to define and sets the address of your device and delay. From there you need to implement functions that match this interface:
#define BNO055_BUS_WRITE_FUNC(dev_addr, reg_addr, reg_data, wr_len)\
bus_write(dev_addr, reg_addr, reg_data, wr_len)
#define BNO055_BUS_READ_FUNC(dev_addr, reg_addr, reg_data, r_len)\
bus_read(dev_addr, reg_addr, reg_data, r_len)
The functions I gave you above should be pretty close stand-ins. Good luck!

For example
s8 BNO055_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
u8 array[I2C_BUFFER_LEN] = { BNO055_INIT_VALUE };
array[BNO055_INIT_VALUE] = reg_addr;
if (write(file, array, 1) != 1) {
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
s8 res = read(file, reg_data, cnt);
if (res > 0) return 0;
else return -1;
}
s8 BNO055_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
char buf[1 + cnt];
buf[0] = reg_addr;
memcpy(&buf[1], reg_data, cnt);
if (write(file, buf, cnt+1) != cnt+1) {
return -1;
}
return 0;
}

Here is a simple program which reads and writes I2C registers, using the linux kernel I2C driver. This is tested on a raspberry pi.
#include <stdio.h>
#include <stdint.h> // uint8_t, uint16_t, uint32_t, uint64_t
#include <linux/i2c-dev.h> // I2C_SLAVE
#include <fcntl.h> // open(), O_RDONLY
#include <unistd.h> // read(), write(), usleep()
#include <sys/ioctl.h> // ioctl()
#define I2C_BUS_NUMBER 1
#define I2C_DEVICE_NUMBER 0x1C
#define I2C_REGISTER_NUMBER 4
int open_i2c_device ( int i2c_bus_number , int i2c_device_number ) {
char filename[12] ;
snprintf ( filename , 11 , "/dev/i2c-%i", i2c_bus_number) ;
int file_descriptor = open ( filename, O_RDWR ) ;
if ( file_descriptor < 0 ) {
printf ( "failed to open %s, open() returned %i\n" , filename , file_descriptor ) ;
}
if ( ioctl ( file_descriptor , I2C_SLAVE, i2c_device_number ) < 0 ) {
printf ( "failed to find device %i on i2c bus %i\n" , i2c_device_number , i2c_bus_number ) ;
}
return ( file_descriptor ) ;
}
void i2c_write_register ( int file_descriptor , uint8_t register_to_write_to , uint8_t data_to_write_to_register ) {
uint8_t message[2] ;
message[0] = register_to_write_to ;
message[1] = data_to_write_to_register ;
int i = write ( file_descriptor , message , 2 ) ;
if ( i != 2 ) {
printf ( "error: i2c write returned %i instead of 2\n" , i ) ;
}
}
uint8_t i2c_read_register ( int file_descriptor , uint8_t register_to_read_from ) {
uint8_t message[1] ;
message[0] = register_to_read_from ;
int i = write ( file_descriptor , message , 1 ) ;
if ( i != 1 ) {
printf ( "error: i2c write returned %i instead of 1\n" , i ) ;
}
i = read ( file_descriptor , message , 1 ) ;
if ( i != 1 ) {
printf ( "error: i2c read returned %i instead of 1\n" , i ) ;
}
return ( message[0] ) ;
}
int main () {
int file_descriptor_for_I2C_device = open_i2c_device ( I2C_BUS_NUMBER , I2C_DEVICE_NUMBER ) ;
i2c_write_register ( file_descriptor_for_I2C_device , I2C_REGISTER_NUMBER , 0xA1 ) ;
uint8_t i = i2c_read_register ( file_descriptor_for_I2C_device , I2C_REGISTER_NUMBER ) ;
printf ( "register %02hhx is %02hhx\n" , I2C_REGISTER_NUMBER, i) ;
}
Usually ordinary users are not allowed to access I2C, so you probably need
to run I2C programs with sudo.
You might need to load the linux kernel I2C driver module, and change some
parameters like I2C speed.
Documentation for the I2C linux kernel driver:
https://www.kernel.org/doc/Documentation/i2c/dev-interface
For accessing I2C, you need three numbers: the number of the I2C bus, the
number of the I2C device, and the number of the I2C register. All three of
these numbers are sometimes called address. If someone or some
documentation refers to I2C address, ask which number they are talking
about.
If you do not know what I2C bus number to use, run the command sudo i2cdetect -l
If you do not know what I2C device number to use, run the command sudo i2cdetect -y B,
where B is the I2c bus number.
If you do not know what I2C register number to use, run the command sudo i2cdump -y B D
where B is the I2C bus number and D is the I2C device number.
The I2C protocol includes some special combinations of clock and data pulses
which start a transmission, and which acknowledge a transmission. If a
program is implementing I2C by switching gpio pins between LOW and HIGH, the
program needs to do start and acknowledge. But if a program is using the
I2C linux kernel driver, then the I2C linux kernel driver does start and
acknowledge, so the program should NOT do start and acknowledge.
The linux kernel driver creates a /dev/i2c* for each I2C bus. Your program
should open() the /dev/i2c*, then use ioctl() to associate a single I2C
device number with the I2C bus. So what if you have more than one I2C
device number for an I2C bus? The documentation does not say. Maybe
your program should open() the /dev/i2c* multiple times, then associate
a different I2C device number with each file descriptor. Maybe your
program should change which I2C device number is associated with
the I2C bus with every read or write.
The I2C protocol is that to do a read, first start doing a write, select
which register to write, abort the write without writing any data, then
read.
The I2C protocol is to begin every transmission with a device number. If a
program is implementing I2C by switching gpio pins between LOW and HIGH, the
program needs to begin every transmission with the I2C device number. But
if a program is using the I2C linux kernel driver, then the I2C linux kernel
driver adds the I2C device number to every transmission (which is why the
program must use ioctl() to provide the correct I2C device number to the
I2C linux kernel driver), so the program should NOT begin a transmission
with the I2C device number.
Notice that the I2C protocol and the I2C linux kernel driver protocol are
different. Some details about the I2C protocol do not apply to the
I2C linux kernel driver protocol, and vice versa.
The I2C bus probably runs at a slower speed than the cpu. If a program
writes some data to the I2C controller, it probably takes some time for the
I2C controller to write the data to the I2C bus, and more time to read data
back. So when reading an I2C register, I thought maybe there should be a
delay or timeout. But these functions work without a delay or timeout, so I
guess the linux kernel I2C driver takes care of that. Maybe the linux
kernel driver knows how long I2C access should take, and delays read() for
that amount of time. On a raspberry pi with a 400 khz I2C bus, I found
about 5 milliseconds to read 1 register. I think the code executes faster
than that, so it is probably waiting for the I2C data. If you want your
program to do something else while waiting for I2C data, try multithreading.
Or maybe you could code a loop which checks multiple sensors, and the first
pass through the loop the code sends the request, the second pass through
the loop the code reads the response, the third pass through the loop the
code sends another request, etc.

Related

Array's data is changed if I don't printf it

I am writing a C program on Eclipse to communicate from my ARM Cortex M4-F microcontroller in I2C with its master, another MCU.
In my I2C library, I use a static global variable to store all the parameters of the communication (address, lenghts, data buffers). The issue is that a part (an array containing the data to be transmitted, which are 8 bits integers) of this variable gets modified when the interrupt (Start condition followed by the slave's address on the I2C bus) happens, even before executing the code I put the handler. It gets assigned to 8, whatever the initial value.
I tried to put breakpoints basically everywhere, and a watchpoint on the variable, the changes arises seemingly from nowhere, not in the while loop, and before the call to my_I2C_Handler(), so the interrupt is the cause apparently.
I also tried setting the variable as volatile, but that changed nothing.
I noticed one interesting thing: putting a printf of the array's data during my_I2C_Init() or my_SlaveAsync(), like so:
printf("%d\n", req.tx_data[0]);
corrects this problem, but why? I want to remove all prints after debugging.
#include <stdint.h>
#include "my_i2c.h"
void I2C1_IRQHandler(void)
{
printf("\nI2C Interrupt\n");
my_I2C_Handler(MXC_I2C1); // MXC_I2C1 is a macro for the registry used
}
int main(void)
{
int error = 0;
printf("\nStarting I2C debugging\n");
// Setup the I2C
my_I2C_Shutdown(MXC_I2C1);
my_I2C_Init(MXC_I2C1);
NVIC_EnableIRQ(I2C1_IRQn); // Enable interrupt
my_I2C_SlaveAsync(MXC_I2C1); // Prepare to receive communication
while (1)
{
LED_On(0);
LED_Off(0);
}
printf("\nDone testing\n");
return 0;
}
The structure of the request containing the parameters of the communication is like this:
typedef struct i2c_req i2c_req_t;
struct i2c_req {
uint8_t addr; // I2C 7-bit Address
unsigned tx_len; // Length of tx data
unsigned rx_len; // Length of rx
unsigned tx_num; // Number of tx bytes sent
unsigned rx_num; // Number of rx bytes sent
uint8_t *tx_data; // Data for mater write/slave read
uint8_t *rx_data; // Data for master read/slave write
};
Is declared like so in the beginning of the file:
static i2c_req_t req;
and assigned this way in my_I2C_Init():
uint8_t rx[1] = {0};
uint8_t tx[1] = {12};
req.addr = 0xAA;
req.tx_data = tx;
req.tx_len = 1;
req.rx_data = rx;
req.rx_len = 1;
req.tx_num = 0;
req.rx_num = 0;
Many thanks for your help

GPS UART data written multiple times to Buffer

I am receiving/reading data from a GPS module sent via USART3 to the STM32F091.
The data gets there just fine which I confirm by sending it to my PC COM3 port and feeding it to 'u-center' (GPS evaulation software).
My problem is that I want to evaluate the data myself in my C program, and for that purpose I feed it into a Ring Buffer, however, every character of the GPS signal is written multiple times to the buffer, instead of one by one.
For example
GGGGGGGPPPPPPPPSSSSSSSS instead of GPS
I am unsure what I'm doing wrong, maybe it's something really obvious I'm overlooking after staring at this code so long.
Here's the relevant code.
stm32f0xx_it.c
#include <main.h>
void USART3_8_IRQHandler(void)
{
if (USART_FLAG_RXNE != RESET)
{
uint16_t byte = 0;
/* Data reception */
/* Clear Overrun Error Flag, necessary when RXNE is used */
USART_GetITStatus(USART3, USART_IT_ORE);
/* Read from Receive Data Register and put into byte */
byte = USART_ReceiveData(USART3);
(*pRXD3).wr = ((*pRXD3).wr + 1) % (*pRXD3).max;
(*pRXD3).Buffer[(*pRXD3).wr] = byte;
/* Send Data to PC, and reset Transmission Complete Flag */
USART_GetITStatus(USART1, USART_IT_TC);
USART_SendData(USART1, byte);
return;
}
return;
}
uartGPS.h
....
struct GPSuart
{
BYTE Buffer[255];
WORD max;
WORD re;
WORD wr;
};
....
main.h
....
extern volatile BYTE B_ser_txd_3[255];
extern volatile BYTE B_ser_rxd_3[255];
extern volatile struct GPSuart TXD_uart_3;
extern volatile struct GPSuart RXD_uart_3;
extern volatile struct GPSuart *pRXD3;
extern volatile struct GPSuart *pTXD3;
....
Let me know if I should provide additional information.
This:
if (USART_FLAG_RXNE != RESET)
does not test a flag, that code is inspecting the flag constant itself, which is not what you meant.
You need more code, to access the UART's status register and check the flag:
if (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) != RESET)

STM32F4-Discovery Board: Print out data from connected sensor on the PC screen

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

need to display time using pointer

In the following code I am trying to display clock time, which I get using other functions.
While calling this function from main I am passing addresses in hexadecimal form to this function, and I want to print the time, but it is not printing correctly.
void DS1307_GetTime(unsigned char *h_ptr, unsigned char *m_ptr, unsigned char *s_ptr)
{
I2C_Start(); // Start I2C communication
DS1307_Write(DS1307_ID); // connect to DS1307 by sending its ID on I2c Bus
DS1307_Write(SEC_ADDRESS); // Request Sec RAM address at 00H
I2C_Stop(); // Stop I2C communication after selecting Sec Register
I2C_Start(); // Start I2C communication
DS1307_Write(0xD1); // connect to DS1307( under Read mode) by sending its ID on I2c Bus
*s_ptr = DS1307_Read(); I2C_Ack(); // read second and return Positive ACK
*m_ptr = DS1307_Read(); I2C_Ack(); // read minute and return Positive ACK
*h_ptr = DS1307_Read(); I2C_NoAck(); // read hour and return Negative/No ACK
*s_ptr = bcd_to_dec(*s_ptr);
*m_ptr = bcd_to_dec(*m_ptr);
*h_ptr = bcd_to_dec(*h_ptr);
printf("Time is in ss:mm:hh = %u:%u:%u\n", s_ptr, m_ptr, h_ptr);
I2C_Stop(); // Stop I2C communication after reading the Time
}
I think my problem is in my pointer declaration or printf statement, but I can't figure out exactly what the problem is.
You need to dereference the pointers in printf. Right now you're printing the pointer values (i.e. addresses) of s_ptr, m_ptr, and h_ptr.
printf("Time is in ss:mm:hh = %u:%u:%u\n", *s_ptr, *m_ptr, *h_ptr);
(This is, of course, assuming your other internal time generating functions are working as intended)

How to read and write GPIO values with inb() and outb()

I have an atom board with a Fintek F75111 GPIO. I have info from the manufacturer that the SMbus address to access the chip is 06EH.
I am trying to read and write values to the GPIO in Linux. I have a sample program from manufacturer written for Windows that looks like this.
#include “math.h”
#include “stdio.h”
#include “dos.h”
void main(void){
int SMB_PORT_AD = 0x400;
int SMB_DEVICE_ADD = 0x6E;
/*75111R’s Add=6eh */
//programming DIO as output //0:input 1:Output
/* Index 10, GPIO1x Output pin control */
SMB_Byte_WRITE(SMB_PORT_AD,SMB_DEVICE_ADD,0x10,0xff); delay(10);
//programming DIO default LOW
/* Index 11, GPIO1x Output Data value */
SMB_Byte_WRITE(SMB_PORT_AD,SMB_DEVICE_ADD,0x11,0x00); delay(10);
}
unsigned char SMB_Byte_READ (int SMPORT, int DeviceID, int REG_INDEX)
{
unsigned char SMB_R;
outportb(SMPORT+02, 0x00); /* clear */
outportb(SMPORT+00, 0xff); /* clear */
delay(10);
outportb(SMPORT+04, DeviceID+1); /* clear */
outportb(SMPORT+03, REG_INDEX); /* clear */
outportb(SMPORT+02, 0x48); /* read_byte */
delay(10);
SMB_R= inportb(SMPORT+05);
return SMB_R;
}
void SMB_Byte_WRITE(int SMPORT, int DeviceID, int REG_INDEX, int REG_DATA)
{
outportb(SMPORT+02, 0x00); /* clear */
outportb(SMPORT+00, 0xff); /* clear */
delay(10);
outportb(SMPORT+04, DeviceID); /* clear */
outportb(SMPORT+03, REG_INDEX); /* clear */
outportb(SMPORT+05, REG_DATA); /* read_byte */
outportb(SMPORT+02, 0x48); /* read_byte */
delay(10);
}
I have tried to translate this to Linux compatible functions inb() and outb() and this is what I got.
#include <stdio.h>
#include <sys/io.h>
unsigned int gpio_read(int PORT, int DEVICE, int REG_INDEX){
unsigned int RESPONSE;
outb(0x00, PORT+02);
outb(0xff, PORT+00);
usleep(100);
outb(DEVICE+1, PORT+04);
outb(REG_INDEX, PORT+03);
outb(0x48, PORT+02);
usleep(100);
RESPONSE = inb(PORT+05);
return RESPONSE;
}
unsigned int gpio_write(int PORT, int DEVICE, int REG_INDEX, int REG_DATA){
outb(0x00, PORT+02);
outb(0xff, PORT+00);
usleep(100);
outb(DEVICE, PORT+04);
outb(REG_INDEX, PORT+03);
outb(DATA, PORT+05);
outb(0x48, PORT+02);
usleep(100);
}
void main() {
int PORT = 0x400;
int DEVICE = 0x6E;
unsigned int RESPONSE;
// Ask access to port from kernel
ioperm(0x400, 100, 1);
// GPIO1x set to input (0xff is output)
gpio_write(PORT, DEVICE, 0x10, 0x00);
RESPONSE = gpio_read(PORT, DEVICE, 1);
printf("\n %u \n", RESPONSE);
}
GPIO1X index 0x10 is used to set if the 8 GPIO ports that are connected to GPIO1x are output ports or input ports.
Output values for GPIOs are set using the index 0x11, and if the ports work as input ports then index 0x12 is used for reading input values.
The problem is that I do not know if this is correct or how to read the values (why the read function outputs something before reading?!?)
When I run:
RESPONSE = gpio_read(PORT, DEVICE, X);
Changing X with values from 1..9 I get this as output: 0 8 8 0 0 0 0 0 0
The number 8 confuses me...
Instead of writing directly to the SMBus port, I'd rather use the i2c libraries. I2C (and SMBUS) use two Port Pins, one for the clock and one for the data. The data is trasmitted and received at the clock edges (synchronous), Reading the code, I cannot see clearly which of them (clock or data) is being accessed and when.
To get started I'd use i2ctools as a start point (check this site: http://elinux.org/Interfacing_with_I2C_Devices). This tool helps you to find devices connected to the I2C bus to your microprocessor and also perform basic commmunication.
Hope it helps...

Resources