Seven Segment Display Scrolling - c

I am coding SSD display using 2 cascaded shift registers. I am using a mikroC for PIC compiler. I can display a static sequence of numbers upto 4 digits with my code Static Display of 4 Numbers
#define SHIFT_CLOCK PORTB.F1 //Clock Connection of 74HC595 SSD Driver
#define SHIFT_LATCH PORTB.F3 //Latch Connection of 74HC595 SSD Driver
#define SHIFT_DATA PORTB.F2 //Data Connection of 74HC595 SSD Driver
char array4[4] = {6, 91, 79, 102}; //Display 1234 on SSD
char digit[4] = {0xFE, 0xFD, 0xFB, 0xF7}; //Switch on the SSD digits one by one
char i,j,temp,flag1,flag2;
void InitTimer0()
{
OPTION_REG = 0x86;
TMR0 = 6;
INTCON = 0xA0;
}
void latch595()
{
SHIFT_LATCH = 1;
Delay_us(1);
SHIFT_LATCH = 0;
}
void shiftdata595(unsigned char _shiftdata)
{
int i;
unsigned char temp;
temp = _shiftdata;
i=8;
while (i>0)
{
if (temp.F7==0)
{
SHIFT_DATA = 0;
}
else
{
SHIFT_DATA = 1;
}
temp = temp<<1;
SHIFT_CLOCK = 1;
SHIFT_CLOCK = 0;
i--;
}
}
void Interrupt()
{
if (TMR0IF_bit)
{
TMR0IF_bit = 0;
TMR0 = 6;
flag1 = 1;
flag2 = 1;
}
}
void main()
{
TRISB = 0;
TRISC.F1 = 1;
InitTimer0();
while (1)
{
if (PORTC.F1==0)
{
if (flag2==1)
{
shiftdata595(digit[i]);
i++;
if(i==4)
{
i=0;
}
if (flag1==1)
{
shiftdata595(array4[j]);
latch595();
j++;
if (j==4)
{
j=0;
}
}
}
}
else if(PORTC.F1==1)
{
shiftdata595(0);
shiftdata595(0);
latch595();
}
}
}
If I add more digits to the array4[], say upto 9, I will need to scroll the digits to the left sequentially.
I tried shifting the array by
temp = array4[0];
for (n=1; n<8; n++)
{
array4[j-1] = array[j];
}
array[9] = temp;
I hoped that this code will left shift the array and the display will scroll, but all I am getting is a jumbled up display. If I add a delay, I can see that the numbers are getting displayed but without scrolling.
Is the basic algorithm faulty or can it be used by modifying the code?

I did figure out the problem with my code. The loop was wrong. I am using two 74HC595 in cascaded mode. One controls the digits(common cathode) and other controls the segments. Since there is only one data line that passes on the information to the digits as well as the segments, the timing of the digits advancement and the segments needs to be coordinated. The entire problem was that I was unnecessarily concentrating on the timing part which was coordinated but instead, I should have concentrated on the loop condition which advances the segments and then left shifts the array.
On friend pointed out that I need to write down the data values on a piece of paper for each loop and that solved the problem. Instead of feeding the loop the length of the array manually like array4[8] or array4[10], I used the sizeof() function and then after the first four iterations of the loop, left shifted the array one place. Now the scrolling works perfect.
Here is the updated and working code,
shiftdata595(array4[tmp7]);
latch595();
Delay_ms(32);
tmp7++;
if (tmp7>=4)
{
tmp7=0;
tmp1 = sizeof(array4)/sizeof(array4[0]);
tmp2 = array4[0];
for (n=1;n<tmp1;n++)
{
array4[n-1] = array4[n];
}
array4[tmp1-1] = tmp2;
}

Related

How to rotate 2D array of characters

I'm trying to rotate this clockwise 90 degrees, but it works not as expected. Must be something to do with pointers which I'm not that familiar.
OUTPUT Before Rotation:
hiivp
nxhxd
tszeg
xdlqo
kwpae
void rotate_right(char **m, int n) {
char **temp = m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
m[i][j] = temp[n-1-j][i];
}
}
}
OUTPUT After Rotation:
kxtnk
wdsdx
plzst
aqsdn
entxk
You're partially overwriting your matrix.
To perform a 90° rotation, let us consider the four corners of the square (in caps):
HiivP
nxhxd
tszeg
xdlqo
WwpaE
You need to put the H in the P place, save the P, and put the P in the E place, then the E in the W place, and finally the W in the H place. These four shift must be done each time saving the overwritten value, that becomes the new running value for the next stage.
Then you can see that each cell is part of such a four-cell shift: the corners are numbered 1, and 1 is the first shift. Then you work cells in position 2, then those in 3, and so on.
12341
45652
36-63
25654
14321
Having an odd side, the central square does not rotate and there is no cycle number 7. After six cycles, each involving four cells, the square is rotated (6*4 = 24, and 24+1 = 25). With a larger square, you can see the "law" linking the x,y coordinates for each cycle.
Building on LSerni's answer, adding an implementation for completeness sake.
Assuming the following matrix:
#define STRING_SIZE (5)
char m[][STRING_SIZE+1] = { "hiivp",
"nxhxd",
"tszeg",
"xdlqo",
"kwpae" };
You can define the rotate function like this:
char override_and_backup(char* dest, char val)
{
char tmp = *dest;
*dest = val;
return tmp;
}
void rotate_right(char m[][STRING_SIZE+1], int size)
{
// rotate layer by layer
for (int layer = 0; layer < (size / 2); ++layer)
{
int current_size = size - (2*layer); // side size of current layer
int ncycles = current_size - 1; // number of cycles to complete rotation
// perform the rotating cycles
for (int cycle = 0; cycle < ncycles; ++cycle)
{
char* first = &m[layer][layer+cycle];
char* second = &m[layer+cycle][layer+current_size-1];
char* third = &m[layer+current_size-1][layer+current_size-cycle-1];
char* fourth = &m[layer+current_size-cycle-1][layer];
char tmp = *fourth;
tmp = override_and_backup(first, tmp);
tmp = override_and_backup(second, tmp);
tmp = override_and_backup(third, tmp);
override_and_backup(fourth, tmp);
}
}
}

Arduino: detecting buttons pressed inside a while loop

I have been trying to write a code to basically add points to a score whenever I pressed a button while a certain amount of time is running down. The problem I am finding is that it doesn't detect when the button is pressed while the time is decreasing, in fact it can only detect when the time starts to decrease and then it doesn't matter at which state the button is it will continue to add to the score. Anyway here is the main code:
void loop() {
buttonState01 = digitalRead(button01);
buttonState02 = digitalRead(button02);
buttonState03 = digitalRead(button03);
if (buttonState01){
time = 3000;
while(time > 0){
if (buttonState02){
score += 10;
Serial.println(score);
}
time--;
Serial.println(time);
}
}
}
And here is the full code if needed:
int button01 = 4;
int button02 = 3;
int button03 = 2;
int buttonState01 = 0;
int buttonState02 = 0;
int buttonState03 = 0;
float time;
int score;
void setup() {
score = 0;
time = 0;
pinMode(button01, INPUT);
pinMode(button02, INPUT);
pinMode(button03, INPUT);
Serial.begin(9600);
}
void loop() {
buttonState01 = digitalRead(button01);
buttonState02 = digitalRead(button02);
buttonState03 = digitalRead(button03);
if (buttonState01){
time = 3000;
while(time > 0){
if (buttonState02){
Serial.println("Points");
}
time--;
Serial.println(time);
}
}
}
You should read the status of a button inside the while loop. Like this:
while(time > 0)
{
buttonState02 = digitalRead(button02);
if (buttonState02){
Serial.println("Points");
}
time--;
Serial.println(time);
}
And in your code, there is no logic to add points to the score.
A hardware interrupt would do exactly what you need.
Attach an interrupt routine to the pin your button is linked to, and get it to set the 'score' variable. Make sure you introduce some sort of timeout to avoid button-bounce (I.e. set LastTimeIncremented when you increase the score, and only increment score if LastTimeIncremented is more than 1 second ago)
This way the score will always be set regardless of what else the program may be doing.
Information on this can be found in the Arduino https://www.arduino.cc/en/Reference/attachInterrupt
The example on that page would do exactly what you want, just replace 'blink' with 'incrementScore' and you're pretty much done
const byte ledPin = 13;
const byte interruptPin = 2;
int score = 0;
int increment = 1;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), incScore, RISING);
}
void loop() {
digitalWrite(ledPin, state);
}
void incScore() {
score = score+increment;
// add anti-bounce functionality here
}

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

Arduino - Reading Serial Data while performing for loop

I just got my arduino the other day. I am currently making a program in java to control my arduino using serial communication. So far, I only have the program turn it on or off. But I ran into an issue. I have my arduino fade two rgb leds, looping through every color. I run into my issue here. When I press the button to turn it off(java program), it doesn't turn off until its ran through every color(complete the for loops). I want it to instantly shut off. Is there any way I can read serial data in the for loops, or is there any possible way I can turn it off instantly, not having to wait for the for loops to complete? Here is the code:
const int redPins[] = {11,6};
const int greenPins[] = {10,5};
const int bluePins[] = {9, 3};
const int pinCountPerColor = 2;
const int sensorPin = 0;
int lightLevel;
int val = 0;
boolean isOn;
void setup() {
Serial.begin(9600);
setColourRgb(0,0,0);
isOn = true;
}
void loop() {
if(isOn) {
unsigned int rgbColour[3];
lightLevel = analogRead(sensorPin);
if(lightLevel >= 400) {
rgbColour[0] = 255;
rgbColour[1] = 0;
rgbColour[2] = 0;
for (int decColour = 0; decColour < 3; decColour += 1) {
int incColour = decColour == 2 ? 0 : decColour + 1;
for(int i = 0; i < 255; i += 1) {
lightLevel = analogRead(sensorPin);
if(lightLevel <= 400) {
setColourRgb(255, 255, 255);
} else {
rgbColour[decColour] -= 1;
rgbColour[incColour] += 1;
setColourRgb(rgbColour[0], rgbColour[1], rgbColour[2]);
delay(5);
}
}
}
} else {
setColourRgb(255, 255, 255);
}
}
}
void setColourRgb(unsigned int red, unsigned int green, unsigned int blue) {
for(int r = 0; r < pinCountPerColor; r++) {
analogWrite(redPins[r], red);
}
for(int g = 0; g < pinCountPerColor; g++) {
analogWrite(greenPins[g], green);
}
for(int b = 0; b < pinCountPerColor; b++) {
analogWrite(bluePins[b], blue);
}
}
void serialEvent()
{
while (Serial.available())
{
val = Serial.parseInt();
if(val == 1)
{
isOn = true;
//do nothing
}
else if(val == 0)
{
setColourRgb(255, 255, 255);
isOn = false;
}
}
Serial.println("Succesfully received.");
}
It is best to create a state machine for the colors, that does not have any blocking for loops, to dwell in. Especially loops with delays. So that each cycle through the loop changes the color and polls the Serial.
This is the art to writing real time code, or simply non-blocking code.
Note it is possible to create a timer interrupt to better schedule RGB updates. See Answer about Timer Interrupts to write precision updates.
#mpflaga answered your question correctly.
I would add that if you projects get bigger than just two leds fading, you might want to use something more reliable than a hand-made state machine.
That is called a Real Time Operating System (RTOS) and it allows you to have different threads running at different frequencies.
I personally use ChibiOS/RT for my robot project. There is a port for Arduino you can download here, it's very well documented and I'd say pretty easy to use once you get the basics. The nice thing to do is to add a higher level layer to manage the threads.
Here is a page with describing it and other solutions : Arduino rtoslibs
And here are some tutorials on real time and ChibiOS for Arduino:
It’s time for real-time
Blinking in real-time
ChibiOS for the Arduino IDE
Hope it helps! :)

Arduino integer array returns value of 3 for index 0 always

I wrote this sketch to send codes to a speakjet sound processor. It will store any number of three digit integers from 128 to 254. I type them into the serial monitor and hit enter after each one. it is stored in an array, and after 900 is entered, the values stored in the array are sent to the speakjet and also read back to the serial monitor. The issue I'm having is that the array will return a value of three for index 0 every time. I have no clue as to why this is.
Here is the code.
int in=0;
int code;
int phrase[] {};
int x=0;
String stack="";
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
Serial.println("Ready");
}
void loop() {
while (Serial.available()>0) {
int inbyte = Serial.read();
if (in<=2);
{
stack += (char)inbyte;
in++;
//Serial.println(stack);
//Serial.println(in);
}
if (in==3) {
if (stack == "900") {
for(int i=0;i<x;i++) {
Serial1.write(phrase[i]);
Serial.println(i);
Serial.println(phrase[i]);
Serial.println();
}
stack="";
x = 0;
in = 0;
Serial.println("Ready");
} else {
phrase[x]=0;
int code = stack.toInt();
if (code < 128) {
code = 253;
}
phrase[x]=code;
Serial.println(x);
Serial.println(phrase[x]);
x++;
in=0;
Serial.write(code);
Serial.println();
stack = "";
}
}
}
}
Thanks for any advice, or help with this matter.
Brewer.
well, first of all, I noticed while reformatting your code you've got a typo here:
if (in<=2); // <-- if the condition is true, it does nothing
{
// this block of code always gets executed
stack += (char)inbyte;
in++;
}
Then I find it really surprising that you can store anything at all in your array:
int phrase[] {};
as it is allocated no memory to store something.
I guess those two errors alone can explain a few undefined behaviors for your program. Given those basic things are totally wrong, I did not analyze the validity of your algorithm itself, because it's being late, and I'm about to go to sleep ;-)

Resources