I'm learning embedded programming, and have a LED board and button board hooked up to the same PORT.
The commented out if statement under the chk_button function, certain buttons when activated will add to the num variable its intended increment + 1. IE: pushing button 1 will increment num by three. The current case statement works to spec.
I understand case statements when compiled effectively become look up tables, and thus have smaller footprint and run faster,but I am uncertain as to WHY im getting the bug that I am.
I'm also open to any other criticisms in my code.
// HARDWARE SETUP:
// PORTA is connected to the segments of the LED display. and to the pushbuttons.
// PORTA.0 corresponds to segment a, PORTA.1 corresponds to segement b, etc.
// PORTB bits 4-6 go to a,b,c inputs of the 74HC138.
// PORTB bit 7 goes to the PWM transistor base.
#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 16000000
#define DIGIT_ONE 0x00
#define DIGIT_TWO 0x10
#define DIGIT_COLON 0x20
#define DIGIT_THREE 0x30
#define DIGIT_FOUR 0x40
//******************************************************************************
// debounce_switches
//Checks the state of the button number passed to it. It shifts in ones till
//the button is pushed. Function returns a 1 only once per debounced button
//push so a debounce and toggle function can be implemented at the same time.
//Adapted to check all buttons from Ganssel's "Guide to Debouncing"
//Expects active low pushbuttons on PINA port. Debounce time is determined by
//external loop delay times 12.
//Saves status of button (i) into a state array.
//******************************************************************************
int8_t debounce_switches(uint16_t *state, uint8_t i) {
state[i] = (state[i] << 1) | (! bit_is_clear(PINA, i)) | 0xE000;
if (state[i] == 0xF000) return 1;
return 0;
}
//******************************************************************************
// chk_buttons
//Checks the buttons. Calls debounce_switches in a loop passing both the state array
//and the current switch being checked.
//If debounce_switches returns a 1 for a switch, case statements determine which switch
//was activated, and increments count by the appropriate value.
//******************************************************************************
void chk_buttons(uint16_t *state, uint16_t *num)
{
uint8_t itr;
for( itr=0; itr<8; itr++)
{
if( debounce_switches(state, itr))
{
switch(itr)
{
case 0:
*num += 1;
break;
case 1:
*num += 2;
break;
case 2:
*num += 4;
break;
case 3:
*num += 8;
break;
case 4:
*num += 16;
break;
case 5:
*num += 32;
break;
case 6:
*num += 64;
break;
case 7:
*num +=128;
break;
}
/*
if (itr == 0) *num += 1;
else if(itr == 1) *num += 2;
else if(itr == 2) *num += 4;
else if(itr == 3) *num += 8;
else if(itr == 4) *num += 16;
else if(itr == 5) *num += 32;
else if(itr == 6) *num += 64;
else if(itr == 7) *num += 128;
*/
}
}
}
//******************************************************************************
// itoseven
// Accepts a number from 0 -9 Returns a hex value to display on the seven segment..
//******************************************************************************
uint8_t itoseven(uint8_t num)
{
uint8_t segnum;
switch(num)
{
case 0:
segnum = ~0x3F;
return segnum;
case 1:
segnum = ~0x06;
return segnum;
case 2:
segnum = ~0x5B;
return segnum;
case 3:
segnum = ~0x4F;
return segnum;
case 4:
segnum = ~0x66;
return segnum;
case 5:
segnum = ~0x6D;
return segnum;
case 6:
segnum = ~0x7D;
return segnum;
case 7:
segnum = ~0x07;
return segnum;
case 8:
segnum = ~0x7F;
return segnum;
case 9:
segnum = ~0x6F;
return segnum;
}
}
//***********************************************************************************
// segment_sum
//takes a 16-bit binary input value and displays the result to the LED board.
//***********************************************************************************
void segsum(uint16_t num) {
uint8_t ones;
uint8_t tens;
uint8_t hundreds;
uint8_t thousands;
//break up decimal sum into 4 digit-segments
ones = num % 10;
tens = (num/10)%10;
hundreds = (num/100)%10;
thousands = (num/1000)%10;
//determine how many digits there are
if(num < 10)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(num);
}
else if(num > 9 && num <100)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
}
else if(num > 99 && num < 1000)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
_delay_ms(2);
PORTB = DIGIT_THREE;
PORTA = itoseven(hundreds);
}
else if (num >999)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
_delay_ms(2);
PORTB = DIGIT_THREE;
PORTA = itoseven(hundreds);
_delay_ms(2);
PORTB = DIGIT_FOUR;
PORTA = itoseven(thousands);
}
}
uint8_t main()
{
DDRB = 0xF0; //set port bits 4-7 B as outputs
uint16_t num = 0;
uint16_t state[8];
//initialize array values for debouncing
for(int i=0; i<8; i++)
{
state[i]= 0;
}
while(1)
{
//make PORTA an input port with pullups
DDRA = 0x00;
PORTA = 0xFF;
//enable tristate buffer for pushbutton switches
PORTB = 0x70;
//check the buttons. Increment by appropriate value if switch is active.
chk_buttons(&state, &num);
//rollover at 1023
if(num > 1023)
num = 1;
//switch PORTA to output
DDRA = 0xFF;
//Send num to display
segsum(num);
_delay_ms(2);
}
return 0;
}
I've update these functions to remove the switches as they are not needed at all. Note how the first uses an algorithm in place of a switch and the second uses a table that is easy to adjust in the future. Also, assert/error check your inputs and you'll be a happier camper when things go sideways.
void chk_buttons(uint16_t *state, uint16_t *num)
{
// Assert bad state and num here.
uint8_t itr;
for( itr=0; itr<8; itr++)
{
if( debounce_switches(state, itr))
{
*num += 1 << itr; // Could also use |= as that is what we are doing.
}
}
}
uint8_t itoseven(uint8_t num)
{
// assert num <= 9
const uint8_t kLookup[10] =
{~0x3f, ~0x06, ~0x5B, ~0x4F, ~0x66,
~0x6D, ~0x7D, ~0x07, ~0x7F, ~0x6F};
return kLookup[num];
}
For the last function, if you start at the 4-digit number check and work backwards to the single digit, it becomes a very simple loop to do your complex digit calculation. Just divide your mask from 10,000 to 10. I'll post that code a bit later as I have time.
Update
Here is something I coded up that demonstrates the idea. You will need to modify it for your own functions as right now it just outputs ASCII. It fails for negative numbers (as does your code), but that is very easy to add with a simple if check / ABS / prepend a minus character.
int main(void)
{
int num = 9876;
// Starting at the 5th digit. You could use 1000 instead if
// there were only 4 digits or less ever.
int digitMask = 10000;
char output[16];
int index = 0;
// Handle 0 case.
if(num== 0)
{
output[index++] = '0';
}
else
{
// Skip leading 0's
while((num / digitMask) == 0)
{
digitMask /= 10;
}
// While there may be digits to process...
while(digitMask)
{
// Grab MS Digit and mod it away from total.
int digit = num / digitMask;
num %= digitMask;
// Convert to ASCII (you would use your own function)
output[index++] = digit + '0';
// Update mask - note we could use a for/loop here just as easily.
digitMask /= 10;
}
}
// Terminate string and print it.
output[index] = 0;
printf("%s", output);
return 0;
}
Related
I have a uint8_t array of size n>1 and would like to cast it into a uint16_t array of the same size n>1. I actually use the uint8_t array for ASCII characters and would like to use now UNICODE.
Any ideas how this conversion can be done?
Edit:
I would like to use this function here, which works fine for const char *string as argument and not for const uint16_t *string. So somehow I need to cast it.
srv_err_t gui_write_text_16bit(const uint16_t *string, Layout_type_t layout,
Layout_field_t field, Text_inverted_t inv) {
srv_err_t err;
uint8_t charCount;
uint8_t byteCount;
uint16_t bitmapCol = 0;
uint16_t bitmapRow = 0;
uint8_t textLength = 0;
uint8_t textHeight = GUI_FONT_NAME.FontHeight;
uint16_t offset;
uint8_t mask;
lcd_rectangle_t position;
if (LAYOUT_A == layout) {
if (LAYOUT_FIELD6 == field) {
// Position 6 is not available in Layout A
err.bits.input_parameter = true;
return err;
}
}
GUI_CONST_STORAGE GUI_CHARINFO
*pcharInfo;
GUI_CONST_STORAGE
unsigned char* pchar;
GUI_CONST_STORAGE GUI_FONT_PROP
*pfontProp;
//uint8_t textBitmap [bitmapLength * textHeight];
uint8_t textBitmap[(LCD_COLUMN_NUMBER_DISPLAY / 8) * GUI_FONT_HEIGHT] = { 0 };
/* Calculate needed space in the array */
// for (charCount = 0; charCount < stringLength; charCount++)
for (charCount = 0; string[charCount] != '\0'; charCount++) {
pfontProp = GUI_FONT_NAME.FontProp;
while (0 != pfontProp) {
if (pfontProp->First <= string[charCount]
&& pfontProp->Last >= string[charCount]) {
offset = string[charCount] - pfontProp->First;
pcharInfo = (pfontProp->pCharInfoFirstChar) + offset; // Pointer to the right character
textLength += pcharInfo->XSize; // Text length in Pixels
break; // exit while loop and beginn with next character
}
pfontProp = pfontProp->pNext;
}
}
textLength = (textLength / 8) + 1; // Text length in Bytes
// for(charCount = 0; charCount < stringLength; charCount++)
for (charCount = 0; string[charCount] != '\0'; charCount++) {
pfontProp = GUI_FONT_NAME.FontProp;
while (0 != pfontProp) {
if (pfontProp->First <= string[charCount]
&& pfontProp->Last >= string[charCount]) {
// Character in Range found
offset = string[charCount] - pfontProp->First;
pcharInfo = (pfontProp->pCharInfoFirstChar) + offset; // Pointer to the right character
pchar = pcharInfo->pData;
for (bitmapRow = 0; bitmapRow < textHeight; bitmapRow++) {
uint16_t bitmapByte = 0;
uint16_t charByte = 0;
uint8_t pixelShift;
for (byteCount = 0; byteCount < pcharInfo->BytesPerLine;
byteCount++) {
//bitmapByte = bitmapRow * bitmapLength + (bitmapCol / 8) + byteCount;
bitmapByte = bitmapRow * textLength + (bitmapCol / 8) + byteCount;
charByte = pcharInfo->BytesPerLine * bitmapRow + byteCount;
pixelShift = bitmapCol % 8;
if (byteCount == (pcharInfo->BytesPerLine - 1)) {
// Last Byte in row
switch (pcharInfo->XSize % 8) {
case 1:
mask = 0x80;
break;
case 2:
mask = 0xC0;
break;
case 3:
mask = 0xE0;
break;
case 4:
mask = 0xF0;
break;
case 5:
mask = 0xF8;
break;
case 6:
mask = 0xFC;
break;
case 7:
mask = 0xFE;
break;
case 0:
mask = 0xFF;
break;
default:
break;
}
textBitmap[bitmapByte] |= (pchar[charByte] & mask) >> pixelShift;
textBitmap[bitmapByte + 1] |= (pchar[charByte] & mask)
<< (8 - pixelShift);
//bitmapCol += pcharInfo->XSize % 8;
} else {
/* charByte is not aligned with the bitmapByte. A direct copy is not possible */
textBitmap[bitmapByte] |= pchar[charByte] >> pixelShift;
textBitmap[bitmapByte + 1] |= pchar[charByte]
<< (8 - pixelShift);
}
}
}
bitmapCol += pcharInfo->XSize;
break; // exit while loop and beginn with next character
}
pfontProp = pfontProp->pNext;
}
}
if (layout == LAYOUT_A) {
position = Layout_A_Text_Field[field];
/* place Bitmap on the right display Position */
if (LAYOUT_TITLE == field) {
gui_place_text(&position, textLength, textHeight, ALIGN_CENTER);
} else {
gui_place_text(&position, textLength, textHeight, ALIGN_LEFT);
}
if (LAYOUT_FIELD2 == field) {
lcd_draw_text(position, textBitmap, sizeof(textBitmap),
Layout_A_Field[field], DRAW_INVERSE);
} else {
lcd_draw_text(position, textBitmap, sizeof(textBitmap),
Layout_A_Field[field], DRAW_NORMAL);
}
}
return err;
}
Casting would be non sense because each element in a char array uses 1 single byte while 2 are needed for a UNICODE uint16_t array. But provided your initial string is composed of only ASCII or Latin1 characters, you can use the fact the the unicode code point of an ASCII or Latin1 character is just the character value.
So if the resulting array is allocated in caller, you could use code as simple as:
// Convert an ASCII or Latin1 string to a 16bits unicode string:
void tounicode(uint16_t *dest, const char *src, size_t nchars) {
while(nchars-- > 0) { // process nchars characters
*dest++ = (unsigned char) *src++; // one at a time
}
}
If you are using x86 hardware check out the SSE2 routines that convert a 16-byte vector into two 8-element uint16 vectors, in 2 machine ops. _mm_unpacklo_epi8() and ...unpackhi...
I currently can use my raspberry pi 3 to redirect USB Audio input to an icecast stream. Currently it works fine in small tests when I pipe arecord from USB audio hw input to avconv (ffmpeg equivalent) on Raspbian Jessie Lite.
Arecord has a built in text vu meter for audio levels when you use the verbose setting.
I think the code is located in the attached function. I'm wondering if it's possible to rewrite this function to output to Raspberry Pi 3 RGB LED - to somehow send the red/yellow/green based on volume levels - using the function's peak variable?
I've include the whole function - and the print function. If it could be done, I think the code could probably replace print_vu_meter(perc, maxperc);
Is it possible to modify arecord to get the Raspi 3 to handle the processing during the pipe? Is there a way to use another thread?
Way out of my league here - just looking for a start, or some ideas to get the idea out of my head or to say it's possible.
peak handler
static void compute_max_peak(u_char *data, size_t count)
{
signed int val, max, perc[2], max_peak[2];
static int run = 0;
size_t ocount = count;
int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
int ichans, c;
if (vumeter == VUMETER_STEREO)
ichans = 2;
else
ichans = 1;
memset(max_peak, 0, sizeof(max_peak));
switch (bits_per_sample) {
case 8: {
signed char *valp = (signed char *)data;
signed char mask = snd_pcm_format_silence(hwparams.format);
c = 0;
while (count-- > 0) {
val = *valp++ ^ mask;
val = abs(val);
if (max_peak[c] < val)
max_peak[c] = val;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 16: {
signed short *valp = (signed short *)data;
signed short mask = snd_pcm_format_silence_16(hwparams.format);
signed short sval;
count /= 2;
c = 0;
while (count-- > 0) {
if (format_little_endian)
sval = __le16_to_cpu(*valp);
else
sval = __be16_to_cpu(*valp);
sval = abs(sval) ^ mask;
if (max_peak[c] < sval)
max_peak[c] = sval;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 24: {
unsigned char *valp = data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 3;
c = 0;
while (count-- > 0) {
if (format_little_endian) {
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
} else {
val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
}
/* Correct signed bit in 32-bit value */
if (val & (1<<(bits_per_sample-1))) {
val |= 0xff<<24; /* Negate upper bits too */
}
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp += 3;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 32: {
signed int *valp = (signed int *)data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 4;
c = 0;
while (count-- > 0) {
if (format_little_endian)
val = __le32_to_cpu(*valp);
else
val = __be32_to_cpu(*valp);
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
default:
if (run == 0) {
fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
run = 1;
}
return;
}
max = 1 << (bits_per_sample-1);
if (max <= 0)
max = 0x7fffffff;
for (c = 0; c < ichans; c++) {
if (bits_per_sample > 16)
perc[c] = max_peak[c] / (max / 100);
else
perc[c] = max_peak[c] * 100 / max;
}
if (interleaved && verbose <= 2) {
static int maxperc[2];
static time_t t=0;
const time_t tt=time(NULL);
if(tt>t) {
t=tt;
maxperc[0] = 0;
maxperc[1] = 0;
}
for (c = 0; c < ichans; c++)
if (perc[c] > maxperc[c])
maxperc[c] = perc[c];
putchar('\r');
print_vu_meter(perc, maxperc);
fflush(stdout);
}
else if(verbose==3) {
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
for (val = 0; val < 20; val++)
if (val <= perc[0] / 5)
putchar('#');
else
putchar(' ');
printf(" %i%%\n", perc[0]);
fflush(stdout);
}
}
print_vu_meter
static void print_vu_meter_mono(int perc, int maxperc)
{
const int bar_length = 50;
char line[80];
int val;
for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
line[val] = '#';
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
line[val] = ' ';
line[val] = '+';
for (++val; val <= bar_length; val++)
line[val] = ' ';
if (maxperc > 99)
sprintf(line + val, "| MAX");
else
sprintf(line + val, "| %02i%%", maxperc);
fputs(line, stdout);
if (perc > 100)
printf(_(" !clip "));
}
Doesn't look like there's much interest in this, but I thought I'd post my answer for future, in case anyone else tries to delve into this type of project.
Findings:
arecord is just a linked copy of aplay
you can use the incredible wiringPi c library to add a thread to the C code
by adding a static volatile int variable to the code - it becomes shared with between thread and main program
setting the variable to the perc value means it's updated immediately in the threaded program
I was able to simulate a 6 level audio level meter with LEDs using this code:
I used a button on the breadboard to toggle between LED levels being displayed and not.
setAudioLEDS threaded function:
PI_THREAD (setAudioLEDs)
{
// Only update LEDS if button is pressed
// Gets Audio Level from global var: globalAudioLevel
// Wiring Pi Constants for led and button
// Pin number declarations. We're using the Broadcom chip pin numbers.
#define CYCLE_UPDATE '0'
#define CYCLE_STEADY '1'
int last_button;
int last_cycle;
int this_cycle;
// Button is released if this returns 1
// HIGH or LOW (1 or 0)
last_button = digitalRead(butPin);
last_cycle = CYCLE_STEADY;
this_cycle = last_cycle;
while (1)
{
if (digitalRead(butPin) != last_button) {
if (last_cycle == CYCLE_UPDATE)
this_cycle = CYCLE_STEADY;
else
this_cycle = CYCLE_UPDATE;
last_cycle = this_cycle;
}
switch (this_cycle) {
case CYCLE_UPDATE:
// Set LEDS based on audio level
if (globalAudioLevel > 20)
digitalWrite(led1, HIGH); // Turn LED ON
else
digitalWrite(led1, LOW); // Turn LED OFF
if (globalAudioLevel > 40)
digitalWrite(led2, HIGH); // Turn LED ON
else
digitalWrite(led2, LOW); // Turn LED OFF
if (globalAudioLevel > 60)
digitalWrite(led3, HIGH); // Turn LED ON
else
digitalWrite(led3, LOW); // Turn LED OFF
if (globalAudioLevel > 70)
digitalWrite(led4, HIGH); // Turn LED ON
else
digitalWrite(led4, LOW); // Turn LED OFF
if (globalAudioLevel > 80)
digitalWrite(led5, HIGH); // Turn LED ON
else
digitalWrite(led5, LOW); // Turn LED OFF
if (globalAudioLevel > 90)
digitalWrite(led6, HIGH); // Turn LED ON
else
digitalWrite(led6, LOW); // Turn LED OFF
break;
default:
/* Button hasn't been pressed */
digitalWrite(led1, LOW); // Turn LED OFF
digitalWrite(led2, LOW); // Turn LED OFF
digitalWrite(led3, LOW); // Turn LED OFF
digitalWrite(led4, LOW); // Turn LED OFF
digitalWrite(led5, LOW); // Turn LED OFF
digitalWrite(led6, LOW); // Turn LED OFF
}
}
}
I have something working as a ALSA scope plugin so any audio played will start my led vu meter. All you have to do is install the plugin and edit /etc/asound.conf to include the plugin.
I found pivumeter (https://github.com/pimoroni/pivumeter) which is based on ameter (http://laugeo.free.fr/ameter.html) and created my own devices for a 2 channel stereo 16 LED using GPIO pins and another that uses two shift registers and SPI.
My fork can be found here: https://github.com/linuxgnuru/pivumeter
To add your own device, you'll need to edit Makefile.am, src/pivumeter.c (at the bottom add your function), src/devices/all.h, and your code in src/devices/YOURDEVICE.c and src/devices/YOURDEVICE.h
I plan on going through the ALSA C library for the scope plugin and making my own plugin from scratch.
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___scope.html
The purpose of the code I'm writing is that when my 0th bit of PINA is 1, the number displayed on PORTB is incremented. Likewise, if the 1st bit of PINA is 1, the number is decremented. If the button is held down, it will increment/decrement at a rate of once per second. If the button has been held down for at least 3 seconds, the rate is increased to one every 400 ms. If both buttons are pressed, the number displayed on PORTB is reset to 0. The timer period is set at 100 ms.
I split the state machine into 2 state machines running "concurrently". One SM adjusts the period that I'm incrementing/decrementing at, and the other does the transitions between waiting/adding/subtracting/resetting states.
The problem I'm having is that my state machine that modifies the period is not doing anything at all. I have a global variable to keep track of how many ticks (initialized to 10) I have to wait before incrementing/decrementing, and that this machine should modifying it when the number of ticks stayed in my Incr/Decr states exceeded 30 ticks (3 seconds). But this machine is not doing anything at all. I modified so that it will immediately and permanently change to 4 ticks when my concurrent SM starts, but that still doesn't do anything; incrementing/decrementing is still happening every 1 second rather than every 400 ms.
What's wrong with my code?
Code in question:
enum States {Init, ButPres, Incr, Wait, Decr, Reset} state;
enum other_States {Init1, change_tick_total} state1;
unsigned char button = 0x00;
unsigned char cnt = 0x00;
unsigned char tick_cnt = 0;
unsigned char tick_total = 0x0A;
void change_period_tick() {
switch (state1) {
case Init1:
state = change_tick_total;
break;
case change_tick_total:
state = change_tick_total;
break;
default:
state = Init1;
break;
}
switch (state1) {
case change_tick_total:
if (tick_cnt > 30)
tick_total = 0x04;
else
tick_total = 0x0A;
break;
default:
break;
}
}
void tick(){
switch(state){
case Init:
state = ButPres;
break;
case ButPres:
if(button == 0x01)
state = Incr;
else if(button == 0x02)
state = Decr;
else if(button == 0x03)
state = Reset;
else
state = ButPres;
break;
case Incr:
if(button == 0x01 && cnt < 9)
state = Incr;
else
state = Wait;
break;
case Decr:
if(button == 0x02 && cnt > 0){
state = Decr;
}
else
state = Wait;
break;
case Wait:
if(button == 0x03)
state = Reset;
else if(button)
state = Wait;
else
state = ButPres;
break;
case Reset:
if(button == 0x03)
state = Reset;
else
state = Init;
break;
default:
state = Init;
break;
}
switch(state){
case Init:
cnt = 0x00;
PORTB = cnt;
break;
case Incr:
if(cnt < 0x09){
++tick_cnt;
if (tick_cnt % tick_total == 1)
++cnt;
PORTB = cnt;
}
break;
case Decr:
if(cnt > 0x00){
++tick_cnt;
if (tick_cnt % tick_total == 1)
--cnt;
PORTB = cnt;
}
break;
case Reset:
cnt = 0;
PORTB = cnt;
default:
tick_cnt = 0; //once we're out of INCR/DECR, the tick_cnt resets, as we're on a new cycle of counting ticks
break;
}
}
int main(void)
{
state = Init;
state1 = Init1;
DDRA = 0x00; PORTA = 0xFF;
DDRB = 0xFF; PORTB = 0x00;
// Initializes the LCD display
TimerSet(100);
TimerOn();
tick_total = 0x0A;
while(1) {
button = ~PINA;
change_period_tick(); //this does no seem to be running at all
tick();
while(!TimerFlag){}
TimerFlag = 0;
}
}
My project involves sending commands from PuTTY to a Dragon12-Plus2 development board(CodeWarrior 5.1) via an XBEE Wifi. I am using 4 commands to control the leds on the Dragon board {led_enable(), leds_on(integer in hex or decimal here), leds_off(), and led_disable()} from the LBE_DRAGON12_Plus project from LBEbooks.com. Here is my code:
// Final Project: Control leds via XBEE Wifi
#include <hidef.h> /* common defines and macros */
#include <mc9s12dg256.h> /* derivative information */
#include "queue.h"
#pragma LINK_INFO DERIVATIVE "mc9s12dg256b"
#include "main_asm.h" /* interface to the assembly module */
void main(void){
int x,y,z,parser;
int cmdLen = 0;
char c,d,e;
char cbuff[20];
PLL_init(); // set system clock frequency to 24 MHz
lcd_init(); // enable lcd
SCI1_init(9600); // initialize SCI1 at 9600 baud
SCI0_init(9600); // initialize SCI0 at 9600 baud
seg7_disable(); // disable 7 segment display
led_enable(); // enable 8 leds
while(1){
if(SCI1SR1_RDRF == 1){
c = inchar1();
cbuff[cmdLen] = c;
outchar0(cbuff[cmdLen]);
if(c == ';'){
if((cbuff[0] == 'l') && (cbuff[10] == ';')){
leds_off();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[12] == ';')){
led_enable();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[4] == 'd')){
led_disable();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[13] == ';')){
d = cbuff[10];
e = cbuff[11];
switch(d){ // change first number to integer
case('0'):
x = 0x00;
break;
case('1'):
x = 0x10;
break;
case('2'):
x = 0x20;
break;
case('3'):
x = 0x30;
break;
case('4'):
x = 0x40;
break;
case('5'):
x = 0x50;
break;
case('6'):
x = 0x60;
break;
case('7'):
x = 0x70;
break;
case('8'):
x = 0x80;
break;
case('9'):
x = 0x90;
break;
case('a'):
x = 0xa0;
break;
case('b'):
x = 0xb0;
break;
case('c'):
x = 0xc0;
break;
case('d'):
x = 0xd0;
break;
case('e'):
x = 0xe0;
break;
case('f'):
x = 0xf0;
break;
default:
break;
}
switch(e){ // change second number to integer
case('0'):
y = 0x00;
break;
case('1'):
y = 0x01;
break;
case('2'):
y = 0x02;
break;
case('3'):
y = 0x03;
break;
case('4'):
y = 0x04;
break;
case('5'):
y = 0x05;
break;
case('6'):
y = 0x06;
break;
case('7'):
y = 0x07;
break;
case('8'):
y = 0x08;
break;
case('9'):
y = 0x09;
break;
case('a'):
y = 0x0a;
break;
case('b'):
y = 0x0b;
break;
case('c'):
y = 0x0c;
break;
case('d'):
y = 0x0d;
break;
case('e'):
y = 0x0e;
break;
case('f'):
y = 0x0f;
break;
default:
break;
}
z = (x + y); // add integers together
leds_on(z); // display hex number on leds
cmdLen = 0; // reset parser
}
for(parser = 0; parser < 20; parser++){
cbuff[parser] = '\0';
}
}
cmdLen++;
}
}
}
The first command i send works as expected (for instance if i type leds_on(0xaa); in PuTTY the 4 corresponding leds light up), but any command I send after does nothing. I am not sure how to properly reset cbuff so that it operates the same way each time, not just the first time.
EDITED TO SHOW SUGGESTED CHANGES!
If you want to clear all of the elements in the array, then memset would be a wise choice:
memset(cbuff, 0, 20);
That being said, I would strongly agree with #John Bollinger that separating the input and the interpretation functionality would be wise.
However, depending on the application memset might be overkill. A simple cleared array may also be achieved by simply setting the elements of the array to null character ('\0'). Simply call cbuff[0] = '\0' for each element in the array.
What an odd parser. I'd suggest you separate your input stage from your command interpretation stage. That is, read and count characters until you receive a semicolon (;) command terminator, and only then try to interpret the command.
Reset the parser variable (which would more accurately be named something like command_length) only after each attempt to interpret the command, whether successful or not, or if the buffer is about to overflow.
I have this code of a transmitter and I have a problem where when I get input from my transmitter I save it in a char array "rec" and after I have finished receiving I compare it with my other arrays and base on that comparison I light up a bulb in portb. But the code gives me no errors and no bulb lights (the connection works I've tried it) it has something to do with the comparing arrays part. Am I doing this right? Thank you
char rec[3];
char cmp1[]= "1000";
char cmp2[]= "1010";
char cmp3[]="1111";
char cmp4[]="1001";
int i=0;
int beginrecord;
void main(void)
{
TRISB=0;
TRISD=0;
TRISC=255;
PORTC=0;
PORTB=0;
while(1)
{
if(PORTC==1)
{
rec[i]=1;
i++;
beginrecord = 1;
delay_ms(1);
}
if (PORTC==0 && beginrecord==1)
{
rec[i]=0;
i++;
delay_ms(1);
}
if(i==4) {
beginrecord = 0;
i = 0;
if(rec == cmp1){
portb = 0x01;}
else if(rec == cmp2){
portb = 0x02;}
else if(rec == cmp3){
portb = 0x04;}
else if(rec == cmp4){
portb = 0x08;}
}
}
}
You must use strcmp(rec, cmp1) == 0 instead of rec == cmp1.
The way you do it now, you compare only the pointers, which are never the same.
And are you sure that your port returns a character 1or 0 and not the values 0/1?
In that case your comparison also wouldn't work.
Some other issues with your code:
I assume that the char form the port is a binary value and not an ASCII character.
If it is indeed an ASCII character and you use strcmp() then you must add a zero byte at the end of the buffer.
If you receive multiple bytes and not bits, you must use memcmp() instead of strcmp(). In that case your intialisation of cmpX would be also wrong. If it are bits you receive, you would want to combine them into bytes before comparing (assuming a serial port).
PORTC is always 0, so it can not become 1 in your code.
You seem to have mixed case PORTB vs. portb.
your variables have no type.
Here you are declare three cmp[] of char type which is a string so it is like "1000". it is 4 byte data. and your rec[3] is only 3 byte. i think you try to compare a int type. like
1001 == 1001 in binary
Declare them as unsigned int then try to compare.
And make all your values full 8 bit like 00001000b not 1000.
or may be i get it wrong.
There are a number of issues that confuse your goal - see above comment, but I've tried to address them below.
Rather than accumulate the rec as an array, the bits are accumulated in an int. This simplifies the compares to your original style.
beginrecord needed initialization. Variables were brought into a local scope.
Cleaned up the indentation.
const int cmp1 = 8; // "1000";
const int cmp2 = 10; // "1010";
const int cmp3 = 15; // "1111";
const int cmp4 = 9; // "1001";
void main(void) {
TRISB = 0;
TRISD = 0;
TRISC = 255;
PORTC = 0;
PORTB = 0;
int i = 0;
int beginrecord = 0;
int rec = 0;
while (1) {
if (PORTC == 1) {
rec <<= 1; /* rec = rec * 2 */
rec |= 1;
beginrecord = 1;
delay_ms(1);
i++;
}
else if (PORTC == 0 && beginrecord == 1) {
rec <<= 1;
delay_ms(1);
i++;
}
if (i == 4) {
if (rec == cmp1) {
PORTB = 0x01;
} else if (rec == cmp2) {
PORTB = 0x02;
} else if (rec == cmp3) {
PORTB = 0x04;
} else if (rec == cmp4) {
PORTB = 0x08;
}
beginrecord = 0;
i = 0;
rec = 0;
}
}
}