Passing PIN status as a function parameter - c

I want to write a function for my AVR ATmega328 that debounces switches using state space to confirm a switch press. After finishing it I wanted to generalize my function so that I may reuse it in the future with little work, but that involves passing the pin I want to use as a function parameter, and I just can't get that to work.
This is what I have now:
int debounceSwitch(unsigned char *port, uint8_t mask)
{
int n = 0;
while (1)
{
switch (n)
{
case 0: //NoPush State
_delay_ms(30);
if(!(*port & (1<<mask))){n = n + 1;}
else {return 0;}
break;
case 1: //MaybePush State
_delay_ms(30);
if(!(*port & (1<<mask))){n = n + 1;}
else {n = n - 1;}
break;
case 2: //YesPush State
_delay_ms(30);
if(!(*port & (1<<mask))){return 1;}
else {n = n - 1;}
break;
}
}
}
I have a hunch my issue is with the data type I'm using as the parameter, and I seem to have gotten different answers online.
Any help would be appreciated!

Well in AVR ports are special IO registers and they are accessed using IN and OUT instructions. Not like memory using LDR etc.
From the port definition you can see that you need to make the port pointer volatile. which the compiler would have also told you as a warning when you would had tried to pass PORT to the function.
#define PORTB _SFR_IO8(0x05)
which maps to
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

Various issues:
The function should be void debounceSwitch(volatile uint8_t* port, uint8_t pin). Pointers to hardware registers must always be volatile. It doesn't make sense to return anything.
Never use 1 signed int literals when bit-shifting. Should be 1u << n or your program will bug out when n is larger than 8.
Burning away 30ms several times over in a busy-delay is horrible practice. It will lock your CPU at 100% doing nothing meaningful, for an eternity.
There are many ways to debounce buttons. The simplest professional form is probably to have a periodic timer running with interrupt every 10ms (should be enough, if in doubt measure debounce spikes of your button with a scope). It will look something like the following pseudo code:
volatile bool button_pressed = false;
void timer_interrupt (void)
{
uint8_t button = port & mask;
button_pressed = button && prev;
prev = button;
}
This assuming that buttons use active high logic.

What I dislike on your implementation is the pure dependency on PORT/IO handling and the actual filter/debouncing logic. What are you doing then, when the switch input comes over a signal e.g. from CAN?
Also, it can be handled much easier, if you think in configurable/parameterizable filters. You implement the logic once, and then just create proper configs and pass separate state variables into the filter.
// Structure to keep state
typedef struct {
boolean state;
uint8 cnt;
} deb_state_t;
// Structure to configure the filters debounce values
typedef struct {
uint8 cnt[2]; // [0] = H->L transition, [1] = L->H transition
} deb_config_t;
boolean debounce(boolean in, deb_state_t *state, const deb_config_t *cfg)
{
if (state->state != in) {
state->cnt++;
if (state->cnt >= cfg->cnt[in]) {
state->state = in;
state->cnt = 0;
}
} else {
state->cnt = 0;
}
return state->state;
}
static const deb_config_t debcfg_pin = { {3,4} };
static const deb_config_t debcfg_can = { {2,1} };
int main(void)
{
boolean in1, in2, out1, out2;
deb_state_t debstate_pin = {0, 0};
deb_state_t debstate_can = {0, 0};
while(1) {
// read pin and convert to 0/1
in1 = READ_PORT(PORTx, PINxy); // however this is defined on this architecture
out1 = debounce(in1, &debstate_pin, &debcfg_pin);
// same handling, but input from CAN
in2 = READ_CAN(MSGx, SIGxy); // however this is defined on this architecture
out2 = debounce(in2, &debstate_can, &debcfg_can);
// out1 & out2 are now debounced
}

Related

How to increment or decrement a 7-segment display 00-99 using button

i'm using code composer studio and data signals(D0-7) are obtained from tms320f28335
i have incremented and decremented specific led on 7-segment using switch statement. but that is kind if manual incrementing( where i already provide values as shown below)
void display(void)
{
B1 +=1;
if(B1 > B1_lim) { B1 = 0; }
switch(B1)
{
case 0: Display[0] = 0xf7; break; //9
case 1: Display[0] = 0xfF; break; //8
case 2: Display[0] = 0xf0; break; //7
case 3: Display[0] = 0x9F; break;
}
i have also tried to run counter but it never comes out of the loop because of while(1) statement and then i cant use other buttons, the code is shown below it works for 00-99 with never ending. furthermore, i tried to use button in below code but it loops like button can just start the increment/decrement process
void display(void)
{
unsigned char a[]={0xfb,0xb0,0xeD,0xf5,0xb6,0xd7,0x9F,0xf0,0xfF,0xf7}; //[0,1,2,3,4,5,6,7,8,9]
unsigned char b[]={0xfb,0xb0,0xeD,0xf5,0xb6,0xd7,0x9F,0xf0,0xfF,0xf7}; //[0,1,2,3,4,5,6,7,8,9]
int k,j;
while(1)
{
Display[0] = 0xfb;
Display[1] = 0xfb;
DELAY_US(200000L);
for(j=0;j<=9;j++)
{
Display[1]=b[j];
led_display(0x00); DELAY_US(200000L);
for(k=0;k<=9;k++)
{
Display[0] = a[k];
led_display(0x00); DELAY_US(200000L);
DELAY_US(2L);
}
}
}
}
on my 7-segment driver board, I have 4 buttons, and i want to use two of them for incrementing and decrementing.
I would like to know if there is any logical way to use a switch statement such that for each button press I can increment or decrement.
the simplest way show increment is shown in above code but i want to using second code.
I think you should use interrupt for button, don't use while(1) in your function.
Create a function to update value for 7-segment. When you press button, interrupt function will call. In this, you update your value to display.
First of all you need a coherent design following the tight cohesion, loose coupling principles. Your display function is attempting to doo too much - it is not cohesive - it contains logic related to your specific application and unrelated to display. It should do only what its name suggests and display a desired value. For example:
void display( uint8_t value )
{
// 0 1 2 3 4 5 6 7 8 9
static const uint8_t digits[]={0xfb,0xb0,0xeD,0xf5,0xb6,0xd7,0x9F,0xf0,0xfF,0xf7};
if( value < 100 )
{
uint8_t display[2] = {0,0} ;
display[0] = value % 10u ; // least significant digit
display[1] = value / 10u ; // most significant digit
led_display( 0x00 ) ;
}
}
Then let's assume (because you have not told us) that you have some GPIO input for the button and a clock source, hypothetically I will call these getSwitchState() and getClockMillisec(), then you might have a button state function something like:
bool getButtonState()
{
static const uint32_t DEBOUNCE_MILLIS = 20 ;
static uint32_t timestamp = getClockMillisec() ;
static bool debounced_button_state = getSwitchState() ;
uint32_t now = getClockMillisec() ;
uint32_t button_state = getSwitchState() ;
if( now - timestamp > DEBOUNCE_MILLIS &&
button_state != debounced_button_state )
{
debounced_button_state = button_state ;
timestamp = now ;
}
return debounced_button_state ;
}
Then you will want to act only on a button change of state, so you might have:
bool isButtonDownEvent()
{
static bool button_state = getButtonState() ;
bool button_down_event = false ;
// On change of state
if( getButtonState() != button_state )
{
button_state = !button_state ;
// True if button change was released to pressed (down event)
button_down_event = button_state ;
}
return button_down_event ;
}
Then your application loop can be built from those primitives thus:
uint8_t num = 0 ;
for(;;)
{
display( num ) ;
if( isButtonDownEvent() )
{
num++ ;
num %= 99 ;
}
}
Clearly if you have more than one button for separate increment/decrement that will need some modification to handle separate buttons, but since you have provided no information on the button hardware or software interface to it, I'll leave that to you to consider.
Also of course that is not the only possible solution to button input and debounce - you may already have suitable code. Usefully perhaps this method does not need a dedicated h/w timer or interrupt. It does however require regular polling to avoid missing events.
Critically note that the solution has no delays and no blocking, you can do other useful work in the loop whilst maintaining the display (so long as that too is non-blocking and does not take excessive CPU time such that button events might be missed.

How does stdio.h actually print to console (under the hood) in C on Windows?

All the C programming language can do, without importing any libraries, is "basic" programming, i.e. creating for loops, defining a few data types, allocating memory etc.
On the other hand once we include < stdio.h > we are now able to print to the console. What on earth is happening here? How is C, a language with no ability to interface with the outside world "by itself", able to do so?
I know it has something to do with system calls and ultimately being converted to machine code, but my understanding is quite hazy and hand-wavey once I start to really think about it. Could anyone help me conceptually understand how < stdio.h > interfaces with Windows itself to actually display information on the screen?
How is C, a language with no ability to interface with the outside
world "by itself", able to do so?
Every language uses library functions or methods for this purpose.
C language can very easily communicate with the external world on a very low level without using any library functions.
Example (TFT driver registers exposed in the address space):
#define LCD ((LCD_DataReg_Typedef *)(0x60000000))
typedef struct
{
volatile uint16_t reg; /* 0 */
volatile uint16_t data; /* 2 */
}LCD_DataReg_Typedef;
static INLINE void TFT_24_7789_StartWriteGRAM(void)
{
TFT_24_7789_Write_Command(0x002c);
}
static INLINE void TFT_24_7789_Write_Data(uint16_t data)
{
LCD -> data = data;
}
static INLINE uint16_t TFT_24_7789_Read_Data(void)
{
return LCD -> data;
}
static INLINE void TFT_24_7789_Write_Command(uint16_t command)
{
LCD -> reg = command;
}
void TFT_24_7789_SetOrientation(TFT_ORIENTATION orient)
{
switch(orient)
{
case TFT_PORTRAIT:
TFT_24_7789_Write_Command(0x0036);
TFT_24_7789_Write_Data(0b00000000);
g7789.xSize = 240; g7789.ySize = 320;
break;
case TFT_PORTRAIT180:
TFT_24_7789_Write_Command(0x0036);
TFT_24_7789_Write_Data(0b11000000);
g7789.xSize = 240; g7789.ySize = 320;
break;
case TFT_LANDSCAPE:
TFT_24_7789_Write_Command(0x0036);
TFT_24_7789_Write_Data(0b01100000);
g7789.xSize = 320; g7789.ySize = 240;
break;
case TFT_LANDSCAPE180:
TFT_24_7789_Write_Command(0x0036);
TFT_24_7789_Write_Data(0b10100000);
g7789.xSize = 320; g7789.ySize = 240;
break;
default:
return;
break;
}
TFT_24_7789_SetWindow(0, g7789.xSize - 1, 0, g7789.ySize - 1);
}
void TFT_24_7789_Init(void)
{
int n;
TFT_24_7789_Write_Command(0x0011);
//exit SLEEP mode
TFT_delay(100);
//MADCTL: memory data access control
TFT_24_7789_Write_Command(0x003A);
TFT_24_7789_Write_Data(0b01010101);
//COLMOD: Interface Pixel format
TFT_24_7789_Write_Command(0x00B2);
TFT_24_7789_Write_Data(0x000C);
TFT_24_7789_Write_Data(0x0C);
TFT_24_7789_Write_Data(0x00);
TFT_24_7789_Write_Data(0x33);
TFT_24_7789_Write_Data(0x33);
//PORCTRK: Porch setting
TFT_24_7789_Write_Command(0x00B7);
TFT_24_7789_Write_Data(0x0035);
//GCTRL: Gate Control
TFT_24_7789_Write_Command(0x00BB);
TFT_24_7789_Write_Data(0x002B);
//VCOMS: VCOM setting
TFT_24_7789_Write_Command(0x00C0);
TFT_24_7789_Write_Data(0x002C);
//LCMCTRL: LCM Control
TFT_24_7789_Write_Command(0x00C2);
TFT_24_7789_Write_Data(0x0001);
TFT_24_7789_Write_Data(0xFF);
//VDVVRHEN: VDV and VRH Command Enable
TFT_24_7789_Write_Command(0x00C3);
TFT_24_7789_Write_Data(0x0011);
//VRHS: VRH Set
TFT_24_7789_Write_Command(0x00C4);
TFT_24_7789_Write_Data(0x0020);
//VDVS: VDV Set
TFT_24_7789_Write_Command(0x00C6);
TFT_24_7789_Write_Data(0x000F);
//FRCTRL2: Frame Rate control in normal mode
TFT_24_7789_Write_Command(0x00D0);
TFT_24_7789_Write_Data(0x00A4);
TFT_24_7789_Write_Data(0xA1);
//PWCTRL1: Power Control 1
TFT_24_7789_Write_Command(0x00E0);
TFT_24_7789_Write_Data(0x00D0);
TFT_24_7789_Write_Data(0x0000);
TFT_24_7789_Write_Data(0x0005);
TFT_24_7789_Write_Data(0x000E);
TFT_24_7789_Write_Data(0x0015);
TFT_24_7789_Write_Data(0x000D);
TFT_24_7789_Write_Data(0x0037);
TFT_24_7789_Write_Data(0x0043);
TFT_24_7789_Write_Data(0x0047);
TFT_24_7789_Write_Data(0x0009);
TFT_24_7789_Write_Data(0x0015);
TFT_24_7789_Write_Data(0x0012);
TFT_24_7789_Write_Data(0x0016);
TFT_24_7789_Write_Data(0x0019);
//PVGAMCTRL: Positive Voltage Gamma control
TFT_24_7789_Write_Command(0x00E1);
TFT_24_7789_Write_Data(0x00D0);
TFT_24_7789_Write_Data(0x0000);
TFT_24_7789_Write_Data(0x0005);
TFT_24_7789_Write_Data(0x000D);
TFT_24_7789_Write_Data(0x000C);
TFT_24_7789_Write_Data(0x0006);
TFT_24_7789_Write_Data(0x002D);
TFT_24_7789_Write_Data(0x0044);
TFT_24_7789_Write_Data(0x0040);
TFT_24_7789_Write_Data(0x000E);
TFT_24_7789_Write_Data(0x001C);
TFT_24_7789_Write_Data(0x0018);
TFT_24_7789_Write_Data(0x0016);
TFT_24_7789_Write_Data(0x0019);
//NVGAMCTRL: Negative Voltage Gamma control
TFT_24_7789_SetOrientation(TFT_LANDSCAPE);
TFT_delay(10);
TFT_24_7789_Write_Command(0x0029);
}
void TFT_24_7789_SetWindow(uint16_t xStart, uint16_t xEnd, uint16_t yStart, uint16_t yEnd)
{
// TFT_24_7789_Write_Command(0x0028);
TFT_24_7789_Write_Command(0x002A);
TFT_24_7789_Write_Data(xStart >> 8);
TFT_24_7789_Write_Data(xStart & 0xff);
TFT_24_7789_Write_Data(xEnd >> 8);
TFT_24_7789_Write_Data(xEnd & 0xff);
TFT_24_7789_Write_Command(0x002B);
TFT_24_7789_Write_Data(yStart >> 8);
TFT_24_7789_Write_Data(yStart & 0xff);
TFT_24_7789_Write_Data(yEnd >> 8);
TFT_24_7789_Write_Data(yEnd & 0xff);
TFT_24_7789_Write_Command(0x0029);
}
void GUI_PutPixelFAST(uint32_t xpos, uint32_t ypos, uint32_t colour)
{
TFT_24_7789_SetWindow(xpos, xpos, ypos, ypos);
TFT_24_7789_StartWriteGRAM();
TFT_24_7789_Write_Data(colour);
}
/* and much more */
Now we can draw pixel. Using pixels we can write functions which will print character. Then we can print strings. Wihout using even a single standard library functions.

7 Segment Rfid Counter

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.

Arduino problems with array

Hi I am working on a project lighting my house lights using an Arduino.
I have it working in a basic form, now I want to store the previous state of the light switch. I am using an array that stores the current state of the switch so it can be compared on the next loop. I have 7 switches, I have created it so one switch has the ability to turn on many lights, if required.
I need to store the previous state as the next part is to introduce web control the test project for this is already written
The strange thing is zones 7/8 work perfect. the other zones turn on, but don't turn off. When I turn on another switch a light may go off if it's switch is in the off position.
If I remove the condition for the previous state check all switches perfectly fine.
const int zone2[] = {8,13,0};
const int zone3[] = {11,0};
const int zone4[] = {7,0};
const int zone5[] = {9,0};
const int zone6[] = {12,0};
const int zone7[] = {6,0};
const int zone8[] = {14,0};
const int * zones[]={zone2,zone3,zone4,zone5,zone6,zone7,zone8};
int buttonState[] = {0,0}; // variable for reading the pushbutton status
int previousState[]={0,0,0,0,0,0,0,0}; // array for holding the previous state of the input button
void setup()
{
//initialize the output pins that will control lights
pinMode(6,OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9,OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13,OUTPUT);
pinMode(14,OUTPUT);
// initialize the pushbutton pin as an input:
//set all light switches to the same block ie pins 30 - 35
byte i;
//this loop sets all the 30-35pins as inputs
for (i=30;i< 37;i++) {
pinMode(i, INPUT);
digitalWrite(i,HIGH); // this makes it connect to the internal resistor
}
}
void loop()
{
int arrayPosition;
for (int z = 0; z < 7; ++z)//zero based array needs to be as many as there are zones!!!
{
buttonState[z] = digitalRead(z+30);
for (arrayPosition = 0;zones[z][arrayPosition] ; arrayPosition++)
{
if ((buttonState[z] == HIGH) ) && (previousState[z] == LOW )) {
// turn LED on:
digitalWrite(zones[z][arrayPosition],HIGH);
}
else if ((buttonState[z] == LOW) && (previousState[z] == HIGH )) {
// turn LED off;
digitalWrite(zones[z][arrayPosition],LOW);
}
}
//each light assigned to the zone has been turned on or off, now set previousstate
//the statement is here the inner loop has finsihed turning lights on or off that belong to that zone
previousState[z] = buttonState[z];
}
}
You allocate two elements to the array buttonState, but you are accessing seven:
[…]
int buttonState[] = {0,0};
[…]
for (int z = 0; z < 7; ++z)
{
buttonState[z] = digitalRead(z+30);
[…]
You should increase the number of elements in the buttonState array to be as many as you actually need.
It might be better to define the size of the arrays at a single place:
#define NUM_ZONES (7)
[…]
const int * zones[NUM_ZONES]={zone2,zone3,zone4,zone5,zone6,zone7,zone8};
int buttonState[NUM_ZONES] = {0,0,0,0,0,0,0};
int previousState[NUM_ZONES]={0,0,0,0,0,0,0};
[…]
for (int z = 0; z < NUM_ZONES; ++z)
[…]
This will also reveal that your previousState array is larger than needed (the compiler will complain that you have too many initialisers for that array). It is then important to re-use that constant everywhere instead of writing the magic number 7.

Inline assembly in C: INT command and C variables

I'm trying to use assembly in C code using C variables.
My code looks like this:
__asm { INT interruptValue };
Where 'interruptValue' is a variable I get from the user (e.g 15 or 15h).
When I try to compile I get:
Assembler error: 'Invalid instruction
operands'
I don't know what is the correct type for interruptValue . I tried long\int\short\char\char* but none of them worked.
The INT opcode does not allow to specify a variable (register or memory) as an argument. You have to use a constant expression like INT 13h
If your really want to call variable interrupts (and I cannot imagine any case for doing so), use something like a switch statement to decide which interrupt to use.
Something like this:
switch (interruptValue)
{
case 3:
__asm { INT 3 };
break;
case 4:
__asm { INT 4 };
break;
...
}
EDIT:
This is a simple dynamic aproach:
void call_interrupt_vector(unsigned char interruptValue)
{
//the dynamic code to call a specific interrupt vector
unsigned char* assembly = (unsigned char*)malloc(5 * sizeof(unsigned char));
assembly[0] = 0xCC; //INT 3
assembly[1] = 0x90; //NOP
assembly[2] = 0xC2; //RET
assembly[3] = 0x00;
assembly[4] = 0x00;
//if it is not the INT 3 (debug break)
//change the opcode accordingly
if (interruptValue != 3)
{
assembly[0] = 0xCD; //default INT opcode
assembly[1] = interruptValue; //second byte is actual interrupt vector
}
//call the "dynamic" code
__asm
{
call [assembly]
}
free(assembly);
}

Resources