/* * biwire.h */ #ifndef BIWIRE_H_ #define BIWIRE_H_ // Define connections (PORTC only!) #define BWPORT1 1 #define BWPORT2 2 // Set pin functions #define ADCPORT BWPORT1 #define GNDPORT BWPORT2
If suddenly you had one extra leg, then it is quite possible to use its pull-up in this place instead of the resistor R1 // State for interrupt loop #define ADC_SET 0x01 #define ADC_WAIT 0x02 // LED flashing frequency (in parrots, without small wing) #define SCAN_FREQ 48 // Work together with SCAN_FREQ to get orange color #define MAX_COLOR_MIX 50 // Event bits when scan complete // Main keyboard event indicator - must be cleared after handle #define BW_EVENT 0x80 // Button pressed #define BW_EVBT1 0x01 #define BW_EVBT2 0x02 // add additional flags here, like BW_EVBT3=0x04 // EVFIN - event "Indication finished" #define BW_EVFIN 0x10 // Button released #define BW_EVOPN 0x20 // If button holds more than BW_LONG_PRESS cycles #define BW_EVLNG 0x40 // How long we wait for 'LongPress' #define BW_LONG_PRESS 10
Here you can write approximate values plus or minus bus stop. The main thing for us is to set boundaries for the precise definition of buttons. // ADC compare values #define BW_VBTN1 0x1F #define BW_VBTN2 0xFE // Define names for signal array enum SignalNames { LedOff = 0, OneGreen = 1, OneRed = 2, OneOrange = 3, TwoGreen = 4, TwoRed = 5, Alarm = 6, OrangeRG = 7
Similarly, add your signals to taste }; // ====== Externals ======= // LED sequence to indicate extern volatile enum SignalNames BwIndicate; // Data about button events extern volatile uint8_t BwButton; // If not null - delay of led indication, then switch LED off extern volatile uint8_t BwLedDelay; // Call on start void biwire_init(void); #endif /* BIWIRE_H_ */
/* * biwire.c */ #include <avr/io.h> #include <avr/interrupt.h> #include <stdint.h> #include "biwire.h" // Led blink matrix. Only upper 6 bits used for indication loop static uint8_t bwsignalsR[] = { 0x00, 0x00, 0xC0, 0xC0,0x00, 0xA0, 0xA0, 0xFC }; static uint8_t bwsignalsG[] = { 0x00, 0xC0, 0x00, 0xC0,0xA0, 0x00, 0x50, 0xC0 }; // LED sequence to indicate volatile enum SignalNames BwIndicate; // FSM and sequence counter static volatile uint8_t BwState; // "LongPress" counter static volatile uint8_t btn_cnt; // Data about button events volatile uint8_t BwButton; // Orange color created by mixing green and red static volatile uint8_t color_mix; // If not null - delay of led indication, then switch LED off volatile uint8_t BwLedDelay; void biwire_init(void) { // Timer interrupt TCCR0B = _BV(CS00) | _BV(CS01) | _BV(WGM02); // use CLK/64 prescale value, clear timer/counter on compareA match OCR0A = SCAN_FREQ; // preset timer0 OCR byte TIMSK0 = _BV(OCIE0A); // enable Output Compare 0 overflow interrupt // PIN init DDRC |= _BV(BWPORT1) | _BV(BWPORT2); // ADC init ADCSRA = _BV(ADEN) | _BV(ADPS2); // Activate ADC with Prescaler 16 --> 1Mhz/16 = 62.5kHz ADMUX = ADCPORT | _BV(REFS0) | _BV(REFS1) | _BV(ADLAR); // Select input pin using MUX, left adjust and Internal reference
Atmel's internal reference is just 1.1 volts, which in the absence of pressed buttons gives a reliable and impenetrable 0xFF at the ADC output. DIDR0 = _BV(ADCPORT); // disable digital input for ADC // Set initial state for bw BwState = ADC_SET; BwIndicate = OneGreen; BwButton = BW_EVOPN; color_mix = 0; BwLedDelay = 0; }
Timer interruption can be customized to your liking. Playing dividers and color_mix you can select the speed of the flash and the reaction time at the touch of a button. SIGNAL (SIG_OUTPUT_COMPARE0A) // handler for Output Compare 0 overflow interrupt { if (BwState&ADC_SET) { // Prepare to keyscan PORTC &= ~_BV(GNDPORT); DDRC &= ~_BV(ADCPORT); ADCSRA |= _BV(ADSC); //Start conversion BwState = ADC_WAIT; color_mix = MAX_COLOR_MIX; if (BwLedDelay) if (!(--BwLedDelay)) { BwIndicate = LedOff; BwButton |= BW_EVFIN; } } else { if (BwState&ADC_WAIT) { // Check if ADC complete if (ADCSRA & _BV(ADSC) ) return; // Wait another tick PORTB = ADCH; if (ADCH > BW_VBTN2) { // Both buttons open if (BwButton & (BW_EVBT1|BW_EVBT2)) { BwButton = BW_EVENT + BW_EVOPN; btn_cnt = 0; } } else if (ADCH > BW_VBTN1) { // Button 2 pressed PORTB ^= 0xff; if (BwButton & (BW_EVBT1|BW_EVOPN)) { BwButton = BW_EVENT + BW_EVBT2; btn_cnt = 1; }
Similarly, you can determine the range for the third and all subsequent buttons, but with accuracy of operation you will have to tinker } else { // Button 1 pressed. Add compare values here if you need more than 2 buttons if (BwButton & (BW_EVBT2|BW_EVOPN)) { BwButton = BW_EVENT + BW_EVBT1; btn_cnt = 1; } } if (btn_cnt) { if(++btn_cnt > BW_LONG_PRESS) { // New event - long press BwButton |= BW_EVENT + BW_EVLNG; btn_cnt = 0; } } // Cleanup if (!(BwButton&(BW_EVBT1|BW_EVBT2|BW_EVOPN))) BwButton = BW_EVOPN; }
If you don’t skip through the ADC_WAIT state here, then you can add the seventh bit to the LED blinking cycle. But I have six bits enough. And for a sixteen-bit BwState, you can make a crazy disco in general from LEDs. if (color_mix++ > MAX_COLOR_MIX) { // Get next state (LED sequence) BwState = (BwState >> 7) | (BwState << 1); // rotate left, replace by _asm to compact code // Indicate DDRC |= _BV(ADCPORT); PORTC &= ~(_BV(GNDPORT)|_BV(ADCPORT)); if(bwsignalsR[BwIndicate]&BwState) PORTC |= _BV(BWPORT2); if((bwsignalsG[BwIndicate]&BwState) && (!(bwsignalsR[BwIndicate]&BwState)) ) PORTC |= _BV(BWPORT1); color_mix = 0; } if((bwsignalsR[BwIndicate]&BwState) && (bwsignalsG[BwIndicate]&BwState)){ PORTC ^= (_BV(BWPORT1) | _BV(BWPORT2)); } } }
#include "biwire.h" int main(void) { biwire_init(); sei(); while (1) { if ( BwButton & BW_EVENT ) { BwButton &= ~BW_EVENT; // Clear event flag if (BwButton & BW_EVLNG) { BwIndicate = OrangeRG; BwLedDelay = 5; } else if (BwButton & BW_EVOPN) { //BwIndicate = LedOff; } else if (BwButton & BW_EVBT1) { BwIndicate = OneRed; BwLedDelay = 5; } else if (BwButton & BW_EVBT2) { BwIndicate = OneGreen; BwLedDelay = 5; } } } }
Source: https://habr.com/ru/post/141696/
All Articles