PaulStoffregen / CapacitiveSensor

Detect touch or proximity by capacitve sensing
http://www.pjrc.com/teensy/td_libs_CapacitiveSensor.html
391 stars 146 forks source link

Arduino Nano RP2040 Connect compatibility #42

Open PalovskyTomas opened 2 years ago

PalovskyTomas commented 2 years ago

Description

Library does not compile on Arduino Nano RP2040 Connect. Problem is very similar to #37 , but solution does not work for Arduino Nano RP2040 Connect.

Steps To Reproduce Problem

Download latest library. It is v0.5.1 as of today.

Hardware & Software

Board - Arduino Nano RP2040 Connect Shields / modules used - none Arduino IDE version - 1.8.19 Teensyduino version (if using Teensy) - none Version info & package name (from Tools > Boards > Board Manager) - Mbed OS Nano boards Operating system & version - Windows 10 Any other software or hardware? - none

Arduino Sketch

none.

#include <CapacitiveSensor.h>

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

Errors or Incorrect Output

CapacitiveSensor.h:417:2: error: 'IO_REG_TYPE' does not name a type
  IO_REG_TYPE sBit;   // send pin's ports and bitmask
  ^~~~~~~~~~~
CapacitiveSensor.h:418:11: error: 'IO_REG_TYPE' does not name a type
  volatile IO_REG_TYPE *sReg;
           ^~~~~~~~~~~
CapacitiveSensor.h:419:2: error: 'IO_REG_TYPE' does not name a type
  IO_REG_TYPE rBit;    // receive pin's ports and bitmask
  ^~~~~~~~~~~
CapacitiveSensor.h:420:11: error: 'IO_REG_TYPE' does not name a type
  volatile IO_REG_TYPE *rReg;
           ^~~~~~~~~~~
exit status 1
Error compiling for board Arduino Nano RP2040 Connect.
Gerriko commented 2 years ago

I believe the reason for this is that the RP2040 (ARDUINO_ARCH_RP2040) board is not defined in the header file.

However, one wonders if RP2040 PIO functionality might be better suited for the SenseOneCycle function, hence removing the need for rReg, rBit, sReg, sBit etc.

Gerriko commented 2 years ago

I tested out the CapacitiveSensor::SenseOneCycle(void) function using PIO and I think it works pretty well. However, I am unable to validate this... also PIO is restrictive as only have 2 and only have 4 state machines (I used 2 SM's for 1 cap sense). As such limited to max 4 capsensors.

;
; Copyright (c) 2022 C Gerrish (gerriko)
;
; SPDX-License-Identifier: BSD-3-Clause
;

.program capsense1
.side_set 1 opt

; Initialise READ pin with TRIG pin for capsense timing detection

begin1:
    pull                    ; used as a manual trigger
    nop             side 0  ; Trig Pin write low
    set pindirs, 0          ; Read Pin set as input
    set pindirs, 1          ; Read Pin set as output
    set pins, 0             ; Read Pin set low as an output
                    ; delay for 10 us (the length of the trigger pulse)
    set x 19        ; set x to 10011 (and clear the higher bits)
    mov ISR x       ; copy x to ISR
    in NULL 6       ; shift in 6 more 0 bits
    mov x ISR       ; move the ISR to x (which now contains 10011000000)
delay1:
    jmp x-- delay1  ; count down to 0: a delay of (about) 10 us
    set pindirs, 0          ; Read Pin set as input
    nop             side 1  ; Trig Pin write high
    jmp begin1

.program capsense2
.side_set 1 opt

; Initialise READ pin with TRIG pin for capsense timing detection

begin2:
    pull                    ; used as a manual trigger
    nop             side 0  ; Trig Pin write low
    set pins, 1             ; set Read Pin high as input
    set pindirs, 1          ; set Read Pin as output
    set pins, 1             ; set Read Pin high as input
    set pindirs, 0          ; set Read Pin as input
    nop             side 0  ; Trig Pin write low
    jmp begin2

% c-sdk {
// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin
void capsense_program_init(PIO pio, uint sm1, uint offset1, uint sm2, uint offset2, uint trig_pin, uint read_pin) {

  uint32_t pins_assigned = (1u << trig_pin) | (1u << read_pin);
  uint32_t pins_setstate = (0u << trig_pin) | (0u << read_pin);
  uint32_t pins_setdirection = (1u << trig_pin) | (1u << read_pin);

  gpio_set_pulls(read_pin, false, false);         // make sure internal pull-up / pull-down not enabled

  pio_sm_set_pins_with_mask(pio, sm1, pins_setstate, pins_assigned);
  pio_sm_set_pindirs_with_mask(pio, sm1, pins_setdirection, pins_assigned);
  pio_sm_set_pins_with_mask(pio, sm2, pins_setstate, pins_assigned);
  pio_sm_set_pindirs_with_mask(pio, sm2, pins_setdirection, pins_assigned);
  pio_gpio_init(pio, trig_pin);
  pio_gpio_init(pio, read_pin);

  pio_sm_config c1 = capsense1_program_get_default_config(offset1);
  pio_sm_config c2 = capsense2_program_get_default_config(offset2);

  // use set config for the read pin to allow direction and state to change
  sm_config_set_set_pins(&c1, read_pin, 1);
  sm_config_set_set_pins(&c2, read_pin, 1);

  // use out and sideset config for the trig pin to allow output state to change in parallel with read_pin
  sm_config_set_sideset_pins(&c1, trig_pin);
  sm_config_set_sideset_pins(&c2, trig_pin);

  // Load our configuration, and jump to the start of the program
  pio_sm_init(pio, sm1, offset1, &c1);
  pio_sm_init(pio, sm2, offset2, &c2);
  // Set the state machine running
  pio_sm_set_enabled(pio, sm1, true);
  pio_sm_set_enabled(pio, sm2, true);

}
%}

Arduino sketch to test:

#include "hardware/pio.h"
#include "hardware/clocks.h"

#include "capsense.pio.h"

#define F_CPU 125000000         // Assuming 125MHz for the Pico

static const uint32_t CS_Timeout_Millis = (2000 * (float)310 * (float)F_CPU) / 16000000;;

static const int TRIGPIN =  13;
static const int RECPIN =  12;
PIO pio = pio0;

// Find a free state machine on our chosen PIO (erroring if there are
// none). Configure it to run our program, and start it, using the
// helper function we included in our .pio file.
unsigned int sm1 = 0;
unsigned int sm2 = 0;

uint32_t captimer = 0L;

void setup() {
  // put your setup code here, to run once:
  // put your setup code here, to run once:
  Serial.begin(115200);
  while(!Serial) {;;}

  uint offset1 = pio_add_program(pio, &capsense1_program);
  Serial.println("Loaded program1 at "+String(offset1));

  uint offset2 = pio_add_program(pio, &capsense2_program);
  Serial.println("Loaded program1 at "+String(offset2));

  sm1 = pio_claim_unused_sm(pio, true);
  Serial.println("A free state machine instance for our 1st routine was set as "+String(sm1));
  sm2 = pio_claim_unused_sm(pio, true);
  Serial.println("A free state machine instance for our 2nd routine was set as "+String(sm2));

  capsense_program_init(pio, sm1, offset1, sm2, offset2, TRIGPIN, RECPIN);
  Serial.println("Capsense PIO instance now initialised");

}

void loop() {
  uint32_t total = 0L;
  captimer = micros();

  pio_sm_put_blocking(pio, sm1, 0);         // This triggers the first PIO routine

  while (!digitalRead(RECPIN) && (total < CS_Timeout_Millis)) {  // while receive pin is HIGH  AND total is less than timeout
   total++;
  }
  if (total <= CS_Timeout_Millis) {

    pio_sm_put_blocking(pio, sm2, 0);       // This triggers the 2nd PIO routine

    while (digitalRead(RECPIN) && (total < CS_Timeout_Millis)) {  // while receive pin is HIGH  AND total is less than timeout
      total++;
    }
    if (total <= CS_Timeout_Millis) {
      Serial.println("CapSensing Time: "+String(micros()-captimer));
    }
    else {
      Serial.println("CapSensing Timeout");
    }
  }
  delay(500);

}
Gerriko commented 2 years ago

I couldn't see way to merge my RP2040 PIO based code with this library, so I created a new library (with some improvements and fixes to the above PIO code) just for the RP2040 if you want to check it out... https://github.com/Gerriko/PicoCapSense

Designcorporal commented 2 years ago

Hi! I use nano 33 ble! I was interested in testing this library but I couldn't add it. how do i include it?