problem bit banging using digitalPinToBitMask et. al.

Status
Not open for further replies.

scarmichael

New member
Not sure if this a bug, or PEBCAK... I'm trying to use this library on Teensy 3.1 [https://github.com/jimblom/sha204-Breakout] to communicate with an ATSHA204 [https://www.sparkfun.com/products/11551] that uses a single wire interface. The library uses bitbanging to affect the protocol. For some reason the pin voltages are not being set at all. The lib uses the low level macros digitalPinToPort, portModeRegister, etc. Below is my example sketch, from my understanding, the voltage on pin 12 should be set high then low ad infinitum. When I test the pin with a multimeter I get 0V, and when using the library to try and communicate with the ATSHA204, the SDA pin stays high at a constant voltage of 3.3V. I've looked at this discussion [http://forum.pjrc.com/threads/24116...fnition-confusion?highlight=portModeRegister] and it appears as though although the definition is different, the macros should behave the same, so I can't figure out why no joy.


const int pin = 12;
uint8_t device_pin;
volatile uint8_t *device_port_DDR, *device_port_OUT, *device_port_IN;




void setup() {
// initialize the pin as an swi port.
device_pin = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
device_port_DDR = portModeRegister(port);
device_port_OUT = portOutputRegister(port);
device_port_IN = portInputRegister(port);

}

void loop() {
swi_set_signal_pin(1);
delay(1000); // wait for a second
swi_set_signal_pin(0);
delay(1000); // wait for a second
}

void swi_set_signal_pin(uint8_t is_high)
{
*device_port_DDR |= device_pin;
if (is_high)
*device_port_OUT |= device_pin;
else
*device_port_OUT &= ~device_pin;
}
 
digitalPinToBitMask and digitalPinToPort are not working

I wrote a quick .ino to test digitalPinToBitMask() and digitalPinToPort() on several boards. While these macros work on the UNO, MEGA2560, and several compatibles, they are not returning the proper PORT or MASK on the Teensy3.0 and 3.1. The PORT = PIN, and Mask = 1 for all the pins. My test code is included. This could be the cause of your code failure. I would also like to use these macros in my code. By the way these functions have been broken on the DUE for quite awhile - gives a *PIO structure on compile.

View attachment digitalWriteFastTest.ino
 
They are likely non functional because they directly manipulate hardware that's Atmel specific. The Teensy 3.x boards are Arm based, just as the Due.

The data sheet to the device states it uses 1MHz I2C. The usual Arduino hardware I2C only goes up to 400KHz which is likely the reason the library referenced on the Sparkfun site uses bit banged I2C.
The Teensy 3.x has much more capable hardware and can use I2C well in excess of 1MHz. You'd need to use the alternative i2c_t3 library to access these speeds.
And of course you'll need to modify the library for the ATSHA204
 
Last edited:
code below showed, on a Teensy 3.1
start
fast version of benchmark:12
old version of benchmark:112


Tweaks to code, below.
#define on pin number.
Changed long to uint32_t because long is a signed type and you can get into trouble mixing signed with timer values which are unsigned.
No comment on methodology in code. Not sure what the goal was.

Code:
#include <Arduino.h>
//digitalWriteFast is a built in Teensy3 function
#define pinNum 13

void setup(void) {

  Serial.begin(9600);
  //Serial.flush(); 
  delay(10000);          //time to set tools/serial port: to 11, then open serial monitor
  Serial.println("start");
  pinMode(pinNum,OUTPUT);
  uint32_t baseMillis=millis();
  uint32_t i=300000, x;
  while (i--) {
    digitalWriteFast(pinNum,HIGH);
  }
  uint32_t bench = millis()-baseMillis;
  Serial.print("fast version of benchmark:");
  Serial.println((int)bench);
  uint32_t baseMillis2=millis();
  i=300000;
  while (i--) {
    digitalWrite(pinNum,HIGH);
  }
  bench = millis()-baseMillis;
  Serial.print("old version of benchmark:");
  Serial.println((int)bench);

}


void loop (void){
  //when done timing just display on/off
  pinMode(pinNum,OUTPUT);
  int i=30000;
  while (i--) {
    digitalWriteFast(pinNum,HIGH);
    delay(1000);
    digitalWriteFast(pinNum,LOW);
    delay(1000);
 }

}
 
My understanding is that Teensyduino supports these two macros, and the port/mask format for Arduino compatibility.
 
Looked at the data sheet again and it states that I supports:
Multiple I/O options
  1. High-Speed, Single-Wire Interface
  2. 1MHz I2C interface

So the high speed one wire interface is what the library is written for. However, given the I2C hardware abilities of the Teensy I believe I2C communication is preferable. Unfortunately there is no library available for it.
 
The ATSHA204 package I'm using only supports the single wire interface. I haven't played around with getting the io working a week or so, but will tonight. I'm going to adjust that library to use GPIO port manipulation directly, or digitalwrite fast.
 
Pins on Teensy 3.1 default to disabled. Once in GPIO mode, those macros will work. Just use pinMode(pin, INPUT) to first get the pin into GPIO mode.

Pretty much all modern microcontrollers default pins to disabled, so the input buffers can't consume power by default.
 
I added pinMode(pin,INPUT) before requesting info on each pin. Teensy3.1 does not return the port and bit mask even after this is added. The UNO32 reports properly even without this statement.
 
I added pinMode(pin,INPUT) before requesting info on each pin. Teensy3.1 does not return the port and bit mask even after this is added.

Please post this test code, and I'll take a look.

I see you posted test code on reply #2 using digitalWriteFast, but not the macros.

Post the actual code I should look into.... let's get the right sample code here, so I can get this resolved without guesswork about what you're attempting.
 
I should mention 3 things.

#1: On Teensy 3.x, the addresses returned by these macros use an ARM Cortex-M4 feature called Bitband Addressing. The mask is always 1, because Bitband addresses always use the number 1. The use of bitband addressing solves 2 problems. First, it allows pointers to 8 bits, even when the underlying ARM hardware is 32 bit registers, which gives best compatibility with AVR code that assumes all registers are only 8 bits. Second, bitband addressing is always implemented with atomic read-modify-write to the bit within an I/O register. This solves the subtle bug in many Arduino libraries and sketches which don't properly disable interrupts while doing bitwise read-modify-write on shared registers. If you look only at the code and see the value is fixed at 1, you might incorrectly conclude the code is wrong, if you don't carefully consider the use of ARM's Cortex-M4 Bitband feature.

#2: The "port" lookup simply returns the pin, because you can only use that in the register lookup functions. On Arduino, the port lookup gives you an integer, which can only be used for a second lookup to get the input, output and direction register address. This 2-level lookup saves only a tiny amount of flash memory, but results in larger and slower code. Teensy does the entire thing in only a single level lookup, using a larger table. The fact that PORT = PIN may seem incorrect if you are only looking at that particular code, without understanding how it's used. Because the integer returned by the digitalPinToPort() can only be used as input to portOutputRegister(), portInputRegister() and portModeRegister(), simply implementing digitalPinToPort(pin) to return "pin" IS CORRECT if the other 3 macros are written to fully resolve pin number to the 3 addresses.

#3: These macros do work. They're used by several widely used libraries that are confirmed working. Examples include DmxSimple, OneWire, SoftPWM, UTFT, CapacitiveSensor. If they're not working for you, and you're getting the pin into GPIO mode before using them, I'm very certain it's a bug in your code.
 
Status
Not open for further replies.
Back
Top