/* SDCARD write protect function */
static uint8_t SDCARD_IsWriteEnabled(void) {
#if FATFS_USE_WRITEPROTECT_PIN
	/* Check if write enabled, pin LOW if write enabled */
	return !TM_GPIO_GetInputPinValue(FATFS_WRITEPROTECT_PORT, FATFS_WRITEPROTECT_PIN);
#endif
	
	/* Card is not write protected */
	return 1;
}
/* SDCARD detect function */
static uint8_t SDCARD_IsDetected(void) {
#if FATFS_USE_DETECT_PIN
	/* Check if detected, pin LOW if detected */
	return !TM_GPIO_GetInputPinValue(FATFS_DETECT_PORT, FATFS_DETECT_PIN);
#endif
	
	/* Card is detected */
	return 1;
}
uint8_t TM_FATFS_CheckCardDetectPin(void) {
	uint8_t status = 1;
	
#if FATFS_USE_DETECT_PIN > 0
	if (TM_GPIO_GetInputPinValue(FATFS_USE_DETECT_PIN_PORT, FATFS_USE_DETECT_PIN_PIN) != 0) {
		status = 0;
	}
#endif
	
	/* Return status */
	return status;
}
Example #4
0
uint32_t TM_HCSR04_Read(TM_HCSR04_t* HCSR04) {
	uint32_t time, timeout;
	/* Trigger low */
	TM_GPIO_SetPinLow(HCSR04->TRIGGER_GPIOx, HCSR04->TRIGGER_GPIO_Pin);
	/* Delay 2 us */
	tm_delay(2);
	/* Trigger high for 10us */
	TM_GPIO_SetPinHigh(HCSR04->TRIGGER_GPIOx, HCSR04->TRIGGER_GPIO_Pin);
	/* Delay 10 us */
	tm_delay(10);
	/* Trigger low */
	TM_GPIO_SetPinLow(HCSR04->TRIGGER_GPIOx, HCSR04->TRIGGER_GPIO_Pin);
	
	/* Give some time for response */
	timeout = HCSR04_TIMEOUT;
	while (!TM_GPIO_GetInputPinValue(HCSR04->ECHO_GPIOx, HCSR04->ECHO_GPIO_Pin)) {
		if (timeout-- == 0x00) {
			return -1;
		}
	}
	
	/* Start time */
	time = 0;
	/* Wait till signal is low */
	while (TM_GPIO_GetInputPinValue(HCSR04->ECHO_GPIOx, HCSR04->ECHO_GPIO_Pin)) {
		/* Increase time */
		time++;
		/* Delay 1us */
		tm_delay(1);
	}
	
	/* Convert us to cm */
	HCSR04->Distance =  (float)time * HCSR04_NUMBER;
        	
	/* Return distance */
	return time;
}
/* Internal functions */
static void TM_BUTTON_INT_CheckButton(TM_BUTTON_t* ButtonStruct) {
	uint32_t now, status;
	
	/* Read values */
	now = TM_DELAY_Time();
	status = TM_GPIO_GetInputPinValue(ButtonStruct->GPIOx, ButtonStruct->GPIO_Pin);
	
	/* First stage */
	if (ButtonStruct->State == BUTTON_STATE_START) {
		/* Check if pressed */
		if (status == ButtonStruct->GPIO_State) {
			/* Button pressed, go to stage BUTTON_STATE_START */
			ButtonStruct->State = BUTTON_STATE_DEBOUNCE;
			
			/* Save pressed time */
			ButtonStruct->StartTime = now;
		}
	}

	if (ButtonStruct->State == BUTTON_STATE_DEBOUNCE) {
		/* Button still pressed */
		/* Check for debounce */
		if (status == ButtonStruct->GPIO_State) {
			if (now > (ButtonStruct->StartTime + ButtonStruct->PressDebounceTime)) {
				/* Button debounce OK, Goto Normal Press */
				ButtonStruct->State = BUTTON_STATE_PRESSED;

				/* Try to call user function */
				if (ButtonStruct->ButtonHandler) {
					/* Call function callback */
					ButtonStruct->ButtonHandler(ButtonStruct, TM_BUTTON_PressType_OnPressed);
				}
			}
		} else if (status != ButtonStruct->GPIO_State) {
			/* Not pressed */
			/* It was bounce, start over */
			/* Go to state BUTTON_STATE_START */
			ButtonStruct->State = BUTTON_STATE_START;
		}
	}
	
	if (ButtonStruct->State == BUTTON_STATE_PRESSED) {
		/* Button still pressed */
		/* Check for long press */
		if (status == ButtonStruct->GPIO_State) {
			if (now > (ButtonStruct->StartTime + ButtonStruct->PressLongTime)) {
				/* Button pressed OK, call function */
				if (ButtonStruct->ButtonHandler) {
					/* Call function callback */
					ButtonStruct->ButtonHandler(ButtonStruct, TM_BUTTON_PressType_Long);
				}
				
				/* Go to stage BUTTON_STATE_WAITRELEASE */
				ButtonStruct->State = BUTTON_STATE_WAITRELEASE;
			}
		} else if (status != ButtonStruct->GPIO_State) {
			/* Not pressed */
			if (now > (ButtonStruct->StartTime + ButtonStruct->PressNormalTime)) {
				/* Button pressed OK, call function */
				if (ButtonStruct->ButtonHandler) {
					/* Call function callback */
					ButtonStruct->ButtonHandler(ButtonStruct, TM_BUTTON_PressType_Normal);
				}
				
				/* Go to stage BUTTON_STATE_WAITRELEASE */
				ButtonStruct->State = BUTTON_STATE_WAITRELEASE;
			} else {
				/* Go to state BUTTON_STATE_START */
				ButtonStruct->State = BUTTON_STATE_START;
			}
		} else {
			/* Go to state BUTTON_STATE_START */
			ButtonStruct->State = BUTTON_STATE_START;
		}
	}
	
	if (ButtonStruct->State == BUTTON_STATE_WAITRELEASE) {
		/* Wait till button released */
		if (status != ButtonStruct->GPIO_State) {
			/* Go to stage 0 again */
			ButtonStruct->State = BUTTON_STATE_START;
		}
	}
	
	/* Save current status */
	ButtonStruct->LastStatus = status;
}
Example #6
0
void TM_EXTI_Handler(uint16_t GPIO_Pin) {
	/* Handle external line 0 interrupts */
	if (GPIO_Pin == DTMF_BIT4_PIN) {
		
		if(TM_GPIO_GetInputPinValue(DTMF_BIT0_PORT, DTMF_BIT0_PIN)==0){ // Q1  =0
			if(TM_GPIO_GetInputPinValue(DTMF_BIT1_PORT, DTMF_BIT1_PIN)==0){//  Q2 =0
				if(TM_GPIO_GetInputPinValue(DTMF_BIT2_PORT, DTMF_BIT2_PIN)==0){// Q3 =0
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)// Q4 =0 Q321=0 
						express = 'D';
					else express = '8';
						}
				else{ // Q1 =0,Q2=0,Q3=1
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)
								express = '4';
					else express = '#';
						}
			}
			else //Q1=0,Q2=1,
			{
				if(TM_GPIO_GetInputPinValue(DTMF_BIT2_PORT, DTMF_BIT2_PIN)==0){// Q3 =0
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)// Q4 =0  
						express = '2';
					else express = '0';
						}
				else{ // Q1 =0,Q2=1,Q3=1
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)
								express = '6';
					else 	express = 'B';
				}
			}
		}
		else{					//Q1=1
				if(TM_GPIO_GetInputPinValue(DTMF_BIT1_PORT, DTMF_BIT1_PIN)==0){//  Q2 =0
					if(TM_GPIO_GetInputPinValue(DTMF_BIT2_PORT, DTMF_BIT2_PIN)==0){// Q3 =0
						if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)// Q4 =0 
						express = '1';
						else express = '9';
						}
					else{ // Q1 =1,Q2=0,Q3=1
				if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)
								express = '5';
					else express = 'A';
					}
				}
				else //Q1=1,Q2=1,
				{
				if(TM_GPIO_GetInputPinValue(DTMF_BIT2_PORT, DTMF_BIT2_PIN)==0){// Q3 =0
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)// Q4 =0  
						express = '3';
					else express = '.';
						}
				else{ // Q1 =1,Q2=1,Q3=1
					if(TM_GPIO_GetInputPinValue(DTMF_BIT3_PORT, DTMF_BIT3_PIN)==0)
						express = '7';
					else express = 'C';
				}
			}
		}
		
		TM_USART_Putc(USART3,express);
		/* Toggle RED led */
//		TM_DISCO_LedToggle(LED_RED);
//		/* Check counter */
		if (++counter >= 10) {
			/* Detach external interrupt for GPIO_Pin_0 no matter on which GPIOx is connected */
			TM_EXTI_Detach(GPIO_Pin_0);
		}
	}
	
//	/* Handle external line 13 interrupts */
	
	if (GPIO_Pin == W1_D0_PIN) { /* run W1 D0 - dieu khien RELAY_DK1_PORT , RELAY_DK1_PIN*/	
		if(!flag_W1D1)
		{ 
		flag_W1D0 =1 ;
		flag_W1D1 =0;
		timeout = value_dip;
		timer_dk1 =0;
		}
		
	}
	if (GPIO_Pin == W1_D1_PIN) {	/* run W1 D1 - dieu khien RELAY_DK2_PORT , RELAY_DK2_PIN*/
		if(!flag_W1D0) {
		flag_W1D1 =1 ;
		flag_W1D0 =0 ;
		timeout = value_dip;
		timer_dk2 =0;
		}
		
	}
	if (GPIO_Pin == W2_D1_PIN) { // ngat cac cong 
		/* run w2 D1 */
	timerdk1=(timeout*2);
	timerdk2=(timeout*2);	
}
}
Example #7
0
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);                
            }            
        }
    }
}