Author Topic: Why set a status and control register this way ??  (Read 1147 times)

0 Members and 1 Guest are viewing this topic.

Offline MathWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 1599
  • Country: ca
Why set a status and control register this way ??
« on: September 14, 2024, 04:55:48 am »
I'm learning about the LCD display drive in an ATmega169pa (and AVR, C and assembly). I've seen how it works IRL on a scope, and I know what most of the basic settings would be.

In this example from the datasheet below, they want to set the "LCDCRB – LCD Control and Status Register B" to the desired b11100111, which would be for

MSB b7=1 for external clock
b6=1 for  Vcc/3 bias
b5:4=10 for 1/3 duty cycle
b3=0 reserved
b2:0=111 if up to segment 24, is to be used (ignoring they only want SEG21 to SEG24 for now)

So if someone wanted to load this into the chip, wouldn't they just store this one 8-bit number in the memory, and then load it up into the LCDCRB register when the MCU starts running ? Maybe just to not use that 1 byte, it would add up for all the other stuff that needs setting ? But the program code takes space too so ??


Instead they do it with bitwise leftshift, and bitwise OR. I'm probably missing something, otherwise, I don't get why they do all this.

from page 241
Quote
Assembly Code Example(1)
LCD_Init:
; Use 32 kHz crystal oscillator
; 1/3 Bias and 1/3 duty, SEG21:SEG24 is used as port pins
ldi r16, (1<<LCDCS) | (1<<LCDMUX1)| (1<<LCDPM2)
sts LCDCRB, r16
; Using 16 as prescaler selection and 7 as LCD Clock Divide
; gives a frame rate of 49 Hz
ldi r16, (1<<LCDCD2) | (1<<LCDCD1)
sts LCDFRR, r16
; Set segment drive time to 125 µs and output voltage to 3.3 V
ldi r16, (1<<LCDDC1) | (1<<LCDCC3) | (1<<LCDCC2) | (1<<LCDCC1)
sts LCDCCR, r16
; Enable LCD, default waveform and no interrupt enabled
ldi r16, (1<<LCDEN)
sts LCDCRA, r16
ret
C Code Example(1)
Void LCD_Init(void);
{
/* Use 32 kHz crystal oscillator */
/* 1/3 Bias and 1/3 duty, SEG21:SEG24 is used as port pins */
LCDCRB = (1<<LCDCS) | (1<<LCDMUX1)| (1<<LCDPM2);
/* Using 16 as prescaler selection and 7 as LCD Clock Divide */
/* gives a frame rate of 49 Hz */
LCDFRR = (1<<LCDCD2) | (1<<LCDCD1);
/* Set segment drive time to 125 µs and output voltage to 3.3 V*/
LCDCCR = (1<<LCDDC1) | (1<<LCDCC3) | (1<<LCDCC2) | (1<<LCDCC1);
/* Enable LCD, default waveform and no interrupt enabled */
LCDCRA = (1<<LCDEN);


Just from the " (1<<LCDCS) | (1<<LCDMUX1)| (1<<LCDPM2) " section, that they want to load into r16

The initial value of the LCDCS, Clock select bit b7 is 0. And so are all the other initial values. So for 1<<LCDCS, isn't that just 1<<0 = 1

And then the same for the other 2, and then bitwise OR  would be (1|1|1)=1 ?? , so at best b0000001, and not b11100111 ??


What am I missing here ?


Ok they don;t even use LCD2B is the 1st section, so yeah it would never work.

But for the expression
1<<LCDCS, would they really mean 10000000<<0, since the LCDCS is the 8th bit ? Then I can see the OR getting somewhere.
« Last Edit: September 14, 2024, 05:17:43 am by MathWizard »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4441
  • Country: nz
Re: Why set a status and control register this way ??
« Reply #1 on: September 14, 2024, 05:46:04 am »
In this example from the datasheet below, they want to set the "LCDCRB – LCD Control and Status Register B" to the desired b11100111, which would be for

MSB b7=1 for external clock
b6=1 for  Vcc/3 bias
b5:4=10 for 1/3 duty cycle
b3=0 reserved
b2:0=111 if up to segment 24, is to be used (ignoring they only want SEG21 to SEG24 for now)

:

; Use 32 kHz crystal oscillator
; 1/3 Bias and 1/3 duty, SEG21:SEG24 is used as port pins
ldi r16, (1<<LCDCS) | (1<<LCDMUX1)| (1<<LCDPM2)
sts LCDCRB, r16

Just from the " (1<<LCDCS) | (1<<LCDMUX1)| (1<<LCDPM2) " section, that they want to load into r16

The initial value of the LCDCS, Clock select bit b7 is 0. And so are all the other initial values. So for 1<<LCDCS, isn't that just 1<<0 = 1

And then the same for the other 2, and then bitwise OR  would be (1|1|1)=1 ?? , so at best b0000001, and not b11100111 ??

There is presumably some #include of a file that defines LCDCS, LCDMUX1, LCDPM2.

It's not clear to me how those names map to the fields you describe, except LCDCS presumably Clock Select, and therefore will equal 7.

I'd expect names such as LCDBIAS=6, LCDDUTY=4. I don't understand the encoding of bits 2:0 well enough to name that but let's say LCDSEG=0.

So to code CS=1, bias=1, duty=10, segment select=111 you'd write:

(1<<LCDCS) | (1<<LCDBIAS) | (2<<LCDDUTY) | (7<<LCDSEG)

which is

(0b1<<7) | (0b1<<6) | (0b10<<4) | (0b111<<0)

which is

0b10000000 | 0b1000000 | 0b100000 | 0b111

which is

0b11100111

The compiler or assembler does this calculation at compile time, so it's exactly the same as if you'd written 0b11100111 yourself, or 0xE7, or 231.

Note: 0bxxx syntax might or might not be accepted by your compiler or assembler.
 
The following users thanked this post: MathWizard

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4280
  • Country: nl
Re: Why set a status and control register this way ??
« Reply #2 on: September 14, 2024, 07:01:46 am »
Like brucehoult wrote, it boils down to the same in the final machine code.

It just makes it somewhat easier to read in a way that you know what the configuration is. A single hex or binary number says nothing until you translate the bits back to the actual configuration. Using the defined names can be more informative.

The construct with the shifting of the setting bit makes it a bit less readable. I myself like to use defines that contain that shifting in it. Like in C:

Code: [Select]
#define LCDCS_TOSC1    (1<<LCDCS)
#define LCDCS_CLKIO    (0<<LCDCS)

Sure shifting a 0 does nothing, but using the LCDCS_ names in the code can be more meaningful.

Edit: modified the names in the C defines to match the datasheet
« Last Edit: September 14, 2024, 07:07:17 am by pcprogrammer »
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 444
  • Country: be
Re: Why set a status and control register this way ??
« Reply #3 on: September 14, 2024, 12:45:27 pm »
Instead they do it with bitwise leftshift, and bitwise OR. I'm probably missing something, otherwise, I don't get why they do all this.

You might want to read the following discussion in the neighbouring thread: https://www.eevblog.com/forum/microcontrollers/is-st-cube-ide-a-piece-of-buggy-crap/msg5638431/#msg5638431

The bottom line is: a good program is written for a person, and then for a compiler.
 
The following users thanked this post: ajb, SiliconWizard

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 3744
  • Country: us
Re: Why set a status and control register this way ??
« Reply #4 on: September 14, 2024, 01:48:31 pm »
I agree with the others who suggest it is for readability.  Many years ago, it was not uncommon to see something like this in PIC Assembly:
Code: [Select]
PIC Assembly

movlw b'abcdefgh'      ;a = something
;b = something else
;c =  ""
;d-h = ""
        movwf      OPTION

That was done for readability on reviewing the code.
 

Online Andy Chee

  • Super Contributor
  • ***
  • Posts: 1023
  • Country: au
Re: Why set a status and control register this way ??
« Reply #5 on: September 14, 2024, 02:30:44 pm »
That's how I coded PIC assembly:

Code: [Select]
OPTIONSETUP equ B'01011110' ;the default option setup for this program
; | |  |||
; | |  +++---- WDT prescaler 1:64  (<PS2:PS0> = 110)  18ms*64 = 1.152s
; | +--------- TMR0 increments on internal clock  (T0CS lo)
; +----------- GPIO pullups on  (GPPU lo)

clrf TMR0 ;clear TMR0, and THEN WDT to ensure no accidental watchdog reset
clrwdt
movlw OPTIONSETUP ;setup option register for TMR0 interupt source
bsf STATUS,RP0
movwf OPTION_REG
bcf STATUS,RP0
 
The following users thanked this post: Siwastaja, Nominal Animal

Offline MathWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 1599
  • Country: ca
Re: Why set a status and control register this way ??
« Reply #6 on: September 14, 2024, 07:15:07 pm »
Thanks, so for
Quote
So to code CS=1, bias=1, duty=10, segment select=111 you'd write:

(1<<LCDCS) | (1<<LCDBIAS) | (2<<LCDDUTY) | (7<<LCDSEG)

which is

(0b1<<7) | (0b1<<6) | (0b10<<4) | (0b111<<0)
write your desired value on the left, then left shift it, to the starting/LSB bit position of the variable in question.

Yeah so for say LCDPM2,LC2PM1, LCDPM0,  at bit position at 2,1,0 of the 8-bit LCDCRB register, that's equiv to LCDPM2:0 , so those would set to b00000111

1<<LCDPM2 would be 1<<2 goes to b00000100
1<<LCDPM1    "   1<<1   "   b00000010
1<<LCDPM0  "  1<<0   "  b00000001
and then bitwise OR them

or just
b111<<0 equates to b111

The way I tried it would only work, if all the initial values were 0, so that won't really work.
« Last Edit: September 14, 2024, 07:16:39 pm by MathWizard »
 

Online Andy Chee

  • Super Contributor
  • ***
  • Posts: 1023
  • Country: au
Re: Why set a status and control register this way ??
« Reply #7 on: September 15, 2024, 03:22:44 am »
The way I tried it would only work, if all the initial values were 0, so that won't really work.
Normally the hardware reset will set the registers to a known condition, so it should work provided you reset it.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4280
  • Country: nl
Re: Why set a status and control register this way ??
« Reply #8 on: September 15, 2024, 05:44:58 am »
If it is necessary to change the bits to a new setting during runtime one can use bitwise AND with the negated bits.

In C:
Code: [Select]
char a = 0;

//Initial configuration
a = (1 << 5) | (1 << 3);

//Need to change the configuration
a &= ~(1 << 5);
a |= (1 << 6);

Offline m k

  • Super Contributor
  • ***
  • Posts: 2404
  • Country: fi
Re: Why set a status and control register this way ??
« Reply #9 on: September 15, 2024, 02:28:36 pm »
With shifting bits they can define bits directly by their locations.
It's also how datasheets are written.

Writing a code is a bit more difficult first, but completely mechanical after that.
Just some copy/paste and actual order is also irrelevant.

Resetting a bit is a bit more complex, but just a bit.
Advance-Aneng-Appa-AVO-Beckman-Danbridge-Data Tech-Fluke-General Radio-H. W. Sullivan-Heathkit-HP-Kaise-Kyoritsu-Leeds & Northrup-Mastech-REO-Simpson-Sinclair-Tektronix-Tokyo Rikosha-Topward-Triplett-Tritron-YFE
(plus lesser brands from the work shop of the world)
 

Offline MathWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 1599
  • Country: ca
Re: Why set a status and control register this way ??
« Reply #10 on: September 18, 2024, 12:09:52 am »
I'm decoding the Intel Hex, into opcodes, of an ATtiny13A with a blink program on there, so far I'm up to WORD 45 in the program flash. And so far it's gone ok, since I found 1 of the only AVR OPCODE tables in existence it seems.

https://fruttenboel.nl/AVR/opcodes.html

It's only a few lines

Line 2
:20002000B0E001C01D92A436B207E1F727D03EC0E7CF1F920F920FB60F9211248F939F9369
program data should be
B0E001C01D92A436B207E1F727D03EC0E7CF1F920F920FB60F9211248F939F93

which in Assembly should be

LDI R11,0
RJMP 1
SR X+,R1
CPI R25,97
CPC R27,R17
BRNE 124
RCALL 39
RJMP 62
RJMP 3877
PUSH R1
PUSH R0
IN R0,63
PUSH R0
CLR R17
PUSH R24
PUSH R25

I need to see how that matches up with the very short blink code written in C by ArduinoIDE
« Last Edit: September 18, 2024, 12:16:22 am by MathWizard »
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4290
  • Country: us
Re: Why set a status and control register this way ??
« Reply #11 on: September 19, 2024, 06:47:53 am »
Quote
found 1 of the only AVR OPCODE tables in existence it seems.
See also: https://drive.google.com/file/d/0B6dMB5dovDUZQ1pPMWtfMkJSU1k/view?usp=sharing&resourcekey=0-mB8Ye-PIxvODzztiWa9xzg
Quote
I'm decoding the Intel Hex, into opcodes, of an ATtiny13A with a blink program on there
Why?  avr-objdump will do that for you...
Code: [Select]
> avr-objdump -D -mavr Arduino1.8.13Build/Blink.ino.hex

Arduino1.8.13Build/Blink.ino.hex:     file format ihex


Disassembly of section .sec1:

00000000 <.sec1>:
   0:   09 c0           rjmp    .+18            ;  0x14
   2:   28 c0           rjmp    .+80            ;  0x54
   4:   27 c0           rjmp    .+78            ;  0x54
   6:   26 c0           rjmp    .+76            ;  0x54
   8:   25 c0           rjmp    .+74            ;  0x54
   a:   24 c0           rjmp    .+72            ;  0x54
   c:   23 c0           rjmp    .+70            ;  0x54
   e:   22 c0           rjmp    .+68            ;  0x54
  10:   21 c0           rjmp    .+66            ;  0x54
  12:   20 c0           rjmp    .+64            ;  0x54
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61
  1c:   01 d0           rcall   .+2             ;  0x20
  1e:   1b c0           rjmp    .+54            ;  0x56
  20:   78 94           sei
  22:   b9 9a           sbi     0x17, 1 ; 23
  24:   c1 9a           sbi     0x18, 1 ; 24
  26:   89 ee           ldi     r24, 0xE9       ; 233
  28:   93 e0           ldi     r25, 0x03       ; 3
  2a:   01 97           sbiw    r24, 0x01       ; 1
  2c:   61 f4           brne    .+24            ;  0x46
  2e:   c1 98           cbi     0x18, 1 ; 24
  30:   89 ee           ldi     r24, 0xE9       ; 233

 
The following users thanked this post: MathWizard

Offline MathWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 1599
  • Country: ca
Re: Why set a status and control register this way ??
« Reply #12 on: September 20, 2024, 06:41:10 am »
Ok I'll use something like that down the road. I had a few errors so far.

But I'm doing it all in a spreadsheet, and putting notes everywhere. So far I think I get it, I'm up to a Branch if NOT Equal at instruction 21.

In the ArduinoIDE, I think that's related to the  "while :" function, that they use as they bitshift through one of the LED port register to turn it, or set direction.  And it delays 500 or 1000ms depending on where it is in that bitshift of the port register. But yeah I'm not that familiar with C or programming either.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf