Codevision AVR access external eeprom 24c02B using TWI - c

I'm a beginner in C. I'm trying to write and read to external eeprom (AT24c02B) then show the data bytes that store in eeprom to LED in PORTB and or to LCD. So I know the data successfully stored in eeprom.
LED in PORTB is active low.
Here is the code, i got it from cvAVR help:
#include <mega16a.h>
// Alphanumeric LCD functions
#include <alcd.h>
// Declare your global variables here
// TWI functions
#include <twi.h>
#include <delay.h>
/* 7 bit TWI bus slave address of the AT24C02B 2kbyte EEPROM */
#define EEPROM_TWI_BUS_ADDRESS (0xA0 >> 1)
void main(void)
{
// Declare your local variables here
struct
{
struct
{
unsigned char msb;
unsigned char lsb;
} addr;
unsigned char data;
} twi_eeprom;
unsigned char eeprom_rd_data;
// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRA=(1<<DDA7) | (1<<DDA6) | (1<<DDA5) | (1<<DDA4) | (1<<DDA3) | (1<<DDA2) | (1<<DDA1) | (1<<DDA0);
// State: Bit7=0 Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);
// Port B initialization
// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
// State: Bit7=1 Bit6=1 Bit5=1 Bit4=1 Bit3=1 Bit2=1 Bit1=1 Bit0=1
PORTB=(1<<PORTB7) | (1<<PORTB6) | (1<<PORTB5) | (1<<PORTB4) | (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);
// TWI initialization
// Mode: TWI Master
// Bit Rate: 100 kHz
twi_master_init(100);
// Alphanumeric LCD initialization
// Connections are specified in the
// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:
// RS - PORTA Bit 0
// RD - PORTA Bit 1
// EN - PORTA Bit 2
// D4 - PORTA Bit 4
// D5 - PORTA Bit 5
// D6 - PORTA Bit 6
// D7 - PORTA Bit 7
// Characters/line: 16
lcd_init(16);
// Global enable interrupts
#asm("sei")
/* write the byte 0x55 to the AT24C02B EEPROM address 0x210 */
twi_eeprom.addr.msb=0x02;
twi_eeprom.addr.lsb=0x10;
twi_eeprom.data=0x55;
twi_master_trans(EEPROM_TWI_BUS_ADDRESS,(unsigned char *) &twi_eeprom,3,0,0);
/* 10ms delay to complete the write operation */
delay_ms(10);
/* read the byte back into the eeprom_rd_data variable */
twi_master_trans(EEPROM_TWI_BUS_ADDRESS,(unsigned char *) &twi_eeprom,2,&eeprom_rd_data,1);
while (1)
{
// Place your code here
PORTB = ; //What variable should I call?
delay_ms(3000);
}
}
twi.h
/******************************************************************************
TWI driver library for the CodeVisionAVR C V2.05.1+ Compiler
Copyright (C) 2010-2011 Pavel Haiduc, HP InfoTech S.R.L., All rights reserved.
*******************************************************************************/
#ifndef _TWI_INCLUDED_
#define _TWI_INCLUDED_
#include <stdbool.h>
// TWI transaction result values
#define TWI_RES_OK 0
#define TWI_RES_BUFFER_OVERFLOW 1
#define TWI_RES_ARBITRATION_LOST 2
#define TWI_RES_BUS_ERROR 3
#define TWI_RES_NACK_RECEIVED 4
#define TWI_RES_BUS_TIMEOUT 5
#define TWI_RES_FAIL 6
#define TWI_RES_UNKNOWN 7
extern unsigned char twi_tx_index; // data index in the transmit buffer
extern unsigned char twi_rx_index; // data index in the receive buffer
extern unsigned char twi_result; // holds the result of the last TWI transaction
// TWI master initialization
// bit_rate - SCL bit rate [kHz]
void twi_master_init(unsigned int bit_rate);
// function used for performing a TWI master transaction
// slave_addr - 7 bit address of the TWI slave with which the transaction must be performed
// tx_data - pointer to the buffer that holds the data to be transmitted to the slave
// tx_count - number of bytes that must be transmitted to the slave during the transaction
// rx_data - pointer to the buffer that holds the data received from the slave
// rx_count - number of bytes that must be received from the slave during the transaction
// returns true on success
bool twi_master_trans(
unsigned char slave_addr,
unsigned char *tx_data, unsigned char tx_count,
unsigned char *rx_data, unsigned char rx_count);
// TWI slave initialization
// match_any_addr - if true, the slave match address logic responds to all received addresses
// addr - 7 bit address of the TWI slave
// rx_buffer - pointer to the slave receive buffer
// rx_buffer_size - size of the slave receive buffer
// tx_buffer - pointer to the slave transmit buffer
// slave_rx_handler - pointer to the TWI slave receive processing function
// slave_tx_handler - pointer to the TWI slave transmit processing function
void twi_slave_init(
bool match_any_addr,
unsigned char addr,
unsigned char *rx_buffer,
unsigned char rx_buffer_size,
unsigned char *tx_buffer,
bool (*slave_rx_handler)(bool rx_complete),
unsigned char (*slave_tx_handler)(bool tx_complete)
);
#pragma library twi.lib
#endif
What I want to ask is:
1. After writing to eeprom, what variable should I call to show the bytes on 8 LED?
ex: after writing bytes: 0xF0, then LED in PORTB will be 0xF0 (11110000)
I did search on internet but still confused with this line
twi_master_trans(EEPROM_TWI_BUS_ADDRESS,(unsigned char *) &twi_eeprom,3,0,0);
twi_master_trans(EEPROM_TWI_BUS_ADDRESS,(unsigned char *) &twi_eeprom,2,&eeprom_rd_data,1);
could someone please explain it? what does the &twi_eeprom,3,0,0 actually mean?
Device : Atmega16a
Program : Codevision AVR 3.12
external eeprom : AT24c02b
Any answer and comment would be appreciate.
Thanks and pardon my english.
Ipin

TWI is a method for transferring data both ways from a master device to a slave device. In your case, your code runs on the master, and the EEPROM is the slave. The data moves between the devices at the same time. That is, a byte is transferred to the slave simultaneously to a byte being transferred back from the slave.
The functions given in twi.h describe a way to start this process, and the parameters are described there.
unsigned char slave_addr,
unsigned char *tx_data,
unsigned char tx_count,
unsigned char *rx_data,
unsigned char rx_count
The first parameter is a constant given in your code.
The next two params are the address of the buffer to take data from and the length to take.
The next two are the address of the buffer to put the received data into, and the length.
To write 0x55 to the EEPROM at a specific address, you need to send three bytes to the EEPROM. These are arranged in a struct for you. If you give the function the address of the struct and the length 3, the function will take care of it for you. (unsigned char *) &twi_eeprom is the address of the struct.
You don't care about receiving right now, so just put in 0 and 0 for the address and length to receive.
When you read back the value, you only need to send the address of the byte to the EEPROM. So send the same struct, but only two bytes of it. You also need to save the received byte. It will go into the variable eeprom_rd_data, since you passed the address of that variable into the function. Set PORRTB to equal eeprom_rd_data to display its value.

Related

Using Interrupt to Transmit via USART on AVR MCU

I believe I understand how to use interrupts to receive serial data on UART of an ATmega328p, but I don't understand the mechanics of how to transmit data.
Here is a basic program that I want to use to transmit the character string "hello" using interrupts to drive transmission. I understand that the character 'o' will likely be transmitted twice, and I am ok with that.
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 1
void initUART(unsigned int baud, unsigned int speed);
volatile uint8_t charIndex = 0;
volatile unsigned char command[5] = "hello";
int main(void)
{
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
sei();
//What do I put here to initiate transmission of character string command?
//Is this even correct?
UDR0 = command[0];
while(1)
{
}
}
ISR(USART_TX_vect)
{
// Transmit complete interrupt triggered
if (charIndex >= 4)
{
//Reach the end of command, end transmission
return;
}
//transmit the first char or byte
UDR0 = command[charIndex];
//Step to the next place of the command
charIndex++;
}
void initUART(unsigned int baud, unsigned int speed)
{
unsigned int ubrr;
if(speed)
{
//double rate mode
ubrr = F_CPU/8/baud-1;
//set double speed mode
UCSR0A = (speed << U2X0);
}
else
{
//normal rate mode
ubrr = F_CPU/16/baud-1;
}
//set the baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)(ubrr);
//enable Tx and Rx pins on MCU
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//enable transmit interrupt
UCSR0B = (1 << TXCIE0);
//set control bits, 8 bit char, 0 stop, no parity
UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}
My understanding is that if I wrote the first character to UDR0 (as I did in main()), this would then trigger a Transmit Complete Interrupt, and then the next byte would be transmitted via the ISR. This does not seem to work.
The code shown here compiles using gcc. Can someone offer an explanation?
The key thing to understand is that the USART has 2 separate hardware registers that are used in the data transmission: UDRn and the Transmit Shift Register, which I'll just call TSR from now on.
When you write data to UDRn, assuming no tx is in progress, it'll get moved to the TSR immediately and the UDRE irq fires to tell you that the UDRn register is "empty". Note that at this point the transmission has just started, but the point is that you can already write the next byte to UDRn.
When the byte has been fully transmitted, the next byte is moved from UDRn to TSR and UDRE fires again. So, you can write the next byte to UDRn and so on.
You must only write data to the UDRn when it is "empty", otherwise you'll overwrite the byte it's currently storing and pending transmission.
In practice, you don't usually mind about the TXC irq, you want to work with the UDRE to feed more data to the USART module.
The TXC irq, however, is useful if you need to perform some operation when the transmission has actually completed. A common example when dealing with RS485 is to disable the transmitter once you're done sending data and possibly re-enable the receiver that you could have disabled to avoid echo.
Regarding your code
Your main issue is that you're setting UCSR0B 2 times in initUART() and the second write clears the bits you just set, so it's disabling the transmitter. You want to set all bits in one go, or use a |= on the second statement.

MAX77651 Can't read register with i2c

I am trying to test the i2c communication of the MAX77651 chip before programming it.
So here is my setup to do so:
I have an UMFT4222ev connected to my Linux laptop by USB. This chip has his SCL and SDA linked to the SDA and SCL of my MAX77651 thanks to the Evaluation Kit for the MAX77651. My MAX77651evkit is powered with 3,7V on the Vbatt pin.
I also installed the mraa librarie from git hub and the libft4222. I know mraa is well installed because i tried it with and example.
I was told that the mraa library takes in charge the setup of the FT4222 so i only used mraa functions to make my program.
I searched on the website of Maxim integrated the i2c slave address and one register where i could read data and check if everyting is working. I then read the i2c protocol of communication to read a single register which is available here : https://datasheets.maximintegrated.com/en/ds/MAX77650-MAX77651.pdf at the page 78.
With all those informations I tried to make my "test program". I solved the compiling errors but when I execute the program I can't get what is in the register which should be 0xFF.
Here is my program:
#include "stdio.h"
#include "syslog.h"
#include "string.h"
#include "unistd.h"
#include "mraa/i2c.h"
#include "mraa.h"
#define I2C_ADDR 0x48
int
main(int argc, char *argv[])
{
uint8_t *message;
*message=0XAC;
int i,j,k;
char reg_a_lire = 0x06;
mraa_init();
mraa_i2c_context i2c;
i2c = mraa_i2c_init(0);
mraa_i2c_frequency(i2c,MRAA_I2C_FAST);
mraa_i2c_address(i2c, I2C_ADDR);
mraa_i2c_write_byte(i2c,0x90);
mraa_i2c_read(i2c, message,1);
mraa_i2c_write_byte(i2c,reg_a_lire);
mraa_init();
mraa_i2c_write_byte(i2c,0x91);
mraa_i2c_read(i2c, message,1);
mraa_i2c_read(i2c, message,1);
printf("%02X \n", *message);
mraa_i2c_stop(i2c);
return 0;
}
Here is the actual output :
alex#cyclonit-laptop ~/Test_alex/tests $ ./a.out
AC
And i would like to get FF instead of AC.
I think my error could come from something i missed to initialize the FT4222 or from the MAX77651 which I maybe did nt power up correctly and its not sufficient to put 3,7V on Vbatt. But maybe this is a problem with my program because i don't know much about uint8_t and I made something wrong.
I am hoping someone has experience with FT4222 and/or MAX77651 and can help me.
I think you are confused by pg. 75 of the MAX77651 datasheet.
ADDRESS | 7-BIT SLAVE ADDRESS | 8-BIT WRITE ADDRESS | 8-BIT READ ADDRESS
Main Address | 0x48, 0b 100 1000 | 0x90, 0b 1001 0000 | 0x91, 0b 1001 0001
(ADDR = 1)* | | |
-------------+---------------------+---------------------+-------------------
Main Address | 0x40, 0b 100 0000 | 0x80, 0b 1000 0000 | 0x81, 0b 1000 0001
(ADDR = 0)* | | |
Depending on your view of the I²C specification, you either use the 7-bit address or use both 8-bit addresses. The reason is that the first I²C transaction sends the following 8 bit:
76543210
\_____/\-- R/#W
\------ 7-bit Slave Address
In your case that's 0x48 shifted one bit to the left followed by either a 1 for read mode or a 0 for write mode. Notice that
(0x40 << 1) | 0x00 == 0x80 == 8-bit Write Address (ADDR = 0)
(0x40 << 1) | 0x01 == 0x81 == 8-bit Read Address (ADDR = 0)
(0x48 << 1) | 0x00 == 0x90 == 8-bit Write Address (ADDR = 1)
(0x48 << 1) | 0x01 == 0x91 == 8-bit Read Address (ADDR = 1)
Some people like to use the slave address while other people prefer to give separate addresses so it's clear what the first byte of communication will look like.
So you should remove the extraneous mraa_i2c_write_byte(i2c, ...); lines from your code, as their intention seems to be to send the read or write address to the slave.
Check out the MRAA library API reference. There are functions that abstract reading and writing registers, either byte-wide [8-bit] registers or word-wide [16-bit] registers:
mraa_i2c_read_byte_data
mraa_i2c_read_bytes_data
mraa_i2c_write_byte_data
In both cases you should check the return codes to know if the action succeeded. In your case, your message is likely not altered, because there was a stop/error condition beforehand, because the slave did either not ACK its slave address or it did not ACK the 0x90/0x91 data bytes you mistakenly sent, as they don't show up in the Programmer's Guide as valid addresses.
Another issue is that you try to read register INTM_GLBL (0x06; Global Interrupt Mask Register) using two consecutive read operations. I'm not sure if your intention is to read register 0x06 twice or if you intended to read INT_M_CHG (0x07; Global Interrupt Mask for Charger), because that's not what it will do. Notice the description on pg. 78 of the datasheet:
Note that when the MAX77650/MAX77651 receive a stop
they do not modify their register pointer.
This is typical behavior for I²C slaves that support multi-byte/sequential reads. You will have to issue a sequential read operation for multiple bytes of data if you want to read multiple registers, e.g. using mraa_i2c_read_bytes_data.
Something like the following might get you on the right track. It is supposed to read the CID and CLKS settings and wait forever if a read error occurred. If successful, it will disable charging, all outputs, setup the red LED to blink once every other second and -- as soon as you uncomment it -- transition the On/Off Controller into On Via Software state to enable bias and the LED.
#define MAX77651_SLA 0x48u
#define CNFG_GLBL_REG 0x10u
#define CID_REG 0x11u
#define CNFG_CHG_B_REG 0x19u
#define CNFG_SBB0_B_REG 0x2Au
#define CNFG_SBB1_B_REG 0x2Cu
#define CNFG_SBB2_B_REG 0x2Eu
#define CNFG_LDO_B_REG 0x39u
#define CNFG_LED1_A_REG 0x41u
#define CNFG_LED1_B_REG 0x44u
#define CNFG_LED_TOP_REG 0x46u
int main(int argc, char *argv[]) {
uint8_t data;
int res;
mraa_init();
mraa_i2c_context i2c0;
i2c0 = mraa_i2c_init(0);
mraa_i2c_frequency(i2c0, MRAA_I2C_STD);
mraa_i2c_address(i2c0, MAX77651_SLA);
res = mraa_i2c_read_byte_data(i2c0, CID_REG);
if (res < 0) {
printf("Reading CID_REG failed.\n");
mraa_i2c_stop(i2c0);
while(1);
}
data = res;
printf("CID_REG: CLKS = %02X CID = %02X\n", ((data & 0x70u) >> 4), (data & 0x0Fu));
/* you should check return values here */
mraa_i2c_write_byte_data(i2c0, 0x00u /* CHG_EN = off */, CNFG_CHG_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_LDO = off */, CNFG_LDO_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB0 = off */, CNFG_SBB0_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB1 = off */, CNFG_SBB1_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB2 = off */, CNFG_SBB2_B_REG);
/* set up red led to toggle every second */
mraa_i2c_write_byte_data(i2c0, 0x17u /* P = 1s, D = 50% */, CNFG_LED1_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x98u /* FS = 6.4mA, BRT = 5.0mA */, CNFG_LED1_A_REG);
mraa_i2c_write_byte_data(i2c0, 0x01u /* EN_LED_MSTR = on */, CNFG_LED_TOP_REG);
// DANGER ZONE: enable only when you know what this is supposed to do
//mraa_i2c_write_byte_data(i2c0, 0x10u /* SBIA_EN = on, SBIA_LPM = normal */, CNFG_GLBL_REG);
mraa_i2c_stop(i2c0);
while(1);
}
I managed to read I2C registers of MAX77651.
First looking at the hardware part I had to make sure that VIO had the right voltage like #FRob said in hid comment.
Then for the software, I stopped using the mraa library because i could'nt control everything. I used the FT4222 library which allowed me to open and initiate the FT4222 device. I took some part of the I2C example in this library for the initialization of the device. Then I noticed that I had to first use the FT4222's write function to send the register i wanted to read , then simply use the read function to get my result. Those are the two steps required.
I won't post the whole program i made as it is mainly taken from the I2C example for initialization but here is the part I added to read my register 0X06 which is supposed to contain 0xFF:
uint8 resultat=0x11;
uint8 *p_resultat=&resultat;
int chiffre;
slaveAddr
uint16 bytesToWrite2 = 1;
uint16 bytesWritten2=1;
uint8 valeur= 0x06; // REGISTER TO READ
uint8 *p_valeur=&valeur;
FT4222_I2CMaster_Write(ftHandle,slaveAddr,
p_valeur,bytesToWrite2,&bytesWritten); //INDICATES WHICH REGISTER TO
// READ
chiffre = FT4222_I2CMaster_Read(ftHandle,
slaveAddr,p_resultat,1, &bytesRead); // READ REGISTER
printf("The content of the register %02X is : %02X \n " ,
valeur,resultat); // DISPLAY RESULT
printf("reading success if : %d = 0 \n " , chiffre);
// READING SUCCESS ?
With this code and the initialization i get the following result :
alex#cyclonit-laptop ~/Downloads/libft4222-1.2.1.4/examples $ ./a.out
Device 0 is interface A of mode-0 FT4222H:
0x0403601c FT4222 A
Chip version: 42220200, LibFT4222 version: 01020104
The content of the register 06 is : FF
reading success if : 0 = 0
Skipping interface B of mode-0 FT4222H.
If someone has the same problem you are free to ask me your question by answering this post.I am very thankful to the people here who helped me!

Converting from C++ language to C (from class to struct) to use HDC1080 sensor with ATTiny841

Today I'm trying to convert this library: Arduino library for ClosedCube_HDC1080 Humidity and Temperature sensor to use it with an ATTiny841 ATTiny841 datasheet, to do it I'm going to use the Fleury's Library to use the ATTiny841 as Master and the HDC1080 as Slave.
My problem is that I have some issues to convert from the C++ language that is used in the HDC1080 library for Arduino to the ATTiny, which use C language, for that I would show you header files to explain myself.
HDC1080_Registers;
class ClosedCube_HDC1080 {
public:
ClosedCube_HDC1080();
void begin(uint8_t address);
uint16_t readManufacturerId(); // 0x5449 ID of Texas Instruments
uint16_t readDeviceId(); // 0x1050 ID of the device
HDC1080_Registers readRegister();
void writeRegister(HDC1080_Registers reg);
void heatUp(uint8_t seconds);
float readTemperature();
float readHumidity();
float readT(); // short-cut for readTemperature
float readH(); // short-cut for readHumidity
private:
uint8_t _address;
uint16_t readData(uint8_t pointer);
};
#endif
This is from the header (.h) file of HDC1080 sensor, I read that class function is not available in C language, so I decide to use struct function, which I saw that is available in C language, but in examples that I saw on Internet, they only use struct function to declare variables, and functions not like:
ClosedCube_HDC1080();
void begin(uint8_t address);
HDC1080_Registers readRegister();
void writeRegister(HDC1080_Registers reg);
void heatUp(uint8_t seconds);
They are define it outside of the struct function, so I get confused with those void's which are inside of the class function, this is reason why I looking for some help. I just need to know:
How to declare those functions to work with the struct
Is going to be much easier to define them later in the C (.c) file?
Thanks for your time and your patience, is my first time converting from one language to another.
Because the only internal state in that class is a single uint8_t, and this code is run on a microcontroller, it is much better to just supply that address as a parameter to each function.
First, note that my suggestions below are derivative of the ClosedCube HDC1080 Arduino library, and therefore licensed under the same license:
/*
Arduino Library for Texas Instruments HDC1080 Digital Humidity and Temperature Sensor
Originally written by AA for ClosedCube; this suggested conversion by Nominal Animal
---
The MIT License (MIT)
Copyright (c) 2016-2017 ClosedCube Limited
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
ATtinys are small 8-bit microcontrollers, so it's best to avoid using the float type. Instead, I suggest you use units of 1/1000 degrees Celsius for temperature (25000 referring to 25 degrees Celsius), and 1/100000 for humidity (50000 referring to 50% of relative humidity).
The interface to the adapted library can be quite simple:
void HDC1080_begin(const uint8_t address);
void HDC1080_heater(const uint8_t address, const uint8_t seconds);
uint16_t HDC1080_manufacturer(const uint8_t address);
uint16_t HDC1080_device_id(const uint8_t address);
int32_t HDC1080_temperature(const uint8_t address);
uint32_t HDC1080_humidity(const uint8_t address);
If we assume we keep temperature and humidity precision at 14 bits (configured by HDC1080_begin(), we don't need the user to set or read the configuration register.
The example code shows that HDC1080_manufacturer() should return 0x5449, and HDC1080_device_id() should return 0x1050.
Since we don't know what kind of interface the "Fleury's Library" has, I shall show the implementation of the above functions with comments (starting with /* I2C:) instead of actual library calls.
The static ones are internal functions only used by the abovementioned functions, not "user" code.
#define HDC1080_TEMPERATURE ((uint8_t)0x00)
#define HDC1080_HUMIDITY ((uint8_t)0x01)
#define HDC1080_CONFIGURATION ((uint8_t)0x02)
#define HDC1080_SERIAL_ID_FIRST ((uint8_t)0xFB)
#define HDC1080_SERIAL_ID_MID ((uint8_t)0xFC)
#define HDC1080_SERIAL_ID_LAST ((uint8_t)0xFD)
#define HDC1080_MANUFACTURER_ID ((uint8_t)0xFE)
#define HDC1080_DEVICE_ID ((uint8_t)0xFF)
/* Bits 15..8 of the HDC1080 configuration.
Heater disabled, 14 bit precision for
both temperature and relative humidity.
See page 15 of
http://www.ti.com/lit/ds/symlink/hdc1080.pdf
for other possibilities.
*/
#define HDC1080_CONFIG_HI ((uint8_t)0)
static uint16_t HDC1080_read(const uint8_t address, const uint8_t item)
{
uint8_t hi, lo;
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits 'item'. */
/* I2C: End transmission. */
/* Delay for 9 ms (0.009 seconds). */
/* I2C: Prepare to read 2 bytes from i2c address 'address'. */
hi = /* I2C: Read byte. */
lo = /* I2C: Read byte. */
return (((uint16_t)hi) << 8) | lo;
}
void HDC1080_begin(const uint8_t address)
{
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: HDC1080_CONFIG_HI (=0x00). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
}
void HDC1080_heater(const uint8_t address, const uint8_t seconds)
{
uint8_t s, i;
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: 48 (=0x30). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
for (s = 0; s < seconds; s++) {
for (i = 0; i < 66; i++) {
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits 'item'. */
/* I2C: End transmission. */
/* Delay for 20 ms (0.020 seconds). */
/* I2C: Prepare to read 4 bytes from i2c address 'address'. */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
}
}
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: HDC1080_CONFIG_HI (=0x00). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
}
uint16_t HDC1080_manufacturer(const uint8_t address)
{
return HDC1080_read(address, HDC1080_MANUFACTURER_ID);
}
uint16_t HDC1080_device_id(const uint8_t address)
{
return HDC1080_read(address, HDC1080_DEVICE_ID);
}
int32_t HDC1080_temperature(const uint8_t address)
{
uint16_t temp1 = HDC1080_read(address, HDC1080_TEMPERATURE) >> 2;
return (int_t)(((uint32_t)20625 * temp1 + (uint32_t)1024) >> 11) - (int32_t)40000;
}
uint32_t HDC1080_humidity(const uint8_t address)
{
uint16_t temp1 = HDC1080_read(address, HDC1080_HUMIDITY);
return ((uint32_t)3125 * temp1 + (uint32_t)1024) >> 11;
}
Note that the relative humidity range is 0 to 99998, i.e. 0%RH to 99.998% RH. This is not an error, and the formula is taken from the datasheet and losslessly scaled to the new range. Similarly, the possible temperature range is -40000 to +124989, corresponding to -40°C to +124.989°C, and this too is according to the TI datasheet, losslessly scaled to the new range. The + (uint32_t)1024 term in both ensures correct rounding (to nearest).
You do still need to implementing the /* I2C: and /* Delay comments using whatever library you have at hand.

MSP430 - Mixing Ports Registers inside an struct

I am now doing a program for a MSP430 in C and I am using Port 1.6, 1.7, 2.0, 2.1, and 2.2 to drive some LEDs. Now, in order to turn ON all LEDs I simply have to write:
P1OUT |= 0xC0;
P2OUT |= 0x07;
However, I would like to know if is possible to create an C structure (with the name LED_ACTIVATION) that points to bits 0, 1, and 2 of the P2OUT Register (0x0029) and to bits 6 and 7 from P1OUT Register (0x0021), that will allow me to write something like this:
LED_ACTIVATION = 0x2F;
where port 2.2 is the most significant bit (bit 5) and port 1.6 is the lowest significant bit(bit 0).
In the MSP we can do this assignment for each register like this:
__no_init volatile union
{
unsigned char P1OUT; /* Port 1 Output */
struct
{
unsigned char P0 : 1; /* */
unsigned char P1 : 1; /* */
unsigned char P2 : 1; /* */
unsigned char P3 : 1; /* */
unsigned char P4 : 1; /* */
unsigned char P5 : 1; /* */
unsigned char P6 : 1; /* */
unsigned char P7 : 1; /* */
}P1OUT_bit;
} #0x0021;
or like this:
#define P1OUT_ (0x0021u) /* Port 1 Output */
DEFC( P1OUT , P1OUT_)
But is it possible to mix both register address?
Thanks.
The MSP430 is not bit/pin addressable. So, no, you can't create a structure that contains individual pins from multiple ports.
In order to make:
LED_ACTIVATION = 0x2F;
You would need to use a function or a macro.
See also: Accessing individual I/O pin on MSP430

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