Posted: 06 Sep 2018, 02:17
Slom the Silent TI doesn't have a demultiplexor it just writes directly to the enable lines. My MicroSwitch board does have a demultiplexor on it so I had to take this into account when I did my conversion. I think DorkVader was referring to the source code for my SD board not the code that you wrote for the Silent 700.
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
this function takes in an unsigned 8 bit integer defined as column and then returns an unsigned 8 bit integer. It's a decimal to binary converter it takes an 8 bit number and returns a 4 bit number.
The first line
just creates an unsigned variable called p2 that is used to hold the result of our calculation below,
When you And a value with a Port I think of it as using the value as a mask so if you And 1111 1111 with 0000 0001 you would be left with 0000 0001 on the Port,
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.
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.
this gives the enable time to get through the demux and the switch time to output, its basically just a slight delay so that the read occurs after the enable. This value could be tuned.
we enable the demux and then take in the value from the 8 pins tied to the test points this is stored as an 8 bit number so if you depressed a key on row 6 we would get back 1111 1011. We then bring the enable on the demux high.
We send back the 8 bits to the calling function which in our case is Direct_Scan(void) which is a function that takes no value and returns no value. It gets used in the top part of that function here is the relevant code.
I and J are stored as ints which are 16 bit numbers this allows us to store values between -32768 and 32767 which is more than enough for our purposes. Then we count up from 0 to 15 each time we perform a Direct_Read(i) with the value of i as our column value and store the result in the global array named DirectNKeyStates in the index number i . Each time you do this you are getting an 8 bit number that represents the 8 row locations in that column. We do it 16 times and this gives us the entire key array.
We then put the value stored at DirectNKeyStates['i '] into the variable keys. Then we Exclusive-OR also called XOR the value the truth table for XOR is
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
So if there has been no change we just bail out of this function right here there is no reason to continue. If a key was down it is still down or if a key was not hit it has not been hit since the last scan. We are only concerned with new keypresses or releases.
The rest of the scan function looks like this,
We place the value of Keys into the global variable DirectKeyStates['i'] at location i
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
In this section
we then test again to see if the vaue in Keys which is the value stored in DirectKeyStates from earlier is a 1 we know a key has been released and we then enter the upper block and call the KeyUp function sending it the code that we calculated above. It the value is a 0 we have a key depressed so we enter the else block and call the function KeyDown sending it the code we calculated above.
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.
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.