Yes DorkVader those are the correct lines for the scanner logic in the source I will try to explain exactly whats going on but explaining this stuff is actually harder than understanding it. Its proof that to really be a master at something you must first be able to help another achieve mastery.
here is the key function
Code: Select all
static uint8_t Direct_Read(uint8_t column)
{
uint8_t p2;
SC_ADDR_PORT = (SC_ADDR_PORT & ~(0x0F << SC_ADDR_SHIFT)) | (column << SC_ADDR_SHIFT);
SC_STROBE_PORT &= ~SC_STROBE;
_delay_us(5);
p2 = SC_KEYS_PIN;
SC_STROBE_PORT |= SC_STROBE;
return p2;
}
The first line
Code: Select all
uint8_t p2;
Code: Select all
SC_ADDR_PORT = (SC_ADDR_PORT & ~(0x0F << SC_ADDR_SHIFT)) | (column << SC_ADDR_SHIFT);
SC_STROBE_PORT &= ~SC_STROBE;
Or works just as it does in logic 1111 1111 with 0000 0001 becomes 1111 1111. These are used as methods for grabbing specific values from a binary string and placing them on a port or reading a certain value from a port.
~ is the inverse operator so it just takes the complement of a number ~1111 would become 0000.
0x0f which is decimal 15 or 0000 1111 in binary. We then shift the number by the size of SC_ADDR_SHIFT which is defined at the top of the file as 4. This gives us 1111 0000. If you then AND this with SC_ADDR_PORT and you are left with 0000. This operation just truncates the trailing 4 zeros.
Code: Select all
(column << SC_ADDR_SHIFT);
We then take the value we passed in, which is stored in variable Column, lets say its the number 13. We then take the binary of that number and shift it by four places, the 8 bit binary for 13 is 0000 1101 so when I shift it by 4 I get 1101 0000. So we are taking in a number between 0 and 15 and shifting it by four placees we now have the correct four bits to send to the demux to select the proper output we just need to get rid of the trailing zeros and put the bit pattern onto the port.
We do this by OR'ing the two values together
so 0000 OR 1101 0000, returns 1101
But we also need to bring the enable pin on the demux low to output any signal at all so to do this we AND the inverse of SC_STROBE(defined as 1<<0 which is another way to write 0001 or 0x01) and place the result on the SC_STROBE_PORT which is just the last four pins of port B we send the demux address on the first four pins. So we send 0000 which puts a opens a path to ground through the pin for the enable of the demux.
Code: Select all
_delay_us(5);
Code: Select all
p2 = SC_KEYS_PIN;
SC_STROBE_PORT |= SC_STROBE;
Code: Select all
return p2;
Code: Select all
static void Direct_Scan(void)
{
int i;
int j;
for (i = 0; i < 16; i++)
{
DirectNKeyStates[] = Direct_Read(i);
}
Code: Select all
for (i = 0; i < 16; i++)
{
uint8_t keys;
uint8_t change;
keys = DirectNKeyStates[i ];
change = keys ^ DirectKeyStates[i ];
if (change == 0) continue;
00 = 0
1 1 = 0
0 1 = 1
1 0 = 1
I think of it as an either or, but not both operation, XORing these values together we will get a one as long as the two values are not both one, so in this case as long as there is a change between the two values.
This gives us an easy way to test if the value at that location has changed or not since we last checked it
Code: Select all
if (change == 0) continue;
The rest of the scan function looks like this,
Code: Select all
DirectKeyStates[i] = keys;
for (j = 0; j < 8; j++)
{
if (change & (1 << j))
{
int code = (i * 8) + j;
if (keys & (1 << j))
{
KeyUp(&Keys[code1]);
}
else
{
KeyDown(&Keys[code1],false);
}
We then go through this loop 8 times for every time we go through the outer loop once(the outer loop is 16 iterations)
we test to see if a key was hit or released by checking it against what the location was previously, 1 in binary is 0000 0001 This is shifted by j which gives us the location in the 8 bits that we are testing. So if j was 7 we would be testing against 0100 0000. If a key in that column had been hit the test would pass and we would move into this block
Code: Select all
int code = (i * 8) + j;
if (keys & (1 << j))
{
KeyUp(&Keys[code]);
}
else
{
KeyDown(&Keys[code],false);
}
In this section
Code: Select all
int code = (i * 8) + j;
[/code[
we first create a variable and multiply [i] i [/i] by the length of a column and then add j this just turns a multidimensional array location into a linear array location, Our keyarray is linear its all one big chunk but when we index through the array we do it by rows and columns.
[code]
if (keys & (1 << j))
{
KeyUp(&Keys[code]);
}
else
{
KeyDown(&Keys[code],false);
}
I'm sure I didn't do that good of a job explaining that and I apologize if you already knew alot of the information I didn't know how much you know about C and programming for a MIcro in general. If I left anything out or I was unclear just ask me about that part and I'll try to explain it better.
Bit banging is tough even the best programmers say that bit manipulation is some of the trickiest most finicky code you can write so I'm sure someone else with more experience could explain it better.
(The key to understanding all of this for me was just breaking the habit of thinking of a number as decimal. In programming binary is what's important. Hex is used becaue its easier to talk about binary numbers by saying F instead of 0000 1111. The important thing to remember is that 0x0F is 0000 1111 to the computer so we do operations on the binary not its hex representation. I hope I explained that correctly, I didn't go to college or anything I learned C from reading books when I was a teenager and a few online tutorials that I've found. So take everything I say and go check it out for yourself. Which is just good life advice in general)
I should have that TI code up by tommorow afternoon the new Dragon Quest came out so its been taking up a bit of my free time, I love keyboards and coding but I love me some Dragon Quest too.
Im reworking the keymap for my SD board I need to check out those Function key locations and make sure they are all correct. I wasn't as concerned with those when I was figuring out the keymap so its possible that alot of them could be wrong. :/
We'll get there though.