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.
Related
I'm adding a clock to my program, but for some reason I cannot lower the value of my "sec" variable.
I have a millisecond timer that raises a flag every ms. So, every 1000 ms I clear the ms variable; this happens without a problem.
Now every 10 (for debug purposes) seconds I want to clear the sec variable and everything stops.
I'm using a pic18f67k22.
I should just be able to assign 0 to it, and it is VERY frustrating. Every value below 10 crashes; values above 10 resume like nothing happened.
minimal reproducible example, I left out lcd code as it was just for me to see if it still happened with minimal code, left out delay because it's just a bunch of "nop"'s, fuses and pin.h do not appear relevant either, fuses might be but your pins would be different):
main.c:
#include "fuses.h"
#include <stdbool.h>
#include <stdio.h>
#include <xc.h>
#include "clock.h"
#include "interrupt.h"
#include "LCDSerialTerminalMaster.h"
uint8_t prevSec = 0;
void main(void)
{
// disable comparators
CM1CONbits.CON = 0;
CM2CONbits.CON = 0;
CM3CONbits.CON = 0;
// disable analog input
ADCON0bits.ADON = false;
ANCON0 = 0;
ANCON1 = 0;
ANCON2 = 0;
INT_Init();
CLK_Init();
LCD_Init();
while(1)
{
if(CLK_GetSec() != prevSec)
{
prevSec = CLK_GetSec();
LCD_Write();
}
}
}
interrupt.c:
#include <stdbool.h>
#include "interrupt.h"
#include "clock.h"
#include "pin.h"
void INT_Init(void)
{
// Enable Interrupt Priority Vectors
IPEN = 1;
// RCI - high priority
UART1_RC_IP = 1;
UART2_RC_IP = 1;
UART1_TX_IP = 0;
UART2_TX_IP = 0;
MASTER_SYNC_IP = 1;
INT_Toggle(true);
}
void INT_Toggle(bool toggle)
{
INT_ToggleHigh(toggle);
INT_ToggleLow(toggle);
}
void INT_ToggleHigh(bool toggle)
{
INTCONbits.GIEH = toggle;
}
void INT_ToggleLow(bool toggle)
{
INTCONbits.GIEL = toggle;
}
void __interrupt() INT_InterruptManagerHigh(void)
{
if(PIR4bits.CCP4IF)
CLK_ISR();
}
clock.c
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <xc.h>
static uint8_t hour = 0;
static uint8_t min = 0;
static uint8_t sec = 0;
static volatile uint16_t ms = 0;
void CLK_Init(void)
{
T3CONbits.TMR3CS = 0b00; // Source = Fosc/4
T3CONbits.SOSCEN = 0b1; // SOSC disabled
T3CONbits.T3CKPS = 0b00; // Prescaler 1:1
CCP4CONbits.CCP4M = 0b1011; // Compare Mode: Special Event Trigger, reset TMR3 on CCP4 match
CCPTMRS1bits.C4TSEL = 0b01; // TMR3/TMR4
// set compare value to 5000
CCPR4L = 136;
CCPR4H = 19;
// Disable TMR3 interrupt.
PIE2bits.TMR3IE = 0;
// clear CCP4 interrupt.
PIR4bits.CCP4IF = 0;
// enable CCP4 interrupt.
PIE4bits.CCP4IE = 1;
IPR4bits.CCP4IP = 1;
// Start the Timer by writing to TMRxON bit.
T3CONbits.TMR3ON = 1;
}
void CLK_GetTime(char *buffer)
{
sprintf(buffer, "%02hhu:%02hhu:%02hhu", hour, min, sec);
}
uint8_t CLK_GetSec(void)
{
return sec;
}
void CLK_ISR(void)
{
PIR4bits.CCP4IF = 0;
ms++;
if(ms == 1000)
{
ms = 0;
sec++;
if(sec == 10)
{
sec = 0;
min++;
if(min == 60)
{
min = 0;
hour++;
if(hour == 24)
hour = 0;
}
}
}
}
the error happens in CLK_ISR, I can't assign a value lower than the value i'm comparing to, if I try sec-- the same thing happens.
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++;
}
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.
I started C and I am trying to write a tic tac toe with an opponent which searches a random number between 1 to 9 and then fill the slot with “O”. However, when the random number detects an occupied slot, it will continue to fill other empty slots without giving the player the turn. How do I resolve this?
I made two arrays, one for the game screen, one for the memory of the slots.
I’m sorry I couldn’t chuck out the code since I thought it’s all important and is there any better way to do this? Sorry for my writing style if it is a bit confusing, and I think there are unimportant variables.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char values[3][4] = { // screen board
{46, 46, 46,'\n'},
{46, 46, 46,'\n'},
{46, 46, 46,'\n'}
};
double memory[3][4] = { // memory board to put values
{0, 0, 0,'\n'},
{0, 0, 0,'\n'},
{0, 0, 0,'\n'}
};
int player(int i, char values[3][4]) {
int input;
printf("enter position: ");
scanf("%i", &input);
int x = (((input) / 3.3) - 3) * -1; // important math to convert to num
int y = (input + 2) % 3; // important math to convert to num
if (memory[x][y] == 0) {
values[x][y] = 'X';
memory[x][y] = 1;
printf("%s", values);
getchar();
return 0;
getchar();
} else {
printf("Wrong!, choose another line\n");
printf("%s", values);
getchar();
player(i, values);
}
}
int opponent(char values[3][4]) { //function opponent
int count = 0;
srand(time(NULL));
int random = (rand() % 9) + 1; // create random number
for (count = 0; count < 9; count++) {
int x = (((random) / 3.3) - 3) * -1;
int y = (random + 2) % 3;
if (memory[x][y] == 0) { // if memory is empty, do the following, loop stucks here
values[x][y] = 'O';
memory[x][y] = 2;
printf("Opponent Move\n");
printf("%s", values);
count++;
return 0;
} else { // if memory is not 0, do this. Error starts here
getchar();
printf("Move is %i", random);
opponent(values); // it calls itself to do a loop,
}
}
}
int main() {
int input;
int i = 2;;
for (i = 2; i < 9; i++) {
player(i, values); //Player goes first
getchar();
opponent(values);
}
}
Instead of having two independent values and memory, use an enum array to represent the game map.
enum Square { VACANT, X, O } squares[3][3], move = X;
It is initialised according to The initialization of static variables in C.
You probably need a function to decide if a player has won,
/* Checks if the player who's move it was won. */
static int is_win(void) {
return (squares[0][0]==move && squares[0][1]==move && squares[0][2]==move)||
(squares[1][0]==move && squares[1][1]==move && squares[1][2]==move)||
(squares[2][0]==move && squares[2][1]==move && squares[2][2]==move);
/* Etc. */
}
As well as a function for printing the board,
static const char letters[] = { '/', 'X', 'O' };
static void print_board(void) {
printf("%c %c %c\n%c %c %c\n%c %c %c\n",
letters[squares[0][0]], letters[squares[0][1]], letters[squares[0][2]],
letters[squares[1][0]], letters[squares[1][1]], letters[squares[1][2]],
letters[squares[2][0]], letters[squares[2][1]], letters[squares[2][2]]);
}
The code as you have it shadows the global state with parameters to the functions. This is very confusing. Think about whether you need the parameter to do the function's job. When one has complicated states that are defined in multiple files, it's probably best to have a agglomeration game object, but for simple games I think it's fine to have a global state.
Instead of playing until 7 moves, use a simple state machine to keep track of the game state. One can typedef the functions (How do function pointers in C work?) player and opponent and put them in a static array to simplify greatly the game loop. Consider,
/* Move returns whether we should continue. */
typedef int (*Move)(void);
/* Implements Move. */
static int player(void) {
printf("player:\n");
/* FIXME: player move. */
return is_win() ? 0 : (move = O, 1);
}
/* Implements Move. */
static int opponent(void) {
printf("opp:\n");
/* FIXME: Chose randomly from all of it's allowed moves? */
return is_win() ? 0 : (move = X, 1);
}
static const Move states[] = { 0, &player, &opponent };
Then your main is just,
int main(void) {
while(states[move]()) print_board();
printf("%c wins.\n", letters[move]);
return 0;
}
Edit: Definitely have a state where it's a tie, perhaps when there are no moves left.
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