Mux errors...

Post here first, or if you can't find a relevant section!
Post Reply
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Mux errors...

Post by neo_nmik »

Hi guys,

Now, this may be down to pin selection, but I wanted to ask here first, see if anyone has any insight...

I'm using some muxes (3x 4051's) with a design I started on an Uno, and am now trying to move over to the Bluepill.

Problem is, when I scan the keys on the Uno, everything works, but when I move the code (both attached below) and change the pins over to the Bluepill, it's giving me weird results.

It should (and does with the Uno) serial print the "numbers" of the keys, 0 to 24. But with the Bluepill, it gives me all the numbers on a mux (0-7 or 8-15, etc.).

I thought I had this working, but I moved the pins due to need the PWM pins for an RGB LED... Have I just chosen the wrong pins? or is it something I'm doing?

It's all running off a 3v3 LDO I've attached to the 5v rail of the Bluepill, other than that the main board has all the muxes and keys on, so there no differences apart from 3v3, but they should run with that logic level fine. Other than that I'm guessing its the pins?

The pins I'm using are PB12-15, and PB3-4. I chose these, because it didn't didn't get in the way of some other stuff I've got going on (SPI1 for a DAC, PA0-3 for Pots and PB6-8 for an RGB LED).

Any help would be hugely appreciated!

EDIT: I've tried this on the latest Arduino IDE, and just dipping my toe into PlatformIO, and its the same, and I'm using a bluepill with the HID Bootloader that's been working great so far!

Bluepill Code:

Code: Select all

// define pins to read buttons
#define MUX_SEL_A PB14
#define MUX_SEL_B PB13
#define MUX_SEL_C PB12
#define MUX_OUT_0 PB4
#define MUX_OUT_1 PB3
#define MUX_OUT_2 PB15

#define LED PC13   // LED

// this 32 bit number holds the states of the 24 buttons, 1 bit per button
uint32_t keyTable = 0xFFFFFFFF;
uint32_t oldkeyTable = 0xFFFFFFFF;
bool isActive[25];

void setup(){
  
  // configure pins for multiplexer
  pinMode(MUX_SEL_A, OUTPUT);  // these are the select pins
  pinMode(MUX_SEL_B, OUTPUT);
  pinMode(MUX_SEL_C, OUTPUT);

  pinMode(MUX_OUT_0, INPUT);
  pinMode(MUX_OUT_1, INPUT);
  pinMode(MUX_OUT_2, INPUT);

  digitalWrite(MUX_SEL_A, 1);   // multiplexer outputs, 8 each
  digitalWrite(MUX_SEL_B, 1);
  digitalWrite(MUX_SEL_C, 1);

  //turn on LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, 1);

  Serial.begin(9600);
}


void loop(void)
{
  getButtons();
  getKeys();
  delay(5);   // wait 10 ms
  
}

// this funcion reads the buttons and stores their states in the global 'buttons' variable
void getButtons(void){
  int i;
  keyTable = 0;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_2) << i;  
  }
  keyTable <<= 8;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_1) << i;
  }
  keyTable <<= 8;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_0) << i;   
  }
  keyTable |= 0x1000000; // masks 25th key
}

void getKeys() {
  if (oldkeyTable != keyTable) {
    for (int i = 0; i < 25; i++) {            // read through the button array and if pressed assigns the number to the Key[] (lowest first... need to change that)
      if ((!((keyTable >> i) & 1)) && !isActive[i]) {
        Serial.println(i);
        isActive[i] = true;

      }
      if ((((keyTable >> i) & 1)) && isActive[i]) {
        isActive[i] = false;
      }
    }
  }
  oldkeyTable = keyTable;
}

Arduino Code:

Code: Select all

// define pins to read buttons
#define MUX_SEL_A 4
#define MUX_SEL_B 3
#define MUX_SEL_C 2
#define MUX_OUT_0 7
#define MUX_OUT_1 6
#define MUX_OUT_2 5

#define LED 8   // LED

// this 32 bit number holds the states of the 24 buttons, 1 bit per button
uint32_t keyTable = 0xFFFFFFFF;
uint32_t oldkeyTable = 0xFFFFFFFF;
bool isActive[25];

void setup(){

  digitalWrite(10, HIGH); // This needs to be set here first as theres no physical pullup for the SPI SS (pin 10)
  
  // configure pins for multiplexer
  pinMode(MUX_SEL_A, OUTPUT);  // these are the select pins
  pinMode(MUX_SEL_B, OUTPUT);
  pinMode(MUX_SEL_C, OUTPUT);

  pinMode(MUX_OUT_0, INPUT);
  pinMode(MUX_OUT_1, INPUT);
  pinMode(MUX_OUT_2, INPUT);

  digitalWrite(MUX_SEL_A, 1);   // multiplexer outputs, 8 each
  digitalWrite(MUX_SEL_B, 1);
  digitalWrite(MUX_SEL_C, 1);

  //turn on LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, 1);

  Serial.begin(9600);
}


void loop(void)
{
  getButtons();
  getKeys();
  delay(5);   // wait 10 ms
  
}

// this funcion reads the buttons and stores their states in the global 'buttons' variable
void getButtons(void){
  int i;
  keyTable = 0;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_2) << i;  
  }
  keyTable <<= 8;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_1) << i;
  }
  keyTable <<= 8;
  for (i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    keyTable |= digitalRead(MUX_OUT_0) << i;   
  }
  keyTable |= 0x1000000; // masks 25th key
}

void getKeys() {
  if (oldkeyTable != keyTable) {
    for (int i = 0; i < 25; i++) {            // read through the button array and if pressed assigns the number to the Key[] (lowest first... need to change that)
      if ((!((keyTable >> i) & 1)) && !isActive[i]) {
        Serial.println(i);
        isActive[i] = true;

      }
      if ((((keyTable >> i) & 1)) && isActive[i]) {
        isActive[i] = false;
      }
    }
  }
  oldkeyTable = keyTable;
}
by GonzoG » Fri Apr 02, 2021 4:35 pm
Multiplexers need some time after changing I/O address and STMs are much faster then Arduino UNO.
Try with 1ms delay before reading data.

Also, your code is highly inefficient. You can read all 3 multiplexers at once:

Code: Select all

void getButtons(void){
  keyTable = 0;
  for (uint8_t i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    delay(1);   //1ms for test, if it works, you can try going lower
    keyTable |= digitalRead(MUX_OUT_2) << i+16;  
    keyTable |= digitalRead(MUX_OUT_1) << i+8;
    keyTable |= digitalRead(MUX_OUT_0) << i;   
  }
  keyTable |= 0x1000000; // masks 25th key
}
Go to full post
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Mux errors...

Post by stevestrong »

We just need to know which core do you use.
see this for your reference: viewtopic.php?f=2&t=301
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Re: Mux errors...

Post by neo_nmik »

stevestrong wrote: Fri Apr 02, 2021 3:50 pm We just need to know which core do you use.
see this for your reference: viewtopic.php?f=2&t=301
Sorry, of course.

Using the official STM32duino core, stm32duino (STM Cores 1.9.0).
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: Mux errors...

Post by GonzoG »

Multiplexers need some time after changing I/O address and STMs are much faster then Arduino UNO.
Try with 1ms delay before reading data.

Also, your code is highly inefficient. You can read all 3 multiplexers at once:

Code: Select all

void getButtons(void){
  keyTable = 0;
  for (uint8_t i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    delay(1);   //1ms for test, if it works, you can try going lower
    keyTable |= digitalRead(MUX_OUT_2) << i+16;  
    keyTable |= digitalRead(MUX_OUT_1) << i+8;
    keyTable |= digitalRead(MUX_OUT_0) << i;   
  }
  keyTable |= 0x1000000; // masks 25th key
}
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Re: Mux errors...

Post by neo_nmik »

GonzoG wrote: Fri Apr 02, 2021 4:35 pm Multiplexers need some time after changing I/O address and STMs are much faster then Arduino UNO.
Try with 1ms delay before reading data.

Also, your code is highly inefficient. You can read all 3 multiplexers at once:

Code: Select all

void getButtons(void){
  keyTable = 0;
  for (uint8_t i = 0; i < 8; i++){
    digitalWrite(MUX_SEL_A, i & 1);
    digitalWrite(MUX_SEL_B, (i >> 1) & 1);
    digitalWrite(MUX_SEL_C, (i >> 2) & 1);
    delay(1);   //1ms for test, if it works, you can try going lower
    keyTable |= digitalRead(MUX_OUT_2) << i+16;  
    keyTable |= digitalRead(MUX_OUT_1) << i+8;
    keyTable |= digitalRead(MUX_OUT_0) << i;   
  }
  keyTable |= 0x1000000; // masks 25th key
}
Thank you GonzoG! I did think that it might be an issue with speed... But I could've sworn I had it working before I built my little Bluepill-Uno strip board (I was also having loads of issues with bad breadboard cables :roll: )

And I've been trying to figure out how to improve the code for ages, but I couldn't get my head around the bitwise operations, so thank you a tonne for that! :D

Cheers for the help!

EDIT: Any recommendations for slowing this down without Delay? I'm using an interrupt routine for the DAC/SPI transfer, so maybe I could use that to send a tick.
fredbox
Posts: 125
Joined: Thu Dec 19, 2019 3:05 am
Answers: 2

Re: Mux errors...

Post by fredbox »

Any recommendations for slowing this down without Delay? I'm using an interrupt routine for the DAC/SPI transfer, so maybe I could use that to send a tick.
Put your scan code inside systick:

Code: Select all

void HAL_SYSTICK_Callback(void) 
{
 // hal systick gets called automatically every millisecond
	get_buttons() 
}
You may have to slow it down even more as a 4051 mux at 3 volts is going to be fairly slow.
Try setting the mux pins on one systick and reading it on the next. This gives you a millisecond for the pins to settle. When scanning keystrokes, you probably don't need any faster than about 20 scans per second (50 milliseconds per scan). Variables accessed by systick need to be declared volatile..

Example blinky using systick instead of delay:

Code: Select all

// blink the led once per second using 1 millisec systick interrupt
void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
}
void loop() 
{
}
void HAL_SYSTICK_Callback(void) 
{
  static volatile uint32_t ledCount = 0;
  if (++ledCount == 500) 
  {
    ledCount = 0;
    digitalToggleFast(digitalPinToPinName(LED_BUILTIN));
  }
}
mlundin
Posts: 94
Joined: Wed Nov 04, 2020 1:20 pm
Answers: 6
Location: Sweden

Re: Mux errors...

Post by mlundin »

Use the loop, set a MUX_SEL combination and record current millis(), then on next loop check if a predetrmined number of millis has elapsed,
if so read the MUX_OUTS and set next MUX_SEL and current millis(),
if not just keep looping

no delay, just keep looping until the intended delay has elapsed, for fine grained timing use micros() instead of millis().

Placing the code in systick is a nice technique but must be used sparingly, I would reserve that for code that MUST be run at exact intervals, scanning keycodes probably doesn't qualify for such preferential treatment.
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: Mux errors...

Post by GonzoG »

neo_nmik wrote: Fri Apr 02, 2021 5:00 pm ...
Thank you GonzoG! I did think that it might be an issue with speed... But I could've sworn I had it working before I built my little Bluepill-Uno strip board (I was also having loads of issues with bad breadboard cables :roll: )
I had same problem with Uno/Nano using port manipulation (instead of digitalWrite/Read) and 74HC4067.
You can speed up read/write operations using digitalWriteFast(digitalPinToPinName(pin),bit); and digitalReadFast(digitalPinToPinName(pin));
neo_nmik wrote: Fri Apr 02, 2021 5:00 pm
EDIT: Any recommendations for slowing this down without Delay? I'm using an interrupt routine for the DAC/SPI transfer, so maybe I could use that to send a tick.
According to HC4051 data sheet you should be good with 200us delay.
Without using delays it will be bit more complicated as you won't be able to use for loop.


Depending on how fast (often) you want to read keys you can use systick (1ms) or init a timer that triggers interrupt eg. every 200us. Then read data, change multiplexer address.
Or it your program loop is longer then 200-300us you can just read 1 address every loop, just change address after reading data, not before.

EDIT:
There's also a different hardware approach - use shift registers instead of multiplexers as they are much faster.
I've played with 74HC165 and reading 4 registers in series takes about 5-6us (on STM32F411). They also need only 3-4 pins on MCU.
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Re: Mux errors...

Post by neo_nmik »

There's also a different hardware approach - use shift registers instead of multiplexers as they are much faster.
I've played with 74HC165 and reading 4 registers in series takes about 5-6us (on STM32F411). They also need only 3-4 pins on MCU.
I was toying with this SR of idea, as I’m already planning on using them on a project with a Raspberry Pi, but, as far as I can tell, the small amount of speed gain/cost saving on the chip itself will cost me in all the extra pull-up resistors (that I currently don’t need with the mux) and the time spent soldering them.

Unless I’m actually missing something, and I don’t need pull ups on every button... (could I do as I’ve done here and just have a pull up on the output pins, maybe have one on the serial out pin? :? )

Cheers,

Nick
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: Mux errors...

Post by GonzoG »

You need pull-up/down resistors on every button with shift registers.
Post Reply

Return to “General discussion”