📄 tusb2136kbfw programmer guide.txt
字号:
case 2: P0.7 = 0; break;
case 1: P3.7 = 0; break;
default: break;
}
The above code is the main process involved in polling each of the 18 lines that make up the keyboard scan matrix. The code loops starting at scanline 18 and counts down to 1. At the beginning of each iteration, P0 and P1, along with the high bits of P3, are all set. This effectively unselects all keyboard scan lines. Then, according to the scanline we're currently dealing with, a single pin is brought low which selects the keyboard scanline we wish to read.
keypress = ~P2;
The value(s) returned on P2 reflect the state of the up to 8 keys that are placed on a given keyboard scanline. However, these keys are active low, which means a "0" in a given bit means that the corresponding key is pressed. Likewise, a "1" means that the corresponding key is not pressed (or not connected). The above instruction inverts all the bits read from the keyboard such that a "1" indicates a key has been pressed and a "0" represents a non-pressed or non-connected key.
while(keyDefs[keyDefOffset].scanLine >= scanLine )
{
The while loop above performs a table-traversing function. All entries in the keyDefs[] table are arranged in descending scanline order. Thus the first entries in the keyDefs[] table are for scanline 18, subsequently scanline 17 entires are included, etc. Thus the while() loop above causes the following code to be executed (and keyDeffOset incremented) as long as the element we're pointing to in the table belongs to a scanline that is greater than or equal to the scanline we're currently processing.
if(keyDefs[keyDefOffset].scanLine == scanLine)
{
This conditional, within the while() above, causes the keypress comparison code which follows to be executed only if the element in the keyDefs[] table belongs to the scanline we're currently processing. Otherwise, it is possible the pointer keyDeffOset is still pointing to a previous scanline and has yet to be incremented to point to the current scanline in the table. In that case, the element pointed to by keyDefOffset is not part of the current scanline, is not compared, and the keyDefOffset pointer will be incremented and the comparsion will be repeated until it points to an entry that belongs to the current scanline.
if( (keypress & keyDefs[keyDefOffset].keyPressMask) &&
(vidPidMask & keyDefs[keyDefOffset].vidPidMask))
{
This conditional above first ANDs the current keypress we read from P2 with the keyPressMask contained in the keyPressMask in the keyDefs[] table. If this AND produces a non-zero value, it means the key whose entry is pointed to by keyDefOffset is currently being pressed.
The code then continues to verify whether the vidPidMask of the given key in the keyDefs[] table includes the bit which is held in the vidPidMask. Since the keyDefs[] table may hold key mappings for many products, this condition ensures that the current product configuration only processes keys that are enabled for the configuration.
If, and only if, the key is detected and the key is part of the current product configuration, the following code is executed to handle the keypress.
if(keyDefs[keyDefOffset].modBit != 0x00 )
{
modifierByte |= keyDefs[keyDefOffset].modBit;
}
else if(strlen(keypressBuffer) < KEYPRESS_BUFFER_SIZE )
{
keypressBuffer[strlen(keypressBuffer)] = keyDefs[keyDefOffset].hidCode;
}
The code above is executed if a valid key for the current product configuration has been detected. In that case, the code verifies whether or not the modBit for the keypress' entry in the keyDefs[] table is non-zero. A non-zero value in the modBit means that the given key is reported to the host by setting a bit in the modifierByte variable rather than adding an entry to the keypressBuffer. Thus if the modBit is non-zero, that specific bit is set in modifierByte.
If modBit is not set, it means the keypress should be added to the actual keypressBuffer, as long as there is still room in the buffer. Thus the else section of the code verifies that there is room in the buffer and, if there is, the corresponding HID code for that keypress is added to the buffer.
keyDefOffset++;
}
Finally, the keyDefOffset variable is incremented to point to the next entry in the keyDefs[] table.
if(keyDefs[keyDefOffset].scanLine == 0 ||
strlen(keypressBuffer) >= KEYPRESS_BUFFER_SIZE)
break;
At the end of the loop, the above conditional checks to see whether the new keyDefOffset is pointing to scanline "0" in the keyDef[] table. A scanline of "0" in the keyDefs[] table is a flag that indicates there are no more lines to scan. In this case, the while() loop that is traversing the keyDefs[] table is terminated since there is no possibility of finding additional keys.
Additionally, if the current keypressBuffer is already completely filled we also abort the keyboard polling since, even if there are more keys being pressed, we have no room left in the buffer to report them. Continuing to poll the remaining keyboard scan lines would be a waste of time; thus we again terminate the while() loop.
UpdateIEP1WithKeypress();
After the entire keyboard has been scanned, the modifierByte and keypressBuffer contain the results of the scan. We call UpdateIEP1WithKeypress regardless of whether or not there was any data detected so that we can report the new keyboard condition to the host.
ClearKeyBoardBuffer() Function: The ClearKeyBoardBuffer() function, as the name implies, is used to clear any data that may be held in the keyboard buffer.
void ClearKeyBoardBuffer(void)
{
BYTE bTemp;
for(bTemp = 0; bTemp < KEYPRESS_BUFFER_SIZE; bTemp++)
keypressBuffer[bTemp] = 0x00;
}
The function consists of a single for() loop that is used to clear each byte of the keypressBuffer[] variable.
UpdateIEP1WithKeypress () Function: The UpdateIEP1WithKeypress() function determines whether the new keypress inforamtion which has been collected by the main program is such that the current data reflects a change in the state previously reported to the host and, therefore, must be sent to the host.
void UpdateIEP1WithKeypress(void)
{
BYTE bTemp;
if(bIEPDCTX_1 != EPBCT_NAK)
{
ClearKeyBoardBuffer();
return;
}
The code above checks with the TUSB2136 status register IEPDCTX_1 contains a value that is not a NAK. The normal "idle" state of the Input Endpoint CTX register is NAK, meaning that there is no activity. However, sending data to the host results in this value being sent to a non-NAK value. If the routine is ready to send a packet of data to the host and finds IEPDCTX_1 in a non-NAK state, it presumably means a previously prepared packet of data has not yet been transmitted to the host. In such a situation, we clear the keyboard buffer and do not send the packet to the host. Only after the previous packet has been sent do we consider sending a new packet to the host.
iep1Buffer[0] = modifierByte; // Holds shift, alt, etc.
iep1Buffer[1] = 0x00; // Second byte is always 0x00
for(bTemp = 0; bTemp < 6; bTemp++)
iep1Buffer[2 + bTemp] = keypressBuffer[bTemp];
These four lines of code construct the IEP1 buffer that will eventually be sent to the host. According to the HID specification, the first byte is the modified byte, the second byte is not used and is always set to 0x00, and the following 6 bytes is a sequence of HID Usage Codes that will have already been inserted in the keypressBuffer. The for() loop copies the keypressBuffer to its corresponding position in the HID packet that will be sent to the host.
if( memcmp(iep1Buffer, previousIEP1packet, 8) == 0)
{
ClearKeyBoardBuffer();
return;
}
As a final check, we compare the IEP1 buffer we just constructed with the last packet that we sent to the host. If they are the same then we clear the keypressBuffer and abort the transfer. This is due to the fact that we only transmit changes in the status of the keypresses to the host. Thus if the buffer we just prepared is actually the same as the last packet we sent, there is no reason to send it again because it has not changed.
bIEPDCTX_1 = 8;
Finally, if program execution reaches this final instruction it means that the packet we've constructed should be sent to the host. Setting IEPDCTX_1 to 8 instructs the MCU that there is a packet ready to be sent; the TUSB2136 will send it to the host the next time the function is polled.
IEP1InterruptHandler () Function: The IEP1InterruptHandler() function normally would be included in the Usb.C source file, but was moved to this source file since it is an important part of the keyboard application.
The IEP1InterruptHandler() function is called when a packet has been successfully sent to the host. The TUSB2136 provokes the interrupt to indicate that the last packet has been sent.
void IEP1InterruptHandler(void)
{
memcpy(previousIEP1packet, iep1Buffer, 8);
ClearKeyBoardBuffer();
}
Once the packet has been sent, the interrupt is provoked and the code above is executed. The function copies the packet that was sent (which is still held in iep1Buffer) to the previousIEP1packet buffer which is used in future calls to UpdateIEP1WithKeypress() to decide whether there has been a change in the state of the keyvoard. The keypressBuffer[] is then cleared so that the main() function can construct new buffers to send to the host.
OEP0SetLEDs() Function: The OEP0SetLEDs function is called when data is received from the host with information to update the three LEDs of the keyboard.
void OEP0SetLEDs(void)
{
if(bLED & BIT_NUMLOCK)
PIN_NUMLOCK = 0;
else
PIN_NUMLOCK = 1;
if(bLED & BIT_CAPSLOCK)
PIN_CAPSLOCK = 0;
else
PIN_CAPSLOCK = 1;
if(bLED & BIT_SCROLLLOCK)
PIN_SCROLLLOCK = 0;
else
PIN_SCROLLLOCK = 1;
}
The routine above simply checks the status of each bit and sets the LED accordingly. Note that the LEDs are active low.
KeyDefs[] Table: The KeyDefs[] table is used to define and map each key that is assigned in the various configurations. All possible keys for all configurations are defined in this table.
The first several lines of the structure are:
struct KEYMAP_STRUCT code keyDefs[] =
{
//VID, Col, Ret, USB, ModByteBit
{0xFFFF, 18, 0x02, usbUsageLeftGUI, 0x08},
{0xFFFF, 17, 0x80, usbUsageRightAlt, 0x40},
Each element in the table corresponds to a single key, and the 5 fields in each element define the characteristics of the key.
Field #1: VID Mask. The first field is the VID Mask. This is a 16-bit unsigned integer in which each bit corresponds to one of the possible 16 VID/PID settings. If a bit corresponding to a given VID/PID setting is set, the key applies to that VID/PID setting. For example, a VID Mask of 0x0003 would indicate that that key is available in VID/PID setting 1 and 2. A VID Mask of 0x0010 would indicate that that key is available in VID/PID setting 5 (since bit 5 is set). Most keys have a VID Mask of 0xFFFF, which means they are available in all VID/PID configurations.
Field #2: Column (Scanline). The second field indicates on which scanline the given key is found on. This firmware, as written, takes into account 18 scanlines which occupy all the lines of P0 and P1, as well as P3.6 and P3.7. Each scanline may hold a maximum of 8 keys, or as few as one. The Column field indicates which of the 18 scanlines must be brought low to read the key.
Field #3: Return Value. The third field indicates that, when the given scanline is enabled, which bit will represent that the given key is being pressed. For example, the first key has a column (scanline) of 18 and a Return Value of 0x02. This means that when scanline 18 is enabled, if bit 0x02 is active then the "Left GUI Key" is being pressed.
Field #4: HID Usage Code. The fourth field indicates the HID Usage Code (see HID Usage Specification) that is to be delivered to the host when this key is detected. Every key is assigned a unique HID Usage Code and the fourth field is essentially the translation between the keypresses and their equivalent HID Usage Code. The HID Usage Codes are declared in usb.h.
Field #5: Modifier Byte Bit. The fifth, and final, field is optional. Most keys do not have a value for this field, which means it defaults to zero. Some keys on the keyboard are not returned using their HID Usage Code; rather they cause a bit to be set in the Modifier Byte which is sent as the first byte of the Keyboard Report. Specifically, the ALT, Shift, and CTRL keys are all returned in this manner. If this Modifier Byte Bit field is non-zero it means that the specified bit in the Modifier Byte should be set rather than returning the HID Usage Code to the host. For example, the second entry in the table relates to the Right Alt key. When this key is pressed and analyzed by the main() routine it determines that the Modifier Byte Bit is 0x40 (non-zero). Thus, rather than adding the HID Usage Code to the keypressBuffer[], it simply sets bit 0x40 in the Modifier Byte.
SOURCE FILE: "VIDPID.C"
The VIDPID.C source file contains two components: 1) The GetVidPidSetting() which is called from main() in the Keyboard.c source file. 2) The funcDefs[] structure that is used to define underlying information for each possible VID/PID setting.
GetVidPidSetting Function: The GetVidPidSetting function is called from main() at initial boot time to establish the configuration setting indicated in the VIDSTA port.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -