|
In the following code, I tried to return a 2D local array from the getArray function and display it in the main function.
I use the following line to display the matrix
cout << " " << *(*(ptr+i*COL)+j) but it is not displayed.
Does anyone know how to display the array?
#include <iostream>
#define ROW 3
#define COL 4
using namespace std;
int main()
{
int **ptr;
int **getArray();
ptr = getArray();
cout << "\n Array tab[][] in main(): " << endl << endl;
for (int i=0; i<ROW; i++)
{
for (int j=0; j<COL; j++)
cout << " " << *(*(ptr+i*COL)+j);
cout << endl;
}
return 0;
}
int **getArray()
{
static int tab[ROW][COL] = {
11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34,
};
return (int**)tab;
}
|
|
|
|
|
Try this:
for (int j=0; j<COL; j++)
cout << " " << (int)*(ptr+i+j) << endl;
cout << endl;
|
|
|
|
|
Why this line?
cout << " " << (int)*(ptr+i+j) << endl;
Can you give me more details ?
Why do you need a type-casting conversion ?
|
|
|
|
|
Multidimensional arrays declared the way you have are just an abstraction for programmers,
since the same results can be achieved with a simple array, by multiplying its indices:
int jimmy [3][5]; // is equivalent to
int jimmy [15]; // (3 * 5 = 15)
In effect GetArray returns a 1D pointer array and you could have simply returned it as int* as it's really a big 1 dimensional array
You returned int** so the typecast is to deal with it but it would have been simpler like this
int *getArray()
{
static int tab[ROW][COL] = {
11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34,
};
return (int*)tab;
}
Then the access becomes more obvious
int *ptr = getArray();
cout << "\n Array tab[][] in main(): " << endl << endl;
for (int i = 0; i<ROW; i++)
{
for (int j = 0; j<COL; j++)
cout << " " << ptr[i*COL + j];
cout << endl;
}
In vino veritas
|
|
|
|
|
For some reason, cout was printing the numbers in hex rather than decimal.
|
|
|
|
|
The return type for getArray needs to be int (*getArray())[COL]; . An expression of type T [M][N] decays to type T (*)[N] , not T ** .
So your code needs to be
int main()
{
int (*ptr)[N];
int (*getArray())[N];
...
}
int (*getArray())[N]
{
...
return tab;
}
|
|
|
|
|
How does one know which Dll and Functions to include in the program ?
|
|
|
|
|
Hi,
Your question is unclear. If you are missing some dependencies then you can use Dependency Walker[^] to get the list.
You can also use DUMPBIN[^] to get a list of dependencies.
Best Wishes,
-David Delaune
|
|
|
|
|
You know which functions to add by deciding what your code needs to do and studying the documentation for those features. The documentation will also show which DLL contains which functions. If you are talking about the C library then see C Run-Time Library Reference[^].
|
|
|
|
|
|
" volatile " keyword instructs the compiler NOT to optimize the code.
It is commonly used in interrupts. Fine.
The library code I am using was apparently written with intent to use ether interrupts or multiprocessor hardware. Fine.
The following snippet class variables are declared as " volatile uint32_t *".
But the <b>code does not perform simple addition</b> - it always returns "1".
It fails even when declared as local variable - see "TEST".
Am I missing something ?
<pre lang="c++">
bcm2835_peripherals = (uint32_t*) BCM2835_PERI_BASE;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS / 4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE / 4;
bcm2835_gpio = (uint32_t*)bcm2835_peripherals + BCM2835_GPIO_BASE / 4;
volatile uint32_t *TEST = bcm2835_peripherals + BCM2835_GPIO_BASE / 4;
cout <<"bcm2835_peripherals "<<bcm2835_peripherals<< endl;
cout <<"BCM2835_GPIO_BASE / 4 "<< hex << BCM2835_GPIO_BASE / 4<< endl;
cout <<"bcm2835_gpio "<< hex << +bcm2835_gpio<< endl;
cout <<" TEST cm2835_gpio "<< hex << TEST << endl;
</pre>
Thanks for any comments.
Cheers Vaclav
|
|
|
|
|
volatile uint32_t *TEST
You are declaring TEST as a pointer, try it without the asterisk. But either way we do not know the values of bcm2835_peripherals or BCM2835_GPIO_BASE .
BTW please unclick the checkbox at the bottom which says "Treat my content as plain text, not as HTML", so your code blocks get formatted properly and are clearer to read.
|
|
|
|
|
Sorry for the format issue.
These lines just add two pointers / addresses , and it is the addition which is failing when the resulting pointer is set to "1".
The values of individual terms is irrelevant - the function is.
It works fine - produces correct sum of terms / addresses without "volatile" keyword.
Would there be a conflict if the terms are not declared as "volatile" ?
|
|
|
|
|
Vaclav_ wrote: The values of individual terms is irrelevant On the contrary, they are the most important pieces of information. If you are adding two values and the answer is 1, then we need to know those values.
|
|
|
|
|
OK, but I cannot copy it from IDE when its runing on remote.
I am not sure I can post screen shots here.
I'll get back to you soon.
Here is a copy of the cout debugging without volatile
bcm2835_periopherals 0x2000000
BCM28356_GPIO_BASE / 4 80000
bcm2836_gpio 0x20200000
Here is something which MAY explain the issue.
<a href="https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html">Using the GNU Compiler Collection (GCC): Volatiles</a>[<a href="https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html" target="_blank" title="New Window">^</a>]
-- modified 17-May-18 11:56am.
|
|
|
|
|
Vaclav_ wrote: Here is something which MAY explain the issue.
https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
I don't think that's the issue here, but it does represent something I wish the Standard would address. Although there may be some platforms and application fields for which gcc's behavior would be reasonable in a quality compiler, there are many purposes for which it is not. On many platforms, it is possible for an access to a volatile location to trigger operations that usefully affect other storage (e.g. starting an "in-place" background I/O operation). For an implementation to be suitable for systems-programming on such a platform, it must provide a way of ensuring that such operations are sequenced relative to other operations on non-qualified storage. An implementation can support systems programmings on such platforms without requiring the use of special directives by treating volatile accesses as triggering a call to an unknown function, and I would suggest that quality implementations for such platforms should provide an option to treat them in such fashion.
Unfortunately, even though the Standard has to date expressly avoided quality-of-implementation issues, the authors of gcc seem to think either that the Standard fully describes everything necessary to make something a quality implementation, or that users should not expect gcc to behave like a quality implementation when any of its optimizations are enabled.
While there might some cases where it might be unnecessarily expensive to treat volatile accesses as sequenced relative to non-qualified accesses to objects that would be accessible by outside code, in most cases the cost would be negligible, and would be less than the cost of adding "volatile" qualifiers and accesses everywhere else that would otherwise be necessary to ensure correct semantics.
When the Standard was written, it may have been reasonable to expect compiler writers to exercise good judgment about how quality compilers intended for various purposes should be expected to behave in circumstances beyond those mandated by the Standard, and for programmers to be reliant upon compiler writers' sound judgment. Such expectation and reliance are no longer tenable. If the authors of the Standard don't want to mandate that all compilers treat "volatile" more strongly, they should at minimum specify a predefined macro to allow programmers to say something like:
#if !(__STDC_VOLATILE_SEMANTICS >= 3)
#error "This compiler, as configured, cannot handle the necessary volatile semantics."
#endif
and then expect that every compiler will either process their code with required semantics or refuse to process it altogether. Compiler writers that find such semantics impractical would not be required to support them, but would merely be required to refrain from defining __STDC_VOLATILE_SEMANTICS with a value indicating such support. Code which includes the above test and relies on such semantics would be less portable than code which does neither, but its behavior would be defined on all systems which process it.
|
|
|
|
|
In this case, 'volatile' refers to the object to which it is pointing, not the pointer itself.
The intent of this code confuses me; why are items being cast to be a pointer?
|
|
|
|
|
As far as I can tell - these are memory addresses and are being passed to functions.
The author of the code does not tell much about WHY he does things this way.
What I have gather so far - the application writes into "user space" (?) (Linux term) memory, not directly to hardware. The OS does the actual "writing" to hardware.
|
|
|
|
|
STOP THE PRESSES !
I have some serious problems with initial setup.
The "code" runs in "debug" mode which should be called
"demo" mode and is missing few lines to make the memory management work.
|
|
|
|
|
Allow me to help you out ... you are trying to map all the GPIO hardware registers so here is the struct from the manual
#include <stdbool.h>
#include <stdint.h>
struct __attribute__((__packed__, aligned(4))) GPIORegisters {
uint32_t GPFSEL[6];
uint32_t reserved1;
uint32_t GPSET[2];
uint32_t reserved2;
uint32_t GPCLR[2];
uint32_t reserved3;
const uint32_t GPLEV[2];
uint32_t reserved4;
uint32_t GPEDS[2];
uint32_t reserved5;
uint32_t GPREN[2];
uint32_t reserved6;
uint32_t GPFEN[2];
uint32_t reserved7;
uint32_t GPHEN[2];
uint32_t reserved8;
uint32_t GPLEN[2];
uint32_t reserved9;
uint32_t GPAREN[2];
uint32_t reserved10;
uint32_t GPAFEN[2];
uint32_t reserved11;
uint32_t GPPUD;
uint32_t GPPUDCLK[2];
};
Now you define the base address of the Pi .. for a Pi1 its 0x20000000, for other models 0x3F000000
#define RPi_IO_Base_Addr 0x20000000
Now what you want is to map the ALL THE REGISTERS to an address with a volatile on the pointer
#define GPIO ((volatile __attribute__((aligned(4))) struct GPIORegisters*)(uintptr_t)(RPi_IO_Base_Addr + 0x200000))
Thats it now its all done you can simply use the pointer to hit the registers ... so lets show you a function
bool gpio_output (uint8_t gpio, bool on)
{
if (gpio < 54)
{
uint_fast32_t regnum = gpio / 32;
uint_fast32_t bit = 1 << (gpio % 32);
volatile uint32_t* p;
if (on) p = &GPIO->GPSET[regnum];
else p = &GPIO->GPCLR[regnum];
*p = bit;
return true;
}
return false;
}
Now we can go on from there lets enumerate the GPIO port functions
typedef enum {
GPIO_INPUT = 0b000,
GPIO_OUTPUT = 0b001,
GPIO_ALTFUNC5 = 0b010,
GPIO_ALTFUNC4 = 0b011,
GPIO_ALTFUNC0 = 0b100,
GPIO_ALTFUNC1 = 0b101,
GPIO_ALTFUNC2 = 0b110,
GPIO_ALTFUNC3 = 0b111,
} GPIOMODE;
Having done that we can now set the port to any function
bool gpio_setup (uint8_t gpio, GPIOMODE mode)
{
if (gpio > 54) return false;
if (mode < 0 || mode > GPIO_ALTFUNC3) return false;
uint_fast32_t bit = ((gpio % 10) * 3);
uint32_t mem = GPIO->GPFSEL[gpio / 10];
mem &= ~(7 << bit);
mem |= (mode << bit);
GPIO->GPFSEL[gpio / 10] = mem;
return true;
}
If you want to try it then it all becomes pretty simple now.
int main (void){
gpio_setup(17, GPIO_OUTPUT);
gpio_output(17, true);
}
The assembler becomes reasonably optimal you will struggle to write faster .. if you want to try here is what it produces
gpio_output(unsigned char, bool):
cmp r0, #53
bhi .L5
mov r2, #1
and r3, r0, #31
cmp r1, #0
lsl r2, r2, r3
lsr r0, r0, #5
ldrne r3, .L7
ldreq r3, .L7+4
lsl r0, r0, #2
add r3, r0, r3
str r2, [r3]
mov r0, #1
bx lr
.L5:
mov r0, #0
bx lr
.L7:
.word 538968092
.word 538968104
gpio_setup(unsigned char, GPIOMODE):
cmp r0, #54
cmpls r1, #7
bls .L16
mov r0, #0
bx lr
.L16:
str lr, [sp, #-4]!
mov lr, #7
ldr ip, .L17
umull r2, r3, r0, ip
lsr ip, r3, #3
and r3, ip, #255
lsl r3, r3, #2
add r3, r3, #536870912
add ip, ip, ip, lsl #2
add r3, r3, #2097152
sub r0, r0, ip, lsl #1
ldr r2, [r3]
and r0, r0, #255
add r0, r0, r0, lsl #1
bic r2, r2, lr, lsl r0
orr r0, r2, r1, lsl r0
str r0, [r3]
ldr lr, [sp], #4
mov r0, #1
bx lr
.L17:
.word -858993459
main:
mov r1, #131072
ldr r2, .L20
ldr r3, [r2, #4]
bic r3, r3, #14680064
orr r3, r3, #2097152
str r3, [r2, #4]
mov r0, #0
str r1, [r2, #28]
bx lr
.L20:
.word 538968064
In vino veritas
modified 18-May-18 14:31pm.
|
|
|
|
|
Thanks, I have not yet looked at your post.
You must have spent lots of time on it, and I appreciate it.
I have the code running , but...
I had to delete all volatiles to make it go.
Here is my simplistic view on my code and I am trying to figure out in what point the volatile keyword is causing the issue,
I basically see four steps in manipulating the hardware addresses.
As soon as I post this I'll study your post.
I am sure it will help me to understand this "addressing" mess better.
Sorry for the mess, but I have not figured out how to put parts of code under same roof.
if ((fp = fopen(BMC2835_RPI2_DT_FILENAME, "rb"))) {
unsigned char buf[4];
fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_base = (uint32_t *) (buf[0] << 24 | buf[1] << 16
| buf[2] << 8 | buf[3] << 0);
fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16
| buf[2] << 8 | buf[3] << 0);
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals = (uint32_t*) mapmem("gpio", bcm2835_peripherals_size,
memfd, (uint32_t) bcm2835_peripherals_base);
bcm2835_gpio = (uint32_t*) bcm2835_peripherals +
BCM2835_GPIO_BASE / 4;
// volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0 / 4 + (pin / 10);
paddr = bcm2835_gpio + BCM2835_GPFSEL0 / 4 + (pin / 10);
|
|
|
|
|
OK, the "base" comes from this mess.
I am including it all, including my debugging stuff.
So no peanut gallery comments, just the facts ma'm.
First obstacle in the code is the funky (debug) - it "defaults to zero but I have not found where. Also gpiomem has to be passed as zero (?).
The " BMC2835_RPI2_DT_FILENAME "
#define BMC2835_RPI2_DT_FILENAME "/proc/device-tree/soc/ranges"
is a "device-tree structure (?) " of RPi 3 and up(?) _ - and its access "returns " bcm2835_peripherals_base (pointer) and bcm2835_peripherals_size.
Neither use "volatile" keyword.
And I have to ask - why not start with volatile at the base?
Now gpiomem must have something to do with identifying the actual gpio " tree branch" or what does it do?
To be continued.
<pre lang="c++">
#define DEBUG_bcm2835_init
int C_BCM2835_SPI_TFT::bcm2835_init(int gpiomem) {
int memfd;
int ok;
FILE *fp;
if (debug) {
bcm2835_peripherals = (uint32_t*) BCM2835_PERI_BASE;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS / 4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE / 4;
bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE / 4;
bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM / 4;
bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE / 4;
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE / 4;
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE / 4;
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE / 4;
#ifdef DEBUG_bcm2835_init
cout << "\033[1;32m VERIFY TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
exit(1);
#endif
return 1; /* Success */
}
/* Figure out the base and size of the peripheral address block
// using the device-tree. Required for RPi2, optional for RPi 1
* Or actually needed on RPi3 or Zero
*/
if ((fp = fopen(BMC2835_RPI2_DT_FILENAME, "rb"))) {
unsigned char buf[4];
fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_base = (uint32_t *) (buf[0] << 24 | buf[1] << 16
| buf[2] << 8 | buf[3] << 0);
fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16
| buf[2] << 8 | buf[3] << 0);
#ifdef DEBUG_bcm2835_init
cout << "\033[1;31mTRACE \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
cout << " device tree " << BMC2835_RPI2_DT_FILENAME << endl;
cout << " bcm2835_peripherals_base " << bcm2835_peripherals_base
<< endl;
cout << " bcm2835_peripherals_size " << hex << bcm2835_peripherals_size
<< dec << endl;
//exit(1);
#endif
fclose(fp);
}
/*
* either use tree
* or "hardwired defaults" ?
*
*
*
*/
/* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */
// so what is this for (?)
/* Now get ready to map the peripherals block */
memfd = -1;
ok = 0;
/* Open the master /dev/memory device */
if (gpiomem) {
// clear bcm2835_peripherals_base (?)
bcm2835_peripherals_base = 0;
if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC)) < 0) {
// failed to open , exiting
#ifdef DEBUG_bcm2835_init
perror("Result /dev/gpiomem ");
fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n",
strerror(errno));
cout << "\033[1;32m TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
//exit(1);
#endif
#ifdef DEBUG_bcm2835_init
cout << "\033[1;32m VERIFY TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
exit(1);
#endif
return -1;
}
} else {
if ((memfd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
#ifdef DEBUG_bcm2835_init
perror("Result /dev/mem ");
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
strerror(errno));
cout << "\033[1;32m TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
exit(1);
#endif
#ifdef DEBUG_bcm2835_init
cout << "\033[1;32m VERIFY TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
exit(1);
#endif
return -1;
}
#ifdef DEBUG_bcm2835_init
cout << "\033[1;32m VERIFY memfd = open \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
cout << "memfd " << memfd << endl;
//exit(1);
#endif
}
// use memory file descriptor
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals = (uint32_t*) mapmem("gpio", bcm2835_peripherals_size,
memfd, (uint32_t) bcm2835_peripherals_base);
#ifdef DEBUG_bcm2835_init
perror("Result bcm2835_peripherals ");
cout << "\033[1;32m TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
cout << " bcm2835_peripherals " << +bcm2835_peripherals << endl;
//exit(1);
#endif
// check validity
if (bcm2835_peripherals == MAP_FAILED) {
#ifdef DEBUG_bcm2835_init
perror("Result bcm2835_peripherals ");
cout << "\033[1;32m TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
cout << " bcm2835_peripherals " << bcm2835_peripherals << endl;
// exit(1);
#endif
return -1;
}
/* Now compute the base addresses of various peripherals,
// which are at fixed offsets within the mapped peripherals block
// Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4
*/
if (gpiomem) {
// checkin what (?)
bcm2835_gpio = bcm2835_peripherals;
} else {
bcm2835_gpio = (uint32_t*) bcm2835_peripherals +
BCM2835_GPIO_BASE / 4;
perror("Result bcm2835_gpio ");
#ifdef BYPASS
/*
bcm2835_gpio = bcm2835_peripherals +
BCM2835_GPIO_BASE / 4;
*/
cout << "bcm2835_peripherals " << bcm2835_peripherals << endl;
cout << "BCM2835_GPIO_BASE / 4 " << hex << BCM2835_GPIO_BASE / 4
<< endl;
cout << "bcm2835_gpio " << bcm2835_gpio << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
exit(1);
#endif
bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM / 4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE / 4;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS / 4;
bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE / 4;
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE / 4; /* I2C */
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE / 4; /* I2C */
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE / 4;
}
ok = 1;
exit: if (memfd >= 0)
close(memfd);
if (!ok)
bcm2835_close(); // TODO not checked
// returns 1 on success !
// shoulds return 0 as every other function
// forced to 0 on sucess
#ifdef DEBUG_bcm2835_init
perror("Result bcm2835_peripherals ");
cout << "\033[1;32m TRACE ENTRY \033[0m\n";
cout << "*** TRACE file " << __FILE__ << endl;
cout << " function " << __FUNCTION__ << endl;
cout << " line " << __LINE__ << endl;
cout << " bcm2835_peripherals " << bcm2835_peripherals << endl;
//exit(1);
#endif
return 0;
}
</pre>
|
|
|
|
|
Haha that is the longest winded writing of these dozen lines of code for the Pi3 .. gpio is the volatile address you use
static volatile uint32_t *gpio;
int fd ;
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0) {
if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0){
printf("Unable to open physical memory handle: %s\n", strerror(errno));
return -1;
}
}
gpio = (uint32_t *)mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x3F200000);
if ((int32_t)gpio < 0){
printf("Mmap failed: %s\n", strerror(errno));
return -1;
}
So looking at the code GPIOmem is an entry value >>>> you provide to the function <<<<.
If you provide 0 it basically means Autodetect the base address
If you provide any number it will use that number as the base address ignoring all the autodetection routines.
Okay this pile of rubbish is just reading the device table file (hence the DT). Its just returning a number from a file it's not volatile and this junk should be a function so I would make it one. It will return 0 if it can't find the entry in the DT file and the address if it find it.
I am going to dump all the long winded debug code, it's just making a mess of what is actually happening so here is your function
uint32_t BaseAddressFromDTFile (void)
{
uint32_t base_addr = 0;
FILE *fp;
if ((fp = fopen(BMC2835_RPI2_DT_FILENAME, "rb"))) {
uint32_t base_size;
unsigned char buf[4];
fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
base_addr = (uint32_t *) (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
base_size = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
fclose(fp);
}
return (base_addr);
}
If that returns 0 it looks like it assumes it's a Pi1
Next it just tries to Obtain handle to physical memory which the reduce form is above in my code
Then if you set GPIOMem to zero it will use that address found otherwise it use the GPIOMem address to map the page.
Then it does the page map with
bcm2835_peripherals = (uint32_t*) mapmem("gpio", bcm2835_peripherals_size,
memfd, (uint32_t) bcm2835_peripherals_base);
We don't have the definition of bcm2835_peripherals but that is the thing that should be volatile like my code above it is their version of gpio.
In vino veritas
modified 20-May-18 10:48am.
|
|
|
|
|
I am really sorry for the messy post.
Being an OF I need to keep track of myself and such code is very difficult to post in public. But if I do not - then I get asked "what is this for etc".
O well.
I am rewriting the whole mess and getting rid of some fluff.
There is one thing I am not sure about
The memory can be mapped using /dev/mem which gives "access to all" but has to have user root.
I am not sure but my "remote TCF" is probably root.
I need to recheck that - this TCF is not too bright to report some problems.
Using /dev/gpiomem lets Linux take care of access permissions , BUT is it then limited ONLY to GPIO ?
I need access to ALT0 - SPI.
Thanks for putting up with my foolishnes.
Cheers
Vaclav
|
|
|
|
|
Each device has it's own name it's basically the name in the process device (look at the /dev directory you can see them all)
Now the SPI is a proper block device and you can write to it like a file unlike the GPIO
so for SPI it's either
/dev/spidev0.0
/dev/spidev0.1
Depending if you want SPI0 or 1
Lets do SPI0
#include <stdbool.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <unistd.h>
int OpenSPI( int SPI_Port, int* SPI_WriteMode, int* SPI_ReadMode, int* SPI_WriteLength, int* SPI_ReadLength, int* SPI_WriteSpeed, int* SPI_ReadSpeed)
{
int status;
int spi_handle = -1;
if (SPI_Port) == 0){
spi_handle = open ("/dev/spidev0.0", O_RDWR);
} else if (SPI_Port == 1) {
spi_handle = open ("/dev/spidev0.1", O_RDWR)
} else {
printf("Invalid SPI port number specified\n");
return -1;
}
if (spi_handle < 0) {
printf("Unable to open physical memory handle: %s\n", strerror(errno));
return -1;
}
status = ioctl(spi_handle, SPI_IOC_WR_MODE, SPI_WriteMode);
if(status < 0) {
printf("Could not set SPIMode (WR)...ioctl fail");
return -1;
}
status = ioctl(spi_handle, SPI_IOC_RD_MODE, SPI_ReadMode);
if(status < 0) {
printf("Could not set SPIMode (RD)...ioctl fail");
return(-1);
}
status = ioctl(spi_handle, SPI_IOC_WR_BITS_PER_WORD, SPI_WriteLength);
if (status < 0) {
printf("Could not set SPI bitsPerWord (WR)...ioctl fail");
return -1;
}
status = ioctl(spi_handle, SPI_IOC_RD_BITS_PER_WORD, SPI_ReadLength);
if (status < 0) {
printf("Could not set SPI bitsPerWord(RD)...ioctl fail");
return -1;
}
status = ioctl(spi_handle, SPI_IOC_WR_MAX_SPEED_HZ, SPI_WriteSpeed);
if (status < 0) {
printf("Could not set SPI speed (WR)...ioctl fail");
return -1;
}
status = ioctl(spi_handle, SPI_IOC_RD_MAX_SPEED_HZ, SPI_ReadSpeed);
if (status < 0) {
printf("Could not set SPI speed (RD)...ioctl fail");
return -1;
}
return (spi_handle);
}
int SpiWrite (int spi_device, int SPI_WriteSpeed, int SPI_WriteLength, unsigned char *data, int length)
{
struct spi_ioc_transfer spi[length];
int i = 0;
int retVal = -1;
for (i = 0 ; i < length ; i++)
{
memset(&spi[i], 0, sizeof (spi[i]));
spi[i].tx_buf = (unsigned long)(data + i);
spi[i].rx_buf = 0;
spi[i].len = length;
spi[i].delay_usecs = 0 ;
spi[i].speed_hz = SPI_WriteSpeed;
spi[i].bits_per_word = SPI_WriteLength;
spi[i].cs_change = 0;
}
retVal = ioctl(spi_device, SPI_IOC_MESSAGE(length), &spi);
if (retVal < 0){
printf("Error - Problem transmitting spi data..ioctl");
return(-1);
}
return retVal;
}
int SpiRead (int spi_device, int SPI_ReadSpeed, int SPI_ReadLength, unsigned char *data, int length)
{
struct spi_ioc_transfer spi[length];
int i = 0;
int retVal = -1;
for (i = 0 ; i < length ; i++)
{
memset(&spi[i], 0, sizeof (spi[i]));
spi[i].tx_buf = 0;
spi[i].rx_buf = (unsigned long)(data + i) ;
spi[i].len = length;
spi[i].delay_usecs = 0 ;
spi[i].speed_hz = SPI_ReadSpeed;
spi[i].bits_per_word = SPI_ReadLength;
spi[i].cs_change = 0;
}
retVal = ioctl(spi_device, SPI_IOC_MESSAGE(length), &spi) ;
if(retVal < 0) {
printf("Error - Problem transmitting spi data..ioctl");
return -1;
}
return retVal;
}
Okay finally thats is all done lets use everything
int main (void){
static int WriteSpeed = 1000000;
static int ReadSpeed = 1000000;
static int WriteLength = 8;
static int ReadLength = 8;
static int WriteMode = SPI_MODE_0 ;
static int ReadMode = SPI_MODE_0;
static int SPI_handle;
SPI_handle = OpenSPI(0, &WriteMode, &ReadMode, &WriteLength, &ReadLength, &WriteSpeed, &ReadSpeed);
if (SPI_handle >= 0){
unsigned char Data[4] = { 0x41, 0x42, 0x43, 0x44};
SpiWrite(SPI_handle, WriteSpeed, WriteLength, &Data[0], 4);
close(SPI_handle);
}
}
Now the read/write routines are less than optimal I would bring the struct out to the interface but it will get you started.
In vino veritas
|
|
|
|
|