C++ How to intialize MCP23017 Config values?

I’m going to update this thread as I stagger through my implementation in the hope that someone else looking for this finds it and avoids wandering in the wilderness like I’ve been doing, or at least as a cautionary tale of what happens when you punch far above your weight in C++. :slight_smile: What I write here is likely to be a compendium of noob mistakes I made, but the template syntax is starting to gel now.

Thanks to @tele_player I got back on track, and that allowed me to make sense of this thread about initializing different I2C peripherals. This is the code:


/*
Simple MCP23017 o-scope test.
*/

#include "daisy_seed.h"
#include "daisysp.h"

using namespace daisy;
using namespace daisysp;
using namespace daisy::seed;
DaisySeed hw;
Mcp23017 mcp;

TimerHandle timer;

void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{
	for (size_t i = 0; i < size; i++)
	{
		out[0][i] = in[0][i];
		out[1][i] = in[1][i];
	}
}

//Timer callback - Can I do control reads here?
void TimerCallback(void* data)
{
    /** Use system time to blink LED once per second (1023ms) */
    bool led_state = (System::GetNow() & 1023) > 511;
    /** Set LED */
    hw.SetLed(led_state);
}

int main(void)
{
	hw.Init();
	
	// Cribbed from another forum post.  This works, at least to the point where
	// I see what looks like I2C waveforms for SCL and SDA on my cheap oscilloscope.
	static constexpr I2CHandle::Config i2c_config
		= {I2CHandle::Config::Peripheral::I2C_1,
		{{DSY_GPIOB, 8}, {DSY_GPIOB, 9}},
		I2CHandle::Config::Speed::I2C_100KHZ,  //3.3v does mushy square waves above this.
		I2CHandle::Config::Mode::I2C_MASTER};
	
	// The address param is set to 0x20, with A0, A1, and A2 pins
	// on the MCP pulled low on the breadboard.  The MCP23017 class
	// defaults to 0x27 on a bare Init().
	static constexpr Mcp23017::Config config {i2c_config, 0b100111};

	/**
	 * Questions:  
	 * 1. Should the I2C_Handle and Mcp23017 objects be static constexpr?
	 * 2. 
	*/

	mcp.Init(config);
	
	mcp.PortMode(MCPPort::A, 0x00, false); //0x00 is in, 0xff is out. All inputs, no invert, GPA7 is output only
	mcp.PortMode(MCPPort::B, 0x00, false);
	
	// Timer stuff.  Placeholder for now.
    TimerHandle         tim5;
    TimerHandle::Config tim_cfg;
    tim_cfg.periph     = TimerHandle::Config::Peripheral::TIM_5;
    tim_cfg.enable_irq = true;
    auto tim_target_freq = 25;
    auto tim_base_freq   = System::GetPClk2Freq();
    tim_cfg.period       = tim_base_freq / tim_target_freq;

    /** Initialize timer */
    tim5.Init(tim_cfg);
    tim5.SetCallback(TimerCallback);

    /** Start the timer, and generate callbacks at the end of each period */
    tim5.Start();;

	// What I expect to see on my scope from this is the SDA waveform changing
	// as it rolls through the pins and changes modes.  At the moment, 
	// the SDA waveform does not change.
	bool led_state = false;
	for(;;)
		{
			for(uint8_t i = 0; i < 7; i++) {
				if (led_state == true)
					mcp.PinMode(uint8_t(i), MCPMode::INPUT_PULLUP, false);
				else
					mcp.PinMode(uint8_t(i), MCPMode::OUTPUT, false);
			
			led_state = !led_state;
		}

		
	};

}

I currently have limited means to test the output pins. I have no 3.3v LEDs available, but I guess I could use the scope. Since most of this is really new to me, I’m not clear on what to expect on the scope, voltage at the pins, or what can be written to the serial port.