Serial DC motor control using 8051 - c

We are developing the code for the DC motor controller using AT89S51 microcontroller. We use embedded C for code development, Keil microvision as IDE, Progisp for uploading .hex files to microcontroller and PUTTY for serial interfacing.
Here is the code:
#include <reg51.h>
#include <stdio.h>
unsigned int k;
sbit motor = P1^0;
void delay ( int q ) {
int w;
for ( w = 0; w < q; w++ );
}
void next_line()
{
SBUF = 10; // go to next line
while(TI == 0);
TI = 0;
SBUF = 13; // go to the beginning of the line
while(TI == 0);
TI = 0;
}
unsigned char receive() //Function to receive serial data
{
unsigned char value;
while ( RI == 0 );
value=SBUF;
P1=value;
RI=0;
return value;
}
void start()
{
SCON=0x50;
TMOD=0x20;
TH1=0xFD;
TR1=1;
TI=1;
}
int i;
main()
{
while (1)
{
unsigned char speed = 0;
start();
speed=receive();
switch (speed)
{
case '1':
k=90; // 90 msec out of 100 msec
next_line();
break;
case '0':
k=10; // 10 msec out of 100 msec
next_line();
break;
};
for(i=0; i<100; i++)
{
if (i<k) {
motor = 0;
}
else {
motor = 1;
}
}
}
}
We expect the motor to work on 90% power when the '1' button is pressed and to switch to 10% power when the '0' button is pressed. However, what we see is that some keyboard buttons (1,4,5,8) switch the motor ON and some buttons (2,3,6,7) switch the motor OFF. I cannot understand the nature of the improper working. Please, could you help us with this issue. Thanks in advance

I am going to hazard a guess as to the problem and risk being downvoted for it. The main loop is only writing to the motor bit for a very short period of time before committing to a read of the serial port, and that motor write leaves a 1 in the control port bit no matter what duty cycle was selected, so the motor should be left either running or stopped. But OP says pressing a key either stops or runs the motor. The receive() function waits for input and then writes it to the motor port (which is not bit-defined) before returning. It's tempting to think that is what is controlling the motor, but the LSB of OP's key presses is not consistent with the motor control. So what is it? The keys that start the motor all have bit 1 clear and the keys that stop the motor all have bit 1 set. So my first guess is that OP has defined the wrong bit for motor control and that the correct declaration is
sbit motor = P1^1;
Then remove this line from the receive() function
P1 = value;
My second guess from bit 1 of the key presses is that the motor logic is inverted so that 0 runs the motor and 1 stops it. On top of that, the main loop should check for serial char input and here is my (untested!) rewrite of main()
main()
{
unsigned char speed;
int k = 10; // default is 'slow'
int i;
start(); // moved outside of loop
while (1) // repeat until power off
{
while (RI == 0) // repeat until serial data in
{
for(i=0; i<100; i++) // duty cycle
if (i<k)
motor = 1; // off: or 0x02 if required for b1
else
motor = 0; // on: I have inverted the logic
}
speed=receive(); // get key press
switch (speed)
{
case '1':
k=90; // 90 msec out of 100 msec
next_line();
break;
case '0':
k=10; // 10 msec out of 100 msec
next_line();
break;
}
}
}

Your while loop in main appears to be setting the 1-bit motor control with a 10% or 90% duty cycle, it does this by writing to motor which has been equated to P1. But after 100 writes it calls receive(), and that should leave the motor running, because the function won't return until a key is pressed. So I don't understand why the motor is sometimes stopped. When the function does return, your case statements do not take into account what other key might have been pressed, so you will be simply using k set from the previous key press. In addition, receive() waits for RXD and then writes the character received to P1 the motor control. So on the face of it, the LSB of RXD is being used to control the motor. But that's a bug, because on return from receive() P1 is again written to.

Related

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.

Called function that loops depending on the argument

I have a function LED_MAIN() whose work is to light up LEDs by calling upon another function LED_MAIN_LINE() which is private to it, meaning when I am testing the code I will only call LED_MAIN().
void LED_MAIN (void) //main function (public) to light all LEDs
{
int i;
int LED_NO_UP = 10 ; //number of LEDs on line going up
int LED_NO_DOWN = 10; //number of LEDs on line coming down
void LED_MAIN_LINE ( int no ); //private function to light LEDs on either side i.e on line going up and on line coming down
for (i=0 ; 1<20 ; i++) //first eliminate any errors in all 20 LEDs
{
LED_ERROR [i] = 0;
}
for ( i=0 ; i<LED_NO_UP ; i++ ) //light LEDs on one side. MAX 10 LEDs. LEDs #1-10
{
LED_MAIN_LINE (i);
}
for ( i=0 ; i<LED_NO_DOWN ; i++ ) //light LEDs on the other side. LEDs #11-20
{
LED_MAIN_LINE (i+10);
}
}
void LED_MAIN_LINE ( int no ) //definition of the actual LED lighting function (private to void LED_MAIN (void))
{
//currently no reference to argument int no in this function
for (i = 10 ; i > 0 ; i++)
{
led_status [no][i] = led_status [no] [i-1]
}
// continued......
}
LED_MAIN_LINE() is supposed to be executed for every LED that there is. If there are 10 LEDs on either line, it will loop 20 times (10x2). If there are 5 LEDs it will only be executed 5 times & so on. The argument is the number of LEDs. However, it is being called by void LED_MAIN() and this is where the looping will happen.
Looking at my definition of void LED_MAIN_LINE() however, I have not passed an argument for the number of LEDs. I have done this in LED_MAIN(). I wonder, is this definition correct? Where should I pass the number of LEDs expected to make LED_MAIN_LINE() loop? With the current code my test results are not as expected.
LED_NO_UP and LED_NO_DOWN are 0. So the for loops in your code are never executed because i < 0 is not possible when i = 0.
Also you have 1<20 in the first for loop.

Function that loops depending on the argument in C [duplicate]

I have a function LED_MAIN() whose work is to light up LEDs by calling upon another function LED_MAIN_LINE() which is private to it, meaning when I am testing the code I will only call LED_MAIN().
void LED_MAIN (void) //main function (public) to light all LEDs
{
int i;
int LED_NO_UP = 10 ; //number of LEDs on line going up
int LED_NO_DOWN = 10; //number of LEDs on line coming down
void LED_MAIN_LINE ( int no ); //private function to light LEDs on either side i.e on line going up and on line coming down
for (i=0 ; 1<20 ; i++) //first eliminate any errors in all 20 LEDs
{
LED_ERROR [i] = 0;
}
for ( i=0 ; i<LED_NO_UP ; i++ ) //light LEDs on one side. MAX 10 LEDs. LEDs #1-10
{
LED_MAIN_LINE (i);
}
for ( i=0 ; i<LED_NO_DOWN ; i++ ) //light LEDs on the other side. LEDs #11-20
{
LED_MAIN_LINE (i+10);
}
}
void LED_MAIN_LINE ( int no ) //definition of the actual LED lighting function (private to void LED_MAIN (void))
{
//currently no reference to argument int no in this function
for (i = 10 ; i > 0 ; i++)
{
led_status [no][i] = led_status [no] [i-1]
}
// continued......
}
LED_MAIN_LINE() is supposed to be executed for every LED that there is. If there are 10 LEDs on either line, it will loop 20 times (10x2). If there are 5 LEDs it will only be executed 5 times & so on. The argument is the number of LEDs. However, it is being called by void LED_MAIN() and this is where the looping will happen.
Looking at my definition of void LED_MAIN_LINE() however, I have not passed an argument for the number of LEDs. I have done this in LED_MAIN(). I wonder, is this definition correct? Where should I pass the number of LEDs expected to make LED_MAIN_LINE() loop? With the current code my test results are not as expected.
LED_NO_UP and LED_NO_DOWN are 0. So the for loops in your code are never executed because i < 0 is not possible when i = 0.
Also you have 1<20 in the first for loop.

Sound sensor for Arduino Uno

I got a short time ago an Arduino Kit, and i am trying a little project. Using a Sound sensor i want to sequentially turn on 3 leds. Meaning one clap turns LED1, then a second one the second one and so on. Problem is that this happens only for LED 1 and LED2, and without waiting for the third clap the third LED turns on and without a 4th clap the lights go off, even though i am counting them and resetting the value from the sensor.
I cannot understand why this code works for the first and second Leds but the 3rd and 4th stages are going automatically .
Thanks!
I tried the following code, but it works only for the first and second LED, when the counter is 0 and 1, when it reaches 2, or 3 it activates even though i am not activating the sensor.
int soundSensor=2;
int LED1=4;
int LED2=5;
int LED3=6;
int counter =0;
int SensorData=0;
void setup() {
Serial.begin(9600);
pinMode(soundSensor,INPUT);
pinMode(LED1,OUTPUT);
pinMode(LED2,OUTPUT);
pinMode(LED3,OUTPUT);
counter = 0;
}
void loop() {
SensorData=digitalRead(soundSensor);
if(SensorData==1 && counter == 0){
digitalWrite(LED1,HIGH);
counter++;
SensorData=2;
delay(1000);
}
if(SensorData==1 && counter == 1){
digitalWrite(LED2,HIGH);
counter++;
SensorData=2;
delay(1000);
}
if(SensorData==1 && counter == 2){
digitalWrite(LED3,HIGH);
counter++;
SensorData=2;
delay(1000);
}
if(SensorData==1 && counter == 3){
digitalWrite(LED1,LOW);
digitalWrite(LED2,LOW);
digitalWrite(LED3,LOW);
counter++;
SensorData=2;
delay(1000);
counter =0;
}
}

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.

Resources