Project: LMT01 Temperature Sensor

This project displays up to three LMT01 temperature sensor readings in degrees C/F, reading one sensor at a time. The design allows for one LMT01 sensor per GPIO pin, and more can be easily added.

Example Output

testall ( only LMT01 sensors 1 and 2 are fitted )

Sensor-1: 25.5C or 77.9F
Sensor-2: 25.4C or 77.7F
Sensor-3: LMT01 faulty or disconnected
Sensor-4: No such sensor !!  ok.

YouTube Video

https://youtu.be/UpIYeZxftcE

The LMT01 Temperature Sensor

The LMT01 provides a digital output in the form of a pulse count that is transmitted by a train of current pulses.

After the LMT01 is powered up, it transmits a very low current of 34 µA for less than 54 ms while performing a temperature to digital conversion. When the temperature-to-digital conversion is complete, the LMT01 transmits a pulse train that toggles from the low current of 34 µA to a high current level of 125 µA.

The total pulse count represents the temperature of the LMT01.

LMT01 Spec

The LMT01 outputs at minimum 1 pulse and a theoretical maximum of 4095 pulses. Each pulse has a weight of 0.0625°C.

One pulse corresponds to a temperature less than -50°C while a pulse count of 4096 corresponds to a temperature greater than 200°C.

Warning

The LMT01 is only ensured to operate up to 150°C. Exceeding this temperature by more than 5°C may damage the device. The accuracy of the device degrades as well when 150°C is exceeded.

Software Flowchart

This is the basic design of the Forth software to read multiple LMT01 two wire temperature sensors. My next modification will be to count the pulses from the temperature sensors in a counter timer so the MCU doesn’t need to do it (currently handled by interrupts), freeing up cpu cycles for more important tasks.

_images/f0-lmt01-temp-sensor2-flowchart.jpg

LMT01 TO-92 Package

_images/lmt01-mechanical.jpg

LMT01 Waveforms

Free Running Pulse Block Timing

Taken at the Collector of Q1

_images/lmt01-free-running-pulse-block-timing.png

Pulse Block Width

Taken at the Collector of Q1

_images/lmt01-pulse-block-width.png

Individual Pulse Timing

Taken at the Collector of Q1

_images/lmt01-pulses-expanded.png

The schematic

Note

all parts inside the STM32F0 Discovery Board rectangle are actually built into it by ST, they aren’t parts I’ve added for this project

_images/f0-lmt01-temp-sensor-2-schematic.jpeg

LMT01 Software

This is actually one file f0-lmt01-temp-sensor-2.fs but I’ve broken it up into three parts for clarity. If you copy and paste these files, just load part-1 first, part-2 second, and part-3 third.

Part-2

This is the ‘main’ program which counts the temperature sensor pulses, scales and prints the results.

: lmt01-fault? ( -- ) raw.temperature @ 0 = ;        \ If a sensor has a zero count, it's faulty
: pretty.print ( -- )  0 <# # 46 hold #s #> type  ;  \ Insert one decimal point into final value

: degrees.c ( raw -- C )  \ Print Degrees C
  pretty.print ." C "
;

: degrees.f ( raw -- F )  \ Convert Degrees C to Degrees F  (formula: °C  x  9/5 + 32 = °F
   90 5 / * 3200 + 10 /
   pretty.print ." F "
;

: degrees ( -- )
     lmt01-fault? if ." LMT01 faulty or disconnected "
        else
        raw.temperature @                      \ fetch total number of pulses counted
        10000 4096 */ 256 * 500000 - 1000 /    \ Temp (C) = ((count/4096) *256) -50 scale raw temp into degrees x 10
        dup degrees.c ." or " degrees.f
     then
;

: temperature? ( sensor -- )             \ one temperature? measurement
  0 lmt01-x.count !                      \ lmt01-x.count increments once per lmt01-x pulse
  0 raw.temperature !                    \ raw.temperature is lmt01-x.count total count
  0 ready.flag !                         \ ready.flag is set when raw.temperature value is stable and ready to be used
     case                                \ power up selected sensor
        1 OF lmt01-1.power.on ENDOF
        2 OF lmt01-2.power.on ENDOF
        3 OF lmt01-3.power.on ENDOF
        ." No such sensor !! " drop exit         \ sensor not implimented fall thru warning
     endcase
  green-on                               \ mark start of count window
  tim6_cr1_cen                           \ start timer6 (end of timer kills the power to the lmt01)
     begin                               \ loop until raw.temperature is stable
        pause
        ready.flag @ 1 =
     until
  degrees cr
;

: t temperature? ;

: testall cr ( -- ) \ simple test of sensors
  ." Sensor-1: " 1 t
  ." Sensor-2: " 2 t
  ." Sensor-3: " 3 t
  ." Sensor-4: " 4 t
;

Part-1

Contains all the low level routines, variables and constants

compiletoram

0 variable lmt01-x.count
0 variable raw.temperature
0 variable ready.flag

\ RCC Words
: rcc_apb2enr_syscfgen-set ( -- )  %1  0  lshift  rcc_apb2enr bis! ;   \ rcc_apb2enr_syscfgen syscfg clock enable
: rcc_apb1enr_tim6en ( -- ) %1 4 lshift rcc_apb1enr bis! ;             \ rcc_apb1enr_tim6en tim6 clock enable

: init.gpio ( -- )
  \ One Port pull up for all sensors
  rcc_apb2enr_syscfgen-set   \ REQUIRED!! for using GPIO's as interrupts!
  pull-up ( %xx -- ) 0 lshift gpioc_pupdr bis!  \ gpioc_pupdr_pupdr0 PC0
;

\ LMT01 related Words
: syscfg_exticr1_exti0 ( -- ) %0010  0  lshift  syscfg_exticr1 bis! ;  \ pc-0 is interrupt source  x010: pc[0] pin
: exti_imr_mr0-set ( -- )  %1  0  lshift  exti_imr bis! ;              \ exti_imr_mr0 set interrupt mask on line 0
: exti_pr_pr0 ( -- ) %1 0 lshift exti_pr bis! ;                        \ exti_pr_pr0    pending bit 0
: exti_ftsr_tr0-set ( -- )  %1  0  lshift  exti_ftsr bis! ; \ exti_ftsr_tr0 falling trigger event configuration of  line 0
: exti_pr_pr0-set ( -- )  %1  0  lshift exti_pr  bis! ;                \ exti_pr_pr0 interrupt pending flag for line 0
: nvic_iser_setena-exti0_1 ( -- )  %100000 0  lshift nvic_iser  bis! ; \ nvic_iser_setena-exti0_1
: nvic_icer_clrena-exti0_1 ( -- )  %100000 0  lshift nvic_icer  bis! ; \ nvic_icer_clrena-exti0_1

: lmt01-1.power.on  ( -- )
  output  ( %xx -- ) 2 lshift gpioc_moder bis!    \ gpioc_moder_moder1  PC1
  %1 1 lshift gpioc_bsrr bis!                     \ gpioc_bsrr_bs1; output high
;

: lmt01-2.power.on  ( -- )
  output ( %XX -- ) 4 lshift GPIOC_MODER bis!     \ GPIOC_MODER_MODER2  PC2
  %1 2 lshift gpioc_bsrr bis! ;                   \ gpioc_bsrr_bs2; output high


: lmt01-3.power.on  ( -- )
  output ( %XX -- ) 6 lshift gpioc_moder bic!     \ gpioc_moder_moder3  PC3
  %1 3 lshift gpioc_bsrr bis! ;                   \ gpioc_bsrr_bs3; output high


: lmt01-1.power.off %11 ( -- ) 2 lshift gpioc_moder bic! ; \ gpioc_moder_moder1  PC1
: lmt01-2.power.off %11 ( -- ) 4 lshift gpioc_moder bic! ; \ gpioc_moder_moder2  PC2
: lmt01-3.power.off %11 ( -- ) 6 lshift gpioc_moder bic! ; \ gpioc_moder_moder3  PC3

: lmt01-x.power.off ( -- ) \ turn off all lmt01's by switching the port from output to input mode
  lmt01-1.power.off
  lmt01-2.power.off
  lmt01-3.power.off
;

\ Tim6 related Words
48000000 constant fCK

: nvic_iser_setena-tim6 ( -- ) 1 17 lshift nvic_iser bis! ;     \ nvic_iser_setena   tim6
: nvic_icer_clrena-tim6 ( -- ) 1 17 lshift nvic_icer bis! ;     \ nvic_icer_clrena  tim6
: nvic_icpr_clrpend-tim6 ( -- ) 1 17 lshift nvic_icpr bis! ;    \ nvic_icpr_clrpend  tim6
: tim6_cnt@ ( -- ) tim6_cnt h@ ;  \ tim6_cnt_cnt   16 bit counter value
: tim6_cr1_opm ( -- ) %1 3 lshift tim6_cr1 hbis! ;      \ tim6_cr1_opm one-pulse mode
: tim6_cr1_cen ( -- ) %1 0 lshift tim6_cr1 hbis! ;      \ tim6_cr1_cen    counter enable
: tim6_cr1_cdis ( -- ) %1 0 lshift tim6_cr1 hbic! ;     \ tim6_cr1_cen    counter disable
: tim6_dier_uie ( -- ) %1 0 lshift tim6_dier hbis! ;    \ tim6_dier_uie        update interrupt enable
: tim6_sr_uif ( -- ) %1 0 lshift tim6_sr hbic! ;        \ tim6_sr_uif  clear tim6 int pending flag
: tim6_egr_ug ( -- ) %1 0 lshift tim6_egr hbis! ;       \ tim6_egr_ug  update generation

: preset.psc.no.int ( -- ) \ preset Prescaler but don't cause interrupt. PSC does not get 'active' until the second update event
  tim6_arr h!              \ preset arr
  tim6_egr_ug              \ update generation: (causes a interrupt when run) but loads ARR into shaddow register!
  tim6_sr_uif              \ clear tim6 int pending flag after running tim6_egr_ug
  tim6_dier_uie            \ update interrupt enable
;

: tim6.int.handler ( -- )
  nvic_icpr_clrpend-tim6              \ clear pending iser for tim6
  tim6_sr_uif                         \ clear tim6 int pending flag
  tim6_cr1_cdis                       \ counter disable
  nvic_icer_clrena-tim6               \ disable the tim6 interrupt or dropping power to the LMT01 will re-trigger it
  green-off                           \ mark finish of count window
  lmt01-x.power.off
  lmt01-x.count @ raw.temperature !   \ raw.temperature now contains valid lmt01 count
  1 ready.flag !                      \ it's safe to read raw.temperature now
  nvic_iser_setena-tim6               \ re-enable tim6 iser
  nvic_iser_setena-exti0_1            \ re enable exti0_1 iser ( won't work without this )
;

: init.tim6 ( -- )
  rcc_apb1enr_tim6en      \ enable tim6 in rcc
  tim6_cr1_opm            \ one pulse mode high = Counter stops counting at the next update event (clearing the bit CEN)
  47952 tim6_psc h!       \ preset prescaler for 1mS (47 for 1 uS)
  104 preset.psc.no.int   \ preset PSC --> clear int pending flag, enable interrupt
  ['] tim6.int.handler irq-tim6_dac !
  nvic_iser_setena-tim6   \ enable iser
;

: lmt01.interrupt.handler ( -- )
  exti_pr_pr0                         \ clear pending bit 0
  lmt01-x.count @ 1+ lmt01-x.count !  \ increment temperature count by one
  exti_pr_pr0-set                     \ clear external interrupt pending register (exti_pr)
;

: init.lmt01 ( -- )
  syscfg_exticr1_exti0                         \ negative going pulses from lmt01 trigger pc0 interrupt
  exti_imr_mr0-set                             \ exti0 set interrupt mask on line 0
  exti_ftsr_tr0-set                            \ falling trigger on line 0
  ['] lmt01.interrupt.handler irq-exti0_1 !    \ tie the interrupt handler to the interrupt
  nvic_iser_setena-exti0_1                     \ enable exti0_1 interrupt
;

Part-3

This last part adds initialization and multitasking

: main ( -- )                \ executed about every 5ms @48mHz
 pause
;

: init-general ( -- )        \ executed once at boot-up
  f0-disco-48mhz             \ switch MCU clock from 8 to 48 MHz
  init.gpio                  \ Initialisations
  init.tim6
  init.lmt01
;

task: maintask         \ essential multitasking Word
: main& ( -- )          \ multitasking.fs Word
  maintask activate    \ multitasking.fs Words
  begin main again     \ this is the only task, and it loops forever (about every 5uS @ 48Mhz clock) unless blocked
;

: init ( -- )        \ essential multitasking Word
  init-general      \ main user application inits
  multitask         \ multitasking.fs Word
  main&             \ multitasking.fs Word
;

init                 \ this init for non flash use, i.e. after uploading this program

Waveforms

Window Start to Pulse Block Start Timing

  • Window and Power are the same thing displayed on CH-1

  • Waveform 1: PC-1

  • Waveform 2: Collector Q1

_images/lmt01-pwr-up-delay.png

Pulse Block Start to Window Close Timing

  • Waveform 1: PC-1

  • Waveform 2: Collector Q1

_images/lmt01-pulseblock-window.close.png

Testall

  • Waveform 1: PC-1

  • Waveform 2: Collector Q1

_images/testall.png

Downloads

f0-lmt01-temp-sensor-2.fs

memmap.fs

bitfields.fs

template.xml

screen-f0-lmt01-temp-sens~2-cuaU0-498600-ctsrts.sh

Kernel be337881895e64e60016a3514a0dea8e.bin

be337881895e64e60016a3514a0dea8e.words.txt

be337881895e64e60016a3514a0dea8e.README.txt

lmt01.pdf

Note

The Kernel binary filename is a MD5SUM of the file itself.

md5 be337881895e64e60016a3514a0dea8e.bin
MD5 (be337881895e64e60016a3514a0dea8e.bin) = be337881895e64e60016a3514a0dea8e