void InitializeTimer(TIM_TimeBaseInitTypeDef* timerInitStructure, uint32_t DelayFreq) { /* Enable clock for TIM2 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // Set the Prescaler to zero before calculation timerInitStructure->TIM_Prescaler = 0; // Used to see if the the counter divides in evenly. It is an easy way to check if the counter is correctly set while(1) { if( ((2*84000000/DelayFreq)%(timerInitStructure->TIM_Prescaler + 1)-1) ==0) break; else // Increment the Prescaler and continue while loop timerInitStructure->TIM_Prescaler += 1; // check to see if the code is runing out of range. Hopefully this shouldn't happen if (timerInitStructure->TIM_Prescaler >= 65535 && timerInitStructure->TIM_Prescaler >= 65535) { #ifdef SWOPRINT TM_SWO_Printf("Presaler and period for timer are going out of range :("); #endif break; } } timerInitStructure->TIM_Period = (2*84000000)/((timerInitStructure->TIM_Prescaler + 1)*DelayFreq)-1; /* Count up */ timerInitStructure->TIM_CounterMode = TIM_CounterMode_Up; // Set the reset of the required entries timerInitStructure->TIM_ClockDivision = TIM_CKD_DIV1; timerInitStructure->TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, timerInitStructure); TIM_Cmd(TIM2, ENABLE); }
int main(void) { /* complete state of the keyboard split into nibbles according to the FPGA transfer protocol */ unsigned char nibbles[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* map the pins of the DM65PIC to the columns and rows of the C65 and C64 keyboard a very good source of information is chapter 2.1.2 in the following document: http://www.zimmers.net/cbmpics/cbm/c65/c65manual.txt */ struct GPIO_Mapping { GPIO_TypeDef* GPIOx; /* STM32 GPIO port */ uint16_t GPIO_Pin; /* pin within the specified GPIO port */ }; const char Size_ColMapping_C65 = 9; struct GPIO_Mapping Columns_C65[Size_ColMapping_C65] = { GPIOE, GPIO_Pin_9, /* C0 */ GPIOE, GPIO_Pin_10, /* C1 */ GPIOE, GPIO_Pin_11, /* C2 */ GPIOE, GPIO_Pin_12, /* C3 */ GPIOE, GPIO_Pin_13, /* C4 */ GPIOE, GPIO_Pin_14, /* C5 */ GPIOE, GPIO_Pin_15, /* C6 */ GPIOC, GPIO_Pin_0, /* C7 */ GPIOC, GPIO_Pin_1 /* C8 */ }; const char Size_RowMapping_C65 = 11; struct GPIO_Mapping Rows_C65[Size_RowMapping_C65] = { GPIOE, GPIO_Pin_0, /* R0 */ GPIOE, GPIO_Pin_1, /* R1 */ GPIOE, GPIO_Pin_2, /* R2 */ GPIOE, GPIO_Pin_3, /* R3 */ GPIOE, GPIO_Pin_4, /* R4 */ GPIOE, GPIO_Pin_5, /* R5 */ GPIOE, GPIO_Pin_6, /* R6 */ GPIOE, GPIO_Pin_7, /* R7 */ GPIOE, GPIO_Pin_8, /* R8 is exclusively used for the CAPS LOCK aka ASCII/DIN key */ GPIOC, GPIO_Pin_2, /* K1 special "row 9" used for scanning the CURSOR UP key */ GPIOC, GPIO_Pin_3 /* K2 special "row 10" used for scanning the CURSOR LEFT key */ }; struct GPIO_Mapping Restore_C65 = { GPIOC, GPIO_Pin_15 /* RESTORE key */ }; const char Size_ColMapping_C64 = 8; struct GPIO_Mapping Columns_C64[Size_ColMapping_C64] = { GPIOD, GPIO_Pin_8, /* C0 */ GPIOD, GPIO_Pin_9, /* C1 */ GPIOD, GPIO_Pin_10, /* C2 */ GPIOD, GPIO_Pin_11, /* C3 */ GPIOD, GPIO_Pin_12, /* C4 */ GPIOD, GPIO_Pin_13, /* C5 */ GPIOD, GPIO_Pin_14, /* C6 */ GPIOD, GPIO_Pin_15 /* C7 */ }; const char Size_RowMapping_C64 = 8; struct GPIO_Mapping Rows_C64[Size_RowMapping_C64] = { GPIOD, GPIO_Pin_0, /* R0 */ GPIOD, GPIO_Pin_1, /* R1 */ GPIOD, GPIO_Pin_2, /* R2 */ GPIOD, GPIO_Pin_3, /* R3 */ GPIOD, GPIO_Pin_4, /* R4 */ GPIOD, GPIO_Pin_5, /* R5 */ GPIOD, GPIO_Pin_6, /* R6 */ GPIOD, GPIO_Pin_7 /* R7 */ }; struct GPIO_Mapping Restore_C64 = { GPIOC, GPIO_Pin_14 /* RESTORE key */ }; /* joystick mapping at the FPGA's JB port: GPIO => nibble-pos and bit-pos nbl #18 : joystick 1 : bit0=up, bit1=down, bit2=left, bit3=right nbl #19 : bit0=joy1 fire, bit2=capslock key status, bit3=restore key status nbl #20 : joystick 2 : bit0=up, bit1=down, bit2=left, bit3=right nbl #21 : bit0=joy2 fire, bit3=reset momentary-action switch status in C65 mode, which is the default, port #1 and #2 are swapped due to the way, how the DM65PIC is located in the MEGA65 body housing; this is why in below-mentioned table, joystick #1's left is going to nbl #20 instead of #18 */ const char Size_JoyMapping = 10; struct { GPIO_TypeDef* GPIOx; uint16_t GPIO_Pin; char nibble_count; char bit_count; } Joystick[Size_JoyMapping] = { GPIOC, GPIO_Pin_6, 20, 2, /* Joystick #1 LEFT */ GPIOC, GPIO_Pin_7, 20, 3, /* Joystick #1 RIGHT */ GPIOC, GPIO_Pin_4, 20, 0, /* Joystick #1 UP */ GPIOC, GPIO_Pin_5, 20, 1, /* Joystick #1 DOWN */ GPIOC, GPIO_Pin_12, 21, 0, /* Joystick #1 BUTTON */ GPIOC, GPIO_Pin_10, 18, 2, /* Joystick #2 LEFT */ GPIOC, GPIO_Pin_11, 18, 3, /* Joystick #2 RIGHT */ GPIOC, GPIO_Pin_9 , 18, 0, /* Joystick #2 UP */ GPIOC, GPIO_Pin_8, 18, 1, /* Joystick #2 DOWN */ GPIOC, GPIO_Pin_13, 19, 0 /* Joystick #2 BUTTON */ }; struct GPIO_Mapping LEDs[2] = { GPIOB, GPIO_Pin_5, /* Power LED */ GPIOB, GPIO_Pin_4 /* FDD LED */ }; const char ledPower = 0; const char ledFDD = 1; /* positions of special keys within the matrix */ const char COL_CSR = 0; const char ROW_CSR_UP = 9; const char ROW_CSR_LEFT = 10; const char ROW_CAPSLOCK = 8; /* positions of special keys within the nibbles array */ const char NIBBLE_CSR_DOWN = 1; const char BIT_CSR_DOWN = 3; const char NIBBLE_CSR_RIGHT = 0; const char BIT_CSR_RIGHT = 2; const char NIBBLE_RIGHT_SHIFT = 13; const char BIT_RIGHT_SHIFT = 0; const char NIBBLE_RESTORE = 19; const char BIT_RESTORE = 3; const char NIBBLE_CAPSLOCK = 19; const char BIT_CAPSLOCK = 2; int i, col, row, nibble_cnt, bit_cnt; int tmp_offs, tmp_nbl, tmp_bit; char FPGA_IN_PowerLed = 0; char FPGA_IN_FDDLed = 0; /* if ever any key of a C64 keyboard has been pressed, then DM64PIC switches into a dedicated C64 mode that swaps back the joystick ports to their "natural" (aka as printed on the PBC) order because when the DM65PIC is located in a C64 body housing, the ports are placed correctly */ char C64_Mode = 0; /* Initialize System */ SystemInit(); TM_DELAY_Init(); TM_SWO_Init(); TM_SWO_Printf("DM64PIC firmware running.\n"); /* The matrix scan goes like this: let current flow through the columns and then find out if a key is pressed by checking, if the current from the column arrives at a certain row. That means, we configure all rows as outputs (no pullup/pulldown resistor) and all columns as inputs (pulldown resistor) */ for (i = 0; i < Size_ColMapping_C65; i++) TM_GPIO_Init(Columns_C65[i].GPIOx, Columns_C65[i].GPIO_Pin, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); for (i = 0; i < Size_RowMapping_C65; i++) TM_GPIO_Init(Rows_C65[i].GPIOx, Rows_C65[i].GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_DOWN, TM_GPIO_Speed_High); for (i = 0; i < Size_ColMapping_C64; i++) TM_GPIO_Init(Columns_C64[i].GPIOx, Columns_C64[i].GPIO_Pin, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); for (i = 0; i < Size_RowMapping_C64; i++) TM_GPIO_Init(Rows_C64[i].GPIOx, Rows_C64[i].GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_DOWN, TM_GPIO_Speed_High); /* row 8 is exclusively used for CAPS LOCK (aka ASCII/DIN) and has inverse logic (pulled to GND when the key is pressed), so use pullup resistor */ TM_GPIO_Init(Rows_C65[ROW_CAPSLOCK].GPIOx, Rows_C65[ROW_CAPSLOCK].GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_High); /* The RESTORE key is pulled to GND when pressed, so we need a pullup resistor */ TM_GPIO_Init(Restore_C65.GPIOx, Restore_C65.GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_High); TM_GPIO_Init(Restore_C64.GPIOx, Restore_C64.GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_High); /* Joysticks are inverse logic, too, therefore pullup resistors are needed for the inputs */ for (i = 0; i < Size_JoyMapping; i++) TM_GPIO_Init(Joystick[i].GPIOx, Joystick[i].GPIO_Pin, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_High); /* Initialze outputs for LEDs */ TM_GPIO_Init(LEDs[ledPower].GPIOx, LEDs[ledPower].GPIO_Pin, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_Low); TM_GPIO_Init(LEDs[ledFDD].GPIOx, LEDs[ledFDD].GPIO_Pin, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_Low); /* Initialize outputs for communicating with the FPGA via GPIO port JB for debugging JB1 = PG8: clock; data must be valid before rising edge JB2 = PG9: start of sequence, set to 1 when the first nibble of a new 128bit sequence is presented JB3 = PG10: bit0 of output data nibble JB4 = PG11: bit1 of output data nibble JB7 = PG12: bit2 of output data nibble JB8 = PG13: bit3 of output data nibble JB9 = PG14: bit 0 of input bit pair JB10 = PG15: bit 1 of input bit pair */ #define P_CLOCK GPIO_Pin_8 #define P_START GPIO_Pin_9 #define P_OUT_B0 GPIO_Pin_10 #define P_OUT_B1 GPIO_Pin_11 #define P_OUT_B2 GPIO_Pin_12 #define P_OUT_B3 GPIO_Pin_13 #define P_IN_B0 GPIO_Pin_14 #define P_IN_B1 GPIO_Pin_15 /* defines for debug port JA #define P_CLOCK GPIO_Pin_0 #define P_START GPIO_Pin_1 #define P_OUT_B0 GPIO_Pin_2 #define P_OUT_B1 GPIO_Pin_3 #define P_OUT_B2 GPIO_Pin_4 #define P_OUT_B3 GPIO_Pin_5 #define P_IN_B0 GPIO_Pin_6 #define P_IN_B1 GPIO_Pin_7 */ /* initialize the pins for the FPGA GPIO communication */ TM_GPIO_Init(GPIOG, P_CLOCK, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_START, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_OUT_B0, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_OUT_B1, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_OUT_B2, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_OUT_B3, TM_GPIO_Mode_OUT, TM_GPIO_OType_PP, TM_GPIO_PuPd_NOPULL, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_IN_B0, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_DOWN, TM_GPIO_Speed_High); TM_GPIO_Init(GPIOG, P_IN_B1, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_DOWN, TM_GPIO_Speed_High); /* convenience defines for a better readability */ #define FPGA_OUT(__pin, __value) TM_GPIO_SetPinValue(GPIOG, __pin, __value) #define FPGA_IN(__pin) TM_GPIO_GetInputPinValue(GPIOG, __pin) #define DM_SET_BIT(__nibblepos, __bitpos) nibbles[(__nibblepos)] = nibbles[(__nibblepos)] | (1 << (__bitpos)) #define DM_CLR_BIT(__nibblepos, __bitpos) nibbles[(__nibblepos)] = nibbles[(__nibblepos)] & (~(1 << (__bitpos))) while(1) { /* matrix scan the keyboard */ nibble_cnt = 0; bit_cnt = 0; for (col = 0; col < Size_ColMapping_C65; col++) { /* set all columns to LOW except the currently active one */ for (i = 0; i < Size_ColMapping_C65; i++) { TM_GPIO_SetPinValue(Columns_C65[i].GPIOx, Columns_C65[i].GPIO_Pin, (i == col) ? 1 : 0); if (i < Size_ColMapping_C64) TM_GPIO_SetPinValue(Columns_C64[i].GPIOx, Columns_C64[i].GPIO_Pin, (i == col) ? 1 : 0); } /* perform standard row scanning as the MEGA65 can handle that in an untranslated (raw) way deliberately only scan rows 0..7 as row 8 is only for CAPS LOCK (ASCII/DIN), which needs a special treatment plus: this way of doing it elegantly enables us to scan the C64 rows in parallel */ for (row = 0; row < 8; row++) { /* Commodore 65 */ if (TM_GPIO_GetInputPinValue(Rows_C65[row].GPIOx, Rows_C65[row].GPIO_Pin) == 1) { /* a key is pressed, so set the corresponding matrix bit */ DM_SET_BIT(nibble_cnt, bit_cnt); TM_SWO_Printf("C65 Key: col=%i, row=%i, nibble=%i, bit=%i.\n", col, row, nibble_cnt, bit_cnt); } /* Commodore 64 */ else if (col < Size_ColMapping_C64 ? TM_GPIO_GetInputPinValue(Rows_C64[row].GPIOx, Rows_C64[row].GPIO_Pin) == 1 : 0) { /* detect the C64 mode */ if (!C64_Mode) { C64_Mode = 1; TM_SWO_Printf("C64 mode detected. Swapping joystick ports back to normal.\n"); for (i = 0; i < Size_JoyMapping / 2; i++) { tmp_offs = (Size_JoyMapping / 2) + i; tmp_nbl = Joystick[i].nibble_count; tmp_bit = Joystick[i].bit_count; Joystick[i].nibble_count = Joystick[tmp_offs].nibble_count; Joystick[i].bit_count = Joystick[tmp_offs].bit_count; Joystick[tmp_offs].nibble_count = tmp_nbl; Joystick[tmp_offs].bit_count = tmp_bit; } } /* a key is pressed, so set the corresponding matrix bit */ DM_SET_BIT(nibble_cnt, bit_cnt); TM_SWO_Printf("C64 Key: col=%i, row=%i, nibble=%i, bit=%i.\n", col, row, nibble_cnt, bit_cnt); } /* key is released, so clear the corresponding matrix bit */ else DM_CLR_BIT(nibble_cnt, bit_cnt); bit_cnt++; if (bit_cnt == 4) { bit_cnt = 0; nibble_cnt++; } Delay(1); } } /* C65 only: handle CURSOR UP and CURSOR LEFT: to be emulated as SHIFT+CURSOR DOWN and SHIFT+CURSOR RIGHT */ for (i = 0; i < 8; i++) TM_GPIO_SetPinValue(Columns_C65[i].GPIOx, Columns_C65[i].GPIO_Pin, (i == COL_CSR) ? 1 : 0); if (TM_GPIO_GetInputPinValue(Rows_C65[ROW_CSR_UP].GPIOx, Rows_C65[ROW_CSR_UP].GPIO_Pin) == 1) { /* CURSOR UP */ DM_SET_BIT(NIBBLE_CSR_DOWN, BIT_CSR_DOWN); DM_SET_BIT(NIBBLE_RIGHT_SHIFT, BIT_RIGHT_SHIFT); } Delay(1); if (TM_GPIO_GetInputPinValue(Rows_C65[ROW_CSR_LEFT].GPIOx, Rows_C65[ROW_CSR_LEFT].GPIO_Pin) == 1) { /* CURSOR LEFT */ DM_SET_BIT(NIBBLE_CSR_RIGHT, BIT_CSR_RIGHT); DM_SET_BIT(NIBBLE_RIGHT_SHIFT, BIT_RIGHT_SHIFT); } Delay(1); /* handle RESTORE key (inverse logic) */ if ((TM_GPIO_GetInputPinValue(Restore_C65.GPIOx, Restore_C65.GPIO_Pin) == 0) || (TM_GPIO_GetInputPinValue(Restore_C64.GPIOx, Restore_C64.GPIO_Pin) == 0)) DM_SET_BIT(NIBBLE_RESTORE, BIT_RESTORE); else DM_CLR_BIT(NIBBLE_RESTORE, BIT_RESTORE); Delay(1); /* C65 only: handle CAPS LOCK (aka ASCII/DIN) (inverse logic) */ if (TM_GPIO_GetInputPinValue(Rows_C65[ROW_CAPSLOCK].GPIOx, Rows_C65[ROW_CAPSLOCK].GPIO_Pin) == 0) DM_SET_BIT(NIBBLE_CAPSLOCK, BIT_CAPSLOCK); else DM_CLR_BIT(NIBBLE_CAPSLOCK, BIT_CAPSLOCK); Delay(1); /* handle joysticks */ for (i = 0; i < Size_JoyMapping; i++) { if (TM_GPIO_GetInputPinValue(Joystick[i].GPIOx, Joystick[i].GPIO_Pin) == 0) { DM_SET_BIT(Joystick[i].nibble_count, Joystick[i].bit_count); TM_SWO_Printf("Joystick: i=%i, nibble=%i, bit=%i.\n", i, Joystick[i].nibble_count, Joystick[i].bit_count); } else DM_CLR_BIT(Joystick[i].nibble_count, Joystick[i].bit_count); Delay(1); } /* handle the LEDs */ TM_GPIO_SetPinValue(LEDs[ledPower].GPIOx, LEDs[ledPower].GPIO_Pin, FPGA_IN_PowerLed ? 0 : 1); TM_GPIO_SetPinValue(LEDs[ledFDD].GPIOx, LEDs[ledFDD].GPIO_Pin, FPGA_IN_FDDLed ? 0 : 1); Delay(1); /* transmit current keyboard and joystick state to FPGA and read the LED status from the FPGA */ for (i = 0; i < 32; i++) { FPGA_OUT(P_CLOCK, 0); /* clock = 0 while data is being assembled */ FPGA_OUT(P_START, (i == 0) ? 1 : 0); /* start of sequence = 1 at the very first nibble */ FPGA_OUT(P_OUT_B0, (nibbles[i] & 0x01) ? 0 : 1); /* set data lines and use inverse logic */ FPGA_OUT(P_OUT_B1, (nibbles[i] & 0x02) ? 0 : 1); FPGA_OUT(P_OUT_B2, (nibbles[i] & 0x04) ? 0 : 1); FPGA_OUT(P_OUT_B3, (nibbles[i] & 0x08) ? 0 : 1); Delay(1); /* wait for everything to settle ... */ FPGA_OUT(P_CLOCK, 1); /* ... then clock = 1 to trigger the FPGA to read */ Delay(1); /* give the FPGA's flip/flops some time to read the data */ /* read the LED status */ if (i == 0) { FPGA_IN_PowerLed = FPGA_IN(P_IN_B0); FPGA_IN_FDDLed = FPGA_IN(P_IN_B1); } } } }