attiny85+mc23017+7segment display = no multiplexing  Help!!!! - Page 1
Forum section: electronics / beginners

author: techninja80
author_rank: Regular Contributor
timestamp: September 20, 2022, 03:12:26 pm
content: Sooooo question for you geniuses out there. I tried a thing, and I think it may be fundamentally dumb. I have a 7 segment display, that I wanted to multiplex using an attiny85, and using a mcp23017 IO expansion chip to give the lil guy more arms to pull on stuff. I am using i2c for the comms to the 23017, but when I multiplex even with no delay in the Arduino code, well..... its not fast enough. I can clearly see it trying to each digit, and its not legible. So the question is is the lil guy tooo slow for this, or is it I2c that is bottlenecking it?

author: pcprogrammer
author_rank: Super Contributor
timestamp: September 20, 2022, 03:18:57 pm
content: It would help if you put up some schematic and show your code. I have used both the attiny85 and the mcp23017 in projects and had no problems it being slow, but I did not drive a display with it

author: techninja80
author_rank: Regular Contributor
timestamp: September 20, 2022, 03:57:30 pm
content: Code:#include <Arduino.h>#include <MCP23017_Attiny85.h>MCP23017 mcp;int pinA = 4;int pinB = 5;int pinC = 6;int pinD = 7;int pinE = 8;int pinF = 9;int pinG = 10;int D1 = 0;int D2 = 1;int D3 = 2;int countmax = 3;int count;// the setup routine runs once when you press reset:void setup() {// initialize the digital pins as outputs.mcp.begin();mcp.pinMode(pinA, MCP_OUTPUT);mcp.pinMode(pinB, MCP_OUTPUT);mcp.pinMode(pinC, MCP_OUTPUT);mcp.pinMode(pinD, MCP_OUTPUT);mcp.pinMode(pinE, MCP_OUTPUT);mcp.pinMode(pinF, MCP_OUTPUT);mcp.pinMode(pinG, MCP_OUTPUT);mcp.pinMode(D1, MCP_OUTPUT);mcp.pinMode(D2, MCP_OUTPUT);mcp.pinMode(D3, MCP_OUTPUT);}void one() {mcp.digitalWrite(D1, HIGH);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, LOW);mcp.digitalWrite(pinA, HIGH);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, LOW);mcp.digitalWrite(pinD, HIGH);mcp.digitalWrite(pinE, HIGH);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, HIGH);}void two() {mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, HIGH);mcp.digitalWrite(D3, LOW);mcp.digitalWrite(pinA, LOW);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, HIGH);mcp.digitalWrite(pinD, LOW);mcp.digitalWrite(pinE, LOW);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, LOW);}void three() {mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, HIGH);mcp.digitalWrite(pinA, LOW);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, LOW);mcp.digitalWrite(pinD, LOW);mcp.digitalWrite(pinE, HIGH);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, LOW);}// the loop routine runs over and over again forever:void loop() {if (count == countmax) {count = -1;}count++;one();delay(0);two();delay(0);three();delay(0);}

author: pcprogrammer
author_rank: Super Contributor
timestamp: September 20, 2022, 04:13:02 pm
content: First of all the delay(0); statements in your loop code do nothing, so no delay in between writing to the different digits, but that is not a real issue.It depends on how the MCP23017 class does things if it is to slow or not.But the problem here is that each output of the MCP23017 changes directly after the write. So the moment the display enable lines change the newly selected display will show the status of the previously selected display.A better way to do it would be to disable the enable lines. Write the settings for what the display should show, and then enable that line for a short period of time. Disable it, write the settings for the next one, enable that line, etc.

author: techninja80
author_rank: Regular Contributor
timestamp: September 20, 2022, 04:38:59 pm
content: Quote from: pcprogrammer on September 20, 2022, 04:13:02 pmThank you for the great advice. The delay there was for testing the multiplexing and for troubleshooting. I adjusted the code accordingly, and I did get better readablity, the numbers are clear not and not just a jumble. Just like you said with the pins pulling the last number. However I am still getting blinking as it seems to be too slow, I will try to put a video. See updated code below.Code:A---F | | B| G |---E | | C| |---DThis example code is in the public domain.*/// Pin 2-8 is connected to the 7 segments of the display.#include <Arduino.h>#include <MCP23017_Attiny85.h>MCP23017 mcp;int pinA = 4;int pinB = 5;int pinC = 6;int pinD = 7;int pinE = 8;int pinF = 9;int pinG = 10;int D1 = 0;int D2 = 1;int D3 = 2;int countmax = 3;int count;int timer;// the setup routine runs once when you press reset:void setup() {// initialize the digital pins as outputs.mcp.begin();mcp.pinMode(pinA, MCP_OUTPUT);mcp.pinMode(pinB, MCP_OUTPUT);mcp.pinMode(pinC, MCP_OUTPUT);mcp.pinMode(pinD, MCP_OUTPUT);mcp.pinMode(pinE, MCP_OUTPUT);mcp.pinMode(pinF, MCP_OUTPUT);mcp.pinMode(pinG, MCP_OUTPUT);mcp.pinMode(D1, MCP_OUTPUT);mcp.pinMode(D2, MCP_OUTPUT);mcp.pinMode(D3, MCP_OUTPUT);}void one() {mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, LOW);mcp.digitalWrite(pinA, HIGH);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, LOW);mcp.digitalWrite(pinD, HIGH);mcp.digitalWrite(pinE, HIGH);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, HIGH);mcp.digitalWrite(D1, HIGH);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, LOW);delay(timer);}void two() {mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, LOW);mcp.digitalWrite(pinA, LOW);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, HIGH);mcp.digitalWrite(pinD, LOW);mcp.digitalWrite(pinE, LOW);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, LOW);mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, HIGH);mcp.digitalWrite(D3, LOW);delay(timer);}void three() {mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, LOW);mcp.digitalWrite(pinA, LOW);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, LOW);mcp.digitalWrite(pinD, LOW);mcp.digitalWrite(pinE, HIGH);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, LOW);mcp.digitalWrite(D1, LOW);mcp.digitalWrite(D2, LOW);mcp.digitalWrite(D3, HIGH);delay(timer);}// the loop routine runs over and over again forever:void loop() {timer++;if (timer == 100){timer = 0;}if (count == countmax) {count = -1;}count++;one();delay(0);two();delay(0);three();delay(0);}Here is a video of the above code running. Note it cycles form 0 delay all the way up to 100. I did this to confirm if it was a frequency issue. But I think this shows that its not fast enoungh.https://youtube.com/shorts/bwNYoTUQ6Q8

author: pcprogrammer
author_rank: Super Contributor
timestamp: September 20, 2022, 04:55:55 pm
content: Lines that are already low don't need to be written low again. Saves a couple of instructions and processing time.Code:void one() {mcp.digitalWrite(pinA, HIGH);mcp.digitalWrite(pinB, LOW);mcp.digitalWrite(pinC, LOW);mcp.digitalWrite(pinD, HIGH);mcp.digitalWrite(pinE, HIGH);mcp.digitalWrite(pinF, HIGH);mcp.digitalWrite(pinG, HIGH);mcp.digitalWrite(D1, HIGH);delay(timer);mcp.digitalWrite(D1, LOW);}Will do the job too. But with timer on 0 it might pulse to quick, and you won't see any thing on the display. With timer on 99 it might be to long and the other digits will fade.A better setup would be to have the displays be written to in a constant fashion without delays between the digits in such a way that the timing per digit is always constant. This way they won't flicker that much or when fast enough not at all.

author: Ian.M
author_rank: Super Contributor
timestamp: September 20, 2022, 04:58:55 pm
content: If you are using the USI peripheral to do hardware I2C on an 8MHz ATtiny, 400KHz I2C bus speed should be possible without too much trouble. Allowing for 10% overheads, that gets you about 40KB/s transfer rate. With the MCP23017 configured for Byte mode with IOCON.BANK=0, you can stream data to its two ports alternately, so get a 20KHz update rate per port. If you have to re-address the MCP23017 with a new I2C transaction each time there's an additional two byte overhead, which would drop the update rate to 10KHz. That's still far far faster than you need for flicker-free multiplexing of even a 16 digit 7 segment display.OTOH if you are running the ATtiny at the default 1MHz and using software I2C because of lack of knowledge or poor pin choice, you'll struggle to get an acceptable update rate for even a few digits.However your algorithm sucks - bit-banging individual pins over I2C is Slooooooooooow as each transition needs a minimum of three bytes transferred. If the MCP23017_Attiny85 library doesn't cache the pin states so it has to read the MCP23017's port register to preserve the state of the other pins, it may need six bytes or more per pin transition.The fix for that is to only send whole bytes to the MCP23017 ports, four bytes at a time (with two bytes of overhead for chip and register address). The first two data bytes keep the previous digit selected but blank all the segments (to prevent ghosting), then the final two bytes select a new digit and set a segment pattern. That should get you a digit update rate of around 6.6KHz, (assuming 400KHz hardware I2C using USI), plenty for any reasonable number of multiplexed digits.Note that the arduino-attiny85-mcp23017-library (that I suspect you are using) is a poor choice for this as it re-addresses the MCP23017 (giving two bytes overhead) for each port byte transferred, limiting the max. update rate considerably. However you should still be able to achieve a several KHz digit update rate using mcp.writeGPIO().

author: techninja80
author_rank: Regular Contributor
timestamp: September 20, 2022, 05:01:37 pm
content: Quote from: pcprogrammer on September 20, 2022, 04:55:55 pmThanks again for your fast reply. However I am not sure I understand. Would delay 0 not provide the constant write you are referring to? Or am I way off course, and missing your point.

author: ledtester
author_rank: Super Contributor
timestamp: September 20, 2022, 05:02:40 pm
content: Quote from: pcprogrammer on September 20, 2022, 04:13:02 pmFor instance, see if your library has a way to set all of the GPIO pins in one call instead of having to make individual digitalWrite() calls.This library:https://github.com/9glt/arduino-attiny85-mcp23017-libraryhas a writeGPIO() method which sets all of the outputs in just two I2C transfers.Example:Code:int portA = 0x12;int portB = 0x56;mcp.writeGPIO( (portB << 8) | portA );

author: pcprogrammer
author_rank: Super Contributor
timestamp: September 20, 2022, 05:10:35 pm
content: Quote from: techninja80 on September 20, 2022, 05:01:37 pmIn a way, yes without the delay it would be more or less constant, but like Ian.M and ledtester pointed out is that the basic method of writing to the MCP23017 is slow, and using another lib might help in getting things to work faster, but it depends on your coding skills to get it done.It might also be possible to improve things in hardware, by using latches, or even a latched display. Then the speed of the software does not matter.

author: techninja80
author_rank: Regular Contributor
timestamp: September 20, 2022, 05:13:37 pm
content: Quote from: Ian.M on September 20, 2022, 04:58:55 pmBusted. I am in the beginners sections for a reason. Absolutely lack the knowledge to write this code on the protocol level, hence the libraries. Wanted to use language, and toolset that I am familiar with. Well then, will look into another more efficient library.

author: MikeK
author_rank: Super Contributor
timestamp: September 20, 2022, 07:13:10 pm
content: FYI: The ATtiny85 can run at 20MHz, if needed. Plenty fast enough for what you want to do.