This is the code I currently have that is supposed to detect when a button is pressed. If I create functions such as the firstmenu function where I call the synchronous button function (getkeysync), then the program works and the correct key is returned. However, if I tried the asynchronous route, (via getakey) and followed this example and ran it by pressing the "right" button about 100ms after the program started, then regardless of how much I hold it down, the options for the second menu are skipped and menuthree function is then executed.
I understand menuone, menutwo, menuthree, menufour, and menufive functions aren't implemented yet, but that isn't my issue.
Is there a way I can solve this issue asynchronously? By that I mean, I want to use getakey function more often in my menus and after the user makes his choice, I don't want to put the CPU to a forced endless loop until the key is released.
In fact, I want to be able to process other functions (which I'll create later) while it's the user's turn to make a choice.
KCENTER equ 3 ;this value is returned if both left and right keys are pressed at once.
KLEFT equ 2 ;left key pressed value
KRIGHT equ 1 ;right key pressed value
KCENTERH equ 7 ;both left and right keys held down
mov TL1,#0h ;reset timer reload values
mov TH1,#0h
mov TMOD,#22h ;Timer=0-255
mov SP,#055h ;set stack
setb ET1 ;enable timer interrupt
setb EA ;enable all interrupts
clr TF1 ;clear overflow flag
setb TR1 ;start timer
firstmenu:
lcall printfirstmenu ;function (not shown) to display first menu
lcall getfkey ;stall until key is pressed
cjne R6,#KCENTER,centerb
ljmp menufour ;go to menufour if center is pressed
centerb:
cjne R6,#KLEFT,leftb
ljmp menufive ;go to menufive if left is pressed
leftb:
cjne R6,#KRIGHT,rightb
ljmp somemenu ;go to somemenu if right is pressed (this works)
rightb:
somemenu:
clr KEYDET ;clear detection
lcall printamenu ;function (not shown) to display a menu
menu2:
lcall getakey ;try to get key without stalling. Return 0 if no key.
cjne R6,#KCENTERH,nohold
;center key held down
subb A,#33h ;compare hold time to time it takes to execute 51 interrupts
jc timelow ;if time is high enough...
ljmp othermenu ;then go to previous menu
timelow:
clr C ;clear carry. If center hold doesn't work, then no other key counts.
nohold:
jnc menu2 ;if no static key is pressed, jump back.
cjne R6,#KCENTER,centerk
ljmp menuone ;go to menuone if center is pressed
centerk:
cjne R6,#KLEFT,leftk
ljmp menutwo ;go to menutwo if center is pressed
leftk:
cjne R6,#KRIGHT,rightk
ljmp menuthree ;go to menuthree if right is pressed (but this always executes without waiting for user input!)
rightk:
ljmp menu2
InterruptHandler:
clr EA ;turn interrupts off
clr TR1 ;turn timer off
clr TF1 ;clear timer overflow
push ACC ;save A
push PSW ;save PSW
lcall prockey ;run key function
mov TL1,#0h ;reset reload counters
mov TH1,#0h
pop PSW ;restore PSW
pop ACC ;restore A
setb EA ;turn interrupts on
setb TR1 ;turn timer on
reti ;exit interrupt
;function getkeysync forces stall until key is returned.
;all key values are stored in R6.
getkeysync:
clr KEYDET
getkeysync2:
lcall getakey
jnc getkeysync2
ret
;Get values and return immediately (async function)
getakey:
mov R6,GOTKEY
mov C,KEYDET
mov A,KEYMD
ret
;process key interrupt
;LKEY and RKEY are independant hardware keys with inverted values.
;values: 0=pressed, 1=not pressed
;the KEYS variable stores bit information of captured keys
prockey:
mov C,LKEY
cpl C
mov KEYS.1,C ;2nd LSB of KEYS is LKEY true value
mov C,RKEY
cpl C
mov KEYS.0,C ;LSB of KEYS is RKEY true value
mov A,KEYS
anl A,#3h ;Possible values for A: 0=nothing pressed, 1=Right, 2=left, 3=both.
jz gotnokey
;key detected as pressed.
clr KEYDET
mov KEYTEMP,#0h
inc KEYHOLD ;increment hold counter once per interrupt call
mov A,KEYHOLD ;if counter goes past 255 then assume key press is valid.
jnz keyend
;here, the key is alwats held down for 255 interrupt calls 255 times
;and can safely assume the user hit the key (and not debounce)
setb KEYS.2 ;set flag
mov KEYHOLD,#0FFh ;set to FF so this section executes continuously until key is let go.
mov KEYTEMP,KEYS ;copy key value with flag to a temporary variable
inc KEYMD
mov A,KEYMD ;increase mega delay counter (keymd) to detect long key presses
jnz keyend
mov KEYMD,#0FFh
ljmp keyend
gotnokey:
;here the system thinks key wasn't pressed
mov KEYMD,#0h ;reset extended counter
dec KEYHOLD ;lower short key hold counter
mov A,KEYHOLD
inc A
jnz keyend
;once hold counter is below zero, reset to zero and detect key
mov KEYHOLD,#0h
jnb KEYTEMP.2,keyend
;here, keypress is valid
clr KEYTEMP.2 ;so invalidate the bit
clr KEYS.2 ;in both variables
setb KEYDET ;and set key detection flag
keyend:
;this gets executed at the end of prockey
mov A,KEYTEMP ;get saved key
anl A,#7h ;only accept lower 3 bits
mov GOTKEY,A ;and set output key variable to it
jnb KEYDET,noreskey
mov KEYTEMP,#0h ;clear temporary key if key is detected
noreskey:
ret
To handle multiple actions from different sources you could use a state machine. A variable defines the state. 0 could be the initial state. Menu level 1 could be 1000, 2000 etc, submenu 1100. Each time the handler is called the handler should return as soon as possible. If processing takes more time, use a timer and interrupt to subdivide processing yime or use multiple states.
The main loop calls a button and/or sensor handler, and the menu handler. When the user chooses a menu, the state changes so when menu is called again it knows what to do, an action and/or change state again.
This way it's much easier to keep track of when what happens and keep user interaction asynchronous, and respond quickly.
Related
I write a pintool to dump every instruction with pid, thread id, and address. It is stored at Github gist
However, after I ran one example, the pid of first instruction is the same as the pid in the main function, not the same as the second or following instructions. The output is in the following:
cerr
Pid 7292=========
cout
7292-0-b777c0d0-mov eax, esp
1c7c-0-b777c0d2-call 0xb777f790
1c7c-0-b777f790-push ebp
1c7c-0-b777f791-mov ebp, esp
......
I don't know why it occurs. Maybe I use the PIN_GetPid API in the wrong way. Could someone give me some advice?
7292 in decimal is 0x1c7c in hexadecimal.
std::hex is maintained across separate invocations of the same stream.
Just prefix printing of pid with std::dec.
I'm having trouble with what should be a very simple piece of code. My objective is to turn an LED on while a button is being pressed. In this case the input logic will go low when I press the button. The LED is connected to PORTC.2 and the button is connected to PORTC.0. Here is the code:
dim test as bit
main:
TRISC = %00000001
ANSEL = %00000000
PORTC= 0
cmcon0=0
Testbutton:
test = PORTC.0
if test = 0 then
PORTC = %00000100
goto Testbutton
end if
PORTC = %00000000
end.
The problem is the PIC always outputs high no matter what the input is. So far I have used a multimeter to verify that the input is indeed changing from 5V to 0V when the button is pressed, I have tried using a different input pin, and I have tried using a different PIC. I suspect that since the input is always being read as low that the PIC may not be properly initialized but I'm not entirely sure.
Thanks for any insight that you might be able to give me
The datasheet of 16f684 states on page 42 that :
The ANSEL and CMCON0 registers must be initialized to configure an analog channel as a digital input. Pins configured as analog inputs will read ‘0’.
These registers are actually initialized in your code. ANSEL is initialized to 0, so all analog input are deactivated. Yet, the CMCON0 register should be initialized to xxxxx111 or 07h. See the example 4.3 on page 42. The last three bits of CMCON0 correspond to the mode of operation of comparators and choosing 111 turns them off. See page 60.
Don't forget to add goto Testbutton if you want the led to light up again as the button is released. It may be intentional though : it's up to you !
I want to add a timer to this function so that every time a uid is read it restarts and I can set another function to do a serial.write once a certain time is reached. Let's call this an idle time function. I cannot tie this to the ID read as I have 28 IDs potentially being read. I want to reset my audio player with an ASCII command via serial.write if no iud has been read for say longer than 180 seconds... Suggestions?
if(uid[0] == 0x64 && uid[1] == 0xBF && uid[2] == 0xD8 && uid[3] == 0x51)
{
//pause at beginning
delay (500);
//Serial.write("for Bässgen MM3210")
Serial.write("listplay 1 1");
Serial.write(13);
//pause at end
delay (3000);
}
You could assign each uid a value from the millis() call.
It basically counts the milliseconds since the chip turns on. It is stored in a long so it will reset to zero every 80 days or so I think (I never left it that long)
I did this to time a random array that was writing faster or slower based on how many pieces of data were in it. Instead of using delay() and counted loops, using millis() allowed me to jump in at a certain time.
I'm a fresh tcl user and I've been working on recently on a script, that uses serial port. My greatest problem so far is that I can't effectively read my serial port.
I'm using fileevent to read, but it catches what was send previously. However if I dont send anything before, it waits for external data and catches it. My code:
global forever
proc read_serial {serial} {
set msg [read $serial]
set listOfLetters [split $msg {} ]
set serialIPinHex ""
foreach iChar $listOfLetters { ;#conversion to hex
scan $iChar "%c" decimalValue
set hexValue [format "%02X" $decimalValue ]
set hexValue "0x$hexValue"
set serialIPinHex "$serialIPinHex $hexValue"
}
puts "MCU RESPONSE: $serialIPinHex"
global forever
set forever 1 ;# end event loop
}
set serial [open com1 r+]
fconfigure $serial -mode "19200,n,8,1"
fconfigure $serial -blocking 0 -buffering none
fconfigure $serial -translation binary -encoding binary
fileevent $serial readable [list read_serial $serial ]
global forever
puts -nonewline $serial \x31\xce
flush $serial
delay 10
vwait forever
flush $serial
close $serial
The effect is "MCU RESPONSE: x31 xce", while it should await for sth to come by serial (in my understanding). I'm sure there is no echo function on the other end. Thanks in advance for your help. I hope my bug is not embarassing, I spend last few hours looking for it...
The main thing to note about serial ports is that they are slow. Really slow. A modern computer can run round the block three times before a serial port has passed one character over (well, metaphorically). That means you're going to get characters coming one at a time and you've got to deal with that.
You're advised to use read $serial 1 and to append that to a buffer you are keeping, or write your code to handle a single byte at a time. If you were using line-oriented messaging, you could take advantage of gets's good behaviour in non-blocking mode (the fblocked command is designed to support this), but read isn't quite so friendly (since it knows nothing about record separators).
Don't worry about missing a byte if you use read $serial 1; if there's one left, your callback will get called again immediately to deal with it.
I have a UDP server that reflects every ping message it receives (this works well I think). I the client side I would then like to do two things:
make sure that I fired off N (e.g. 10000) messages, and
count the number of correctly received responses.
It seems that either because of the nature of UDP or because of the forkIO thing, my client code below ends prematurely/does not do any counting at all.
Also I am very surprised to see that the function tryOnePing returns 250 times the Int 4. Why could this be?
main = withSocketsDo $ do
s <- socket AF_INET Datagram defaultProtocol
hostAddr <- inet_addr host
thread <- forkIO $ receiveMessages s
-- is there any better way to eg to run that in parallel and make sure
-- that sending/receiving are asynchronous?
-- forM_ [0 .. 10000] $ \i -> do
-- sendTo s "ping" (SockAddrInet port hostAddr)
-- actually this would be preferred since I can discard the Int 4 that
-- it returns but forM or forM_ are out of scope here?
let tryOnePing i = sendTo s "ping" (SockAddrInet port hostAddr)
pings <- mapM tryOnePing [0 .. 1000]
let c = length $ filter (\x -> x==4) pings
-- killThread thread
-- took that out to make sure the function receiveMessages does not
-- end prematurely. still seems that it does
sClose s
print c
-- return()
receiveMessages :: Socket -> IO ()
receiveMessages socket = forever $ do
-- also tried here forM etc. instead of forever but no joy
let recOnePing i = recv socket 1024
msg <- mapM recOnePing [0 .. 1000]
let r = length $ filter (\x -> x=="PING") msg
print r
print "END"
The main problem here is that when your main thread finishes, all other threads gets killed automatically. You have to get the main thread to wait for the receiveMessages thread, or it will in all likelyhood simply finish before any responses have been received. One simple way of doing this is to use an MVar.
An MVar is a synchronized cell that can either be empty or hold exactly one value. The current thread will block if it tries to take from an empty MVar or insert into a full one.
In this case, we don't care about the value itself, so we'll just store a () in it.
We'll start with the MVar empty. Then the main thread will fork off the receiver thread, send all the packets, and try to take the value from the MVar.
import Control.Concurrent.MVar
main = withSocketsDo $ do
-- prepare socket, same as before
done <- newEmptyMVar
-- we need to pass the MVar to the receiver thread so that
-- it can use it to signal us when it's done
forkIO $ receiveMessages sock done
-- send pings, same as before
takeMVar done -- blocks until receiver thread is done
In the receiver thread, we will receive all the messages and then put a () in the MVar to signal that we're done receiving.
receiveMessages socket done = do
-- receive messages, same as before
putMVar done () -- allows the main thread to be unblocked
This solves the main issue, and the program runs fine on my Ubuntu laptop, but there are a couple more things you want to take care of.
sendTo does not guarantee that the whole string will be sent. You'll have to check the return value to see how much was sent, and retry if not all of it was sent. This can happen even for a short message like "ping" if the send buffer is full.
recv requires a connected socket. You'll want to use recvFrom instead. (Although it still works on my PC for some unknown reason).
Printing to standard output is not synchronized, so you might want to alter this so that the MVar will be used to communicate the number of received packets instead of just (). That way, you can do all the output from the main thread. Alternatively, use another MVar as a mutex to control access to standard output.
Finally, I recommend reading the documentation of Network.Socket, Control.Concurrent and Control.Concurrent.MVar carefully. Most of my answer is stitched together from information found there.