Дребезг контактов побежден!
Итак, для начала немного матчасти
Получается, что если думать что мир идеален и писать программу для обрабтки нажатых клавиш, то реальность поворачивается к нам нелицеприятной стороной и на тесте в железе мы видим что вместо одной реакции по нажатию и отпусканию клавиши мы видим пачку повторных в количестве от 2-х до 10 - тестировалась старая кнопка спаянная с китайского телевизора.
Так что же делать?
Написать алгоритм, который будет отсеивать лишние импульсы!
Логика в трех словах примерно такая.
При первом входящем импульсе - замыкание клавиши программа переходит в режим фиксации, включает таймер и отсчитывает время и пока оно не достигнет нужного вменяемого интервала, все импульсы-РАЗМЫКАНИЯ клавиши игнорируются. Если подобрать интервал близкий к скоростной игре на пианино одним пальцем - 1/32 120bpm будет достаточно, то практически всегда левые импульсы и искры при замыкании клавиши будут фильтроваться.
И наоборот.
Первое входящее разрешенное размыкание тоже фиксируется, отсчитывается таймером и пока время не достигнет нужного вменяемого интервала и все импульсы-ЗАМЫКАНИЯ клавиши игнорируются.
Итог. Рабочий 100% код, который протестирован на железе. Это просто потрясающе и гениально. Работает как часы. Идею продумывал сам.
Поскольку клавиш не одна, а 64, то пришлось эмулировать на базе одного железного таймера 64 пары независимых программных таймера, чтобы клавиши работали неазвисимо.
Задаем ваши ответы))
// _ASSERT_ENABLE_ is used for enabling assert, typical for debug purposes
#define _ASSERT_ENABLE_
#include <string.h>
#include "compiler.h"
#include "main.h"
/**
* \def BUFFER_SIZE
* \brief The size of the UART buffer
*/
#define BUFFER_SIZE 240
// set the correct BAUD and F_CPU defines before including setbaud.h
#include "conf_clock.h"
#include "conf_uart.h"
/**
* \name avr_libc_inc avr libc include files
* @{
*/
#include <util/setbaud.h>
#include <avr/interrupt.h>
//! @}
#include "ring_buffer.h"
#include "lcd.h"
// buffers for use with the ring buffer (belong to the UART)
uint8_t out_buffer[BUFFER_SIZE];
uint8_t in_buffer[BUFFER_SIZE];
uint8_t chan=0;
uint8_t onchan=0x90;
uint8_t offchan=0x80;
uint16_t timer[64];
uint8_t timer1[64];
uint8_t timer2[64];
uint16_t tmd=15625;
uint8_t timerof[64];
uint8_t maxt=7;
uint8_t ecount=1;
char timestr[] = "";
// the string we send and receive on UART
const char test_string[] = " Kingdom MIDI Board^^!";
//! ring buffer to use for the UART transmission
struct ring_buffer ring_buffer_out;
//! ring buffer to use for the UART reception
struct ring_buffer ring_buffer_in;
/**
* \brief UART data register empty interrupt handler
*
* This handler is called each time the UART data register is available for
* sending data.
*/
ISR(UART0_DATA_EMPTY_IRQ)
{
// if there is data in the ring buffer, fetch it and send it
if (!ring_buffer_is_empty(&ring_buffer_out)) {
UDR0 = ring_buffer_get(&ring_buffer_out);
}
else {
// no more data to send, turn off data ready interrupt
UCSR0B &= ~(1 << UDRIE0);
}
}
/**
* \brief Data RX interrupt handler
*
* This is the handler for UART receive data
*/
ISR(UART0_RX_IRQ)
{
ring_buffer_put(&ring_buffer_in, UDR0);
}
/**
* \brief Initialize the UART with correct baud rate settings
*
* This function will initialize the UART baud rate registers with the correct
* values using the AVR libc setbaud utility. In addition set the UART to
* 8-bit, 1 stop and no parity.
*/
ISR(TIMER1_OVF_vect)
{
if (ecount) {
for (int i = 0; i < 64; i++){
if (timer) {
if (timer1<maxt){
timer1++;
setpos(14,0);
sprintf(timestr, "%d", timer1);
str_lcd(timestr);
setpos(14,1);
sprintf(timestr, "%d", timer2);
str_lcd(timestr);
}
}
else
{
if ((timer2<maxt)){
timer2++;
setpos(14,0);
sprintf(timestr, "%d", timer1);
str_lcd(timestr);
setpos(14,1);
sprintf(timestr, "%d", timer2);
str_lcd(timestr);
}
}
}
}
}
/*ISR(TIMER1_OVF_vect)
{
for (int i = 0; i < 64; i++){
timerof = 1;
}
}
*/
static void timer_ini(void)
{
//TCCR1B |= (1<<WGM12); // устанавливаем режим СТС (сброс по совпадению)
//TIMSK |= (1<<OCIE1A); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
//OCR1AH = 0b01111010; //записываем в регистр число для сравнения
//OCR1AL = 0b00010010;
//TCCR1B |= (1<<CS02)|(1<<CS00);//установим делитель.
//TIMSK0|=(1<<TOIE0);
TCCR1B |= /*(1<<CS11)|*/(1<<CS10);//установим делитель.
TIMSK1|=(1<<TOIE1);
//TCCR1B |= (1<<CS11);//установим делитель.
}
static void timer_stop(void)
{
TCCR1B = 0;//установим делитель.
}
static void timer_play(void)
{
TCCR1B |= /*(1<<CS11)|*/(1<<CS10);//установим делитель.
}
static void uart_init(void)
{
#if defined UBRR0H
// get the values from the setbaud tool
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#else
#error "Device is not supported by the driver"
#endif
#if USE_2X
UCSR0A |= (1 << U2X0);
#endif
// enable RX and TX and set interrupts on rx complete
UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
// 8-bit, 1 stop bit, no parity, asynchronous UART
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00) | (0 << USBS0) |
(0 << UPM01) | (0 << UPM00) | (0 << UMSEL01) |
(0 << UMSEL00);
// initialize the in and out buffer for the UART
ring_buffer_out = ring_buffer_init(out_buffer, BUFFER_SIZE);
ring_buffer_in = ring_buffer_init(in_buffer, BUFFER_SIZE);
}
/**
* \brief Function for putting a char in the UART buffer
*
* \param data the data to add to the UART buffer and send
*
*/
static inline void uart_putchar(uint8_t data)
{
// Disable interrupts to get exclusive access to ring_buffer_out.
cli();
if (ring_buffer_is_empty(&ring_buffer_out)) {
// First data in buffer, enable data ready interrupt
UCSR0B |= (1 << UDRIE0);
}
// Put data in buffer
ring_buffer_put(&ring_buffer_out, data);
// Re-enable interrupts
sei();
}
static inline void noteon(uint8_t data)
{
uart_putchar(onchan);
uart_putchar(data);
uart_putchar(0x7F);
}
static inline void noteoff(uint8_t data)
{
uart_putchar(offchan);
uart_putchar(data);
uart_putchar(0x7F);
}
/**
* \brief Function for getting a char from the UART receive buffer
*
* \retval Next data byte in recieve buffer
*/
static inline uint8_t uart_getchar(void)
{
return ring_buffer_get(&ring_buffer_in);
}
/**
* \brief Function to check if we have a char waiting in the UART receive buffer
*
* \retval true if data is waiting
* \retval false if no data is waiting
*/
static inline bool uart_char_waiting(void)
{
return !ring_buffer_is_empty(&ring_buffer_in);
}
/**
* \brief The main application
*
* This application will initialize the UART, send a character and then receive
* it and check if it is the same character as was sent.
*
* \note The RX and TX pins should be externally connected in order to pass the
* test.
*/
int main(void)
{
uint8_t data;
uint8_t cnt;
uint8_t i;
uint8_t note;
uint8_t sta[8];
uint8_t pra[8];
uint8_t rta[8];
uint8_t rnz[8];
uint8_t /*sta=0xFF, */stb=0xFF, stc=0xFF, std=0xFF, ste=0x00, stf=0xFF, stg=0xFF, sth=0xFF, stj=0xFF, stk=0xFF, stl=0x00;
uint8_t /*pra=0xFF, */prb=0xFF, prc=0xFF, prd=0xFF, pre=0x00, prf=0xFF, prg=0xFF, prh=0xFF, prj=0xFF, prk=0xFF, prl=0x00;
uint8_t /*rta=0xFF, */rtb=0xFF, rtc=0xFF, rtd=0xFF, rte=0x00, rtf=0xFF, rtg=0xFF, rth=0xFF, rtj=0xFF, rtk=0xFF, rtl=0x00;
uint16_t timert=0, timerd=0;
char test_string1[] = "Hi1^^!";
char test_string2[] = "Hi2^^!";
char notestr[] = "";
PORTA=0xFF;
DDRA=0x00;//0
PORTB=0x00;
DDRB=0xFF;//1 out scan
PORTC=0xFF;
DDRC=0x00;//2
PORTD=0xFF;
DDRD=0x00;//3
PORTE=0x00;
DDRE=0xFF;//out USART and MIDI CHANNEL LED
PORTF=0xFF;
DDRF=0x00;//4
PORTG=0xFF;
DDRG=0x00;//in control
PORTH=0xFF;
DDRH=0x00;//5
PORTJ=0xFF;
DDRJ=0x00;//6
PORTK=0xFF;
DDRK=0xFF;//7 out scan
PORTL=0x00;
DDRL=0xFF;//out LEDS
cli();
for (int i = 0; i < 64; i++){
timer1 = maxt;
timer2 = maxt;
timer = 0;
}
timer_ini();
for (int j = 0; j < 8; j++){
pra[j]=0xFF;
sta[j]=0xFF;
}
rnz[0]=0b11111110;
rnz[1]=0b11111101;
rnz[2]=0b11111011;
rnz[3]=0b11110111;
rnz[4]=0b11101111;
rnz[5]=0b11011111;
rnz[6]=0b10111111;
rnz[7]=0b01111111;
uart_init();
sei();
LCD_ini(); //Инициализируем дисплей
setpos(4,0);
str_lcd("Kingdom>");
setpos(0,1);
str_lcd("MIDI Board ^_^");
// Send the test string
for (cnt = 0; cnt < strlen(test_string); cnt++) {
uart_putchar(test_string[cnt]);
}
// Check if we have received the string we sent
cnt = 0;
do
{
for (uint8_t j = 0; j < 8; j++)
{
prk=rnz[j];
PORTK = prk;
PORTK = prk;
asm("nop");
asm("nop");
pra[j]=PINA;
rta[j]=pra[j]^sta[j];
for (int i = 0; i < 8; i++)
{
note = (j<<3)+i;
if (rta[j]&(1<<i))
{
if (sta[j]&(1<<i))
{
if (timer2[note]==maxt)
{
timer_stop();
noteon(note);
sta[j]=pra[j];
timer1[note] = 0;
timer[note] = 1;
clearlcd();
setpos(0,0);
str_lcd("Note ON");
setpos(0,1);
sprintf(notestr, "%d", note);
str_lcd(notestr);
timer_play();
}
}
else
{
if (timer1[note]==maxt)
{
timer_stop();
noteoff(note);
sta[j]=pra[j];
timer2[note] = 0;
timer[note] = 0;
clearlcd();
setpos(0,0);
str_lcd("Note OFF");
setpos(0,1);
sprintf(notestr, "%d", note);
str_lcd(notestr);
timer_play();
}
}
}
}
}
} while (1);
}
Comments
Pinned comments
Регулировка скорости.
В атмеге2560 портов столько что можно еще 64 клавиши подрубить - миди контроллеры и модуляции
-Или .уй пополам! или писда вдребезги!!!)))