Skip to content

Commit

Permalink
RP.PIO.Touch_Sense: implement PIO based capacitive touch sensing
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabien-Chouteau authored and JeremyGrosser committed Feb 28, 2024
1 parent dd57b8f commit 70984e0
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/drivers/rp-pio-touch_sense.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
--
-- Copyright (C) 2022 Fabien Chouteau <[email protected]>
--
-- SPDX-License-Identifier: BSD-3-Clause
--
with RP.PIO.Touch_Sense_PIO;

package body RP.PIO.Touch_Sense is

function Initialized (This : Touch_Sensor) return Boolean
is (This.Enabled);

procedure Initialize (This : in out Touch_Sensor;
ASM_Offset : PIO_Address := 0;
Max_Count : HAL.UInt32 := 10_000)
is
Config : PIO_SM_Config := Default_SM_Config;
begin
This.Max_Count := Max_Count;

This.PIO.Load
(Prog => Touch_Sense_PIO.Touch_Sense_Program_Instructions,
Offset => ASM_Offset);

This.Pin.Configure (Output, Floating, This.PIO.GPIO_Function);

Set_Jmp_Pin (Config, This.Pin.Pin);
Set_Set_Pins (Config, This.Pin.Pin, 1);

Set_Wrap (Config,
ASM_Offset + Touch_Sense_PIO.Touch_Sense_Wrap_Target,
ASM_Offset + Touch_Sense_PIO.Touch_Sense_Wrap);

Set_Clock_Frequency (Config, 125_000_000);
This.PIO.SM_Initialize (This.SM, ASM_Offset, Config);
This.PIO.Set_Enabled (This.SM, True);

This.Enabled := True;

-- Do a first read to set a default threshold
This.Threshold := This.Raw_Value + 200;
end Initialize;

function Raw_Value (This : Touch_Sensor) return HAL.UInt32 is
Data : HAL.UInt32;
begin
if not This.Enabled then
return 0;
else
This.PIO.Put (This.SM, This.Max_Count);
This.PIO.Get (This.SM, Data);
return This.Max_Count - Data;
end if;
end Raw_Value;

function Touch (This : Touch_Sensor) return Boolean
is (This.Raw_Value > This.Threshold);

function Threshold (This : Touch_Sensor) return HAL.UInt32
is (This.Threshold);

procedure Set_Threshold (This : in out Touch_Sensor;
Threshold : HAL.UInt32)
is
begin
This.Threshold := Threshold;
end Set_Threshold;

end RP.PIO.Touch_Sense;
77 changes: 77 additions & 0 deletions src/drivers/rp-pio-touch_sense.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
--
-- Copyright (C) 2024 Fabien Chouteau <[email protected]>
--
-- SPDX-License-Identifier: BSD-3-Clause
--
with RP.GPIO;

package RP.PIO.Touch_Sense
with Preelaborate
is
-- Capacitive touch sensing is based on change in capacitance typically
-- introduced by the contact or proximity of the user finger(s) with a
-- pin.
--
-- Using PIO, any RP2040 pin connected to ground through a large resistor
-- (e.g. 1Mohm) can be a capacitive touch sensor.
--
-- The PIO program will charge the internal pin capacitor by configuring
-- the pin as an output and set it high for a few microseconds. And then
-- set the pin as an input and count how many cycles it takes for the
-- capacitor to discharge through the resistor.
--
-- If users touch the pin, the capacitance will increase and therefore the
-- number of cycles it takes to discharge will increase as well.

type Touch_Sensor
(Pin : not null access RP.GPIO.GPIO_Point;
PIO : not null access PIO_Device;
SM : PIO_SM)
is tagged private;

-- Return True if the touch sensor is configured and ready to use
function Initialized (This : Touch_Sensor) return Boolean;

-- Configure the Pin, PIO and state machine for capacitive touch sensing.
-- And set a default detection threshold based on a first measurement.
--
-- ASM_Offset is the location in PIO memory where the PIO assembly code
-- will be installed.
--
-- Max_Count is the maximum number of loops in the PIO program for a
-- single measure. Lowering this number will shorten the measure time
-- in worst case (high capacitance).
procedure Initialize (This : in out Touch_Sensor;
ASM_Offset : PIO_Address := 0;
Max_Count : HAL.UInt32 := 10_000);

-- Trigger a measurement and return the number of cycles it took for the
-- capacitor to discharge.
--
-- User touching the pin will increase capacitance, higher capacitance
-- means higher Raw_Value.
function Raw_Value (This : Touch_Sensor) return HAL.UInt32;

-- Return True if Raw_Value is above the detection threshold
function Touch (This : Touch_Sensor) return Boolean;

-- Return the threshold for touch detection
function Threshold (This : Touch_Sensor) return HAL.UInt32;

-- Set threshold for touch detection
procedure Set_Threshold (This : in out Touch_Sensor;
Threshold : HAL.UInt32);

private

type Touch_Sensor
(Pin : not null access RP.GPIO.GPIO_Point;
PIO : not null access PIO_Device;
SM : PIO_SM)
is tagged record
Enabled : Boolean := False;
Max_Count : HAL.UInt32;
Threshold : HAL.UInt32 := HAL.UInt32'Last;
end record;

end RP.PIO.Touch_Sense;
34 changes: 34 additions & 0 deletions src/drivers/rp-pio-touch_sense_pio.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--------------------------------------------------------
-- This file is autogenerated by pioasm; do not edit! --
--------------------------------------------------------

pragma Style_Checks (Off);

package RP.PIO.Touch_Sense_PIO
with Preelaborate
is

-----------------
-- Touch_Sense --
-----------------

Touch_Sense_Wrap_Target : constant := 0;
Touch_Sense_Wrap : constant := 11;

Touch_Sense_Program_Instructions : RP.PIO.Program := (
-- .wrap_target
16#80a0#, -- 0: pull block
16#e081#, -- 1: set pindirs, 1
16#e001#, -- 2: set pins, 1
16#e03e#, -- 3: set x, 30
16#1f44#, -- 4: jmp x--, 4 [31]
16#a027#, -- 5: mov x, osr
16#e080#, -- 6: set pindirs, 0
16#0049#, -- 7: jmp x--, 9
16#000a#, -- 8: jmp 10
16#00c7#, -- 9: jmp pin, 7
16#a0c1#, -- 10: mov isr, x
16#8020#); -- 11: push block
-- .wrap

end RP.PIO.Touch_Sense_PIO;
30 changes: 30 additions & 0 deletions src/drivers/touch_sense.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
; SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
; SPDX-FileCopyrightText: Copyright (c) 2023 Tod Kurt
;
; SPDX-License-Identifier: MIT


; See rp-pio-touch_sense.ads for more high level explanation of the touch
; sensing process.

.program touch_sense

.wrap_target
pull block ; trigger a reading, get maxcount value from fifo, OSR contains maxcount
set pindirs, 1 ; set GPIO as output
set pins, 1 ; drive pin HIGH to charge capacitance
; set x,24 ; wait time for pin charge
set x,30 ; wait time for pin charge
charge: ; wait (24+1)*31 = 1085 cycles = 8.6us
jmp x--, charge [31]
mov x, osr ; load maxcount value (10_000 usually)
set pindirs, 0 ; set GPIO as input
timing:
jmp x--, test ; decrement x until timeout
jmp done ; we've timed out, so leave
test:
jmp pin, timing ; loop while pin is still high
done:
mov isr, x ; load ISR with count value in x
push ; push ISR into RX fifo
.wrap

0 comments on commit 70984e0

Please sign in to comment.