include "p24F32KA304.inc"
.section __FWDT.sec, code
.global __FWDT
__FWDT: .pword FWDTEN_OFF
.section __FDS.sec, code
.global __FDS
__FDS: .pword DSWDTEN_OFF
.section __FICD.sec, code
.global __FICD
__FICD: .pword ICS_PGx1
.section __FOSCSEL.sec, code
.global __FOSCSEL
__FOSCSEL: .pword FNOSC_PRI & OSCIOFNC_ON & LPRCSEL_HP & IESO_OFF & POSCFREQ_HS
.section __FOSC.sec, code
.global __FOSC
__FOSC: .pword FNOSC_PRI & OSCIOFNC_ON & LPRCSEL_HP & IESO_OFF & POSCFREQ_HS
.section ivt, code
; goto __reset
.pword paddr(KeyScanISR)
.global KeyScanISR
.text
KeyScanISR:
bra KeyScanISR
.global __reset
.text
__reset:
bra __reset
.end
! Be aware the code was only tested in simulator, not in real world./*
* Keybordscanner.c
*
* Created: 04.01.2019 20:00:53
* Author : USER
*/
#define F_CPU 20000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
//********* Testvariablen ***********
uint8_t senddata = 0;
//MiDi variables
#define noteon 0x90 //Note On Byte
#define noteoff 0x80 //Note Off Byte
#define bufferlengh 168 //Bufferlengh for MiDi messages
#define transpose 20 //Transpose of MiDi notes
uint8_t midibuffer [bufferlengh]; //Buffer for MiDi messages
uint8_t midiwrite = 0; //MiDi buffer writepointer
uint8_t midiread = 0; //MiDi buffer readpointer
//scan process variables
#define idle 0
#define notestart 1
#define midisent 2
volatile uint8_t nextscan = 0; //Next Keyboard scan started
//Velocity counter
uint8_t note_velocity [56] =
{
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128
};
//State memory
uint8_t note_state [56] =
{
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
};
//Scan process will start every 300µs
ISR (TIMER1_COMPA_vect)
{
nextscan++;
}
void scan_keyboard (void)
{
uint8_t buffer1 = 0, buffer2 = 0, zeiger = 0, scanloop = 0, i = 0;
/* Outer loop - Pulls down the lines after the diodes */
for(scanloop=0;scanloop<8;scanloop++)
{
buffer1 = PINA;
buffer2 = PINC;
PORTB = ~(1<<(scanloop+1));
//Inner loop readout start velocity and end velocity lines and generate MiDi messages
for(i=0;i<7;i++)
{
//Pointer to actual note
zeiger = i*8 + scanloop;
//Scan velocity start first sample
if((buffer1 & (1<<i)) == 0)
{
if(note_state[zeiger] == idle) //Velocity counting begins
{
note_state[zeiger] = notestart;
}
if(note_state[zeiger] == notestart)
{
if(note_velocity[zeiger] > 1) //Velocity counting go on
{
note_velocity[zeiger]--;
}
}
}
else
{
/*MiDi sent, back to idle or send note off
when key pressed a little and go back to off state
the mode go back to idle.
When a complete scan was processed a Note On velocity 0 was sent */
if(note_state[zeiger] == notestart)
{
note_state[zeiger] = idle;
note_velocity[zeiger] = 128;
}
if(note_state[zeiger] == midisent)
{
midibuffer[midiwrite] = noteon;
midiwrite++;
if(midiwrite>bufferlengh){ midiwrite=0; }
midibuffer[midiwrite]= zeiger+transpose;
midiwrite++;
if(midiwrite>bufferlengh){ midiwrite=0; }
midibuffer[midiwrite]= 0;
midiwrite++;
if(midiwrite>bufferlengh){ midiwrite=0; }
note_state[zeiger] = idle;
}
}
//Velocity Stop second sample generate MiDi
if((buffer2 & (1<<i)) == 0)
{
if(note_state[zeiger] != midisent)
{
midibuffer[midiwrite++] = noteon;
if(midiwrite>bufferlengh){ midiwrite=0; }
midibuffer[midiwrite++]= zeiger+transpose;
if(midiwrite>bufferlengh){ midiwrite=0; }
midibuffer[midiwrite++]= note_velocity[zeiger];
if(midiwrite>bufferlengh){ midiwrite=0; }
note_state[zeiger]=midisent;
note_velocity[zeiger] = 128;
}
}
}
}
}
int main(void)
{
// Declare your local variables here
// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);
// Port B initialization
// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
// State: Bit7=0 Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
// Port C initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
// Port D initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=0xFF
// OC0 output: Disconnected
TCCR0=(0<<WGM00) | (0<<COM01) | (0<<COM00) | (0<<WGM01) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 2500,000 kHz
// Mode: Normal top=0xFFFF
// OC1A output: Clear on compare match
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 26,214 ms
// Output Pulse(s):
// OC1A Period: 26,214 ms
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x02;
OCR1AL=0xEE; //was 0x7F
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0<<AS2;
TCCR2=(0<<FOC2) | (0<<COM21) | (0<<COM20) | (0<<WGM21) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;
OCR2=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (1<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (0<<TOIE0);
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=(0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
MCUCSR=(0<<ISC2);
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 31250
UCSRA=(0<<RXC) | (0<<TXC) | (0<<UDRE) | (0<<FE) | (0<<DOR) | (0<<UPE) | (0<<U2X) | (0<<MPCM);
UCSRB=(0<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (0<<RXEN) | (1<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
UCSRC=(1<<URSEL) | (0<<UMSEL) | (0<<UPM1) | (0<<UPM0) | (0<<USBS) | (1<<UCSZ1) | (1<<UCSZ0) | (0<<UCPOL);
UBRRH=0x00;
UBRRL=0x27;
// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator's positive input is
// connected to the AIN0 pin
// The Analog Comparator's negative input is
// connected to the AIN1 pin
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
SFIOR=(0<<ACME);
// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);
// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);
// TWI initialization
// TWI disabled
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);
// Globally enable interrupts
sei();
PORTB=0b11111110; //Prepare for first scan cycle
while (1)
{
if(nextscan > 0)
{
scan_keyboard();
nextscan=0;
PORTB = 0b11111110; //Prepare for next round
}
//send MiDi data out of the NiDi buffer
while((midiread != midiwrite)&&((UCSRA & (1<<UDRE))>0))
{
senddata = midibuffer[midiread];
UDR = midibuffer[midiread];
midiread++;
if(midiread>bufferlengh){midiread = 0;}
}
}
}
There is not much processing time for other tasks.
I guess it's better to use a dedicated controller for keybed scanning process, when using velocity.
Using of assembler will be a must, when want scanning speed up.
Some of the MC PIC24's will run at 140Mhz ( 70 MIPS ) up to 1024kb program, loads of peripherals, memory and I/O ports ...I don't think a microchip controller works much faster.
The ATMEGA runs nearly with 20 MIPS at 20 MHz ( XMEGA is faster too ).PIC mcu was running at 20Mhz (5 MIPS) as it will do 125k baud exactly !
For info, the pic24 and dspic33 use the same assembly language - there may be more examples available for them.
It does not look like Microchip really expect people to use assembly for these devices; it uses the x16 C compiler as an assembler, which makes things a bit odd possibly.
Re. other posts - I'd advise doing the key scan in a high speed timer "clock" interrupt, read the inputs then update the scan outputs each interrupt.
The time between interrupts allows for any changes to settle at the input side, ready for the next cycle.
That does away with any hold up due to looping through things. The CPU is easily fast enough to do everything you need!
I had some questions about the switch from 5V to 3.3V.
mj
I think You should make some thoughts about the used Display.
When the 2.3" OLED is too big, You can also use a 0.93" one.
Like this:
**broken link removed**
When I would make such a thing like a keytar i would implement splitting points and a free routing of controllers to the pots.
And free set of a MiDi channel to the splitted keys and additional a transpose function.
Additional some presets to space different settings.
A normal 3*7 segment can only display numbers but no alphanumeric characters.
For normal use You can take a big font and let it look like a 7 Segment Display.
For make Settings it is possible to use up to 8 lines with 20 characters.
The disadvantage is that to transfer much more bytes to the display, but with hardware SPI this should be no problem.
But it's Your decision, of course.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?