If need the function to return a value it can't be declared void. As you have it now you have to call the function first, then pass the global variable into the printf function. So what you are doing should work.
To use the function with a return value, do something like this:
unsigned int ReadAdConfReg(void)
{
unsigned int retVal;
SPCR = 0x5D;
ADC_CS = 0;
while (MISO_PIN != 0) ;
spi(0x50);
adcConfig = spi(0xFF);
adcConfig = (adcConfig << 8) | spi(0xFF);
ADC_CS = 1;
return acdConfig;
}
(note - the previous edit used a local retVal, whilst leaving the adcConfig variable present which may have been confusing. You can pass the adcConfig directly as it's the correct type. If you want the function to be independent of the adcConfig though, use the local retVal instead in place of the adcConfig - this is probably better than using a global variable)
Then you can do:
printf("configreg:%d", ReadAdcConfigReg());
The data you're describing (full 24-bit use of program memory to store data) cannot be defined and initialized in C, and cannot read directly via C; the only way to access it is by encapsulating in a C-callable assembly function or an intrinsic.
There are really two questions here:
how to play nicely with the compiler, assembler, and linker, so that when you define your 24-bit data in an assembly file as relocatable data with a symbolic name D1
, rather than unnamed data at a fixed address, the compiler can see this variable to determine its address
how to access the data
The 2nd question (how to access the data) is answered for 33EP parts in DS70613C and should be answered for 33FJ parts in DS70204C (but the examples in the 33FJ manual only use the low 16 bits). Here's an example code snippet from the 33EP reference manual that works for 33EP parts + should for 33FJ (I don't have a 33FJ device easily available):
(note: code uses int
, whereas it would be better to use uint16_t
and #include <stdint.h>
)
int prog_data[10] __attribute__((space(prog))) =
{0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999};
unsigned int lowWord[10], highWord[10];
unsigned int tableOffset, loopCount;
int main(void){
TBLPAG = __builtin_tblpage (prog_data);
tableOffset = __builtin_tbloffset (prog_data);
/* Read all 10 constants into the lowWord and highWord arrays */
for (loopCount = 0; loopCount < 10; loopCount ++)
{
lowWord[loopCount] = __builtin_tblrdl (tableOffset);
highWord[loopCount] = __builtin_tblrdh (tableOffset);
tableOffset +=2;
}
while(1)
;
}
You'll note that the builtin functions __builtin_tblrdl()
and __builtin_tblrdh()
are used to read the low and high 16-bit words of data from a program memory location, and __builtin_tblpage() and __builtin_tbloffset()
can be used to extract the page and offset of the address. In this particular example, the highWord array is always 0, and the lowWord array matches the prog_data defined and initialized in C.
Please note no pointers are used here! Although it is possible to use normal variables that are tagged with const
, so that they are located by the linker in read-only program space, and so that you can read the memory using standard C pointer techniques, with the compiler automatically managing the paging registers for you, you can only store 16-bit data. You need to access the TBLRDL and TBLRDH builtin functions to get all 24 bits of data.
As for how to play nicely with the compiler/linker/etc, you have to fool the compiler and tell it it's only seeing 16-bit data. Here's an example that worked to get at the variable D1 declared elsewhere:
#define D1_SIZE 18
extern uint16_t __attribute__((space(prog))) D1[D1_SIZE];
#define READ_DATA(dst, v, len) readData(dst, __builtin_tblpage(v), __builtin_tbloffset(v), len)
void readData(uint32_t *pdst, uint16_t page, uint16_t offset, uint16_t len)
{
TBLPAG = page;
while (len-- > 0)
{
uint16_t lo = __builtin_tblrdl (offset);
uint16_t hi = __builtin_tblrdh (offset);
*pdst++ = (((uint32_t)(hi)) << 16) | ((uint32_t)(lo));
offset += 2;
}
}
...
uint32_t d1copy[D1_SIZE];
READ_DATA(d1copy, D1, D1_SIZE);
This does correctly read 24-bit values and stores them in the bottom 24 bits of a uint32_t. The extern D1 variable declared in C is a dummy variable that is only used to get at the starting address by taking advantage of the way the compiler/assembler/linker work together. The builtin functions handle the rest of the work.
What I don't know is how to automatically obtain the size of the data, since it's defined + initialized in assembly.
Best Answer
The standard C library is providing the optimized solutions for many problems with considerations based on the architecture, compiler in use and others. The
abs()
function defined instdlib.h
is one of these, and it is used for your purpose exactly. To emphasize the point, here is ARM compiler result when usingabs
vs a version of a homebrewabs
: https://arm.godbolt.org/z/aO7t1nPaste:
results in
And
results in
Notice that the
main
code is identical (only a label name is different) in both programs asmy_abs
got inlined, and its implementation is the same as the standard one.