C Programming for 8051: SFR directions as arguments - Page 1
Forum section: electronics / general

author: BRetonDP
author_rank: Contributor
timestamp: July 17, 2014, 05:03:09 am
content: Hello, EEVB Community!I'm currently making a very simple Tic-Tac-Toe game with an AT89C51RC2 and I am having issues getting the button input right.Basically, the input is a 3x3 Button Matrix which I want to utilize by scanning through the rows with a pulse while checking the columns for a connection; pretty basic. However, the way I wanted to do this was by making a uniquescrollmethod that received the row parameter and set it to HIGH accordingly.I already havesbit R1 = P1^0;all through the needed inputs, but I'm not sure if this is correct.Then, on my main method, I run something likewhile(TRUE){scroll(R1);}which is namedscroll, I have thisvoid scroll (bit P){P = HIGH; //Sets the row being checked to HIGH (HIGH is already defined as a 1). Same goes with the aformentioned TRUE....}So, this is not working. I know I'm not doing this right, but I had to start somewhere. Is what I am attempting to do possible? Sending a single Pin adress as an overload for a method? If so, what datatype should it be? I was juggling between char and bit but I can't settle.I hope my question is understandable! If not, I will gladly elaborate and maybe post all the code (although it is quite messy). Thanks, everyone, in advance!

author: jav
author_rank: Contributor
timestamp: July 17, 2014, 02:41:48 pm
content: Keep in mind that in C, arguments are passed by value, not by reference, so in P you get the current value of pin 0 of P1 when scroll function was called, but modifications of P won't affect R1.You have two limitations here:1.- You cannot define bit pointers, that will allow you to pass the value by reference.2.- SFRs in the 8051 architecture cannot be accessed through indirect addressing, so pointers are of no use.You can either use R1 directly in scroll function (instead of P) or change multiple bits at a time working directly over P1.

author: BRetonDP
author_rank: Contributor
timestamp: July 17, 2014, 07:51:26 pm
content: You seem to have understood my problem perfectly. Thank you very much for answering. Effectively, the changes made to P wouldn't affect R1 at all...In other forums, I have been suggested to use pointers, but that didn't seem to compile. I got an error that saidinvalid ptr to bitor something like that. I don't understand pointers all that well, I will study them further. Someone else suggested my scroll method look something like:Code:void scroll (sbit P) {...}That is, to receive an sbit as parameter. Is this possible? I could try it out, but actually, I want to send another parameter, say, a char named R, so my method would be:Code:void scroll (sbit P, char R) {...}That doesn't compile, and I'm not sure if it's because of the sbit as parameter or because of the other parameter.In the end, I stopped trying to send the adress and have the scroll method only check my columns while I turn R1, R2 and R3 manually in my main code. Out of curiosity, what do you mean withchange multiple bits at a time working directly over P1?EDIT1 — It has been pointed out to me that sbit cannot be used as a parameter or within functions.

author: jav
author_rank: Contributor
timestamp: July 17, 2014, 09:12:33 pm
content: As I said in my previous post, bit pointers aren't supported, so you cannnot do:Code:void scroll (bit * P) {...}What I meant is that if you're just flipping R1, R2 and R3, and those are located in bits 0, 1 and 2 of P1 you can do things like:Code:R1 = 1; // Turn on R1P1 ^= 3; // Turn off R1 and on R2P1 ^= 6; // Turn off R2 and on R3P1 ^= 5; // Turn off R3 and on R1If you know the values of the other bits (pins) of P1, you can even do a direct value assignment instead of doing logical operations on the port.

author: free_electron
author_rank: Super Contributor
timestamp: July 17, 2014, 09:24:22 pm
content: What the hell is anyone smoking these days ? ( i jast was ranting in another topic about some 8051 code about crap programmers not understanding the machine they work on and producing crap code. so sorry if i'm being rude. i'm still fuming from that topic... )The IO SFR's in an 8051 are bit addressable.0x80 is pin 0 , 0x81 is pin 1 , 0x82 is pin 3 and so on (depending on what io port the base address may be different. look it up for your port)So simply pass the number of the pin to the routine.do a CLRB of number +0x79 and a SETB of number 0x80number is 1 based. so pin 1.0 is '1' pin 1.1 is '2' otherwise the trick with CLRB 0x79 doesn't work

author: dannyf
author_rank: Super Contributor
timestamp: July 18, 2014, 02:52:40 am
content: Pointers to bit fields are a no go.a more generic solution to your problem is to use a pointer to the port plus a mask.btw, scanning a keypad is quite inefficient. A faster approach is to set the row or column and read the column or row. It takes two reads.

author: jav
author_rank: Contributor
timestamp: July 18, 2014, 07:15:07 am
content: Quote from: free_electron on July 17, 2014, 09:24:22 pmUnfortunately the CLRB instruction doesn't take indirect addressing, so you'd need to use a switch statement or something similar, and end up doingCode:R1 = 0; // CLRBR2 = 1; // SETBanyway.

author: free_electron
author_rank: Super Contributor
timestamp: July 18, 2014, 03:02:40 pm
content: then don;t use setb (it;s been a while on 8051) but you can load the accumulator with the register you want and then simply write to that location. if you point at the single bit register (that is what the 8051 does. it creates single bit registers in the SFR space that have their own address)it is possible. i have done it. i would have to look it up. don't ask me how to do it in C. 8051 cores are not built for 'c' ( no microcontroller core is built for 'c' , c requires a stack heavy machine. ). Use Pl/M on a 8051. that language was designed from the get go for such controllers.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 18, 2014, 04:04:14 pm
content: Quote from: free_electron on July 18, 2014, 03:02:40 pmeh?From ARM's app note on migrating from 8051 to Cortex: "Unlike many microcontrollers, the Cortex-M3 can be programmed entirely in C. This includes exception handling, reset and initialization as well as application software."link

author: free_electron
author_rank: Super Contributor
timestamp: July 18, 2014, 04:17:07 pm
content: yeah , the cortex is the first machine to do it.all old 8 bitters aren't. C is a language that was designerd for the digital PDP family of machines where a huge memorypool was available and with a unique architecture.microcontrollers don't have this. 'C' wants to creat a stack, a heap and passes all stuff to routines by pushing/popping the stack.good luck on a machine with a 16 level stack... you end up with extremele inefficient code full of workarounds becasue the architecture of the c runtime doesn't fit the hardware.say's right there "UNLIKE many microcontrollers."

author: mikerj
author_rank: Super Contributor
timestamp: July 18, 2014, 11:08:04 pm
content: Quote from: free_electron on July 18, 2014, 04:17:07 pm'C' doesn't want to create a heap unless you tell it to. Very few projects I have worked on implement a heap since dynamic memory allocation is unnecessary for many real time applications.C compilers also don't have to pass parameters via the stack. How do you think C compilers for the low/mid end PICs manage, since that has no accessible stack? The code generation on these compilers can be excellent as well.

author: westfw
author_rank: Super Contributor
timestamp: July 20, 2014, 08:36:59 am
content: QuoteLet's not get carried away; the PDP11 was a 16bit machine with a 64kbyte address space and 8 registers. Memory and speed-wise, a modern 8051 should be on-par with an early PDP11 or LSI11. While many regard the PDP11 architecture as particularly elegant, vendors have been claiming their chips had "pdp-11-like architectures" since the 6800 and 6502. Usually that means at least one "index register" capable of accessing the full address space, some addressing modes useful for accessing frames via the index register, and some aids to implementing stacks. (I'd have to check carefully to remember whether the PDP11 had explicit stack instructions, or whether they just 'fell out' of having the PC be accessible as a register, and the "standard" addressing modes.)The 8051 is not particularly C-friendly, and the 8bit PICs are pretty awful (non-contiguous data memory; ouch!) (although, it might be interesting to see how the added PIC18 instructions really DO help C), but a lot of early 8-bit machines were better - there were credible C compilers for Z80/8080-class machines as early as the late 70s, for example. AVRs are moderately C-friendly, despite some obnoxious departures from PDP11 styles...ARM Cortex M cpus may be programmable in "pure" C, but that as more to do with startup and exception handling than instruction set architectures. Excepting (small amounts of) startup and exceptions, many architectures are programmable "directly in C." Note that the ARM does NOT have a stack-based "call" or "return" instruction, for example (which follows pretty directly from the whole RISC architecture philosophy, I think. Can't have instructions writing memory (the stack) AND a register (two registers! SP, and PC) in the same instructions, can we?)As for the original question, free_electron's post on the SFR registers being bit-addressable seems like the most useful approach. You'll have to check on how your particular C compiler wants to deal with such things, though.

author: miguelvp
author_rank: Super Contributor
timestamp: July 20, 2014, 08:49:12 am
content: One thing the older servers could do, is deal with many users at the same time. Sure processing power has increased, but those VAX 8600 monsters and even the smaller 780 could deal with a lot of users at the same time.Never got my hands on a PDP-11 but it could handle 8 users easily (of course each user running only one process).The architecture is not quite the same I think.

author: westfw
author_rank: Super Contributor
timestamp: July 20, 2014, 09:01:37 pm
content: QuoteFirst of all, PDP11s came in a lot of different sizes, from the single-chip T-11 (http://simh.trailing-edge.com/semi/t11.html) to large multi-user machines like the PDP11/70 that occupied several racks of space. The multi-user machines tended to get their abilities by virtue of having complex Memory management units and intelligent peripherals, so that (for example) you could map and swap a user's memory to disk without much CPU intervention. And by the reduced expectations of the day: I worked on PDP-10s early in my career, and while they were considered ~100 user machines, you're talking text-based apps and multi-minute compilation of student-sized programs for "normal" behavior.The MMU and IO controllers were approximately as complex (and physically large) as the CPU itself (and "memory controllers" were fancy, too. Not so different than modern memory (multi-word burst access for preloading the cache, and such), but less "hidden" and occupying obvious physical space.)Microcontrollers tend to lack "memory management" capabilities, making it harder to implement multi-user systems easily or "safely", but they do have the performance. I've implemented ~10 user systems on 10MHz 80186 or 68000 systems with 512k of memory, for somewhat limited definitions of "user" (small internet "terminal servers"; each user gets a command processor and the ability to make network connections.)

author: IanB
author_rank: Super Contributor
timestamp: July 20, 2014, 10:41:10 pm
content: Quote from: westfw on July 20, 2014, 09:01:37 pmThis is very true, especially with I/O. Where dozens of terminals were connected to a DEC-10, all of those key presses were not interrupting the main CPU, and the main CPU was not doing routine keyboard processing. There were I/O front-ends and I/O multiplexers doing all the low level grunt work, leaving the main CPU to run programs.

author: westfw
author_rank: Super Contributor
timestamp: July 21, 2014, 07:58:33 am
content: Yeah; ttys were connected to a PDP-10 via a PDP11 or PDP-8 front end (or later, via the network.) I'm not exactly sure what sort of offload that provided, since the OS and applications would frequently use single-character IO. It probably helped a lot on output, though. (and again: expectations. The "fast local terminals" on my college PDP10 ran at 2400bps, and most of the dialups were 300bps.)Trying to analyze performance of a CPU with complicated IO controllers is ... complicated. I actually made a bunch of "improvements" in those areas, but it was a lot of "decrease the number of context switches between user and kernel mode" and "prevent the networking code from flushing the pager cache if both packet and buffer memory were in kernel space", which were pretty impossible to measure (or so I thought at the time.)

author: MarkL
author_rank: Unknown
timestamp: July 21, 2014, 08:07:29 pm
content: Quote from: free_electron on July 18, 2014, 03:02:40 pmOk, I won't ask how in C, but how did you do this indirect bit set/clr in 8051 assembler? (Code snippet please...)The 8051 does not support indirect or indexed access to the SFR address space, where the port bits live.Neither is there mapping of individual bits to full 8-bit registers in the SFR space. There are processors, like the ARM Cortex-M3, that have individual bit mapping (their term: "bit-banding"), but the standard 8051 does not unless it's in some specific manufacturer's variant.I've had to do something similar on an 8051 and ended up with a big 'ol switch statement, as jav states. If there's a trick here I'd like to know.

author: Bassman59
author_rank: Super Contributor
timestamp: July 22, 2014, 12:37:02 am
content: Quote from: MarkL on July 21, 2014, 08:07:29 pmWith the Keil compiler, simply declare a variable as a type sbit, which means it's a bit-addressable SFR, and access it with the usual assignment instructions. For example, the 8051's I/O ports are bit-addressable SFRs. Declare the pin as such:Code:sbit PIN_SCLK = P3^0;and the compiler "knows" how to turn assignments to/from it into setb/clr instructions:Code:// toggle the shift clock:PIN_SCLK = 1;PIN_SCLK = 0;compiles to:Code:SETB PIN_SCLKCLR PIN_SCLKQuoteNow here is a clever trick. Say you need an 8-bit shift register (you're going to bit-bang something). Declare a variable in the bdata space:Code:static unsigned char bdata shiftreg;and the compiler will put that variable in the 128 bytes of bit-addressable memory.Now, declare a variable that's the MSb of that shift register:Code:sbit outbit = shiftreg^7;sbit PIN_SDATA = P3^1;;Essentially outbit is an alias of shiftreg. Now when you do the shift:Code:shiftreg <<= 1;the bit you want to drive on the output now gets put into outbit automagically. The assignment:Code:PIN_SDATA = outbit;compiles into two instructions. The processor can't do a move from the bdata space to a port pin directly, so the compiler goes through the carry bit:Code:MOV C, outbitMOV PIN_SDATA, Cand it's reasonably clever and also obvious.Hope this helps.

author: dannyf
author_rank: Super Contributor
timestamp: July 22, 2014, 01:01:05 am
content: The sbit approach is what the op has been using. But the question is to pass it as ab argument to a call.

author: westfw
author_rank: Super Contributor
timestamp: July 28, 2014, 12:41:26 am
content: QuotePeople have been saying this for quite a while, but it took some reading of the datasheet and assorted tutorials for me to realize just how ANNOYING this is! Both in this example, and for 8051 code in general. Hmmph. (Is there a standard mechanism for implementing "variable ports" on 8051 that is better than a case statement? Makes me miss the various "XCT" instructions that mainframe CPUs had for executing an instruction "out of sequence.")It does mean that the code here is particularly poorly-suited to an 8051 architecture:Code:void scroll (bit P){P = HIGH; //Sets the row being checked to HIGH....}In fact, passing "bit identifiers" around is not a very good idea on MOST microcontroller architectures. (Arduino does it. At great expense, and with much criticism.) Your code will work much better if you pass around a bitmask for an assumed port"Code:void scroll (uint8_t mask){P1 = mask; //Sets the row being checked to HIGH....}8051 IO ports are open-collector, with bits set to "1" essentially being inputs, right? So perhaps your mask should have an single 0 bit shifting along, rather than a 1 bit?

author: IanB
author_rank: Super Contributor
timestamp: July 28, 2014, 12:55:53 am
content: I have only weakly been following this argument, since I have never programmed the 8051.But surely the question boils down to:a. Do you want to arbitrarily choose a particular SFR in a function call?b. Do you want to arbitrarily set or clear a bit in a pre-determined SFR in a function call?In the case of (a) then it looks like a switch statement is needed. But in the case of (b), I don't see why a bit number can't be passed as a function argument?(It should be possible to read the whole register, change one or more bit states, then write it back again?)

author: MarkL
author_rank: Unknown
timestamp: July 29, 2014, 11:25:51 pm
content: My use case for arbitrary SFR and bit setting is that I have a debugging CLI that I port from processor to processor for new projects. Among other things, it allows me to read/write any memory location or I/O port, and also set/clear/watch any bit or set of bits in either of those domains. It's very handy for new hardware exploration, development, and verification.This code doesn't make it into any final products, and so I would agree you don't generally need pointers to SFRs or I/O port bits.My implementation was big switch statements. The size of the code wasn't important for that stage of development.I make it a point to know my processor and compiler in as much detail as possible before embarking on a new development effort. When free electron posted in #7 he had done indirect access to SFRs, I was intrigued since I didn't think it was possible. I had looked into it a fair amount before doing it with a switch.Still would like to know how in #7, or if it was a mistake that's fine too. Bump.

author: IanB
author_rank: Super Contributor
timestamp: July 30, 2014, 03:01:35 am
content: Quote from: MarkL on July 29, 2014, 11:25:51 pmAccording to the data sheet, the SFRs share address space with actual memory. If you use indirect addressing you access the memory locations; if you use direct addressing you access the SFRs. So it doesn't look like you can indirectly address SFRs. If you use any indirect addressing modes you will be redirected to the parallel memory locations instead of the SFR space.

author: dannyf
author_rank: Super Contributor
timestamp: July 30, 2014, 11:10:43 am
content: QuoteUnless that poster can produce a piece of code that when burned to a chip can create the desired behavior, I would consider it a mistake.The internet is full of such things that unless verified should be written off as non-sense.

author: MarkL
author_rank: Unknown
timestamp: July 30, 2014, 02:44:27 pm
content: Yes, of course.It was a highly questionable claim. The poster, only a few posts earlier (#4 in this thread), was complaining about other people not knowing about the processor architecture they were working on.

author: dannyf
author_rank: Super Contributor
timestamp: July 30, 2014, 03:55:02 pm
content: Agreed. Put up or shut up.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 30, 2014, 04:25:32 pm
content: Quote from: westfw on July 20, 2014, 09:01:37 pmDamn, dude. You're old!So, SOS or TECO?

author: westfw
author_rank: Super Contributor
timestamp: July 30, 2014, 08:38:41 pm
content: TECO. Later, EMACS (real, TECO-based EMACS on tops20 and tenex. FINE on tops10. (Oh god! BLISS! I think I'm having a PTSD flashback!)I wonder if TECO would run on a moderately-sized microcontroller, and provide an "interesting" user interface? I mean, it ran on PDP-8s...The 8051 situation described in this thread reminds me of the sort of "nostalgia" that people get for their early systems. "Oh yes, the 8080 was swell." (it didn't do indirection to IO ports either, though that was added to Z80. As long as you wanted to do it via the C register.) "The high-res graphics on the c64 were amazing." (320*200, SO much better than the Apple ]['s 280×192 hires mode!) Bleh. The PDP10 instruction set was beautiful. But I really don't want to go back to working on 100-user operating systems written entire in assembly language...It also reminds me of the sorts of things that led people to write "self modifying code." "Kids" today look at that with a mixture of awe and horror, but it used to seem to be the most straightforward way of getting some things done.Is this limitation of the 8051 something that was addressed in any of the "enhanced" varieties that are still in common use?