Sunday, 17 April 2016

IO Expansion

I started checking out AdaCore's support library for STM32F4 boards, particularly to use I2C to talk to the PCF8547A IO expander (for possible use on the AdaPilot project).

I seem to have fritzed one or two of the pins on my STM32F429I-DISC0 board (now replaced by an updated version, STM32F429I-DISC1; that last character was a 0!); after some hair-pulling it turns out that one of said pins is used by the only externally-accessible I2C peripheral on the board; so, until the replacement arrives, here are some interesting facts about the PCF8547A.

Use for output (to an LED)

It turns out that the chip is unlike most of the digital I/O devices you'll find, in that the pins act as current sinks rather than current sources (page down to the hand-drawn circuit diagram). Normally (as for example with the STM32F4), you'd connect a resistor to the pin, the anode of an LED to the resistor, and the cathode of the LED to ground. With the PCF8547A, you connect the anode of the LED to VCC, the cathode of the LED to the resistor, and the other end of the resistor to the pin.

This means that you write a 0 to the pin to turn the LED on.

Use for input (from a switch)

Normally you'd have a pull-up resistor (possibly supplied by the MCU internally), so that when the switch is open the MCU sees 1, when it's closed the MCU sees 0.

For the PCF8547A, where the pins are bidirectional, you configure the pin to provide a pull-up by writing 1 to it from the MCU.

Arduino

As I said above, my STM32F4 board isn't working; so I explored the chip using an Arduino Due (and the Arduino IDE, so the code is C++ even though in a .ino file).

The Due has two I2C channels; the easily-visible pins at the top of the board are labelled SDA1, SCL1 and it took me a while to realise that the first channel uses the pins labelled SDA, SCL hiding away at the bottom right. (To use the second channel, address it as Wire1, e.g.Wire1.begin()). The Arduino code:

#include <Arduino.h>
#include <Wire.h>

const unsigned char chip_address = 0x70 / 2;

void setup() {
    Wire.begin();
}

void loop() {
    unsigned char input;
    unsigned char output;

    Wire.requestFrom(chip_address, 1);
    input = Wire.read();

    // The pushbuttons are on pins 4 .. 7, the LEDs on pins 0 .. 3.
    // If PB4 is 1, write 0 to PB0
    // Always write 1 to the pushbuttons

    output = ~(input >> 4);
    Wire.beginTransmission(chip_address);
    Wire.write(output);
    int status = Wire.endTransmission();

    delay(1000);
}

STM32F4 equivalent

For interest, the draft initialization code for the STM32F4 - the equivalent of the Arduino's Write.begin() - is, using AdaCore's bareboard package,
STM32F4.RCC.GPIOA_Clock_Enable;
STM32F4.GPIO.Configure_Alternate_Function
  (STM32F42xxx.GPIO_A,
   Pin => STM32F4.GPIO.Pin_8,
   AF  => STM32F4.GPIO.GPIO_AF_I2C3);
STM32F4.GPIO.Configure_IO
  (STM32F42xxx.GPIO_A,
   Pin    => STM32F4.GPIO.Pin_8,
   Config => (Mode        => STM32F4.GPIO.Mode_AF,
              Output_Type => STM32F4.GPIO.Open_Drain,
              Speed       => STM32F4.GPIO.Speed_25MHz,
              Resistors   => STM32F4.GPIO.Floating));

STM32F4.RCC.GPIOC_Clock_Enable;
STM32F4.GPIO.Configure_Alternate_Function
  (STM32F42xxx.GPIO_C,
   Pin => STM32F4.GPIO.Pin_9,
   AF  => STM32F4.GPIO.GPIO_AF_I2C3);
STM32F4.GPIO.Configure_IO
  (STM32F42xxx.GPIO_C,
   Pin    => STM32F4.GPIO.Pin_9,
   Config => (Mode        => STM32F4.GPIO.Mode_AF,
              Output_Type => STM32F4.GPIO.Open_Drain,
              Speed       => STM32F4.GPIO.Speed_25MHz,
              Resistors   => STM32F4.GPIO.Floating));

STM32F4.RCC.I2C3_Clock_Enable;
STM32F4.I2C.Configure
  (Port        => STM32F42xxx.I2C_3,
   Clock_Speed => 10_000,
   Mode        => STM32F4.I2C.I2C_Mode,
   Duty_Cycle  => STM32F4.I2C.DutyCycle_2,
   Own_Address => 16#00#,
   Ack         => STM32F4.I2C.Ack_Enable,
   Ack_Address => STM32F4.I2C.AcknowledgedAddress_7bit);

No comments:

Post a Comment