How do these uart structs as function parameters work? - c

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)

Related

Data Structure inside a Union (C Programming)

Anytime structures are thrown inside other structures I just get confused for some reason. I'm writing a driver for a I2C (2-wire Serial Interface) device and I'm using the manufacturers drivers as a reference for creating mine. I have this union statement below (which is defined in a header file) and I just can't understand a few lines inside it. Just a brief background so you know what you're looking at is the main snippet below is setting up this TWI_statusReg variable which holds the information from a status register every time i'm transmitting/receiving data across the I2c bus. This data register is 8 bits long and belongs to a Atmel Atmega328P microcontroller. Here are my questions...
1.) Its hard to formulate this question in words but can you explain in easy terms of why you would declare a data struct inside a union struct like this? What key points should I pick out from this?
2.) In the ".c" header definition file which is too long to post here, there is a single line that says the following
TWI_statusReg.all = 0;
I know there is a char variable in the header file called 'all' as seen in the main snippet of code below. However, I'm not understanding what happens when it gets assigned a zero. Is this setting all the bits in the status register to zero?
3.) The two lines
unsigned char lastTransOK:1;
unsigned char unusedBits:7;
are confusing to me specifically what the colon operator is doing.
The main snippet of CODE
/****************************************************************************
Global definitions
****************************************************************************/
union TWI_statusReg // Status byte holding flags.
{
unsigned char all;
struct
{
unsigned char lastTransOK:1;
unsigned char unusedBits:7;
};
};
extern union TWI_statusReg TWI_statusReg;
1) The main reason for writing such a union is convenience. Instead of doing manually bit masks every time you need to access specific bits, you now have aliases for those bits.
2) Unions let you refer to memory as if its components were different variables representing different types. Unions only allocate space for the biggest component inside them. So if you have
union Example {
char bytes[3];
uint32_t num;
};
such a union would take 4 bytes, since its biggest type uint32_t takes 4 bytes of space. It would probably make more sense to have a union like this though, since you're using that space anyway and it's more convenient:
union Example {
char bytes[4];
uint32_t num;
};
bytes array will let you access individual bytes of num.
Your guess is correct - writing value to all will set the corresponding bits of the union.
3) This construct is called a bit field, and is an optimization of memory usage - if you were to use a struct of 2 chars it would actually take 2 bytes of memory space, instead if you declare a bit field it will only take 1 byte (and you still have 6 more "unused" bits)

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.

overflow? cannot find source, strange

i have the following two structs within my project
typedef volatile struct {
unsigned char rx_buf[MAX_UART_BUF]; //Input buffer over UART
uint8_t rx_flag; //Indicates received data over UART
uint8_t rx_length; //length of the input buffer
} UART;
UART *gUART;
typedef volatile struct {
unsigned int target_s[NR_BLDC]; //Target Position in Signals
int distance_s[NR_BLDC]; //Distance between gActual_s[NR_BLDC] and gTarget_s
unsigned int old_s[NR_BLDC]; //Position before starting to new destination
unsigned int ramp_s[NR_BLDC]; //Duration of ramp(accelerate/decelerate) in Signals
unsigned int count_dt[NR_BLDC]; //Actual nr of PWM ISR timer calls
uint8_t dc_max[NR_BLDC]; //Maximal Duty Cycle set to reach the destination
} POSITIONING;
POSITIONING *gPOS;
when i write to rx_flag or rx_length within *gUART it somehow changes the content of distance_s[0] within the *gPOS struct(error occures when executing one of the two lines in the code section below). i is of type uint8_t and within limits.
gUART->rx_length = i; //set receive byte length
gUART->rx_flag = 1; //Indicate that data ready for use
I have no clue how this can happen, since even if it is an overflow the two structs are at lest separated by multiple bytes within the bss section.
Any tricks/ideas how to find the error.
Thank you in advance
Since you don't show more code, I'll have to make two assumptions: That variables with the prefix g are global, and that you don't initialize those pointer (i.e. make them point anywhere).
Uninitialized global variables are zero initialized by the compiler. For pointers that means that they are initialized to NULL, meaning both pointers are pointing to NULL. Dereferencing a NULL pointer leads to undefined behavior.
In your specific case you're very lucky the whole application don't crash and burn, and more specifically since both pointers are pointing to NULL they are both pointing to the same memory meaning that changing one structure will change both.
Either you need to actually make the pointers point somewhere, or not declare them as pointers at all.

Unusual C pointer behaviour

I have the following snippet of code:
uint8_t *ptrconfig;
uint8_t *ptrspi;
ptrconfig = &TempDeviceConfig.ConfigSize;
ptrspi = &spi_out_array[0];
for ( Count = 0U; Count < DEVICE_USER_SETTINGS_WORDCOUNT +1U; Count++ )
{
*ptrspi++ = *ptrconfig++;
}
However it is not working as expected breakpointing on the second line shows that the address of TempDeviceConfig.ConfigSize is 0x2BD5 however the address the pointer points to is 0x216F.
Why do I get an unexpected value of pointer?
EDIT:
A bit of clarification, I can't say exactly what is inside TempDeviceConfig because I need to be careful how much code appears on the internet, but yes it is a struct and it contains a total of 50+ bytes which are to be written to an external flash chip over SPI. My aim here is to copy the struct into the SPI array which sends the data out.
uint8_t *ptrconfig;
...
ptrconfig = &TempDeviceConfig.ConfigSize;
Apparently TempDeviceConfig is a struct (or union?), and ConfigSize is a member which is of type uint8_t.
*ptrspi++ = *ptrconfig++;
ConfigSize is a single uint8_t object. You can advance the pointer only once; it then points just past the object, and cannot be dereferenced. Your code assumes that ptrconfig points to an element of an array of uint8_t. It doesn't. It doesn't make sense to loop over a single object.
I can't guess what the code should be doing without seeing the declarations of the objects you're using. But if your intent is to copy the entire structure (including any padding) into spi_out_array, a call to memcpy() is a much simpler way to do it. (Copying raw structure contents to an external interface can be quite error-prone, but if the written data is only read by the current system it could be ok.)
If memcpy() is not available because you're programming for a small embedded system, you can easily implement it yourself, either as a function or as inline code. For example:
struct foo { /* ... */ } obj;
unsigned char out_array[sizeof obj]; // or perhaps bigger
unsigned char *from = (unsigned char*)&obj;
unsigned char *to = out_array; // or &out_array[0]
for (int i = 0; i < sizeof obj; i ++) {
*to++ = *from++;
}
I've defined out_array as an array of unsigned char, because that's the most portable 1-byte type in C; an object's representation is defined as a sequence of unsigned char values. But you can almost certainly use uint8_t instead.
If your goal is to copy the entire representation of the structure as raw bytes, it doesn't make sense to refer to a particular member; just treat the entire structure as a byte sequence.
But first check whether your implementation supports memcpy (if so, it should be declared in <string.h>). "Freestanding implementations" aren't required to support any standard library functions, but yours might support some subset of the hosted standard library.

Accessing values from i2c address

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);
}

Resources