"
MODULE P3006

" Copyright (C) 2005 Kevan Hashemi, Open Source Instruments

" This program is free software; you can redistribute it and/or modify
" it under the terms of the GNU General Public License as published by
" the Free Software Foundation; either version 2 of the License, or
" (at your option) any later version.

" This program is distributed in the hope that it will be useful,
" but WITHOUT ANY WARRANTY; without even the implied warranty of
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
" GNU General Public License for more details.

" You should have received a copy of the GNU General Public License
" along with this program; if not, write to the Free Software
" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

TITLE 'P3006A'

"Firmware Version 4"

"We include fourteen signal bits in the transmission, and"
"two checksum bits. We re-compile this code for each new"
"transmitter ID. Edit the transmitter ID bits immediately"
"below to set each ID."

declarations

"Parameters"
TID4=0;TID3=0;TID2=1;TID1=1;TID0=0; "Transmitter Identifier"
TPN2=0;TPN1=0;TPN0=1; "Transmission Protocol Number"

"Inputs and Outputs"
CK   pin K10; "Clock From 32-kHz Oscillator"
RCK pin A5; "Slow Clock, or Global CLK0, Connected to Pin 44"
RCKO pin A4; "Source of RCK"
FCK pin H6; "Fast Clock, or Global CLK1 Connected to Pin 17"
FCKO pin H5 istype 'com'; "Source of CK1"
TCK pin K6; "Transmission Clock, or Global CLK2 Connected to Pin 20"
TCKO pin K7 istype 'reg'; "Source of CK2"
ECK pin C5; "Global CLK3 Connected to Pin 41"
ECKO pin C6 istype 'reg'; "Source of CK3"
RELAY pin K9 istype 'com'; "Relay Connection"
TUNE pin K4 istype 'com'; "Tune Control for Transmitter"
!SHDN pin D10 istype 'com'; "Shutdown Control for Transmitter"
TP1..TP4 pin C1,D1,F1,G1 istype 'com'; "Test Points on P2"
CONV pin C10 istype 'com'; "Convert for ADC"
SDI pin H8 istype 'com'; "Serial Data In for ADC"
SDO pin D8; "Serial Data Out for ADC"
SCK pin E10 istype 'com'; "Serial Clock for ADC"

"Nodes"
T0..T5 node istype 'reg'; "Timer Running off 32-kHz"
R1 node istype 'com,keep'; "Ring Oscillator Bit"
TXS0..TXS5 node istype 'reg,pos'; "Transmitter State"
ACTIVE node istype 'reg'; "Active period of 32-kHz"
RUN node istype 'reg'; "Run the ring oscillator for calibration"
TXD node istype 'com'; "Transmitter Done"
PMS1..PMS0 node istype 'reg,pos'; "Power Master State"
RELAYOE node istype 'com'; "Drive Relay Ouput"
FCS12..FCS0 node istype 'reg'; "Fast Clock Speed"
FCSCLR node istype 'reg'; "Fast Clock Speed Clear"
TCD12..TCD7 node istype 'reg'; "Transmit Clock Divider"
CS0..CS1 node istype 'reg'; "Check Sum"
ADC0..ADC3 node istype 'reg'; "ADC Bits"
TTS0..TTS3 node istype 'reg'; "Transmit Time Shift"

"Sets"
fcs_integer = [FCS12..FCS7]; "Fast Clock Speed, Integer Part"
fcs_fraction = [FCS6..FCS0]; "Fast Clock Speed, Fraction Part"
time = [T5..T0]; "Transmit Cycle Timer"
tcd_integer = [TCD12..TCD7]; "Transmit Clock Divider, Integer Part"
txs = [TXS5..TXS0]; "Transmitter State"
pms = [PMS1..PMS0]; "Power Master State"
checksum = [CS1..CS0]; "Checksum"
adc_bits = [ADC3..ADC0]; "ADC Bits"
xmit_time_shift = [TTS3..TTS0]; "Transmit Time Shift"
active_time = [0,0,TTS3..TTS0]; "Active Time"

"Constants"
tck_divisor = 128;
clear_fcs_time = 28;
set_fcs_time = 30;
pms_asleep_magnet = 0;
pms_asleep_no_magnet = 1;
pms_awake_magnet = 2;
pms_awake_no_magnet = 3;
num_xmit_bits=33;

equations

"We set RCK, one of the global clock pins, equal to"
"the 32-kHz input via the RCKOT pin."
RCKO=CK;

"The transmit cycle timer runs off the 32-kHz clock"
time.clk=RCK;
time:=time+1;

"The ECK clock sets the active_time register and the"
"clears the checksum register at the end of a cycle"
"of tck_divisor RCK periods."
ECKO.clk=RCK;
ECKO:=(time==tck_divisor-2);

"The RELAY pin is connected to 0V through a reed relay"
"and a resistor. Whever transmit_cycle becomes zero, we"
"drive the RELAY pin high for the next half-period of"
"RCK. The Power Master checks the voltage on the RELAY"
"pin at the next rising edge of RCK. If the relay is"
"closed, RELAY.pin will be LO. If the relay is open,"
"the bus hold circuits on the input will hold RELAY.pin"
"HI."
RELAY=1;
RELAYOE=(time==0) & CK;
RELAY.oe=RELAYOE;

"The Power Master watches RELAY.pin and turns on and off"
"transmission as if the relay were a push-button."
pms.clk=RCK;
state_diagram pms;
  state pms_asleep_no_magnet:
    if !RELAY.pin then pms_awake_magnet
    else pms_asleep_no_magnet;
  state pms_asleep_magnet:
    if RELAY.pin then pms_asleep_no_magnet
    else pms_asleep_magnet;
  state pms_awake_no_magnet:
    if !RELAY.pin then pms_asleep_magnet
    else pms_awake_no_magnet;
  state pms_awake_magnet:
    if RELAY.pin then pms_awake_no_magnet
    else pms_awake_magnet;
equations


"When ACTIVE is asserted, we begin a burst transmission."
"When it is unasserted, we reset the burst transmission"
"state machine. We must make sure that ACTIVE remains true"
"for long enough for the burst transmission to complete."
ACTIVE.clk=RCK;
ACTIVE:=
    (time==active_time) & (pms==pms_awake_no_magnet)
  # (time==active_time) & (pms==pms_awake_magnet);

"TXD is true when the transmitter completes its burst"
"transmission."
TXD=(txs==num_xmit_bits+1);

"When we want to calibrate the ring oscillator, we assert"
"RUN for one period of RCK."
RUN.clk=RCK;
RUN:=(time==set_fcs_time) & (pms==pms_awake_magnet);

"The ring oscillator turns on when ACTIVE and remains"
"on until TXD. Each gate in the ring adds 2 ns to the"
"delay around the ring. The period of the oscillation is"
"therefore 4 ns multiplied by the number of gates. Here"
"we use only two gates to give us a ring frequency of"
"125 MHz. The higher frequency makes our approximation"
"of the nominal 238.419 ns bit transmission time accurate"
"to half a period, or +-4ns. After ten bits, this error"
"accumulates to +-40 ns, which is still less than one"
"quarter of the bit time, so reception with an exact"
"238.419 ns period will be accurate."
[FCKO,R1]=[R1,!FCKO & ACTIVE & !TXD # !FCKO & RUN];

"We reset the fast clock speed counters just before we"
"assert RUN."
FCSCLR.clk=RCK;
FCSCLR:=(time==clear_fcs_time) & (pms==pms_awake_magnet);

"When the transmitter is awake with the magnet nearby, it"
"measures the fast clock speed, which is the speed of the"
"ring oscillator. We start with the fractional part set to"
"64 and the integer part set to -1. This places the period"
"of TCKO (the source of TCK) within half an FCK period of"
"the exact value, assuming that the period of FCK during"
"the 30-us calibration interval is equal to that of FCK"
"during the transmission interval. The two periods may differ"
"as the 1.8-V supply to the logic chip drops with current"
"drawn out of its decoupling capacitor. With a 10-uF capacitor,"
"the transmit-time period is 0.3% shorter than the calibration"
"time period. As a result, the TCKO period will be 0.3% shorter"
"than it should be, but for our anticipated 238-ns, this"
"0.3% represents only 0.7 ns, which is small compared to the"
"4-ns half-period of our 125-MHz ring oscillator."
fcs_fraction.clk=FCK;
[FCS6].ap=FCSCLR;
[FCS5..FCS0].aclr=FCSCLR;
when RUN then {
  fcs_fraction:=fcs_fraction+1;
} else {
  fcs_fraction:=fcs_fraction;
}
fcs_integer.clk=FCK;
fcs_integer.ap=FCSCLR;
when RUN & (fcs_fraction==tck_divisor-1) then {
  fcs_integer:=fcs_integer+1;
} else {
  fcs_integer:=fcs_integer;
}

"The transmit clock divider runs off FCK and divides it down"
"to 32.768 * 128 = 4.194 MHz. We eliminate all use of the"
"fractional part of FCS in this version of the code. 
tcd_integer.aclr=!ACTIVE;
tcd_integer.clk=FCK;
TCKO.aclr=!ACTIVE;
TCKO.clk=FCK;
when (tcd_integer>0) then {
  tcd_integer:=tcd_integer-1;
}
when (tcd_integer==0) then {
  tcd_integer:=fcs_integer;
}

"Here we set the mark-space ratio of TCK0, and we do this"
"by setting TCK HI for half the nominal count-down."
TCKO:=(tcd_integer>0) & (tcd_integer<15);

"The transmitter state machine steps through all its"
"states when ACTIVE is asserted, and then stops in its"
"final state, waiting for !ACTIVE, which will reset it"
"to zero."
txs.clk=TCK;
txs.aclr=!ACTIVE;
when (txs==num_xmit_bits+1) then txs:=num_xmit_bits+1
else txs:=txs+1;

"TUNE sets the output frequency of the RF transmitter"
"to the mark frequency."
TUNE= 
    (txs== 0)&0 "idle value"
  # (txs== 1)&1 "start-up bit"
  # (txs== 2)&1 "start-up bit"
  # (txs== 3)&1 "start-up bit"
  # (txs== 4)&1 "start-up bit"
  # (txs== 5)&0 "falling edge"
  # (txs== 6)&TPN2 "protocol bit 2"
  # (txs== 7)&TPN1 "protocol bit 1"
  # (txs== 8)&TPN0 "protocol bit 0"
  # (txs== 9)&TID4 "id bit 4"
  # (txs==10)&TID3 "id bit 3"
  # (txs==11)&TID2 "id bit 2"
  # (txs==12)&TID1 "id bit 1"
  # (txs==13)&TID0 "id bit 0"
  # (txs==14)&1 "punctuation bit"
  # (txs==15)&0 "falling edge"
  # (txs>=16)&(txs<=23)&SDO "ADC bits 15 down to 8"
  # (txs==24)&1 "punctuation bit"
  # (txs==25)&0 "falling edge"
  # (txs>=26)&(txs<=31)&SDO "ADC bits 7 down to 2"
  # (txs==32)&CS1 "checksum bit 1"
  # (txs==33)&CS0; "checksum bit 0"

"SHDN turns off the RF transmitter"
SHDN=(txs==0)#(txs==num_xmit_bits+1);

"The Checksum counts the number of 1's transmitted"
"including punctuation bits."
checksum.clk=TCK;
when (txs==0) then checksum:=checksum;
when (txs>=1)&(txs<=5) then checksum:=0;
when (txs>5)&(txs=num_xmit_bits-1) then 
  checksum:=checksum;

"We record the lower ADC bits so they may be used to"
"set xmit_time_shift."
adc_bits.clk=TCK;
when (txs==30) then ADC3:=SDO
else ADC3:=ADC3;
when (txs==31) then ADC2:=SDO
else ADC2:=ADC2;
when (txs==32) then ADC1:=SDO
else ADC1:=ADC1;
when (txs==33) then ADC0:=SDO
else ADC0:=ADC0;

"Transmit Time Shift dictates the next ACTIVE period"
"by its inclusion in active_time."
xmit_time_shift.clk=ECK;
xmit_time_shift:=adc_bits;

"The CONV input to the ADC provokes conversion on"
"its rising edge, and must be low during readout."
"We don't want to leave it low any longer than we"
"have to, because the ADC goes to sleep only after"
"it is done converting following a CONV rising"
"edge, and when CONV is staying HI."
CONV=(txs==0)#(txs==num_xmit_bits+1);

"We clock the ADC bits out of the chip as we transmit"
"them, so we have no need for a register in which"
"to save them.
SCK=(txs>=16)&(txs<=23)&!TCK
  # (txs>=26)&(txs<=33)&!TCK;

"We set the ADC's SDI input according to the channel"
"we wish to select. We set SDI to 1 always if we want"
"the 0-V reference channel, and 1 for (txs==14) if we"
"want the active input channel."
SDI=(txs==16);

"Test Points"
TP1=ACTIVE;
TP2=TCKO;
TP3=TUNE;
TP4=CONV;


END