I am trying to write a code for a basic dice simulator program. When a switch is pressed the two seven segment displays will rapidly change between 1-6. When the button is released the random number will display on the two seven segment display.
This code will be connected to a pic16F877 in ISIS and I'm using MPLAB for the C Programming.
I'm really new to this programming stuff so its hard for me to get my head around it.
#include <pic.h>
const char patterns[]={0X3F, 0X06, 0X5B, 0x4F, 0X66, 0X6D, 0X7D}
char rand_num1=0;
char rand_num2=0;
void main(void)
{
TRISB=0x00;
TRISC=0x01;
TRISD=0x00;
for(;;)
{
if(RCO==0)
{
rand_num1=rand()%6+1;
rand_num2=rand()%6+1;
}
if (RC0==1)
{
const char patterns[];
}
}
}
Let me clarify my comments above a bit:
First, you don't need to call rand(). The user will be pressing the button for some interval (in a precision of 10 or 20 nanoseconds, which is a reasonable clock for a microcontroller). This interval, and given the precision will probably be more random than calling rand(). Therefore, you can have a single counter (i.e. up to 256) and grab two random numbers from this counter. In code, this would be something like:
int counter = 0;
int lo_chunk, hi_chunk;
if(RCO == 0) { // assuming that RCO == 0 means the button is pressed
counter = (counter + 1) % 256; // keep one byte out of the int
// we'll use that byte two get 2 4-bit
// chunks that we'll use as our randoms
lo_chunk = (counter & 0xF) % 6; // keep 4 LSBits and mod them by 6
hi_chunk = ((counter >> 4) & 0xF) % 6; // shift to get next 4 bits and mod them by 6
// Now this part is tricky.
// since you want to display the numbers in the 7seg display, I am assuming
// that you will need to write the respective patterns "somewhere"
// (either a memory address or output pins). You'll need to check the
// documentation of your board on this. I'll just outline them as
// "existing functions"
write_first_7segment(patterns[lo_chunk]);
write_second_7segment(patterns[hi_chunk]);
} else if(RCO == 1) { // assuming that this means "key released"
rand_num1 = lo_chunk;
rand_num2 = hi_chunk;
// probably you'll also need to display the numbers.
}
I hope that what I wrote in the comments above makes more sense now.
Keep in mind, that by not knowing the exact details of your board, I cannot
tell you how to actually write the patterns to the 7segment display, but I
assume that there will be a function of some sort.
Let's start by breaking this down into pieces, and address them individually. This is good practise anyway, and you should end up with lots of small problems that are individually easy to solve.
We can start with pseudocode for the main loop:
for ever {
while button is pushed {
roll the dice
update the displays
}
/* this ^ loop ends when the button is released
and is never entered until the button is pushed
*/
}
which translates to something like:
int main(void)
{
/* somewhere to keep the current value of each die,
initialized to zero */
char dice[2] = {0,0};
for (;;) {
while (button_pushed()) {
roll(dice);
display(dice);
}
}
}
so now we need to write button_pushed:
/* return true (non-zero) if RC0 is zero/one (delete as appropriate) */
int button_pushed(void)
{
return RC0 == 0;
}
... and roll, and display. Does that get you started?
Related
I have rfid circuits. Im trying to add a counter with a 7 seven segment.
My seven segment giving random numbers like this.Photo
I think these numbers are opposite of my numbers. How can i solve this problem?
#include <16F887.h>
#fuses XT,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,NOPUT,NOWRT,NODEBUG,NOCPD
#use delay(clock=4m,oscillator)
#define Dig1 PIN_D0
#define Dig2 PIN_D1
#define rfid PIN_D2
#define reset PIN_A1
#use fast_io(b)
#use fast_io(d)
char birler = 0, onlar = 0, sayi = 0;
void main()
{
int digit[10]={0b0111111,0b0000110,0b1011011,0b1001111,0b1101101,0b1111101,0b0000111,0b1111111,0b1101111};
set_tris_b(0x00);
output_b(1);
set_tris_d(0b11111100);
output_d(0b11111100);
output_b(0b11111100);
while(1)
{
output_b(digit[onlar]);
output_d(0b11111101);
delay_ms(5);
output_b(digit[birler]);
output_d(0b11111110);
delay_ms(5);
if(input(rfid) == 0)
{
sayi++;
birler = sayi%10;
onlar = sayi/10;
while(input(rfid) == 0)
{
output_b(digit[onlar]);
output_d(0b11111101);
delay_ms(5);
output_b(digit[birler]);
output_d(0b11111110);
delay_ms(5);
}
}
}
}
You really should consider isolating the display from your main loop, and eliminating the inline delays in your code. Pros are increased readability, easier maintenance, and eliminating delays for doing the actual work.
While preparing this response, I found out that your segments table is missing entries. Entry for '4' is missing.
The code below is far from complete. There is a digit missing in the LED segments table, you're using a switch that needs debouncing and it lacks a clock for non-blocking timers.
I've copy/pasted much of your app, and added comments...
#include <16F887.h>
#fuses XT,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,NOPUT,NOWRT,NODEBUG,NOCPD
#use delay(clock=4m,oscillator)
#define Dig1 PIN_D0
#define Dig2 PIN_D1
#define rfid PIN_D2
#define reset PIN_A1
#use fast_io(b)
#use fast_io(d)
// never define const arrays on the stack.
static const int digit[10]= { 0b0111111, 0b0000110, 0b1011011, 0b1001111, /* missing '4' */ 0,
0b1101101, 0b1111101, 0b0000111, 0b1111111, 0b1101111 };
void display(unsigned char value)
{
static char tens = 0;
char dig = (tens) ? (value / 10) : (value % 10);
dig = digit[dig];
output_high((tens} ? Dig2 : Dig1);
output_b(dig); // <-- clobbers the high bit of B
output_low((tens} ? Dig1 : Dig2); // preventing other uses for it.
tens = !tens;
}
void main()
{
char sayi = 0;
output_b(1);
output_d(0b11111100);
output_b(0b11111100); // why set PORTB to 1 earlier? is that a bug?
set_tris_b(0x00); // always init tristate AFTER setting output
set_tris_d(0b11111100);
while(1)
{
display(sayi);
if(input(rfid) == 0) // debouncing needed. 30ms is a good delay for debouncing
{
sayi++; // what happens when we reach 100 ???
}
delay_ms(30); // in real-life, this should not be there.
// there are better ways to throttle a program,
// including going to sleep/idle.
}
}
I think these numbers are opposite of my numbers.
Check if your seven segment is common cathode or not, as it seems that the code is built for common cathode seven segment
And if it is common anode and it is your only choice, you can simply change the code to fit it by toggling all the bits in digit array, for ex zero will be 0b10000000
If your numbers are not appearing correctly, consider changing the digit array to
digit[] = {0b1, 0b10, 0b100, 0b1000, 0b10000, 0b100000, 0b1000000};
and run each pattern for 5 seconds. That will tell you which bit is controlling which segment. What I've noticed about 7-segment displays from different manufacturers is that they don't always number the segments in the same way so 0b111111 may appear as 6 or 9: not necessarily 0.
I have a question that may be hard to understand -- but I will try my best to explain.
I'm programming the Simon Game in C. This implementation specifically read/writes to a hardware DAQ module that has a 4 LED display, and 4 corresponding toggle switches.
As per the rules of the Game, I've seeded and generated a random sequence of numbers between 0 and 3 (sequence length is arbitrarily 5). In the Game, if the player presses the wrong switch (i.e. blue is shown but you press green), the game ends and restarts.
The way I've set up the Game looks like this:
(I haven't included the code for function "blinkLED" here -- it turns the actual LED on/off.)
void runSimon(void){
int sequence[MAX_SEQ_LEN];
int i;
int count = 0;
// Seeds the random number generator.
srand((unsigned)time(NULL));
// Generate the random LED sequence & store it as an array.
for (i = 0; i < MAX_SEQ_LEN; i++){
sequence[i] = (rand() % NUM_LEDS);
}
// The game begins!
while (continueSuperLoop() == TRUE){
// Loop the game while the sequence length is less than the pre-defined maximum (currently it's 5).
while (count < MAX_SEQ_LEN){
for (i = 0; i <= count; i++){
// Blink the first 'count' LEDs in the sequence, one at a time.
blinkLED(sequence[i], 1, ONE_SEC);
//
//
//THE ISSUE SHOULD BE HERE (!)
//
// Monitors whether or not the player has made a mistake...if so, blink the red LED thrice, then restart the game.
if (digitalRead(sequence[ !i ] == SWITCH_ON)){
blinkLED(LED_1_R, 3, HALF_SEC);
Sleep(3 * ONE_SEC);
continue;
}
// Monitors whether or not the correct switch is being pressed -- waits for it to be released
while (digitalRead(sequence[i]) == SWITCH_ON){}
}
count++;
}
// If 'count' is equal to 'MAX_SEQ_LEN', the green LED blinks 3x to indicate the player has won .
if (count == MAX_SEQ_LEN){
blinkLED(LED_0_G, 3, HALF_SEC);
Sleep(3 * ONE_SEC);
}
}
}
Where I indicated an issue, I'm not sure how the "digitalRead(sequence[ ! i ]" behaves; I need this line to read every switch that's not supposed to be pressed.
I don't think the compiler understands what I'm trying to do here, though -- for example, if the first number in the sequence is 3 (representing the 4th LED), I need to specify that every other number (0, 1, 2) and its corresponding switch should not be pressed.
Would a solution be to store the current number in the sequence, having a set of four TRUE/FALSE flags for each LED, and monitoring the three non-current numbers and their corresp. switches to see if they are pressed?
I'm getting quite frustrated with writing this program. I'm pretty new to programming. Any help is appreciated.
I'm not sure I understand the rules of this game correctly but one thing that jumps out instantly is
digitalRead(sequence[ !i ]
I think you want
!digitalRead(sequence[ i ]
Also, you need to fix your game flow. Right now it's:
1. Light LED.
2. Check if user pressed the right button.
You need to wait for some time before checking a switch or wait for ANY switch to be pressed and see if it's the correct one. So something like this:
1. Light LED.
2. Wait for timeout or ANY switch to be pressed.
3. If timeout: error
4. else: check if switch that was pressed is correct.
In C, ! operator is a unary NOT. When applied to an integer i, it is equivalent to if (i == 0) return 1; else return 0;. Then you are using !i as an index for sequence array, so it will be either sequence[0] or sequence[1], and clearly this is not what you want. Also your == is inside of digitalRead call :)
I would suggest explicitly checking for every other button not to be pressed. Like this:
int isOtherPressed = 0;
for (ledId = 0; ledId < NUM_LEDS; ledId++) {
if (ledId != sequence[i] && digitalRead(ledId) == SWITCH_ON) {
isOtherPressed = 1;
}
}
if (isOtherPressed) {
// restart the game
}
However, I'm suspicious about the whole gameplay you have, but maybe it's just because I don't know how digitalRead works. For example, the way you use continue doesn't seem to stop the game. Maybe you meant break?
I need to optimize this function: Any strange way to optimize the for loop? (early break i think can't be possible)
void SeeedGrayOLED::putChar(unsigned char C)
{
if(C < 32 || C > 127) //Ignore non-printable ASCII characters. This can be modified for multilingual font.
{
C=' '; //Space
}
uint8_t k,offset = 0;
char bit1,bit2,c = 0;
for(char i=0;i<16;i++)
{
for(char j=0;j<32;j+=2)
{
if(i>8){
k=i-8;
offset = 1;
}else{
k=i;
}
// Character is constructed two pixel at a time using vertical mode from the default 8x8 font
c=0x00;
bit1=(pgm_read_byte(&hallfetica_normal[C-32][j+offset]) >> (8-k)) & 0x01;
bit2=(pgm_read_byte(&hallfetica_normal[C-32][j+offset]) >> ((8-k)-1)) & 0x01;
// Each bit is changed to a nibble
c|=(bit1)?grayH:0x00;
c|=(bit2)?grayL:0x00;
sendData(c);
}
}
}
I've got a font in the array hallfetica_normal, is an array of array of uint8_t, that maybe compressed or something like that?
This code run on a arduino, ad i've to run a countdown from 500 to 0 with one unit down every 10/20ms.
EDIT
This is the new code after yours indication, thanks all:
I'm looking to organise the font differently to permit less call to pgm_read_byte.. (something like changing the orientation... i wonder)
void SeeedGrayOLED::putChar(unsigned char C)
{
if(C < 32 || C > 127) //Ignore non-printable ASCII characters. This can be modified for multilingual font.
{
C=' '; //Space
}
char c,byte = 0x00;
unsigned char nibble_lookup[] = { 0, grayL, grayH, grayH | grayL };
for(int ii=0;ii<2;ii++){
for(int i=0;i<8;i++)
{
for(int j=0;j<32;j+=2)
{
byte = pgm_read_byte(&hallfetica_normal[C-32][j+ii]);
c = nibble_lookup[(byte >> (8-i)) & 3];
sendData(c);
}
}
}
}
Well, you seem to be reading the same byte twice in a row unnecessarily via pgm_read_byte(&hallfetica_normal[C-32][j+offset]). You could load that once into a local variable.
Additionally, you could avoid the if(i>8){ check per iteration by breaking up the code into two loops; one where i goes from 0 to 8 and another where it goes from 9 to 15. (Although I suspect you really intended >= here, making the loop boundaries 0-7 then 8-15.) That also means things like offset become constant values, which will help.
In an effort to make the inner loop as fast as possible, I'd try to get rid of all branching with a lookup table and see whether that helped.
First, I'd define the lookup table outside the loop:
/* outside the loop */
unsigned char h_lookup[] = { 0, grayH };
unsigned char l_lookup[] = { 0, grayL };
Then inside the loop, since you're testing the least-significant bit, you can use that as an index into the lookup table. If it's clear, then the lookup index will be 0. If it's set, then the lookup index will be 1:
/* inside the loop */
byte = pgm_read_byte(&hallfetica_normal[C-32][j+offset]);
c = h_lookup[((byte >> (8-k)) & 0x01)] |
l_lookup[((byte >> (8-k-1)) & 0x01)]
sendData(c);
Since you're masking and testing 2 adjacent bits, 8-k and 8-k-1, you could list all 4 possibilities in a single lookup table:
/* Outside loop */
unsigned char nibble_lookup[] = { 0, grayL, grayH, grayH | grayL };
And then the lookup becomes dramatically simplified.
/* loop */
byte = pgm_read_byte(&hallfetica_normal[C-32][j+offset]);
c = nibble_lookup[(byte >> (8-k)) & 3];
sendData(c);
The other answer has addressed what to do about the branches in the top part of your inner loop.
I took over a project from unknown predecessor who had gone without proper documents and comments.
Now I am trying to analyze his codes, but it is hard to follow up.
Basically, there are 32 channels hook up with a micro controller. What he seems like trying to do is find slave channels between 32 channels once those information is sent from a server.
call nextslave()
for (scan=0 ; (scan = nextslave(chan, scan)) != -1 ; scan++)
nextslave() looks like below
/**
* nextslave - gets the channel number of the next slave channel
* associated with the master. returns -1 if no more slaves.
* channel and start are zero-based.
*/
short nextslave(short channel, short start)
{
short mask, major, minor;
unsigned char *p;
/* fix-up the slaveflag[] index values */
major = start / 8;
minor = start % 8;
/* init a pointer into the slaveflag[] array */
p = &(chparam[channel].slaveflag[major]);
/* now let us find the next slave channel (if any) */
for (; major < (NUMCHANS / 8) ; major++, p++)
{
minor &= 0x07;
for (mask = (0x01 << minor) ; minor < 8 ; mask <<= 1, minor++)
{
if (*p & mask)
{
/* found one so calculate channel# and return */
return ((major * 8) + minor);
}
}
}
/* if we reach here then there are no (more) slaves */
return (-1);
}
What I have analyzed so far is:
start variable keeps iterating until 32 in nextslave().
when the start var is 0~7, major var is 0 and minor var changes from 0 to 7,also mask var changes 1,2,4,8,16...
When the start var is 8 ~15, major var is 1, and minor var still keep changing from 0 to 7
Keep iterating until major var becomes 4.(I don't know the meaning of major var in his codes)
In second for loop, return something if *p(pointer to values from server) & mask is true
I am not clear about general idea about what he intended to for this process. Especially, in the second for loop, if there is no match up in if(*p&mask), then go back to the first for loop. However, minor variable was being increased without clearing out like 0. So once the code hits minor &= 0x07, the processor will do bitwise with the last value of minor although major var keeps increasing.
For instance, the range of minor var is 0 to 7 and there is no match up value, so ends up becoming 7 in the second for loop. Get out of the loop and go back to the first loop and increases major var by 1. But minor var is still 7, so the second loop will start like mask =(0x01<<minor) with minor =7.
I feel like need to reset minor =0 whenever getting out the second for loop, but I don't know what he was aiming for. He just wrote down "master/slave technique".
My Questions are:
Is it correct with my analysis?
How the master/slave technique is used for 32 channels getting ADC?
Is the reset code for minor var required? whenever getting out the second for loop.
If you have any idea, please answer anything that helps me out to understand his codes.
Thanks,
Jin
I'm working on an MC68HC11 Microcontroller and have an analogue voltage signal going in that I have sampled. The scenario is a weighing machine, the large peaks are when the object hits the sensor and then it stabilises (which are the samples I want) and then peaks again before the object roles off.
The problem I'm having is figuring out a way for the program to detect this stable point and average it to produce an overall weight but can't figure out how :/. One way I have thought about doing is comparing previous values to see if there is not a large difference between them but I haven't had any success. Below is the C code that I am using:
#include <stdio.h>
#include <stdarg.h>
#include <iof1.h>
void main(void)
{
/* PORTA, DDRA, DDRG etc... are LEDs and switch ports */
unsigned char *paddr, *adctl, *adr1;
unsigned short i = 0;
unsigned short k = 0;
unsigned char switched = 1; /* is char the smallest data type? */
unsigned char data[2000];
DDRA = 0x00; /* All in */
DDRG = 0xff;
adctl = (unsigned char*) 0x30;
adr1 = (unsigned char*) 0x31;
*adctl = 0x20; /* single continuos scan */
while(1)
{
if(*adr1 > 40)
{
if(PORTA == 128) /* Debugging switch */
{
PORTG = 1;
}
else
{
PORTG = 0;
}
if(i < 2000)
{
while(((*adctl) & 0x80) == 0x00);
{
data[i] = *adr1;
}
/* if(i > 10 && (data[(i-10)] - data[i]) < 20) */
i++;
}
if(PORTA == switched)
{
PORTG = 31;
/* Print a delimeter so teemtalk can send to excel */
for(k=0;k<2000;k++)
{
printf("%d,",data[k]);
}
if(switched == 1) /*bitwise manipulation more efficient? */
{
switched = 0;
}
else
{
switched = 1;
}
PORTG = 0;
}
if(i >= 2000)
{
i = 0;
}
}
}
}
Look forward to hearing any suggestions :)
(The graph below shows how these values look, the red box is the area I would like to identify.
As you sample sequence has glitches (short lived transients) try to improve the hardware ie change layout, add decoupling, add filtering etc.
If that approach fails, then a median filter [1] of say five places long, which takes the last five samples, sorts them and outputs the middle one, so two samples of the transient have no effect on it's output. (seven places ...three transient)
Then a computationally efficient exponential averaging lowpass filter [2]
y(n) = y(nā1) + alpha[x(n) ā y(nā1)]
choosing alpha (1/2^n, division with right shifts) to yield a time constant [3] of less than the underlying response (~50samples), but still filter out the noise. Increasing the effective fractional bits will avoid the quantizing issues.
With this improved sample sequence, thresholds and cycle count, can be applied to detect quiescent durations.
Additionally if the end of the quiescent period is always followed by a large, abrupt change then using a sample delay "array", enables the detection of the abrupt change but still have available the last of the quiescent samples for logging.
[1] http://en.wikipedia.org/wiki/Median_filter
[2] http://www.dsprelated.com/showarticle/72.php
[3] http://en.wikipedia.org/wiki/Time_constant
Note
Adding code for the above filtering operations will lower the maximum possible sample rate but printf can be substituted for something faster.
Continusously store the current value and the delta from the previous value.
Note when the delta is decreasing as the start of weight application to the scale
Note when the delta is increasing as the end of weight application to the scale
Take the X number of values with the small delta and average them
BTW, I'm sure this has been done 1M times before, I'm thinking that a search for scale PID or weight PID would find a lot of information.
Don't forget using ___delay_ms(XX) function somewhere between the reading values, if you will compare with the previous one. The difference in each step will be obviously small, if the code loop continuously.
Looking at your nice graphs, I would say you should look only for the falling edge, it is much consistent than leading edge.
In other words, let the samples accumulate, calculate the running average all the time with predefined window size, remember the deviation of the previous values just for reference, check for a large negative bump in your values (like absolute value ten times smaller then current running average), your running average is your value. You could go back a little bit (disregarding last few values in your average, and recalculate) to compensate for small positive bump visible in your picture before each negative bump...No need for heavy math here, you could not model the reality better then your picture has shown, just make sure that your code detect the end of each and every sample. You have to be fast enough with sample to make sure no negative bump was missed (or you will have big time error in your data averaging).
And you don't need that large arrays, running average is better based on smaller window size, smaller residual error in your case when you detect the negative bump.