I know this isn't exactly an Arduino question, but I'm using the Arduino IDE and feel it's close enough. So I'm working on a project that uses an ATTiny84, two SN74HC595N SIPO shift registers, and a SN74HC165 PISO shift register. Had a custom PCB printed and after a few bodge wires got everything working. It all works fine on an UNO R3, but when I moved to the ATTiny84, it quit working. Basically, I could update the '595 shift register once and it would be unresponsive after that.
I did some tinkering and narrowed the issue down to the data output from the '165 register. As wired, the 165's data line is connected to PORTB2 (physical pin 5) and when I declare it as an INPUT, the program stops working. The '595 shift registers work fine when declaring PB2 as an output or using a different pin altogether (PA5 for example).
For now, I'm planning on rewiring the '165 and doing some more testing. I'm hoping someone that knows much more than myself can help explain why this happens. Code is below.
Parts I'm using are all from digikey:
- 296-8251-5-ND ---- SN74HC165N
- 296-1600-5-ND ---- SN74HC595N
- ATTINY84A-PU-ND -- ATTINY84 MCU
#define SER PORTA0 // data
#define RCLK PORTA2 // latch
#define SRCLK PORTA1 // clock
// 165 shift register
#define CLK PORTB0 // clock - arduino 7 - ATTiny PORTB0
#define SHLD PORTB1 // shift load - arduino 6 - ATTiny PORTB1
#define Qh PORTA5 // data out - arduino 5 - ATTiny PORTB2 -- THIS IS THE CULPRIT PIN
#define CLKINH PORTA3 // clock inhibit - arduino 4 - ATTiny PORTA3
// status LED
#define STATUS PORTA7
#define numCols 11
//uint8_t read[numCols];
//uint8_t oldRead[numCols];
int currentCol = 1;
#include <timer.h>
timer tim;
void setup() {
tim.setInterval(1000);
// 595
pinMode(SER, OUTPUT);
pinMode(RCLK, OUTPUT);
pinMode(SRCLK, OUTPUT);
//165
pinMode(CLK, OUTPUT);
pinMode(SHLD, OUTPUT);
pinMode(Qh, INPUT);
pinMode(CLKINH, OUTPUT);
//status LED
pinMode(STATUS, OUTPUT);
// set initial state of 165
digitalWrite(CLKINH, HIGH);
digitalWrite(CLK, LOW);
digitalWrite(SHLD, HIGH);
write595(0xAA); // just to see something
}
void loop() {
for(int i=0; i<numCols; i++){
write595(currentCol);
currentCol = currentCol <<1;
//delay(250);
}
currentCol = 1;
}
void write595(uint16_t num){
uint8_t MSB = num >> 8;
uint8_t LSB = num;
digitalWrite(RCLK, LOW);
shiftOut(SER, SRCLK, MSBFIRST, MSB);
shiftOut(SER, SRCLK, MSBFIRST, LSB);
digitalWrite(RCLK, HIGH);
}
// uint8_t read165(void){
// uint8_t data = 0;
// // toggle shift/load to store data to the register
// digitalWrite(SHLD, LOW);
// delayMicroseconds(5);
// digitalWrite(SHLD, HIGH);
// delayMicroseconds(5);
// // shift that data out and return it to the program
// digitalWrite(CLK, HIGH);
// digitalWrite(CLKINH, LOW);
// data = shiftIn(Qh, CLK, MSBFIRST);
// digitalWrite(CLKINH, HIGH);
// return data;
// }
-
1add debugging code ... as it stands now, you have no way of knowing what the program is doingjsotola– jsotola2025年01月20日 04:10:31 +00:00Commented Jan 20 at 4:10
-
1Welcome to SE/Arduino! Please take the tour to learn how this site works, and read "How to Ask" and other pages of the help center. -- "Not working" does not tell us what happens instead, please edit your question to add that. While you're at it, also add a schematic of the non-working circuit, please.the busybee– the busybee2025年01月20日 06:51:41 +00:00Commented Jan 20 at 6:51
-
2A schematic would definitely help. What is "timer.h" ? Supply a link to it. Maybe it is using the "problem" pin or expecting an ISR to be defined. Depending on which Arduino core you are using for the ATtiny84 there are (possibly) different pin numbering schemes for the MCU. Also your loop is very fast. Are you sure you would see any visible effects?6v6gt– 6v6gt2025年01月20日 10:35:13 +00:00Commented Jan 20 at 10:35
2 Answers 2
PORTB0
, PORTA5
, etc. are not intended to be used with the Arduino pinMode
, digitalRead
, etc functions. These are not defined to Arduino pin numbers.
They are bit index numbers to be used with directly manipulating the PORTA
and PORTB
GPIO port registers.
E.g. if you wanted to set GPIO PB3 high, you could use PORTB3
in an expression like PORTB |= 1 << PORTB3;
Many of us don't use the named bit fields specifically for GPIO registers, since they don't make the code any more readable. Instead we'd just put 3
instead of PORTB3
. If anything these bit index macros for GPIO ports have caused more confusion that they've removed.
Anyway, because they're just bit index numbers, PORTB0
and PORTA0
are both 0, PORTB1
and PORTA1
are both 1, and that pattern repeats up to 7.
Rearranging your #define
s slightly it's maybe easier to see conflicts now:
// Conflicting pair; both trying to be Arduino pin 0 when later used with pinMode() etc
#define SER PORTA0 // data
#define CLK PORTB0 // clock - arduino 7 - ATTiny PORTB0
// Conflicting pair; both trying to use Arduino pin 1
#define SRCLK PORTA1 // clock
#define SHLD PORTB1 // shift load - arduino 6 - ATTiny PORTB1
// Conflicting pair when Qh is PORTB2.
#define RCLK PORTA2 // latch
#define Qh PORTA5 // data out - arduino 5 - ATTiny PORTB2 -- THIS IS THE CULPRIT PIN
// Not conflicting anything... yet.
#define CLKINH PORTA3 // clock inhibit - arduino 4 - ATTiny PORTA3
Sometimes people get away with using these PORT
x# macros when using something like an ATtiny85, because it only has one port, PORTB, and the mapping has been designed to use the PORTB bit numbers as the Arduino pin numbers. But that isn't going to work out so well with an ATtiny84 what has multiple ports.
You need to consult the documentation to whatever Arduino core you're using. E.g., for ATtiny84A with ATtinycore you'd follow this:
ATtinycore pin mapping diagram for the ATttiny84, ATtiny44, and ATtiny24
Follow the grayish-blue "Arduino Pin" numbers. There are to sets. You need to choose between them depending on whether you picked clockwise or counterclockwise in the board menus.
I ended up getting this to work. I needed to change the wiring of the 165 shift register and put its data line on PA5 (or pin 5).
Turns out that changing from "PORTxn" nomenclature didn't help (CW or CCW), however, I'm making that change as it definitely makes sense to do so. Thank you, Timemage!
To answer some other questions: I usually use Serial.print to debug, however, the ATTiny's don't have that. That's why I've included a status led on PA7. "timer.h" is a library I wrote that's bases on the blink without delay concept that has one method which returns true whenever the preset interval is up. It let's me write non-blocking code very easily.
Thanks again all!