interrupt based LEDs up counter on an atmega32 - c

l am designing an interrupt based number counter which shows the values as they increment on 8 LEDs using an atmega32. My problem is my ISR(interrupt service routine)is not able to light up the LEDs as l increment from INT0below is the code l made, only the ISR is not lighting the LEDs
enter image description here

In SR, the count variable is on the stack. Its value does not persist across invocations.
Here is your original code [with annotations]:
SR(INT0_vect)
{
DDRA = 0xFF;
// NOTE/BUG: this is on the stack and is reinitialized to zero on each
// invocation
unsigned char count = 0;
// NOTE/BUG: this will _always_ output zero
PORTA = count;
// NOTE/BUG: this has no effect because count does _not_ persist upon return
count++;
return;
}
You need to add static to get the value to persist:
void
SR(INT0_vect)
{
DDRA = 0xFF;
static unsigned char count = 0;
PORTA = count;
count++;
}

Related

member reference base type 'volatile unsigned char' is not a structure or union

I was trying to program a PIC 16f877A Micro-controller to rotate a servomotor, 0 to 180 degrees, but every time i try to build the program i get an error "member reference base type 'volatile unsigned char' is not a structure or union".I am using MPLAB with xc8 compiler. I couldn't find the issue.
#include<xc.h>
void Rotation0()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(800);
PORTB.F0 = 0;
__delay_us(19200);
}
}
void Rotation90()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(1500);
PORTB.F0 = 0;
__delay_us(18500);
}
}
void Rotation180()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(2200);
PORTB.F0 = 0x00;
__delay_us(17800);
}
}
void main()
{
TRISB = 0;
PORTB = 0x00;
do
{
Rotation0();
__delay_ms(2000);
Rotation90();
__delay_ms(2000);
Rotation180();
}while(1);
}
As #user3386109 pointed out in the comments, PORTB isn't a structure.
You can use PORTBbits.RB0 = 1; or just RB0 = 1; as it's also defined. I prefer the first one because it's more verbose.
The same naming convention is used for all registers which have special named bits. Register and bit names match with the ones in the datasheet. Sometimes the same bit name may be used in multiple registers. In this case, you need the verbose method.
PORTB refers the 8-bit register itself and can be used for direct 8-bit assignments, like PORTB = 0x00;

Is this the right way to interact with the button/joystick?

I am currently trying to create memory game with leds, but it becomes humiliating struggle. It would be more logical to start with idea. The idea is that with the help of two diodes some sequence is played and you have to repeat it with the joystick. My program should look something like this...
void main() {
generateNewGame();
blinkLeds();
for (uint8_t index = 0; i < Game_Length; index++) {
if(waitForPress() != GameArray[index])
blinkFail();
return;
}
}
blinkSuccess();
}
This two functions are working fine.
generateNewGame();
blinkLeds();
I have a problem with this function
waitForPress()
My "GameArray" is filled with ones and zeros.
I want to make it so that if I turn the joystick let's say to the left, then the function "waitForPress()" will return 1 and in the main i want to compare this 1 with my "GameArray", and if this is true, check the next element. Comparison with the "Game" array does not work for me. In addition, for some reason,my left led is constantly on, and the joystick does not respond. I hope my words make sense, I am ready to supplement the question if you have any questions.
My code at the moment
#define F_CPU 2000000UL
#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
// Global
uint8_t Game[8];
int i;
const uint16_t n = 500;
void delay(uint16_t n) {
for(uint16_t i = 0; i < n ; i++) {
for(uint16_t j = 0; j < 200 ; j++) {
asm volatile ("NOP");
}
}
}
// Function for filling the game array with ones and zeros
void RandomNumber() {
srand((unsigned int)time(NULL));
for(unsigned int i = 0; i < sizeof(Game)/sizeof(Game[0]); i++) {
int v = rand() % 2;
Game[i] = v;
}
}
// Function for flashing the Game array sequence
void PlayDemo() {
int i;
for(i = 0; i <= 8; i++) {
if(Game[i] == 1) {
PORTA = 0x80;
delay(n);
PORTA = 0x00;
delay(n);
}
else if (Game[i] == 0) {
PORTA = 0x01;
delay(n);
PORTA = 0x00;
delay(n);
}
else {
PORTA = 0x00;
}
}
}
int waitForPress() {
uint8_t x = PINF;
// Until the button is off, do nothing
while(!(x & 0x20) && !(x & 0x08)) {
x = PINF;
}
// Check if we press to the left
if(x & 0x20) {
// Last LED ON
PORTA = 0x80;
// Wait ( Debouncing )
delay(n);
return 1;
}
// Check if we press to the right side
if(x & 0x08) {
// First LED ON
PORTA = 0x01;
// Wait ( Debouncing )
delay(n);
return 0;
}
return 0;
}
int main(void) {
MCUCR |= 0x80;
MCUCR |= 0x80;
DDRA = 0xFF;
PORTF = 0x20;
RandomNumber();
PlayDemo();
while(1)
{
// Check all elements of the "Game" array
for(uint8_t index = 0; index < 8; index++) {
// If the output of the function "waitForPress()" is not equal to "Game" array element
if(waitForPress() != Game[index]) {
// End the game
break;
} else if(waitForPress() == Game[index]) {
// Turn all led ON
PORTA = 0xFF;
// Wait
delay(n);
// Turn all led OFF
PORTA = 0x00;
// Wait
delay(n);
}
}
}
}
There might be multiple issues with your program:
(1) First of all: In the FOR-loop in which you are comparing the user input with your Game-Array, you are calling waitForPress() two times when the user entered the correct answer. This is not what you want, your code should look like this:
//...
if (waitForPress() != Game[index])
{
//...
break;
}
else
{
//... (correct answer)
}
//...
Or you could do:
//...
int userInput = waitForPress();
if (userInput != Game[index])
{
//...
break;
}
else
{
//... (correct answer)
}
//...
(2) Assuming the corrected code in (1), I think another main issue is the way you are processing the user input in general. Let me illustrate the problem:
Starting off, nothing is pressed. Your main program called waitForPress() and is "stuck" in the WHILE-loop, waiting for the user to push the joystick.
When the user finally pushes the joystick, the function returns 1 or 0.
In your main program, this return value is processed according to your IF-statements inside the FOR-loop (see (1)).
Shortly after this, waitForPress() is called again (in the case of a wrong input almost immediately; in the case of a correct input after your delays; I'm assuming this is meant to be something like a short "flash" of all LEDs lasting maybe a few hundred milliseconds).
Because the button is still pressed (humans are slow!), waitForPress() returns immediately. Now, your program thinks you made the same input again although you didn't even make a second input by your understanding.
To solve this, you want to detect a signal edge. The easiest way to do this is to make sure that the joystick is released before waiting until one button comes on. You may modify waitForPress() like:
int waitForPress() {
uint8_t x = PINF;
// Make sure that the user released the joystick
while((x & 0x20) || (x & 0x08)) {
x = PINF;
}
// Until one button comes on, do nothing
while(!(x & 0x20) && !(x & 0x08)) {
x = PINF;
}
//...
Additionally, you might want to add mechanisms for dealing with bouncing.
(3) You never exit the WHILE-loop in your main() function. Therefore, your program always remains in the state of expecting user input.
(4) In your function PlayDemo(), you are accessing a "non-existing" Game-array element (Game[8]) because the condition of your FOR-loop is i <= 8 and not i < 8.
Additionally, there might be hardware or hardware configuration problems. Such problems might be hard to find over StackOverflow because I don't have or know your exact setup. You could run test programs to check if your hardware is working fine.

ATMega random number generation

For some context, I’m working on what is basically “Simon” for an Arduino project. I’m using an ATMEGA2560 and editing the code with Atmel Studio 6.1. The project uses the random number generator to create the sequence for the button input, which is where I’m running into trouble.
My current method of adding some variance is to use a timer interrupt to seed the random number, by incrementing a number to be processed through an equation. However, the timer simply doesn’t work – and the number doesn’t increment. Am I doing anything wrong with initialization, or is something else at fault?
Code:
#define F_CPU 1000000UL // 1 MHz, for timer
#include <avr/io.h> // normal stuff
#include <avr/interrupt.h> // timer interrupt
#include <util/delay.h> // easy delay functions
#include <stdlib.h> // random function
// global var that timer uses
volatile uint8_t count;
// function prototypes
int generateSeq(); // generates random number
int getRandomNumber(); // also generates random number (?)
int main(void)
{
// variables
int sequence[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // actual sequence
int terms = 0; // terms in sequence
int gameState = 0;
int ifWrong = 0; // if sequence is wrong
// timer interrupt (WHAT AM I DOING WRONG)
TCCR0A |= (1<<CS02)|(1<<CS00);
TIMSK0 |= (1<<TOIE0);
TCNT0 = 0;
count = 1;
sei();
while(1)
{
// actual "game" part
while(gameState == 1)
{
// generate term in sequence
// 1 = up, 2 = right, 3 = down, 4 = left
if(ifWrong == 0)
{
sequence[terms] = generateSeq(); // call sequence function
terms++;
}
}
}
}
// random seed for sequence generator (something wrong here?)
ISR(TIMER0_OVF_vect)
{
count++;
if(count >= 255)
{
count = 1;
}
}
int generateSeq() // function to generate sequence
{
// equation is currently a placeholder
int r2 = (int)rand() * (int)count;
int num = r2 * count;
return (num % 4) + 1;
}
gameState is 0, and never changed, so none of the generateSeq() code is called.
You also have a serious problem with sequence[terms] in that you don't check if terms is between 0 and 7. You will run off the end of the array.

Accessing individual bytes in PROGMEM on Arduino/AVR

I've read up on accessing PROGMEM for days now, and combed through several other questions, but I still can't get my code working. Any help would be appreciated.
I've included a full test sketch for Arduino below. The majority of it works, but when I loop through each byte of an "alpha" character, as pointed to by "alphabytes", I'm just getting garbage out so I'm obviously not accessing the correct memory location. The problem is, I can't figure out how to access that memory location.
I've seen several other examples of this working, but none that have different sizes of data arrays in the pointer array.
Please see line beginning with ">>>> Question is..."
// Include PROGMEM library
#include <avr/pgmspace.h>
// Variable to hold an alphabet character row
char column_byte;
// Used to hold LED pixel value during display
char led_val;
// Used to hold the screen buffer for drawing the screen
char matrix_screen[64];
/*
Define Alphabet characters. This should allow for characters of varying byte lengths.
*/
const char alpha_A[] PROGMEM = {0x06, 0x38, 0x48, 0x38, 0x06};
const char alpha_B[] PROGMEM = {0x7E, 0x52, 0x52, 0x2C};
const char alpha_C[] PROGMEM = {0x3C, 0x42, 0x42, 0x24};
/*
The "alphabytes" contains an array of references (pointers) to each character array.
Read right-to-left, alphabytes is a 3-element constant array of pointers,
which points to constant characters
*/
const char* const alphabytes[3] PROGMEM = {
alpha_A, alpha_B, alpha_C
};
/*
This array is necessary to list the number of pixel-columns used by each character.
The "sizeof" function cannot be used on the inner dimension of alphabytes directly
because it will always return the value "2". The "size_t" data
type is used because is a type suitable for representing the amount of memory
a data object requires, expressed in units of 'char'.
*/
const char alphabytes_sizes[3] PROGMEM = {
sizeof(alpha_A), sizeof(alpha_B), sizeof(alpha_C)
};
/**
* Code Setup. This runs once at the start of operation. Mandatory Arduino function
**/
void setup(){
// Include serial for debugging
Serial.begin(9600);
}
/**
* Code Loop. This runs continually after setup. Mandatory Arduino function
**/
void loop(){
// Loop through all alphabet characters
for( int a = 0; a < 3; a++) {
// Reset screen
for (int r = 0; r < 64; r++) {
matrix_screen[r] = 0;
}
// This line works to read the length of the selected "alphabyte"
int num_char_bytes = pgm_read_byte(alphabytes_sizes + a);
for (int b = 0; b < num_char_bytes; b++){
// Based on alphabytes definition,
// Examples of desired value for column_byte would be:
//
// When a=0, b=0 -> column_byte = 0x06
// When a=0, b=1 -> column_byte = 0x38
// When a=0, b=2 -> column_byte = 0x48
// When a=0, b=3 -> column_byte = 0x38
// When a=0, b=4 -> column_byte = 0x06
// When a=1, b=0 -> column_byte = 0x7E
// When a=1, b=1 -> column_byte = 0x52
// When a=1, b=2 -> column_byte = 0x52
// When a=1, b=3 -> column_byte = 0x2C
// When a=2, b=0 -> column_byte = 0x3C
// When a=2, b=1 -> column_byte = 0x42
// When a=2, b=2 -> column_byte = 0x42
// When a=2, b=3 -> column_byte = 0x24
// >>>>> Question is... how to I get that? <<<<<<<
// column_byte = pgm_read_byte(&(alphabytes[a][b])); // This doesn't work
// Thought: calculate offset each time
// int offset = 0;
// for(int c = 0; c < a; c++){
// offset += pgm_read_byte(alphabytes_sizes + c);
// }
// column_byte = pgm_read_byte(&(alphabytes[offset])); // This doesn't work
// column_byte = (char*)pgm_read_word(&alphabytes[a][b]); // Doesn't compile
column_byte = pgm_read_word(&alphabytes[a][b]); // Doesn't work
// Read each bit of column byte and save to screen buffer
for (int j = 0; j < 8; j++) {
led_val = bitRead(column_byte, 7 - j);
matrix_screen[b * 8 + j] = led_val;
}
}
// Render buffer to screen
draw_screen();
// Delay between frames
delay(5000);
}
}
/**
* Draw the screen. This doesn't have the correct orientation, but
* that's fine for the purposes of this test.
**/
void draw_screen(){
for (int a = 0; a < 8; a++) {
for (int b = 0; b < 8; b++) {
Serial.print((int) matrix_screen[a * 8 + b]);
Serial.print(" ");
}
Serial.println();
}
Serial.println();
}
Note that alphabytes it is array, which each element contains a REFERENCE (i.e. address) where corresponding characters are stored. So, you should access it in two steps.
First step is to know address in the progmem of the required item. Addresses are 16bits wide (unless you are using 128+k device).
PGM_VOID_P ptr = (PGM_VOID_P) pgm_read_word(&alphabytes[a]);
and, if you want to access it byte by byte, you can just read using this pointer, and then increment it:
for (int b = 0; b < num_char_bytes; b++) {
uint8_t column_byte = pgm_read_byte(ptr++);
...
}
After much research (and quite frankly a lot of trial and error), I have come across a solution which is working. I don't know if this the the most correct or most elegant solution, but it works.
column_byte = pgm_read_byte(pgm_read_byte(&alphabytes[a]) + b);
The inner call to pgm_read_byte():
pgm_read_byte(&alphabytes[a])
returns a value, which is the address of the character being evaluated (notice the leading "address-of" operator "&").
The outer pgm_read_byte reads that memory at an offset of "b".
This solution can also be broken down into two parts:
int memory_loc = pgm_read_byte(&alphabytes[a]);
column_byte = pgm_read_byte(memory_loc + b);
My C skills are not good enough to really explain if "memory_loc" needs to be an int (I tried it as a "char" and it also worked).

Arduino fill array with values from analogRead

How to fill array with values from analogRead on arduino. every second arduino reads value from analog0 and i want to put these readings to array.
Let's say you want to read up to 100 values, do this:
1. Poor technique (uses blocking code with delay()):
//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];
unsigned int i = 0;
void setup()
{
}
void loop()
{
analogVals[i] = analogRead(A0);
i++;
if (i>=numReadings)
{
i=0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
}
delay(1000); //wait 1 sec
}
Note: you cannot make the number in brackets too large. Ex: analogVals[2000] will not work because it takes up too much RAM.
PS. This is pretty basic stuff that Arduino covers in their help on the website. Please reference here from now on for questions like this, and give it a shot yourself first: http://arduino.cc/en/Reference/HomePage --> click on "array" under "Data Types."
2. Alternate method (also a poor technique, since it uses blocking code with delay()):
//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];
void setup()
{
}
void loop()
{
//take numReadings # of readings and store into array
for (unsigned int i=0; i<numReadings; i++)
{
analogVals[i] = analogRead(A0);
delay(1000); //wait 1 sec
}
}
UPDATE: 6 Oct. 2018
3. Best technique (non-blocking method--no delay!):
//let's say you want to read up to 100 values
const unsigned int numReadings = 100;
unsigned int analogVals[numReadings];
unsigned int i = 0;
void setup()
{
Serial.begin(115200);
}
void loop()
{
static uint32_t tStart = millis(); // ms; start time
const uint32_t DESIRED_PERIOD = 1000; // ms
uint32_t tNow = millis(); // ms; time now
if (tNow - tStart >= DESIRED_PERIOD)
{
tStart += DESIRED_PERIOD; // update start time to ensure consistent and near-exact period
Serial.println("taking sample");
analogVals[i] = analogRead(A0);
i++;
if (i>=numReadings)
{
i = 0; //reset to beginning of array, so you don't try to save readings outside of the bounds of the array
}
}
}
4. Professional-type approach (non-blocking method, avoids global variables by passing around pointers instead, uses C stdint types, and uses static variables to store local, persistent data):
// Function prototypes
// - specify default values here
bool takeAnalogReadings(uint16_t* p_numReadings = nullptr, uint16_t** p_analogVals = nullptr);
void setup()
{
Serial.begin(115200);
Serial.println("\nBegin\n");
}
void loop()
{
// This is one way to just take readings
// takeAnalogReadings();
// This is a way to both take readings *and* read out the values when the buffer is full
uint16_t numReadings;
uint16_t* analogVals;
bool readingsDone = takeAnalogReadings(&numReadings, &analogVals);
if (readingsDone)
{
// Let's print them all out!
Serial.print("numReadings = "); Serial.println(numReadings);
Serial.print("analogVals = [");
for (uint16_t i=0; i<numReadings; i++)
{
if (i!=0)
{
Serial.print(", ");
}
Serial.print(analogVals[i]);
}
Serial.println("]");
}
}
// Function definitions:
//---------------------------------------------------------------------------------------------------------------------
// Take analog readings to fill up a buffer.
// Once the buffer is full, return true so that the caller can read out the data.
// Optionally pass in a pointer to get access to the internal buffer in order to read out the data from outside
// this function.
//---------------------------------------------------------------------------------------------------------------------
bool takeAnalogReadings(uint16_t* p_numReadings, uint16_t** p_analogVals)
{
static const uint16_t NUM_READINGS = 10;
static uint16_t i = 0; // index
static uint16_t analogVals[NUM_READINGS];
const uint32_t SAMPLE_PD = 1000; // ms; sample period (how often to take a new sample)
static uint32_t tStart = millis(); // ms; start time
bool bufferIsFull = false; // set to true each time NUM_READINGS have been taken
// Only take a reading once per SAMPLE_PD
uint32_t tNow = millis(); // ms; time now
if (tNow - tStart >= SAMPLE_PD)
{
Serial.print("taking sample num "); Serial.println(i + 1);
tStart += SAMPLE_PD; // reset start time to take next sample at exactly the correct pd
analogVals[i] = analogRead(A0);
i++;
if (i >= NUM_READINGS)
{
bufferIsFull = true;
i = 0; // reset to beginning of array, so you don't try to save readings outside of the bounds of the array
}
}
// Assign the user-passed-in pointers so that the user can retrieve the data if they so desire to do it this way
if (p_numReadings != nullptr)
{
*p_numReadings = NUM_READINGS;
}
if (p_analogVals != nullptr)
{
*p_analogVals = analogVals;
}
return bufferIsFull;
}
Sample Serial Monitor output from the last code just above:
Begin
taking sample num 1
taking sample num 2
taking sample num 3
taking sample num 4
taking sample num 5
taking sample num 6
taking sample num 7
taking sample num 8
taking sample num 9
taking sample num 10
numReadings = 10
analogVals = [1023, 1023, 1023, 1023, 1023, 687, 0, 0, 0, 0]
taking sample num 1
taking sample num 2
taking sample num 3
taking sample num 4
taking sample num 5
taking sample num 6
taking sample num 7
taking sample num 8
taking sample num 9
taking sample num 10
numReadings = 10
analogVals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
More reading/study:
My thorough answer on how to do time-stamp-based bare-metal cooperative multi-tasking: Best way to read from a sensor that doesn't have interrupt pin and requires some time before the measurement is ready
Arduino's very basic but extremely useful "Blink Without Delay" tutorial: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

Resources