You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: i2c_blocking.rs
    path of file: ./repos/embassy/examples/rp/src/bin
    
    the code of the file until where you have to start completion: //! This example shows how to communicate using i2c with external chips.
//!
//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
//! (https://www.microchip.com/en-us/product/mcp23017)

#![no_std]
#![no_main]

use defmt::*;
    
async fn main(_spawner: Spawner) {
    let p = embassy_rp::init(Default::default());

    let sda = p.PIN_14;
    let scl = p.PIN_15;

    info!("set up i2c ");
    let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());

    use mcp23017::*;

    info!("init mcp23017 config for IxpandO");
    // init - a outputs, b inputs
    i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
    i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
    i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups

    let mut val = 0xaa;
    loop {
        let mut portb = [0];

        i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
        i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();

        info!("portb = {:02x}", portb[0]);
        val = !val;

        Timer::after_secs(1).await;
    }
}