Accessing values from i2c address - c

You can take a look at this website: http://www.hitechnic.com/cgi-bin/commerce.cgi?preadd=action&key=NSK1042 to get a better understanding of what I'm talking about. For example, the website reads: the i2c address of the sensor is 0x10 and the table of the values there reads:
Address Type Contents
00 – 07H chars Serial Version Number
43H byte Sensor 1 DC Signal Strength
How can I access these values in C? Thanks.

These registers can be memory mapped. A few things you'll need to do:
map the device's physical memory to your programs address space
declare any pointers to this region as volatile
The volatile keyword will stop the compiler from "optimizing" the program to be incorrect. e.g. by assuming that reads to the same memory location will yield the same result because the program hasn't written to it.
The easy part of this is to declare a struct such that all the offsets are the same as the device and that each part has the right size.
ie
struct hitech {
char serial_version[8];
char manufacturer[8];
/* etc */
};
volatile struct hitech *my_device;
The second part is working out where the device is mapped. If it's plugged in to your computer you should be able to see this. You might need to do one of the following: mmap the device's physical address. Or just write my_device = 0x< address >. Or a combination of the two.
From the website:
"The I2C address of the IRSeeker V2 sensor is 0x10"
So you want to write 0x10 above for my_device.
Then you'll need to compile for the correct micro-controller and load your program at the correct location as firmware.
You'd be better off using their programming language.

Assuming they're not supplying an SDK for you to access these values:
// I'm assuming these are read-only, hence the "const"
const char *g_serialVersionNumber = (const char *)0x00; // be careful not to access more than 8 bytes
const unsigned char *g_sensor1DCSignalStrength = (const unsigned char *)0x43;
void main()
{
printf("Serial version number: %s\n", g_serialVersionNumber);
printf("Sensor 1 DC Signal Strength: %d\n", *g_sensor1DCSignalStrength);
}

Related

Reading value from memory location (with 24-bit address) in 8-bit microcontroller

I thought it should be as simple as:
uint32_t getCrc(void)
{
uint32_t expectedCrc = *(uint32_t*)0x27FF0;
return expectedCrc;
}
And if the memory location has the following value:
Then the expectedCrc should equal to 0xECD8743D
But surprisingly, the value is: 0x82828282
I tried defining a pointer to uint32_t and assign the memory address to it as following:
uint32_t getCrc(void)
{
uint32_t *ptr = (uint32_t*)0x27FF0;
uint32_t expectedCrc = *ptr;
return expectedCrc;
}
But the value of the pointer itself was 0xFFFF and expectedCrc equal 0x82828282
I found these two values in a different memory address:
I also tried the same with char *ptr = (char*)0x27FF0 but it gave the same values.
Finally, I tried to check what is the size of a pointer to char in this controller using uint8_t size = sizeof(char*); and the answer was 0xb0 which equals 176.
I think it has something to do with the 24-bit memory address and the CPU architecture. I'm working on stm8 controller.
Could someone explain why does this happen?
UPDATE
I tried replacing the address 0x27FF0 with 0xFFF0 and it worked fine. So the problem is with the long address. I want to write the CRC value in the last address to avoid over-writing it with the code itself in case the program went bigger. How can I handle this?
From the Cosmic compiler datasheet https://www.cosmic-software.com/pdf/cxstm8_pd.pdf
cxstm8 provides 2 different memory models depending on the size of the application.
For applications smaller that 64k, the “section 0” memory model provides the best code density by defaulting function calls and pointers to 2 bytes.
For applications bigger than 64k, the standard memory model provides the best flexibility for using easily the linear addressing space. Each model comes with its own set of libraries.
This may be the cause for your problem. If you want to access the memory location of above 16 bit address directly you need to use the correct memory model.
As #Raje answered that it is about the memory models, I did further reading in the COSMIC user's guide and I found the following:
The STM8 compiler supports two memory models for application
larger than 64K, allowing you to choose the most efficient behavior
depending on your processor configuration and your application. All
these models allow the code to be larger than 64K and then function
pointers are defaulted to #far pointers (3 bytes). Data pointers are
defaulted to #near pointers (2 bytes) unless explicitly declared with
the #far modifier.
Therefore, the solution was to add #far to the pointer's type as following:
uint32_t calculatedCrc = 0;
expectedCrc = *(#far uint32_t*)0x27FF0;
This got the problem solved.

How do these uart structs as function parameters work?

I have this code I'm trying to understand but I'm stuck, so maybe one of you good people could point me in the right direction.
Taking it step by step I have this part of an initialisation...
volatile struct UART *pTXD1;
volatile struct UARTINIT *pPort1;
...the strcuts...
struct UARTINIT
{
union SR_BYTE SR; /* Status register */
BYTE DR; /* Data register */
BYTE BRR1; /* Baud Rate reg 1 */
BYTE BRR2; /* Baud Rate reg 2 */
union CR1_BYTE CR1; /* Control register 1 */
union CR2_BYTE CR2; /* Control register 2 */
BYTE CR3; /* Control register 3 */
BYTE CR4; /* Control register 4 */
};
struct UART
{
BYTE *Buffer;
WORD max;
WORD read;
WORD write;
BYTE Baud;
BYTE stopp;
};
...function call...
Transmit_Uart(pTXD1, pPort1);
...the function
void Transmit_Uart(struct UART *Buff, struct UARTINIT *USART)
{
if ((*Buff).write != (*Buff).read)
{
if ((*USART).SR.bit.TC)
{
(*Buff).read = ((*Buff).read + 1) % (*Buff).max;
(*USART).DR = (*Buff).Buffer[(*Buff).read];
}
}
else
(*Buff).stopp = OFF;
return;
}
My problem is that I don't understand what information is written into the structs.
The function is called with pTXD1 and pPort1 as parameters, but those two are just pointers to structs, are they not?
I apologize if my question is not understandable, and will gladly provide further information as needed.
They use the struct UARTINIT as a way to visualize memory-mapped hardware registers in the UART hardware. The struct must correspond exactly to the register layout for this to work, so whoever wrote the code must ensure that no struct padding is active. (Generally not an issue on 8 or 16 bit MCUs, but will be on bigger cores.)
So the struct pointer pPort1 will point at the first register in one of potentially several UART hardwares on the chip. The registers are already "allocated" - they exist is the form of memory-mapped hardware.
The UART struct seems to simply be a user-defined struct used for storing various settings and for shovelling data in and out of the UART hardware buffers. If so, this struct must be allocated somewhere in user RAM. Or possibly it is also a register memory map, in which case you have some very strange hardware.
Some manner of documentation is needed to understand how its used. Code such as ((*Buff).read + 1) % (*Buff).max; looks quite obscure; I have no idea why they aren't simply letting the driver read/write raw data bytes.
The call by reference method of passing arguments to a function copies the address of an argument into the formal parameter. Inside the function, the address is used to access the actual argument used in the call. It means the changes made to the parameter affect the passed argument.
click here for more info
Here you pass the pointers to the struct instead of the struct itself. *Buff "dereferences" the pointer. In other words *Buff would give you the structure pointed to by the pointer.
for tutorial on dereferencing(haven't watched it but seems decent enough)
now you could have sent the struct directly as an argument but what this does is creates a copy of the struct in the scope of the function. So any changes you make inside the function would effect that copy and not the original. So you send a pointer.
Additionally it is not advisable to pass structs to function by value because they usually are pretty large and need to be copied into the stack for the function...blah blah(i could go on)
Bottom line....
you pass pointer to function....function updates values at pointer....drops mic(yayyy).
would be glad to clarify should doubts arise. But try and see some tutorials regarding passing by reference, dereferencing etc.(should help)

Store address as value and use it later on pointer

I'm trying to emulate a system that needs to copy data to a peripheral, bare metal, no OS.
The convention states that the copy function is a C-function which takes as the address of the peripheral an 8-bit address that is written to a certain register. The peripheral uses that internally. However, I'm simulating the thing in C and to test the full functionality I am doing something like the following MWE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[]){
//stack-array to copy
char array[4] = {3,32,-12,99};
//naive emulation of the peripheral memory space
char peripheral_array[4];
//herein lies the address send as a value
char address_reg = (char*)(peripheral_array);
//assume this is the peripheral side
//set ptr to the address of the peripheral_array
char *ptr = (char*) address_reg;
memcpy((void*)ptr,array,sizeof(char)*4);
return EXIT_SUCCESS;
}
I get segmentation fault.
What's the issue at hand here?
How can I store the pointer of array as a value, send it, recast it successfully as the address to an array and perform the memcpy?
You want to emulate something with a 8-bit address space in an environment with a 32-bit or 64-bit address space, and thus you have some difficulties, because it doesn't convert trivially. Specifically, this line char address_reg = (char*)(peripheral_array) cast a wide pointer to a 8-bit value, and lose most of the pointer, which means you won't be able to convert back.
The solution is to push further your simulation and emulate your destination 8-bit address space :
Typedef a 8-bit pointer (cleaner) : typedef uint8_t ptr8;
Declare your 8-bit destination address space : uint8_t my_destination_memory[256]
Define you own memcpy to copy to this address space : void my_memcpy( ptr8 addr_dest, const void * src, int len ) {
    memcpy( my_destination_memory + addr_dest, src, len );
}
This way you can pass around your 8-bit pointer of type ptr8 (or whatever you name it) and copy to it without problem.
Note that I assumed that your source address space is not of importance, buti f it is you can also emulate it. You should be able to emulate 16-bit or even 24-bit address space in the same way (You can use the native pointers if you need 32-bit).
why storing it into char? the variable that can hold an address to a char is a char* - On Both machines - your PC and your embedded MCU!
On your MCU sizeof(char*) may be 1 or 2, and on your PC it may be 4 or 8;
If you want to write platform compatible code, use a char*.
If you want to go a few steps further to simulating the machines address space then you have to provide own implementations of the standard-lib functions. So that they interpret addresses in machines address space as indices to some some memory array that you defined.
However the better approach is mostly to provide some Hardware Abstraction Layer (HAL) that encapsulates system specific tasks, rather than using machine specifica in your business logic.
First of all, please note that storing integer values in the char type is dangerous, since it is a type with implementation-defined signedness. It should never be used for anything but strings.
Instead, always use either int8_t (signed) or uint8_t (unsigned).
The cause of the problem is that a char (or an int8_t for that matter) is not large enough to hold an address.
There is however the uintptr_t in stdint.h which is guaranteed to be large enough to contain an address.
Simply replace
char address_reg = (char*)(peripheral_array);
with
uintptr_t address_reg = (uintptr_t)peripheral_array;

Variable address in eeprom space as function argument in C

I have a simple and quick problem with C in PIC24f16ka102 and xc16 compiler.
I want to pass a variable reference to my function. The variable is in eeprom space:
int __attribute__ ((space(eedata))) eeData; // Variable located in EEPROM,declared as a global variable.
With this sequence I am able to save some data in eeprom memory:
unsigned int offset;
// Set up NVMCON to erase one word of data EEPROM
NVMCON = 0x4004;
// Set up a pointer to the EEPROM location to be erased
TBLPAG = __builtin_tblpage(&eeData2); // Initialize EE Data page pointer
testDebug = TBLPAG;
offset = __builtin_tbloffset(&eeData); // Initizlize lower word of address
__builtin_tblwtl(offset, 0x9876); // Write EEPROM data to write latch
asm volatile ("disi #5"); // Disable Interrupts For 5 Instructions
__builtin_write_NVM(); // Issue Unlock Sequence & Start Write Cycle
while(NVMCONbits.WR == 1);
This way I write value 0x9876 to first 16 bits of eeprom.
But I need to have it as &eeData
I want to write my own function:
void eeprom_writeWord(unsigned int __attribute__ ((space(eedata))) addresOfMyEEpromVariable, unsigned int value)
{
unsigned int offset;
// Set up NVMCON to erase one word of data EEPROM
NVMCON = 0x4004;
// Set up a pointer to the EEPROM location to be erased
TBLPAG = __builtin_tblpage(&addresOfMyEEpromVariable); // Initialize EE Data page pointer
offset = __builtin_tbloffset(&addresOfMyEEpromVariable); // Initizlize lower word of address
__builtin_tblwtl(offset, value); // Write EEPROM data to write latch
asm volatile ("disi #5"); // Disable Interrupts For 5 Instructions
__builtin_write_NVM(); // Issue Unlock Sequence & Start Write Cycle
while(NVMCONbits.WR == 1);
}
but How to pass my address as a function argument so that my function would see that it is still adress in eeprom space? It cannot be just address, becouse if so I get error. __builtin functions need address with some attribute that it is eeprom memory.
How to pass eeprom address with attribute to my function?
Please help
edit:
Thank You for Your advise, but I still get the same error:
error: Argument to __builtin_tbloffset() is not the address
of an object in a code, psv, or eedata section;
Function __builtin_tbloffset needs an address of the eeprom memory, not just an address of something. It works well if I use the whole sequence but not in a function (I mean the sequence in my first post).
Now i tried as You said:
void eeprom_writeWord(unsigned int *addresOfMyEEpromVariable, unsigned int value)
{
//write word
// Set up NVMCON to write one word of data EEPROM
NVMCON = 0x4004;
// Set up a pointer to the EEPROM location to be written
TBLPAG = __builtin_tblpage(*addresOfMyEEpromVariable);
unsigned int offset = __builtin_tbloffset(*addresOfMyEEpromVariable);
// Write Data Value To Holding Latch
__builtin_tblwtl(offset, 0x9999);
// Disable Interrupts For 5 Instructions
asm volatile ("disi #5");
// Issue Unlock Sequence & Start Write Cycle
__builtin_write_NVM();
while(NVMCONbits.WR == 1);
}
or even without '*' sign:
void eeprom_writeWord(unsigned int *addresOfMyEEpromVariable, unsigned int value)
{
//write word
// Set up NVMCON to write one word of data EEPROM
NVMCON = 0x4004;
// Set up a pointer to the EEPROM location to be written
TBLPAG = __builtin_tblpage(addresOfMyEEpromVariable);
unsigned int offset = __builtin_tbloffset(addresOfMyEEpromVariable);
// Write Data Value To Holding Latch
__builtin_tblwtl(offset, 0x9999);
// Disable Interrupts For 5 Instructions
asm volatile ("disi #5");
// Issue Unlock Sequence & Start Write Cycle
__builtin_write_NVM();
while(NVMCONbits.WR == 1);
}
The result is still the same. __builtin_tblpage and other __builtin_(...) functions are functions built into xc16 compiler.
It may be that the builtin routines can deal with hard-coded addresses (as in your first example - compiler/linker knows where eeData is located), but cannot deal with variable addresses.
Two things you can try:
(1) make the eeprom_writeWord an inline function (so that once again, the compiler can hard-code the address). Note that if this works, it still will likely fail for "complicated" situations such as addresses stored in indexed arrays.
(2) look at the assembler code generated for the builtin routines in your working sample and rewrite them for what you want (using C or inline assembler). These routines are only a few instructions long. e.g. __builtin_tblpage is just stripping off the upper part of the address - something easily accomplished in C with masking and/or right shifts.
Consider that prototype:
void eeprom_writeWord(unsigned int addresOfMyEEpromVariable, unsigned int value)
The attribute probably doesn't do anything here, left out for readability. What you do ist not taking the address (which would be of type unsigned int *) but the value itself (a plain unsigned int, that's not magically a pointer just by calling it addressOfSomething ;)), so it just gets copied to your function.
Change it to the following
void eeprom_writeWord(unsigned int *addresOfMyEEpromVariable, unsigned int value)
And use like this
eeprom_writeWord(&eeData, 0x9876);
And, of course, don't use the addressOf operator & inside the function any more ... cause now you already have a pointer. This will then do what you expected.
edit: Regarding your answer, you should delete this (because it is not an answer) and instead edit your original question and put it there.
The version without the asterisks ist the correct one. Not knowing your compiler, it might help to have the attribute in your function prototype as well:
void eeprom_writeWord(
unsigned int __attribute__ ((space(eedata))) *addresOfMyEEpromVariable,
unsigned int value)
Just saying without the asterisk there (marking the argument to be a pointer), it definitely cannot work.

how does u-boot maps peripheral memory?

I am confused how the registers(for ex: for uart controller) are mapped in u-boot?
Are they memory mapped in DDR? If yes How?
If not then what is the meaning of "peripheral addresses are memory mapped" in U-Boot?
Welcome to Stackoverflow.
The registers are mapped to RAM memory address space at hardware level using different type of interconnects in the SoC. e.g Lets take the example of OMAP 35x series and see how UART registers are mapped to the RAM memory address space of the SoC and how we may access them in U-Boot.
In the Technical Reference Manual for OMAP 35x, Table 2-3. L4-Core Memory Space Mapping, we see that base address for UART1 is 0x4806A000 and the size (of RAM that it occupies) is 4KB.
It means that registers for UART1 are mapped to RAM the memory space in a 4KB region starting at this address.
To access a register of UART1, we need to know its offset from the base address to get its actual address. These offsets can be found in Table 17-39 UART IrDA CIR Register Summary.
Once we know the actual address we make a pointer to that register using
unsigned int * (base_address + offset)
Once we have the pointer we can de-reference it to read/write the register. e.g
To read a register in a variable 'x' we use:
unsigned int x;
x = *(unsigned int * (base_address + offset));
Writing a register is also similar
*(unsigned int *(base_address + offset)) = some_value;
To get a better understanding of how memory mapping is implemented at hardware level, skim through the Chapter 2, 'Memory Mapping', of TRM.

Resources