|
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
|
|
|
|
|
Thanks again, I am making much progress with your help.
I think I found the initial problem with using "volatile".
Here is my view in pseudo code - using GPIO as an example :
pointer * to GPIO block = pointer * to base memory block + offset to gpio
Now - what would be of benefit using volatile to POINTER such as
volatile pointer * to GPIO block = pointer * to base memory block + offset to gpio
In my interpretation - volatile keyword is to protect variable VALUE , not the pointer.
It compiles but the resulting pointer is always "1".
Leon,
I really need to resolve this "volatile" problem.
I actually tried few different ways to output data the LCD /TFT device.
Using /dev/spidev0.0 (ioctl) writing and reading , using /dev/spidev0.0 and ioctl "writing / reading messages ", plain GPIO access and now this "library".
The main issue is that the LCD/TFT is really not true SPI device - it requires switching from writing data to writing command. That is why I am trying to use this BCM library.
I figure if I can do the "low level BCM library stuff " I can adopt the ioctl way later.
-- modified 21-May-18 10:39am.
|
|
|
|
|
Sometimes you want to read a status continually in a loop lets show you a sample
#include <stdint.h>
#define STATUS_PORT 0x3F000000;
int main (void){
uint32_t* p = (uint32_t*) STATUS_PORT;
do {} while (*p != 1);
}
Now don't worry about the address the code will fail if you have an optimizer turned on ... it will produce this
main:
mov r3, #1056964608
ldr r3, [r3]
cmp r3, #1
beq .L2
.L3:
b .L3
.L2:
mov r0, #0
bx lr
Hopefully you see the problem with the branch L3 loop its a deadloop doing nothing ... so you may ask why does it do it.
Well the code read the port at line 2 with ldr r3, [r3]
As far as the optimizer is concerned the value never changes so it thinks you are just asking for a deadloop with the while loop.
The optimizer has no way to know the port value can change independent of the running code.
What the volatile does is tell the optimizer the value can change without it knowing so it must read it everytime so lets do this
#include <stdint.h>
#define STATUS_PORT 0x3F000000;
int main (void){
volatile uint32_t* p = (uint32_t*) STATUS_PORT;
do {} while (*p != 1);
}
Now what you get is this
main:
mov r2, #1056964608
.L2:
ldr r3, [r2]
cmp r3, #1
bne .L2
mov r0, #0
bx lr
See the difference in the L2 loop it reads the port everytime
So what the volatile is protecting you from is the optimizer making bad assumptions that the registers can't change.
That is all it's doing it makes sure that the optimizer knows the register value can change without the optimizer knowing.
Now it doesn't have to be a single register so long as you use the pointer anything at the pointer is volatile so consider this
#include <stdint.h>
#define STATUS_PORT 0x3F000000;
struct mystruct {
uint32_t port1;
uint32_t port2;
};
int main(void) {
struct mystruct* p = (struct mystruct*)STATUS_PORT;
do {} while (p->port1 != 1);
do {} while (p->port2 != 2);
}
Again it gets it wrong on both ports ... see L3 and L5 .. deadloops again doing nothing
main:
mov r3, #1056964608
ldr r2, [r3]
cmp r2, #1
beq .L2
.L3:
b .L3
.L2:
ldr r3, [r3, #4]
cmp r3, #2
beq .L4
.L5:
b .L5
.L4:
mov r0, #0
bx lr
Now put a volatile on the pointer
#include <stdint.h>
#define STATUS_PORT 0x3F000000;
struct mystruct {
uint32_t port1;
uint32_t port2;
};
int main(void) {
volatile struct mystruct* p = (struct mystruct*)STATUS_PORT;
do {} while (p->port1 != 1);
do {} while (p->port2 != 2);
}
The compiler knows the ports pointed to by the pointer can change and produces the right code
main:
mov r2, #1056964608
.L2:
ldr r3, [r2]
cmp r3, #1
bne .L2
mov r2, #1056964608
.L3:
ldr r3, [r2, #4]
cmp r3, #2
bne .L3
mov r0, #0
bx lr
So there is your answer what the volatile does, its basically a thing to stop optimizer errors.
You only need it on things that can change outsize the running code like HARDWARE REGISTERS or data accessed by 2 processes of multitask code.
In vino veritas
modified 21-May-18 13:50pm.
|
|
|
|
|
I am "porting" C code into C++, mainly to gain an experience.
Run into this , undocumented , piece of code.
Did some reading and have basic understanding WHAT is suppose to do.
Preventing multiprocessors to clobber each other or multiple access to shared devices / memory.
So far my software is for single CPU and no sharing of I/O , not yet.
That is NOT my concern.
What I would like to know why it stops running the process PAST the first usage of
__sync_synchronize();
It compiles just fine.
<pre lang="c++">
__sync_synchronize();
STOPS HERE
ret = *paddr;
__sync_synchronize();
</pre>
Thanks
Cheers Vaclav
|
|
|
|
|
Hi,
You obviously have a race condition somewhere in your code. As you have probably discovered... that intrinsic function is a memory barrier.
There's no way to fix this without learning how to debug your code. If you are developing on the Windows platform then fire up WinDbg and attach it to the process and type '!locks'
Debugging a Deadlock[^]
Best Wishes,
-David Delaune
|
|
|
|
|