0

So I am working with the SoftwareSerial library. I'm looking at the write function and I see a *reg value being manipulate. I want to find the value of *reg so I traced the value by stepping into each function call.so here we go.

*reg = _transmitPortRegister
_transmitPortRegister = portOutputRegister(port) //I'm working with pin 1 in register D

Continuing...

#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )

const uint16_t PROGMEM port_to_output_PGM[] = {
    NOT_A_PORT,
    NOT_A_PORT,
    (uint16_t) &PORTB,
    (uint16_t) &PORTC,
    (uint16_t) &PORTD,
};

My first question is what is the point of (uint16_t) &PORTD? I am assuming that's just the value of PORTD's register because it is being anded with an empty 16 bit int.

Continuing on,

#define PORTB _SFR_IO8(0x05)
#define PORTC _SFR_IO8(0x08)
#define PORTD _SFR_IO8(0x0B)

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define __SFR_OFFSET 0x20

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

Now, I followed it all the way here. But how do I find out what actually is the deferenced value? Since this is all IDE, i'm not sure how to GDB this either.

LASTLY, the write function takes in a parameter b, which is a byte. Now in the write function, this is the code to write.

// Write each of the 8 bits
for (uint8_t i = 8; i > 0; --i)
{
  if (b & 1) // choose bit
    *reg |= reg_mask; // send 1
  else
    *reg &= inv_mask; // send 0

  tunedDelay(delay);
  b >>= 1;
}

In that block of code, I am not quite sure why they are doing b & 1. Are they just anding the whole byte with 1? If so, then why comment it as "choose byte" and inside the if-else blocks, they're just oring *reg with reg_mask. How does that translate to sending a one?

Sorry for any confusion, i am glad to clarify any confusing parts of this question. Please let me know.

Jonathan
  • 264
  • 3
  • 14

2 Answers2

2

My first question is what is the point of (uint16_t) &PORTD? I am assuming that's just the value of PORTD's register because it is being anded with an empty 16 bit int.

No. You're mistaking the operator. In this context & means the address of, so it is getting the address of the variable called PORTD and casting it to an unsigned 16-bit integer, which it's then storing in the array.

Now, I followed it all the way here. But how do I find out what actually is the deferenced value? Since this is all IDE, i'm not sure how to GDB this either.

Again, we're talking addresses. The port register number is (for port D) 0x0B. Add that to 0x20 and you get 0x2B which is the address of the register in memory. Then comes the tricky bit:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

Expand that out with 0x2B and you get:

(*(volatile uint8_t *)(0x2B))

Now, working from the inside out we have:

(volatile uint8_t *)(0x2B)

That says this is a pointer to address 0x2B.

Then that is de-referenced to get the value from that pointer with a * operator:

*(volatile uint8_t *)(0x2B)

So you end up with "The value that is in the address 0x2B"

If we then feed that back into the first part of your question, so:

PORTD = (*(volatile uint8_t *)(0x2B))

then &PORTD would be:

&(*(volatile uint8_t *)(0x2B))

The initial & and * cancel each other out (you can think of them as opposites - one is get the address from a value, the other get a value from an address), so you effectively end up with:

(volatile uint8_t *)0x2B
Majenko
  • 105,095
  • 5
  • 79
  • 137
  • Oh my, i feel so amateurish. I am so sorry. I can't believe I completely neglected the fact that the & is adress of in this case. Ok, thank you so much! So I know up the value in 0x2B is being deferenced. But are you saying 0x2B is the value that is being returned? or is it something else still? – Jonathan Oct 02 '15 at 18:21
  • 2
    0x2B is the value that is being returned when & is used, since that is the address that the value is stored at. Don't sweat it, it took me years to get my head around pointers. – Majenko Oct 02 '15 at 18:23
  • OOO! the value at 0x2b is being deferenced, and we're trying to find out the address of that value. which IS 0x2b. Thank you so much. Also, why would or *reg |= reg_mask send a one? If i'm using pin 1, then reg_mask equals 0b00000010. so 0b00101011 | 0b00000010 is what is being calculated. but how does that translate to a one being sent? there isn't even a digitalWrite in that block of code. – Jonathan Oct 02 '15 at 19:18
  • 1
    That would OR the contents of reg_mask with the contents of the address pointed to by reg. It basically sets one bit in the register. &= inv_mask clears the same bit. – Majenko Oct 02 '15 at 19:20
  • sorry, I'm not quite understanding how it sets one bit in the register because that OR statement evaluates to 0b00101011. – Jonathan Oct 02 '15 at 19:23
  • Also, just be setting it in the register, how does it actually transmit it? In other words, when does it actually power the pin HIGH and LOW? – Jonathan Oct 02 '15 at 19:25
  • 1
    It expands to *reg = *reg | reg_mask. If *reg contains, say 0b00101001 and reg_mask contains 0b00000010 then *reg ends up as 0b00101011 and the pin goes high. That register is more than just memory - it is actually the physical hardware of the IO pin. You're not just setting values, you're directly affecting real hardware. By setting a bit in the PORT register you are physically changing the voltage level on the pin from 0V to 5V. – Majenko Oct 02 '15 at 19:26
  • Hey, O i see what you're saying. Ok, i just read up on what each register does in arduino. But here's the iffy part. *reg is 0x2b. That evaluates to 0b00101011. So ORing that with 0b00000010 just gives me back 0b00101011. – Jonathan Oct 02 '15 at 19:33
  • 1
    No, reg is 0x2b, *reg is whatever is at 0x2b. – Majenko Oct 02 '15 at 19:34
  • Hmm, so how do I figure out what is at value 0x2b? – Jonathan Oct 02 '15 at 19:51
  • By looking at *reg or PORTD. Why do you need to "figure out" what is at that address anyway? You are just setting or clearing one bit in it. The rest shouldn't concern you – Majenko Oct 02 '15 at 19:52
  • well i was told to figure out what exactly is the startbit. So all of this is just to figure out what startbit is being sent. So am i understanding this properly? the value 0x2b is being transmitted as the startbit? – Jonathan Oct 02 '15 at 20:00
  • 1
    Nonono! There is NO VALUE 0x2B. That is an ADDRESS in memory. The value that is being sent is ONE of the bits within the CONTENTS of that address. You don't need to know what the contents are, since you are only ever manipulating ONE bit within it, and you never care what it was, only what you set it to. Forget that whole register and look at the rest of the program to determine what the bit is being set to and when. Forget the mechanics of how the bit is being set. – Majenko Oct 02 '15 at 20:16
  • Oh I completely understand! ok ok. That makes more sense. Hence this piece of code in the library. And if i just had an arduino uno, the inv would read false, since most arduinos don't use inverse logic. So basically, my start bit is 0, since i'm anding it with inv_mask. // Write the start bit if (inv) reg |= reg_mask; else reg &= inv_mask; Thank you so much for your help. – Jonathan Oct 02 '15 at 20:27
1
// Write each of the 8 bits
for (uint8_t i = 8; i > 0; --i)
{
  if (b & 1) // choose bit
    *reg |= reg_mask; // send 1
  else
    *reg &= inv_mask; // send 0

  tunedDelay(delay);
  b >>= 1;
}

In that block of code, I am not quite sure why they are doing b & 1. Are they just anding the whole byte with 1? If so, then why comment it as "choose byte" and inside the if-else blocks,

The comment is: choose bit

First, variable b is the byte that is being set. Now, since async serial sends the least-significant bit first, at the start of the loop of 8 iterations, it takes the low-order bit to see if it is set or not:

  if (b & 1) // choose bit

If set, it sets the output port to HIGH (by ORing in the appropriate mask to the appropriate register), otherwise it sets the port to LOW (by ANDing in the appropriate inverted mask to the appropriate register). The inverted mask would be set to every bit but one.

Now look at this:

  b >>= 1;

That shifts the bits right one, throwing away the low-order bit. Now what was previously the second lowest-order bit is now the low-order bit. Then the loop repeats. Now the next bit is sent. And so on for 8 bits.

Imagine we were sending 0x39 (0b00111001). For the 8 iterations of the loop we would see:

00111001  --> 1 sent, then "b" shifted right one
00011100  --> 0 sent, then "b" shifted right one
00001110  --> 0 sent, then "b" shifted right one
00000111  --> 1 sent, then "b" shifted right one
00000011  --> 1 sent, then "b" shifted right one
00000001  --> 1 sent, then "b" shifted right one
00000000  --> 0 sent, then "b" shifted right one
00000000  --> 0 sent, then "b" shifted right one

You can see that the right-most bit is sent (the low-order bit) and then b is shifted right (introducing zeroes at the left), until all 8 bits are sent.

Nick Gammon
  • 38,184
  • 13
  • 65
  • 124