Atmel ATSHA204 porting question

Does anybody have a hint helping to port the sparkfun ATMEL library for their Authentication chip:
The only show stopper are the Arduino macros like: digitalPinToBitMask(), digitalPinToPort(), pinModeRegister(). … see below:

// atsha204Class Constructor
// Feed this function the Arduino-ized pin number you want to assign to the ATSHA204's SDA pin
// This will find the DDRX, PORTX, and PINX registrs it'll need to point to to control that pin
// As well as the bit value for each of those registers
atsha204Class::atsha204Class(uint8_t pin)
{	
	device_pin = digitalPinToBitMask(pin);	// Find the bit value of the pin
	uint8_t port = digitalPinToPort(pin);	// temoporarily used to get the next three registers
	
	// Point to data direction register port of pin
	device_port_DDR = portModeRegister(port);
	// Point to output register of pin
	device_port_OUT = portOutputRegister(port);
	// Point to input register of pin
	device_port_IN = portInputRegister(port);
}

Are there already some standard hints how to replace that calls and come to the the same result?

@sparki, the Core does not use the same mechanisms for accessing bits on the GPIO ports. Instead, there is a bitmap structure and are bit set and bit reset registers as follows:

to set a bit high: PIN_MAP[pin].gpio_peripheral->BSRR = PIN_MAP[pin].gpio_pin
to set a bit low: PIN_MAP[pin].gpio_peripheral->BRR = PIN_MAP[pin].gpio_pin

You need to make sure you set the pin to OUTPUT mode first using the pinMode(pin,OUTPUT) function. A valid value for pin is D0…D7, A0…A7. :smile:

1 Like

@ppkay, Ok, thanks a lot!
I have already studied the Adafruit SSD1306:SPI init porting and compared it to the original. Your answer does now help to understand, I will try it.

…Coming back - another question:
I have tried to port the send method. AVR needs at that point a special timing. Do you think I can sum up all micros and did the delay at the end like:

uint8_t atsha204Class::swi_send_bytes(uint8_t count, uint8_t *buffer) {
  //--------------------------------------------------------------------- JH -
  uint8_t i, bit_mask;

  // Disable interrupts while sending.
  noInterrupts();  //swi_disable_interrupts();

  // Set signal pin as output.
  pinMode(device_pin, OUTPUT);
  //AVR *device_port_OUT |= device_pin;
  //AVR *device_port_DDR |= device_pin;

  // Wait turn around time.
  delayMicroseconds(RX_TX_DELAY);        //RX_TX_DELAY;

  for (i = 0; i < count; i++) {
    for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {	
      if (bit_mask & buffer[i]) {
        PIN_MAP[device_pin].gpio_peripheral->BSRR = PIN_MAP[device_pin].gpio_pin;
        delayMicroseconds(8*BIT_DELAY);        //BIT_DELAY_8;
        //AVR *device_port_OUT &= ~device_pin;
        //AVR delayMicroseconds(BIT_DELAY);    //BIT_DELAY_1;
        //AVR *device_port_OUT |= device_pin;
        //AVR delayMicroseconds(7*BIT_DELAY);  //BIT_DELAY_7;
      } else {
        // Send a zero bit.
        PIN_MAP[device_pin].gpio_peripheral->BSRR = PIN_MAP[device_pin].gpio_pin;
        delayMicroseconds(8*BIT_DELAY);        //BIT_DELAY_8;
        //AVR *device_port_OUT &= ~device_pin;
        //AVR delayMicroseconds(BIT_DELAY);    //BIT_DELAY_1;
        //AVR *device_port_OUT |= device_pin;
        //AVR delayMicroseconds(BIT_DELAY);    //BIT_DELAY_1;
        //AVR *device_port_OUT &= ~device_pin;
        //AVR delayMicroseconds(BIT_DELAY);    //BIT_DELAY_1;
        //AVR *device_port_OUT |= device_pin;
        //AVR delayMicroseconds(5*BIT_DELAY);  //BIT_DELAY_5;
      }
    }
  }
  interrupts();  //swi_enable_interrupts();
  return SWI_FUNCTION_RETCODE_SUCCESS;
}

Assuming this could be the right spark way sending data over that GPIO pin, reading is the next task:

At here, I found an analgous GPIO read; and here I found a a great helping example from @BDub.
Now I go with the following changes:

uint8_t atsha204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer) {
  //--------------------------------------------------------------------- JH -
  uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
  uint8_t i;
  uint8_t bit_mask;
  uint8_t pulse_count;
  uint8_t timeout_count;

  // Disable interrupts while receiving.
  noInterrupts(); //swi_disable_interrupts();

  // Configure signal pin as input.
  pinMode(device_pin, INPUT);											//JH
  //AVR *device_port_DDR &= ~device_pin;

  // Receive bits and store in buffer.
  for (i = 0; i < count; i++) {
    for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
      pulse_count = 0;

      // Make sure that the variable below is big enough.
      // Change it to uint16_t if 255 is too small, but be aware that
      // the loop resolution decreases on an 8-bit controller in that case.
      timeout_count = START_PULSE_TIME_OUT;

      // Detect start bit.
      while (--timeout_count > 0) {
        // Wait for falling edge.
        //AVR if ((*device_port_IN & device_pin) == 0)
        //AVR  break;
        if((PIN_MAP[device_pin].gpio_peripheral->IDR &		//JH
            PIN_MAP[device_pin].gpio_pin) == 0)             //JH
		  break;
      }

      if (timeout_count == 0) {
        status = SWI_FUNCTION_RETCODE_TIMEOUT;
        break;
      }

      do {
        // Wait for rising edge.
        //AVR if ((*device_port_IN & device_pin) != 0) {
        if((PIN_MAP[device_pin].gpio_peripheral->IDR & 		//JH
            PIN_MAP[device_pin].gpio_pin) != 0) {           //JH
		  //faster than "pulse_count++".
          pulse_count = 1;
          break;
        }
      } while (--timeout_count > 0);

      if (pulse_count == 0) {
        status = SWI_FUNCTION_RETCODE_TIMEOUT;
        break;
      }

      // Trying to measure the time of start bit and calculating the timeout
      // for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU.
      // So let's just wait the maximum time for the falling edge of a zero bit
      // to arrive after we have detected the rising edge of the start bit.
      timeout_count = ZERO_PULSE_TIME_OUT;

      // Detect possible edge indicating zero bit.
      do {
        if((PIN_MAP[device_pin].gpio_peripheral->IDR &      //JH
            PIN_MAP[device_pin].gpio_pin) == 0) {            //JH
        //AVRif ((*device_port_IN & device_pin) == 0) {
          // For an Atmel microcontroller this might be faster than "pulse_count++".
          pulse_count = 2;
          break;
        }
      } while (--timeout_count > 0);

      // Wait for rising edge of zero pulse before returning. Otherwise we might interpret
      // its rising edge as the next start pulse.
      if (pulse_count == 2) {
        do {
          if((PIN_MAP[device_pin].gpio_peripheral->IDR &    //JH
              PIN_MAP[device_pin].gpio_pin) != 0)           //JH
          //AVR: if ((*device_port_IN & device_pin) != 0)
            break;
        } while (timeout_count-- > 0);
      }
      // Update byte at current buffer index.
      else
        buffer[i] |= bit_mask;  // received "one" bit
    }

    if (status != SWI_FUNCTION_RETCODE_SUCCESS)
      break;
  }
  interrupts(); //swi_enable_interrupts();

  if (status == SWI_FUNCTION_RETCODE_TIMEOUT) {
    if (i > 0)
    // Indicate that we timed out after having received at least one byte.
      status = SWI_FUNCTION_RETCODE_RX_FAIL;
  }
  return status;
}

… This low level API is still a small challange :smirk:

Any confirmation or hints for a better way?

Thank for your great help!
cheers.

1 Like

@sparki, I was just looking at the specs for the ATSHA204. It is basically an I2C device but the 3 pin version used on the Sparkfun breakout, it uses single pin communications. This explains the elaborate “bit-banging” library.

So, the answer to your question regarding delays is no, you cannot sum them up at the end. Each delay provides very specific bit-delay timing to create a correctly timed bit stream to the device. Because this version of the ATSHA204 does not use 2 pins for full I2C operation, all communications must be done in software (ie no hardware assistance).

Looking at the timing diagrams for the chip, I believe the direct port commands I gave you are not absolutely necessary and you could use digitalWrite() and digitalRead() instead. However timing is always tricky. Would you like me take a crack at porting the library?

Yes, You ae right, the I2C version I would also prefer - but it was so easy to get an sparffun breakout from it.
I have adapted the last method I saw …

/* SWI bit bang functions */

void atsha204Class::swi_set_signal_pin(uint8_t is_high) {
  //--------------------------------------------------------------------- JH -
  
  pinMode(device_pin, OUTPUT);
  //AVR *device_port_DDR |= device_pin;

  if (is_high)
    PIN_MAP[device_pin].gpio_peripheral->BSRR = PIN_MAP[device_pin].gpio_pin;  // HIGH
    //AVR *device_port_OUT |= device_pin;
  else
    PIN_MAP[device_pin].gpio_peripheral->BRR  = PIN_MAP[device_pin].gpio_pin;  // LOW
    //AVR *device_port_OUT &= ~device_pin;
}

… then I will test. For me it its just for fun and to learn how to do do the low level things -
But … if you offer your knowledge, I would be happy.
No spark - no fun!
… coming back:
I have done some tests: No success!
I have realized the recommendations/considerations from @peekay123 and done the GPIO handling in the common standard way with digitalRead(), digitalWrite() - but the device does not want to react at the wakeup command and in the following.

OK: No sucess at the moment - may be later - with new ideas!

cheers

1 Like

@Sparki, I took a crack at porting the code here in my repo. I kept the original Arduino code and used conditional compiling to make it Spark specific.

I used the standard digitalRead/Write() commands since they are very fast. Also note that I set the device pin to D4, which you can change of course. Let me know if that works for you :smile:

Thanks for your crack support - super service!
OK I was mostly on the right way - found only a small mistake!
But it’s a pitty, the device wont reply (your source in charge, enriched with the send/read pin’s settings): Every time when its time to speech there is no traffic on the data line.My short suggestions are: I2C would be the better first approach to handle the timming stuff by a proper protocol.
I should start with the wakeup - but I have to break for some hours.
cheers and many thanks.

@sparki, since the device is not using the i2c lines, you should remove any pull-up resistors you are using for the device.

@peekay123 OK, I had a look to the breakout schematic. There it is a SDA pullup (100k) I think this is correct. Additionaly I had verified the timing of the wakeup procedure with my logic.
All seems correct:

  • more than 60us (72us) wakeup down,

  • wait for 2,5msec,

  • sending 0x88

  • listening… but time out!
    This is also reflected by the serial output:

    Sending a Wakup Command. Response should be:
    4 11 33 43:
    Response is:
    0 0 0 0

    Asking the SHA204’s serial number. Response should be:
    1 23 x x x x x x x EE
    Response is:
    4E 16 E2 1A 2D 1C 0 0 0

    Sending a MAC Challenge. Response should be:
    23 6 67 0 4F 28 4D 6E 98 62 4 F4 60 A3 E8 75 8A 59 85 A6 79 96 C4 8A 88 46 43 4E B3 DB 58 A4 FB E5 73
    Response is:
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Something does not match and I do not have a new idea at the moment.
Other guys seems also do not have success.
I have tried to get a I2C sample at ATMEL, but it seems this is only possible for a company.

@sparki, how are you powering the breakout?

@peekay123, I have tested both 3.3V and Vin(5V) - no difference!