Hi,
I'll take a stab at answering my own question. After playing around for the last couple of days this is what I've come up with. If anyone more familiar with conditional assembly etc sees something wrong or better ways of doing things I'm all ears.
The following calculates the reload values for TMR0 based on the defined oscillator speed and a baudrate you tell it.
Code:
DEFINE OSC 8
' This define is used for calculating the proper timer reload value only.
' You still have to setup the USART properly. Either thru DEFINE HSER_BAUD
' or by setting the USART control registers manually.
DEFINE MODBUS_BAUDRATE 9600
' Change this to 0 to remove the messages.
DEFINE MODBUS_DEBUG 1
ASM
; Calculate number of ns per instruction cycle based on the defined
; oscillator speed. If user don't DEFINE OSC it gets set to 4 by default.
#define nS_per_cycle #v((1000/(OSC/4)))
ifdef MODBUS_BAUDRATE
; If the defined baudrate is above 19200 the interframe delay is fixed at 1750us.
; If the baudrate is 19200 or below then the interframe delay should equal the time
; it takes to receive 3.5 characters. Here we calculate with 11 bits per character.
; 1 startbit, 8 databits, 1 parity, 1 stopbit.
; If parity is not used the MODBUS specification says there should be 2 stopbits instead
; sp there's no difference. (Even parity should be the default though).
if MODBUS_BAUDRATE < 19201
; Delay is (1 / (baudrate/11)) * 3.5 seconds
#define MB_Frame_Timeout_ns #v((100000000/(MODBUS_BAUDRATE / 11)) * 35)
else
#define MB_Frame_Timeout_ns 1750000
endif
; Calculate proper interframe delay in number of instruction cycles.
#define MB_Frame_Timeout_cycles #v(MB_Frame_Timeout_ns / nS_per_cycle)
; Calculate the 16 bit reload value for TMR0
#define MB_Timer_Reload_Value #v(65536 - MB_Frame_Timeout_cycles)
; Calculate the individual high and low byte form TMR0 reload
#define MB_Timer_Reload_High #v(MB_Timer_Reload_Value / 256)
#define MB_Timer_Reload_Low #v(MB_Timer_Reload_Value - (MB_Timer_Reload_High * 256))
; If we want to see the debug information display the debug information.
ifdef MODBUS_DEBUG
if (MODBUS_DEBUG == 1)
messg MODBUS baudrate defined to: MODBUS_BAUDRATE BAUD
messg PIC cycletime: #v(nS_per_cycle)ns
messg Interframe timeout: #v(MB_Frame_Timeout_ns / 1000)us or MB_Frame_Timeout_cycles cycles at the defined oscillator speed (#v(OSC)MHz
messg TMR 0 High reload value: MB_Timer_Reload_High
messg TMR 0 Low reload value: MB_Timer_Reload_Low
endif
endif
; If the calculated number of cycles overflows 16bits we can't do it without changing the prescaler.
if MB_Frame_Timeout_cycles > 65535
ERROR MODBUS driver: Baudrate set too low or oscillator speed to high.
endif
else
; The MODBUS_BAUDRATE isn't defined so we can't calculate proper delay values.
ERROR Undefined baudrate for MODBUS driver. Can't calculate timing. Please DEFINE MODBUS_BAUDRATE.
endif
ENDASM
Now, the high and low byte of the timer reload value are in the MB_Timer_Reload_High and MB_Timer_Reload_Low constants respectively. In my current interrupt service routine where I reload the timer I have this code:
Code:
TMR0H = 221
TMR0L = 210
(Only part of actual ISR)
The above is what I want to change so that instead of having the values hardcoded it uses the previously calculated values. Looking at the assembly listing for the above code I see that PBP uses its MOVE?CB macro so my thoughts are to simply do something like:
Code:
ASM
; Don't try to use the constants if we haven't defined them. They don't get defined when
; MODBUS_BAUDRATE isn't defined. This is simply done to supress the errors that would otherwise
; get generated by not having the constants defined.
ifdef MB_Timer_Reload_High
ifdef MB_Timer_Reload_Low
MOVE?CB MB_Timer_Reload_High, TMR0H
MOVE?CB MB_Timer_Reload_Low, TMR0L
endif
endif
ENDASM
(Again, only part of actual ISR)
Now, using DT-INTS, are there any paging/banking problems I might run into by doing this. The idea here is that the code should be as generic as possible and I want to make sure that I'm not just lucky that it works here.
Thanks!
/Henrik.
Bookmarks