Obsolete IIC calls in the RPi build?
Rick Murray (539) 13840 posts |
What happens if you define an area as 256 bytes, and later another at the same base address as 1024 bytes, say? Also, is there a granularity to this? If you specify n bytes, does it get rounded up to page size?
…plus you can’t rely upon it being constant across polls so there’d be a lot of time wasted mapping/unmapping using that method. In short, there’s a huge difference between the behaviour of OS_Memory 13, and OS_Memory 14/15. Sufficient that I think of them as performing entirely separate functions, albeit with consanguinity; similar to Wimp_CreateIcon vs Wimp_PlotIcon. It just seems a little… you know… untidy to map in some I/O space in your program, and not release/unmap it when the program exits. |
Rick Murray (539) 13840 posts |
I was looking around the RPi site (looking for info on USB quirks) and I noticed the RISC OS there is dated 1st November. Are there plans to periodically update it? |
Jeffrey Lee (213) 6048 posts |
Yes, OS_Memory 13 will detect attempts to map in the same address multiple times and return just the one mapping. OS_Memory 13 currently uses a granularity of 1MB. Just don’t rely on that since it might get changed in the future!
Remember that there’s only a finite amount of IO hardware on any given system, and in most cases the HAL will have already mapped in everything on startup (or at least most of the interesting bits). So user programs which try to map in extra stuff at runtime will almost always get given pointers to stuff which has already been mapped in. |
Nathanael Smith (1763) 17 posts |
Thanks – back to school for me then… I’ll see if I can adapt Ricks assembler mapping to fit with my code and then maybe scuttle away in embarrassment when it all gos wrong or ask for help or act smug in an nausiatingly understated manner if it all goes right. There’s nothing like re-inventing the wheel for understanding why things are the way they are. |
Rick Murray (539) 13840 posts |
;-) I spent the evening writing something using DeskLib; it’s been nearly a decade since I last did serious work with it, and the redraw stuff is driving me up the wall partly because I keep forgetting it’s +ve across and -ve down. Hmmm… And then there’s DeskLib being a little retarded in that the GFX routines need absolute co-ordinates while the sprite plot routine uses a virtual co-ordinate and calculates the position itself, so I’m keeping two sets of co-ordinates for the same thing. I’m in danger of ripping out all that crap and veneering the GFX to work in the same sort of way as the sprite plotter. Not to mention the layout I draw up is pixel-based while the redraw/GFX works in OS units. <sigh> Having said that, I am quite pleased that I redraw only the window background that was covered (and returned in the RedrawWindow/GetRectangle block), and then on top of this I repaint everything (about 65 rectangles, 30-odd sprites, and a further 40 or so scaled 50%) on the fly and it draws without flicker, at least on the emulator. I do plan to optimise this (break down the six parts to be their own redraw routines and then use rectangle-detection to work out what actually needs to be redrawn). What I have right now is just some temp code while I work on getting the placements of everything to work well. I was also surprisingly pleased that I only needed a one-line modification to get the program working correctly on rectangle-pixel modes (the 50% scaling, just multiply the divider value by 2 instead of making any assumptions). I think the Beagle/Pi in analogue video mode uses square pixels, so it’s unlikely to ever be an issue…but it was worth doing just so it works, in case it is needed (although the work area will be a little ‘difficult’ in lower-resolution modes, it’s a large window). I feel like I’m relearning a load of stuff I used to know. And, dammit, I really need to find PRM3, for these PDFs are driving me crazy! Can’t beat an actual printed tome… Yikes, 3am! I meant to hit the hay an hour ago. But you know how it is when you’re finishing up some coding and there are minor, like single-pixel misplacements, you just won’t sleep well if you lie there in bed thinking about them. So it’s all tidy, the maths is (currently) messy but I have everything how I want it. Now comes the hard part, everything else. (^_^) |
Nathanael Smith (1763) 17 posts |
Hi Rick Sounds like you are doing useful/interesting work – all the best with it. I studied your memory mapping code: LDR R0, =(1 << 17) ; Page access privilege Read/WriteADD R0, R0, #13 ; OS_Memory 13, Map in I/O memory LDR R1, GPIO5_BASE ; Physical address MOV R2, #256 ; Map in 256 bytes SWI XOS_Memory BVS oh_crap_memmap_fail ; die if it didn’t work STR R3, [R12, #GPIO_ADDR] ; remember logical address I think I understood some of it. I am guessing ‘GPIO5_BASE’ can be any pre-#defined relevant physical memory address to go in R1 to pass to OS_ Memory 13 – in my case that of the Broadcom Serial Controller. I am guessing ‘oh_crap_memmap_fail’ is not a keyword or an instruction set memnomic, and am not sure what its for. I think ‘STR R3, [R12, #GPIO_ADDR] ; remember logical address’ Stores R3 at R12 + GPIO_ADDR (the index) and writes back this value’s new storage address to R3 as an output mechanism. I’m not sure what value I should use for ‘GPIO_ADDR’ (if any) though – any suggestions? I was going to call this code slice from C using asm volatile (“…yadda…”); I’m guessing that after that I need to retreive the value of R3 via r.r[3] having previously called _kernel_swi_regs r; am I warm…? Apologies for being so high maintenance – any help appreciated |
Nathanael Smith (1763) 17 posts |
Perhaps I could avoid assembler and just: _kernel_swi_regs r;r.r[0] = (1 << 17) + 13; r.r[1] = PHYSICAL_ADDRESS; _kernel_swi(0x68, &r.r[0], &r.r[1], &r.r[2]); MAPPED_LOGICAL_ADDRESS=r.r[3]; ? |
Rick Murray (539) 13840 posts |
You are missing r.r[2] = number_of_bytes_to_map_in Try…
I’m at work, writing on a phone, so I’ll cover the rest later, except to say two things:
|
Rick Murray (539) 13840 posts |
;-) Oh, edited. I don’t mind you saying it’s incurable – my mother would probably agree with you. Nah, nothing I do is useful. If it was, I’d have a cool idea, sell out to Facebook or Google for a couple of million, then spend the rest of my pitiful existence haunting Maid Cafés 1 in Akiba…
I’ll go through it line by line. Check what I say with what you thought.
This one is a little complicated. You can only LDR certain values due to the fact that the value is stored in the instruction and the instruction contains its own encoding. Therefore this, with the “=” like that, is specific to the DDE assembler “objasm”. It means "create a word holding the result of “(1 << 17)” and LDR that value into R0. The assembler deals with the allocation of the word and where it is stored, to save you having to do so.
This is simple, just adding the OS_Memory reason code to the value we already loaded into R0.
GPIO5_BASE is label pointing to a word that holds the value of the physical address I’m interested in. I load that into R1.
Because 256 is a small simple number, I can push that directly into R2. No need to LDR here.
Call the SWI. The X form is used so errors set the V flag instead of going to the error handler.
Conditional execution. I “B” (branch) if “VS” (oVerflow Set). In other words, if there was an error, I jump to “oh_crap_memmap_fail”.
As I said before, R12 is a pointer to an array, and GPIO_ADDR is an offset; so I store the word in R3 at the location:
Yup. Anything the language accepts that results in the appropriate address being presented (const long, #define, etc) will work.
(^_^) No, it’s not a keyword. Nor a mnemonic. It’s just a label. You missed the “BVS”.
Err… No. Not quite. It’s just a plan STR. You are right about R12+GPIO_ADDR, but that’s all it does. If you wanted a plain “store value here”, it would be something like:
Volatile? Isn’t that just to prevent C from caching registers that might change externally?
Umm…. I’m guessing you don’t have that much experience with C?
In C, you probably know you can
If you have predefined a structure layout (typedef struct …), then you can define an entire structure in much the same way. Therefore, You could just as easily do:
The reason I pointed you towards OS_Memory 13 is that it ought to provide read/write access to you in USER mode. In other words, with reference to your code fragment posted a few days ago, you should be able to bash the hardware in BASIC by something like:
Obviously, FNwait_i2c_done is defined elsewhere and returns TRUE when done (else FALSE), and all the variables written to !BSC0_xxxx% are also defined elsewhere. I mention BASIC here because it is easy to get on with… For things that require speed or interrupts, C will probably be necessary, but you can go a long way with BASIC on RISC OS. It isn’t like that toy that Microsoft gave away on the MS-DOS 5 discs. 1 I should add, I’m not really into the whole “welcome home master” thing (subserviant bordering on creepy), but hey – cute’s cute – go with it… ;-) |
Nathanael Smith (1763) 17 posts |
Thanks I’m sure you do some useful things. Life can seem pointless if you think about it too hard. I tend to view the universe as a big jelly that we’re all set in, with everyone wobbling as a result of each other’s efforts to make meaningful but ultimately farcical gestures – best just enjoy the ride (deep eh?) I used to use BASIC on my BBC Micro back in the day but have since developed a taste for curly braces. I am the first to admit that My C(++) is rusty but I allegedly have a qualification in it and so have started monkeying about with the Pi partly in an effort to remember it. I cobbled togther the following test code:
|
Jeffrey Lee (213) 6048 posts |
The problem (as the error message is indicating) is that in C you can’t initialise a global variable with a non-constant value (or more correctly, any value which the compiler can’t deduce at compile time). So you need to add yourself an initialisation function, like so: unsigned int BSC_logical; unsigned int volatile * const BSC_C; [...] void init() { BSC_logical = Memory_Map(BSC_physical); BSC_C = (unsigned int *) (BSC_logical+0x0); } [...] int main(int argc, char **argv) { init(); TwoByteWriteI2C(0×48,0xFE,0×03,0); // set lcd backlight to off as test return 0; }
HTML <pre> tags are your friend. Also, you should probably use _kernel_swi(OS_Memory,…,…) rather than _kernel_swi(0×68,…,…). The SWI numbers are #defined in swis.h for a reason! |
Nathanael Smith (1763) 17 posts |
Thanks Jeffrey – the compiler also seems to require me to take out ‘const’ (hope its not important) – compiles now. Almost too scared to run it – might tease myself and go to bed… no… be brave… right, here goes… the tension mounts … not a sausage – not even an error message – what an anticlimax (definately no objasm). perhaps that ‘const’ was important? To save space, I’ll amend my previous post with just this now running code. Right, thats it – I’m going to bed – quite enough farcical wobbling for one day… |
Rick Murray (539) 13840 posts |
Just a random thought… In IIC, as you may know, device IDs are usually bits 1-7 with bit 0 representing the read/write status. Is device 0×48 correct, or would it be (0×48<<1) ?
ROTFL! ;-) If that doesn’t work – time to look at some debugging. That’s always a fun part.
You mean this part:
“const” means “constant”, as in a variable who’s value doesn’t change. I’m not surprised the compiler choked on a volatile pointer constant! [though, in fairness, I bet it’s been done for the Obfuscated C Contest] Think of “const” as being like #define except it is a real thing with a type (an int, a char, a float, etc). Differences are, roughly… A #define is an ephemeral thing. It doesn’t really exist. The preprocessor, when it sees a defined value reference will simply output the content of the defined value. In other words, your code is this:
Your compiler, after the preprocessor, sees this:
You can point to a const. Pointers in C are troublesome for many, but so long as you do not expect the value of the const to change, you can use it like a normal variable, for it is a normal variable. Type checking is not typically performed on defines because they don’t have a type. Errors you may see will be stuff like passing a string to something expecting an int; but there may be unexpected side effects – for if you pass a numeric define to something expecting a pointer, it may work (using a completely bogus pointer); while normally in C you would get an error about “illegal cast of struct to non-equal pointer” or somesuch. On the other hand, you can’t dimension a static array from a predefined int:
will fail. It needs to have A define is a TEXTUAL substitution.
The result is 28. If you make “mynumber” be a const, the result is 36. Can you see why? [there’s a way to make it work – do you know how?] Finally… Things that are defined are “lost” to the debugger. Only the preprocessor sees defined things, it passes on a substitution to the compiler. So if you have I use #define myself in headers for things like fixed margins, boundaries… stuff that is not expected to need to change, but if it will change to have all of them change. But I’m quite aware that defines are not ‘real’ so they are only used where their substituted value is important. |
Nathanael Smith (1763) 17 posts |
Just a random thought… Thanks for your suggestion – I’ll give it a try but I don’t think its the solution. I think the BSC does some jiggery pokery so I don’t have to. I know these are the correct values to pass to the BSC’s registers as they have worked for me before using this approach from Linux. See The Broadcom BCM2835 datasheet for details of how the Broadcom Serial Controller operates. I also know that my hardware is not faulty as I can control it via I2C using BASIC so either I am going about mapping memory the wrong way or I am using incorrect values somewhere. More thought required. My current debugging tactics include getting the program to display the address values used and trying to force errors by inserting rediculous numbers. If all else fails, at least I now understand enough to call IIC_Control from C, so thanks regardless. PS the answer to your brain teaser is brackets. I like #define also. const seems to have limited use. I believe enum can be another subtitute in some circumstances. |
Nathanael Smith (1763) 17 posts |
A-ha! Some transacting is taking place as running the code prints out gobbledy-gook on my LCD rather than turning the back-light off as intended. Progress… …perhaps I’ve overlooked something that additionaly needs setting or flushing….? |
Rick Murray (539) 13840 posts |
Don’t bother. Just looked at the datasheet, it does not require any shifting of the device address, read/write is set in the C register. Off the top of my head (and this may be buggy…), I would try this to see if the values are being passed correctly. Remember – it is UNTESTED.
One discrepancy I noticed was that your code appears to set the BSC0 as &20205000 (or, as you write it:
As a shot in the dark, I suspect the address you are trying to use (as appears in examples all over the Internet) is the I/O base address as mapped in by Linux – in other words, a logical address. By the way – you’re using a revision one Pi, yes? Later versions swapped the IIC around so you’d need to map in BSC1 instead. So if the above doesn’t work, try &7E804000 too.
Hopefully the above will be useful. ;-) By the way, you may or may not know this – using
(^_^)
I suspect where you stand on #define vs const depends upon how you write code. I am always quite aware that #define is something I am telling the preprocessor, not the compiler.
The problem with enum is if you define a parameter that takes a specific set of values (say, WimpMessage codes) and you provide your own custom value, the compiler will whinge:
I aim to have my code compile with zero messages (no errors, no warnings). Stupid things like that warning just annoy me. |
Rick Murray (539) 13840 posts |
Fixed it, by forcing a cast to the expected enum type, namely:
The message_action is not an enum, nor is it defined as a part of the enum. But I said “treat it like the enum anyway” and the compiler did. Hmmm… Compiler goes Wah!. I go WAH!!!. Compiler shuts up. Is this how we write software now? |
Nathanael Smith (1763) 17 posts |
Computers these days (sheesh) – they do need to be reminded who’s boss from time to time. Thanks again. One discrepancy I noticed was that your code appears to set the BSC0 as &20205000 (or, as you write it: #define BSC_physical (0×20000000 + 0×205000) ). This doesn’t agree with the datasheet that I have … I suspect the address you are trying to use (as appears in examples all over the Internet) is the I/O base address as mapped in by Linux – in other words, a logical address. I had the same thought but 0×7E205000 causes the transaction to hang. 0×20205000 at least generates no errors and causes nonsense to be passed to the screen. You could be on to something though as I have never understood why it should be 0×20205000. The 0×20205000 value is the value that is used before mapping to virtual memory in linux, so I assumed it should be the physical valu used in RISC OS. By the way – you’re using a revision one Pi, yes? Its from the first batch ever shipped baby. I aim to have my code compile with zero messages (no errors, no warnings). Stupid things like that warning just annoy me. I am the same with code however I do like to tease my utility provider by witholding payment until they send me a warning letter just to make them work for their money. I will try your BASIC equivelant to see if I have the same problem. |
Nathanael Smith (1763) 17 posts |
Your BASIC code generates ‘Internal Error – Abort on Data Transfer at &FC1ADC40’. This could be revealing if that’s whats happening in my C code without generating an error message, although if that were the case I would expect to see the same garbage on the LCD and I get nothing… More thought required. I think I should double check my C code against that I have working in Linux incase I have omitted a step. Many thanks again for all your help. |
Rick Murray (539) 13840 posts |
Oooh, a crowd pleaser. ;-) Next step is to stick in a liberal dose of PRINT statements ( Question is, did I cock up, or is the address wrong? Looking in the various sources (like here):
I get a real tickle out of “direct DMA access”. ;-) It looks like the 20xxxxxx address is correct. I’m not sure what the difference is between “bus” memory map and “physical” memory map. So, that was a read herring unless the 7Exxxxxx is real and the 20xxxxxx is somehow mapped in by the OS/MMU? I dunno, I would find it galling if the datasheet said something, that didn’t exactly match reality…? I’ll need to fiddle my code to talk to a device I have on my machine, and see what’s up. Probably some idiot error… While I’m here, this line:
Would it not be more logical to do this first? Sort of setting up the state prior to pushing in all the other data. The Pi’s IIC driver is here – it’s quite a succinct piece of code that pulls apart the OS_IICOp data block and throws it to the BCM IIC hardware. No, it doesn’t bit-bang. That’d be kind of insane given the level of assistance the hardware offers. ;-) |
Nathanael Smith (1763) 17 posts |
Nah – too easy (where’s the fun in that?) Anyhow I now have success (of sorts) and identified the problem. This is my current test code: //------------- Header files #include <stdio.h> #include <stdlib.h> #include "kernel.h" #include "swis.h" //------------- Define start of BSC register addresses #define BSC_physical 0x20205000 //------------- Function declarations unsigned int Memory_Map(unsigned int base); void ByteWriteI2C(unsigned short int device_addr, unsigned char data_byte); void wait_i2c_done(); void init(); //------------- Set up pointers to mapped logical address unsigned int BSC_logical; unsigned int volatile * BSC_C; unsigned int volatile * BSC_S; unsigned int volatile * BSC_DLEN; unsigned int volatile * BSC_A; unsigned int volatile * BSC_FIFO; void init() { printf("initialising...\n"); BSC_logical = Memory_Map(BSC_physical); BSC_C = (unsigned int *) (BSC_logical+0x0); BSC_S = (unsigned int *) (BSC_logical+0x04); BSC_DLEN = (unsigned int *) (BSC_logical+0x08); BSC_A = (unsigned int *) (BSC_logical+0x0C); BSC_FIFO = (unsigned int *) (BSC_logical+0x10); } //------------- Map physical addresses to logical addresses unsigned int Memory_Map(unsigned int base) { printf("mapping..."); _kernel_swi_regs r; r.r[0] = (1 << 17) + 13; r.r[1] = base; r.r[2] = 1024; _kernel_swi(OS_Memory, &r, &r); return r.r[3]; } //------------- Define read, write and status bytes #define BSC_C_I2CEN (1 << 15) #define BSC_C_INTR (1 << 10) #define BSC_C_INTT (1 << 9) #define BSC_C_INTD (1 << 8) #define BSC_C_ST (1 << 7) #define BSC_C_CLEAR (1 << 4) #define BSC_C_READ 1 #define START_READ BSC_C_I2CEN|BSC_C_ST|BSC_C_CLEAR|BSC_C_READ #define START_WRITE BSC_C_I2CEN|BSC_C_ST #define BSC_S_CLKT (1 << 9) #define BSC_S_ERR (1 << 8) #define BSC_S_RXF (1 << 7) #define BSC_S_TXE (1 << 6) #define BSC_S_RXD (1 << 5) #define BSC_S_TXD (1 << 4) #define BSC_S_RXR (1 << 3) #define BSC_S_TXW (1 << 2) #define BSC_S_DONE (1 << 1) #define BSC_S_TA 1 #define CLEAR_STATUS BSC_S_CLKT|BSC_S_ERR|BSC_S_DONE //——————— Function to write one byte to I2C bus void ByteWriteI2C(unsigned short int device_addr, unsigned char data_byte) { printf("changing BSC register values...\n"); *BSC_A = device_addr; *BSC_DLEN = 1; *BSC_FIFO = data_byte; *BSC_S = CLEAR_STATUS; printf("starting to write transaction...\n"); *BSC_C = START_WRITE; wait_i2c_done(); } //------------- display I2C transaction status void dump_bsc_status() { unsigned int s = *BSC_S; printf("BSC0_S: ERR=%d RXF=%d TXE=%d RXD=%d TXD=%d RXR=%d TXW=%d DONE=%d TA=%d\n", (s & BSC_S_ERR) != 0, (s & BSC_S_RXF) != 0, (s & BSC_S_TXE) != 0, (s & BSC_S_RXD) != 0, (s & BSC_S_TXD) != 0, (s & BSC_S_RXR) != 0, (s & BSC_S_TXW) != 0, (s & BSC_S_DONE) != 0, (s & BSC_S_TA) != 0 ); } //------------- Function to wait for the I2C transaction to complete void wait_i2c_done() { printf("status before completion - "); dump_bsc_status(); printf("waiting for transaction to complete...\n"); unsigned short int timeout = 50; unsigned short int i; while((!((*BSC_S) & BSC_S_DONE)) && --timeout) { i=0; while(i<5000) { i++; } } if(timeout == 0) printf("wait_i2c_done() timeout. Something went wrong.\n"); printf("status after completion - "); dump_bsc_status(); } //------------- Here we go...! int main(int argc, char **argv) { init(); ByteWriteI2C(0x48,48); //send '0' to lcd ByteWriteI2C(0x48,49); //send '1' to lcd ByteWriteI2C(0x48,50); //send '2' to lcd ByteWriteI2C(0x48,51); //send '3' to lcd ByteWriteI2C(0x48,52); //send '4' to lcd ByteWriteI2C(0x48,53); //send '5' to lcd ByteWriteI2C(0x48,54); //send '6' to lcd ByteWriteI2C(0x48,55); //send '7' to lcd ByteWriteI2C(0x48,56); //send '8' to lcd ByteWriteI2C(0x48,57); //send '9' to lcd printf("done(?)\n"); return 0; } The actual output is ‘@012345678’ – which is close (@=spotty mess). There is some contamination at the start which explains the trouble with multi-byte transactions as tried before. For my device sending a first byte of 0xFE acts as prefix instructing my interface board to expect a command rather than to output data to the lcd screen, so the previous failure figures for multi-byte transfers. Perhaps your BASIC code suffers the same problem. I’m so close I can smell it. Thanks again Rick, Jeffrey and Theo – I couldn’t have got this far without you. |
Nathanael Smith (1763) 17 posts |
could be it!!? |
Nathanael Smith (1763) 17 posts |
nope – think I need to clear the FIFO before hand – time for bed… |
Theo Markettos (89) 919 posts |
To explain the different address formats, you have to understand that BCM2835 is primarily a GPU with a tiny ARM shoved in the side at the last minute. So I/O space is physically at &7Exxxxxx. It then gets translated by the GPU’s MMU into a GPU-land virtual address of &20xxxxxx. But then that goes into the ARM which has another MMU, so as far as the ARM is concerned the physical address is &20xxxxxx and that gets translated into whatever you feel like in ARM virtual address space. But it’s possible the GPU firmware could change this ‘physical’ address to something else, which is why datasheets are written using &7Exxxxxx as that’s the real addresses the hardware uses, even though you won’t see it. |
Rick Murray (539) 13840 posts |
!!!!!!! So if the GPU is “undocumented” and the datasheet is for code running on the ARM, what the <beeeeeep> is the point in putting the real addresses when they’re…inaccessible…? I feel like beating my head off the table, but I’m in the staff break room right now and all the big cheeses are over at the other table. It would just take too long to explain, especially when I have to say it all in French. |