0

I have been reviewing the code for several libraries,mostly by SparkFun, and looking at the defines for for instance the chip select pins for their hardware.

In the defines, rather than being a simple number or string, they have the following syntax:

#define P_MOSI  B,3

with others being preceded by a D instead.

I am surmising this may be a data type definition, but I don't recognise the syntax and am wondering

  1. What does the syntax mean?
  2. What is the effect of nominating the data type of a define?
  3. Is this Arduino specific syntax or general to C/C++?
  4. If I want to modify a value, is it as simple as changing the value after the comma?
1
3

I looked into one of these libraries, namely the SparkFun CAN-Bus Arduino Library. The macro appears to be used inside other macros that require two arguments, for example RESET(P_MOSI);. Given the following definitions from the library:

#define RESET(x) _XRS(x)
#define _XRS(x,y) PORT(x) &= ~(1<<y)
#define PORT(x) _port2(x)
#define _port2(x) PORT ## x
#define P_MOSI B,3

the call RESET(P_MOSI); expands to

PORTB &= ~(1<<3);

PORTB is the name of an IO port of the MCU which controls the outputs of the pins. It is actually a macro from avr-libc that expands to something like:

(*(volatile uint8_t *)(0x05 + 0x20))

where 0x05 is the IO port number of the register (the number depends on the particular AVR chip) and 0x20 is an offset needed to get the memory-mapped address from the IO port number. When the compiler does constant folding, RESET(P_MOSI); resolves to

*(volatile uint8_t *)0x25 = 8;

which is then compiled into

cbi 0x05, 3

a single assembly instruction (Clear Bit in Io port) that executes in two CPU cycles and sets to zero the bit 3 of IO port 0x05.

The same end result could be achieved in a much simpler way:

#define P_MOSI 11  // assuming digital 11 = PB3, as in the Uno
#define RESET(x) digitalWrite(x, LOW)

However, the function digitalWrite() is very slow, mostly because it has to map an Arduino pin number to a port address and a bit number, and it does it at run time. The SparkFun approach, though somewhat convoluted, is way more efficient, because the mappings are performed at compile time.

1
#define P_MOSI  B,3

This is a C style macro definition. During the pre-processor phase of compilation where ever the text P_MOSI is encountered it will be replaced with the text B,3.

This is a purely textual replacement with no understanding of the semantic meaning of what is being replaced. So the validity of the macro will depend on where it is being used.

I am not familiar with how this macro is being used in the library code you are looking at, but from what I have seen from a quick google this macro is used to define things on a per-chip architecture basis. EG for an ATmega16 it is

#define P_MOSI  B,5

But for a AT90USB82 it is

#define P_MOSI  B,2

So if you are trying to adapt a library to your processor you will need to change the macro to suit your architecture.

In general such defines are used to adapt the same code across multiple architectures. For instance here is a snippet of something I found when googling

#if (BUILD_FOR_MCP2515 == 1)
    #if defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) || defined(__AVR_ATmega644__)
        #define P_MOSI  B,5
        #define P_MISO  B,6
        #define P_SCK   B,7
        #define SUPPORT_FOR_MCP2515__
    #elif defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__)
        #define P_MOSI  B,2
        #define P_MISO  B,3
        #define P_SCK   B,1
        #define SUPPORT_FOR_MCP2515__
4
  • That is what I thought as well, in which case it must be arguments to a function, elements in a list/array, have another define for the B and D symbols. If that's the case then that's kind of complicated. It seems to be nonstandard if you've never seen it before. – J Collins Jun 18 '17 at 21:34
  • @JCollins: Most things after #define should be considered non-standard. – Ignacio Vazquez-Abrams Jun 18 '17 at 21:38
  • @JCollins I have a bucket load of experience with C/C++ but virtually zero with Arduino. In fact I only wrote my first Arduino program yesterday. – Peter M Jun 18 '17 at 21:39
  • The is exactly what I am looking to do, connect a piece of SparkFun hardware to a MEGA 2560. I have made what I think are the necessary changes, but the unit still does not communicate, leading to the question. – J Collins Jun 18 '17 at 21:49
1

While we can't see how the macro is being used, it may be worth noting that, on Uno's built-in SPI interface, the MOSI signal is on port-B, bit 3.

Update:

the architecture is broken down into ports and pins.

The AVR processors use memory-mapped, byte-wide I/O ports, where each bit mapped to a physical pin (actually to a multi-function pin-driver/receiver). The Arduino programming environment defines symbols such as PORTB to be the addresses of those ports. It further defines single-pin I/O functions (digitalRead(), digitalWrite() ) that "know" the mapping of pin numbers to ports and bits as implemented in each processor. Most people rely on the single-pin functions but the ability read and write the ports directly is there if you need it. And note that the Arduino pin numbers do not match the physical pin numbers of the pins at the exterior of the chip, but it does match the numbers silk-screened on the PCB. While this relieves you from having to deal with that mapping, it makes those low level details somewhat less visible.

2
  • Here's some interesting information, that the architecture is broken down into ports and pins. Perhaps now I need to find the translation between pin/port numbering and the board's own printed numbering. – J Collins Jun 19 '17 at 8:18
  • As in found here: arduino.cc/en/Hacking/PinMapping2560. This seems to be a step forward..! – J Collins Jun 19 '17 at 8:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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