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.

2 Answers 2

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
14
  • 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 2, 2015 at 18:23
  • 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 2, 2015 at 19:20
  • 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 2, 2015 at 19:26
  • 1
    No, reg is 0x2b, *reg is whatever is at 0x2b.
    – Majenko
    Oct 2, 2015 at 19:34
  • 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 2, 2015 at 20:16
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.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.