I am getting back into programming with Arduino and built a POV fidget spinner inspired by an instructable by MakersBox. I am using the SparkFun AVR Pocket Programmer and an ATTiny84 (8MHz internal clock) with ATTinyCore as the board manager.
I was able to upload the code without error a few days ago using the 'USBTinyISP (ATTinyCore) FAST, for parts running >= 2MHz' programmer selection but it stopped working. Now the below error shows:
avrdude: Version 6.3-20201216
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch
System wide configuration file is "C:\Users\Franklin Marquette\AppData\Local\Arduino15\packages\ATTinyCore\hardware\avr\1.5.2/avrdude.conf"
Using Port : usb
Using Programmer : usbtiny
Setting bit clk period : 0.3
avrdude: usbdev_open(): Found USBtinyISP, bus:device: bus-0:\\.\libusb0-0001--0x1781-0x0c9f
AVR Part : ATtiny84
Chip Erase delay : 15000 us
PAGEL : P00
BS2 : P00
RESET disposition : possible i/o
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :
Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 65 6 4 0 no 512 4 0 4000 4500 0xff 0xff
flash 65 6 32 0 yes 8192 64 128 4500 4500 0xff 0xff
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00
lock 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
lfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
efuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
Programmer Type : USBtiny
Description : USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/
avrdude: programmer operation not supported
avrdude: Setting SCK period to 1 usec
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
avrdude done. Thank you.
Failed programming: uploading error: exit status 1
NOW if I upload using the "USBtinyISP (ATtinyCore) SLOW, for new or 1 MHz parts" programmer option, the code does upload. It is 8x slower though. I uploaded the Blink sketch to a separate ATTiny84 on a breadboard and the connected LED stayed on for ~7.75s when it was programmed to stay on for 1s.
I have no idea what is going on and would really appreciate any help that I can get. Thank you!
For reference, the code is below.
/*
Not all Attiny cores support the tone() function. Try this one
https://github.com/SpenceKonde/ATTinyCore
// ATMEL ATTINY84 / ARDUINO
//
// +-\/-+
// VCC 1| |14 GND
// (D 0) PB0 2| |13 AREF (D 10)
// (D 1) PB1 3| |12 PA1 (D 9)
// PB3 4| |11 PA2 (D 8)
// PWM INT0 (D 2) PB2 5| |10 PA3 (D 7)
// PWM (D 3) PA7 6| |9 PA4 (D 6)
// PWM (D 4) PA6 7| |8 PA5 (D 5) PWM
*/
#include <EEPROMex.h>
#include <avr/pgmspace.h>
#include "font.h"
#include "textAndShapes.h"
const int charHeight = 8;
const int charWidth = 5;
int rows= 8; // Total LED's in a row
int LEDS[] = {0, 1, 2, 3, 4, 5, 7, 6};
bool STATES[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
char charArray[6]; // holds characters to display
unsigned long lastTimeUs; // time (us) magnet sensed
unsigned long revTimeUs; // time (us) of last full rotation
unsigned long dwellTimeUs; // time (us) between LED changes (based on rotation speed)
volatile unsigned long revolutions = 0; // track number of revolutions since last start
long totalRevolutions; // track total number of revolutions (stored in EEPROM)
bool spinning = true; // track reset of time & counts
unsigned long startTimeUs; // time (us) current spinning started
int mode; // current operating mode, stored in EEPROM
int modes = 8; // number of modes available
// 0 -> text "Hello World!"
// 1 -> RPM
// 2 -> time in seconds
// 3 -> spin count
// 4 -> spin count (total)
// 5 -> "lilly pad" pattern
// 6 -> shape 1 (heart)
// 7 -> shape 2 (smile)
byte ledPin = 0;
byte magPin = 0; // Hall effect sensor, pulled-up, goes low when magnet passes
byte magPullUp = 10; // Pin A0 / D10
byte touchPin = 1; // Push button A1 / D9
byte touchPullUp = 9;
volatile boolean rotationFlag = false; // modified by ISR
void setup(){
// setup inputs
pinMode(touchPin, INPUT);
digitalWrite(touchPullUp, HIGH);
pinMode(magPin, INPUT);
digitalWrite(magPullUp, HIGH);
// setup other LEDs
for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
pinMode(LEDS[LED], OUTPUT);
digitalWrite(LEDS[LED], STATES[LED]);
}
// Interupt setting
GIMSK = bit (PCIE0); // Pin change interrupt enable
PCMSK0 = bit (PCINT0); // Enable interupt on PCINT0 (D10)
sei(); // enables interrupts
// get saved mode from eeprom
mode = EEPROM.read(0);
if (mode >= modes) {
mode = 0; // may be very large first time
}
// get saved revolution total from eeprom
totalRevolutions = EEPROMReadlong(1);
if (totalRevolutions < 0 || totalRevolutions > 999999UL){ // will be -1 first time.
totalRevolutions = 0;
EEPROMWritelong(1, 0);
}
// show we are alive and what mode is active
blipLEDs();
lastTimeUs = micros();
}
void loop(){
int sigFigs = 6; // number of significant figures to deplay
unsigned long curTimeUs;
int seconds;
unsigned int rpm;
checkButton();
if (((micros() - lastTimeUs) > 1000000UL)){ // less than 1 rev / sec
if (spinning){
spinning = false;
totalRevolutions = totalRevolutions + revolutions;
EEPROMWritelong(1, totalRevolutions);
revolutions = 0;
clearArray();
blipLEDs();
}
else {
//digitalWrite(LEDS[mode], LOW);
}
}
if (mode == 5){ // lilly pad pattern, show regardles of magnet.
for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
digitalWrite(LEDS[LED], HIGH);
delay(1);
digitalWrite(LEDS[LED], LOW);
}
for(int LED=(sizeof(LEDS)/sizeof(int))-1; LED >= 0; LED--){
digitalWrite(LEDS[LED], HIGH);
delay(1);
digitalWrite(LEDS[LED], LOW);
}
}
else if (rotationFlag){ // we are spinning!
rotationFlag = false;
if (!spinning){
spinning = true;
startTimeUs = micros();
}
curTimeUs = micros();
revTimeUs = curTimeUs - lastTimeUs;
dwellTimeUs = revTimeUs * 3UL / 360UL; // 3 degrees
seconds = (curTimeUs - startTimeUs) / 1000000UL;
rpm = 60000 * 1000 / revTimeUs;
lastTimeUs = curTimeUs;
clearArray();
if (mode == 0){
strcpy (charArray, text);
//sprintf(charArray, "%lu", dwellTimeUs);
}
else if (mode == 1){
sprintf(charArray, "%d", rpm);
sigFigs = 2;
}
else if (mode == 2){
sprintf(charArray, "%d", seconds);
}
else if (mode == 3){
sprintf(charArray, "%lu", revolutions);
}
else if (mode == 4){
sprintf(charArray, "%lu", totalRevolutions + revolutions);
}
else if (mode == 6){ // shape 1 (heart)
for(int k=0; k< sizeof(shape_1);k++){
if (rotationFlag){
break;
}
char b = pgm_read_byte_near(&(shape_1[k]));
for (int j=0; j<charHeight; j++) {
digitalWrite(LEDS[j], bitRead(b, 7-j));
}
dwellTimeUs = revTimeUs * 4UL / 360UL; // 5 degrees
delayMicroseconds(dwellTimeUs);
}
}
else if (mode == 7){ // shape 2 (smile)
for(int k=0; k< sizeof(shape_2);k++){
if (rotationFlag){
break;
}
char b = pgm_read_byte_near(&(shape_2[k]));
for (int j=0; j<charHeight; j++) {
digitalWrite(LEDS[j], bitRead(b, 7-j));
}
dwellTimeUs = revTimeUs * 4UL / 360UL; // 5 degrees
delayMicroseconds(dwellTimeUs);
}
}
// Text in top half
if (mode < 5) {
int digits = 0;
for(int k=0; k< sizeof(charArray);k++){
char c = charArray[k];
if (rotationFlag){
break;
}
if(c){
if (digits < sigFigs){
printLetter(c);
//digits += 1;
}
else{
printLetter('0');
}
digits += 1;
}
}
// Handle display in lower section
clearArray();
if(1 && (revTimeUs < 200000)){
char * ptr = (char *) pgm_read_word (&string_table[mode]);
//char buffer [6]; // must be large enough!
strcpy_P (charArray, ptr);
// wait for it . . .
while((micros() < (lastTimeUs + revTimeUs / 2)) && !rotationFlag){};
// show it
for (int k=sizeof(charArray)-1; k>=0; k--){
if (rotationFlag){
break;
}
printLetterLower(charArray[k]);;
}
}
}
}
}
ISR(PCINT0_vect){ // Magnet sensed
if (!digitalRead(magPullUp)){
rotationFlag = true; // Increment volatile variables
revolutions += 1;
}
}
void dwellDelay(){ // avoid glitch on first rotation having erronious value
if (dwellTimeUs > 2000){
dwellTimeUs = 2000;
}
if (dwellTimeUs < 100){
dwellTimeUs = 100;
}
delayMicroseconds(dwellTimeUs);
}
void printLetter(char ch){
// https://github.com/reger-men/Arduion-POV-clock/blob/master/clock.ino
// make sure the character is within the alphabet bounds (defined by the font.h file)
// if it's not, make it a blank character
if (ch < 32 || ch > 126){
ch = 32;
}
// subtract the space character (converts the ASCII number to the font index number)
ch -= 32;
// step through each byte of the character array
for (int i=0; i<charWidth; i++) {
char b = pgm_read_byte_near(&(font[ch][i]));
for (int j=0; j<charHeight; j++) {
digitalWrite(LEDS[j], bitRead(b, 7-j));
}
dwellDelay();
}
//clear the LEDs
for (int i = 0; i < rows; i++)
digitalWrite(LEDS[i] , LOW);
dwellDelay();
}
void printLetterLower(char ch){
// make sure the character is within the alphabet bounds (defined by the font.h file)
// if it's not, make it a blank character
if (ch < 32 || ch > 126){
ch = 32;
}
// subtract the space character (converts the ASCII number to the font index number)
ch -= 32;
// step through each byte of the character array
for (int i=charWidth-1; i>-1; i--) {
char b = pgm_read_byte_near(&(font[ch][i]));
for (int j=0; j<charHeight; j++) {
digitalWrite(LEDS[j+1], bitRead(b,j));
}
dwellDelay();
}
//clear the LEDs
for (int i = 0; i < rows; i++)
digitalWrite(LEDS[i] , LOW);
// space between letters
dwellDelay();
}
bool touched(){
// returns true if touched, false if not. Light LED until touch released
bool touchVal = digitalRead(touchPullUp);
if (!touchVal){
while(!digitalRead(touchPullUp)){ // wait till touch release
delay(10);
digitalWrite(LEDS[mode], LOW);
}
//digitalWrite(LEDS[0], LOW);
return (true);
}
else{
return (false);
}
}
void checkButton(){
// check button for mode change and display current mode
if (touched()){
mode += 1;
if (mode >= modes){
mode = 0;
}
EEPROM.write(0, mode);
blipLEDs();
}
}
void blipLEDs(){
// something to show we are alive
for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
digitalWrite(LEDS[LED], HIGH);
delay(10);
digitalWrite(LEDS[LED], LOW);
}
for(int LED=sizeof(LEDS)/sizeof(int); LED>mode; LED--){
digitalWrite(LEDS[LED], HIGH);
delay(10);
digitalWrite(LEDS[LED], LOW);
}
digitalWrite(LEDS[mode], HIGH);
}
void EEPROMWritelong(int address, long value) {
//This function will write a 4 byte (32bit) long to the eeprom at
//the specified address to address + 3.
//https://playground.arduino.cc/Code/EEPROMReadWriteLong
//Decomposition from a long to 4 bytes by using bitshift.
//One = Most significant -> Four = Least significant byte
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
//Write the 4 bytes into the eeprom memory.
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
long EEPROMReadlong(long address){
//Read the 4 bytes from the eeprom memory.
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
//Return the recomposed long by using bitshift.
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
void clearArray(){
for(int i=0; i<sizeof(charArray); i++){ // clear array
charArray[i] = 0;
}
}