Skip to content

Commit

Permalink
➕ code
Browse files Browse the repository at this point in the history
  • Loading branch information
gharishkumar authored Jul 24, 2021
1 parent b63bcee commit a254215
Show file tree
Hide file tree
Showing 9 changed files with 699 additions and 0 deletions.
38 changes: 38 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.12)

include(pico_sdk_import.cmake)

project(pico_ir_keyboard C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

add_compile_options(-Wall
-Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int
-Wno-unused-function # we have some for the docs that aren't called
-Wno-maybe-uninitialized
)

add_subdirectory(nec_receive_library)

add_executable(pico_ir_keyboard)

target_sources(pico_ir_keyboard PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)

target_include_directories(pico_ir_keyboard PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)

target_link_libraries(pico_ir_keyboard LINK_PUBLIC
pico_stdlib
hardware_pio
nec_receive_library
)

pico_add_extra_outputs(pico_ir_keyboard)

family_configure_device_example(pico_ir_keyboard)
19 changes: 19 additions & 0 deletions nec_receive_library/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# build a normal library
#
add_library(nec_receive_library nec_receive.c)

# invoke pio_asm to assemble the state machine program
#
pico_generate_pio_header(nec_receive_library ${CMAKE_CURRENT_LIST_DIR}/nec_receive.pio)

target_link_libraries(nec_receive_library PRIVATE
pico_stdlib
hardware_pio
)

# add the `binary` directory so that the generated headers are included in the project
#
target_include_directories (nec_receive_library PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
98 changes: 98 additions & 0 deletions nec_receive_library/nec_receive.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* Copyright (c) 2021 mjcross
*
* SPDX-License-Identifier: BSD-3-Clause
*/


// SDK types and declarations
//
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h" // for clock_get_hz()


// declare the public API functions
//
#include "nec_receive.h"


// import the assembled PIO state machine program
//
#include "nec_receive.pio.h"


// define the public API functions
//

// Claim an unused state machine on the specified PIO and configure it
// to receive NEC IR frames on the given GPIO pin.
//
// Returns: the state machine number on success, otherwise -1
//
int nec_rx_init(PIO pio, uint pin_num) {

// disable pull-up and pull-down on gpio pin
//
gpio_disable_pulls (pin_num);

// install the program in the PIO shared instruction space
//
uint offset;
if (pio_can_add_program (pio, &nec_receive_program)) {
offset = pio_add_program (pio, &nec_receive_program);
} else {
return -1; // the program could not be added
}

// claim an unused state machine on this PIO
//
int sm = pio_claim_unused_sm (pio, true);
if (sm == -1) {
return -1; // we were unable to claim a state machine
}

// configure and enable the state machine
//
nec_receive_program_init(pio, sm, offset, pin_num);

return sm;
}


// Validate a 32-bit frame and store the address and data at the locations
// provided.
//
// Returns: `true` if the frame was valid, otherwise `false`
//
bool nec_decode_frame (uint32_t frame, uint8_t *p_address, uint8_t *p_data) {

// access the frame data as four 8-bit fields
//
union {
uint32_t raw;
struct {
uint8_t address;
uint8_t inverted_address;
uint8_t data;
uint8_t inverted_data;
};
} f;

f.raw = frame;

// a valid (non-extended) 'NEC' frame should contain 8 bit
// address, inverted address, data and inverted data
//
if (f.address != (f.inverted_address ^ 0xff) ||
f.data != (f.inverted_data ^ 0xff)) {
return false;
}

// store the validated address and data
//
*p_address = f.address;
*p_data = f.data;

return true;
}
7 changes: 7 additions & 0 deletions nec_receive_library/nec_receive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "pico/stdlib.h"
#include "hardware/pio.h"

// public API
//
int nec_rx_init (PIO pio, uint pin);
bool nec_decode_frame (uint32_t sm, uint8_t *p_address, uint8_t *p_data);
96 changes: 96 additions & 0 deletions nec_receive_library/nec_receive.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
;
; Copyright (c) 2021 mjcross
;
; SPDX-License-Identifier: BSD-3-Clause
;


.program nec_receive

; Decode IR frames in NEC format and push 32-bit words to the input FIFO.
;
; The input pin should be connected to an IR detector with an 'active low' output.
;
; This program expects there to be 10 state machine clock ticks per 'normal' 562.5us burst period
; in order to permit timely detection of start of a burst. The initailisation function below sets
; the correct divisor to achive this relative to the system clock.
;
; Within the 'NEC' protocol frames consists of 32 bits sent least-siginificant bit first; so the
; Input Shift Register should be configured to shift right and autopush after 32 bits, as in the
; initialisation function below.
;
.define BURST_LOOP_COUNTER 30 ; the detection threshold for a 'frame sync' burst
.define BIT_SAMPLE_DELAY 15 ; how long to wait after the end of the burst before sampling

.wrap_target

next_burst:
set X, BURST_LOOP_COUNTER
wait 0 pin 0 ; wait for the next burst to start

burst_loop:
jmp pin data_bit ; the burst ended before the counter expired
jmp X-- burst_loop ; wait for the burst to end

; the counter expired - this is a sync burst
mov ISR, NULL ; reset the Input Shift Register
wait 1 pin 0 ; wait for the sync burst to finish
jmp next_burst ; wait for the first data bit

data_bit:
nop [ BIT_SAMPLE_DELAY - 1 ] ; wait for 1.5 burst periods before sampling the bit value
in PINS, 1 ; if the next burst has started then detect a '0' (short gap)
; otherwise detect a '1' (long gap)
; after 32 bits the ISR will autopush to the receive FIFO
.wrap


% c-sdk {
static inline void nec_receive_program_init (PIO pio, uint sm, uint offset, uint pin) {

// Set the GPIO function of the pin (connect the PIO to the pad)
//
pio_gpio_init(pio, pin);

// Set the pin direction to `input` at the PIO
//
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);

// Create a new state machine configuration
//
pio_sm_config c = nec_receive_program_get_default_config (offset);

// configure the Input Shift Register
//
sm_config_set_in_shift (&c,
true, // shift right
true, // enable autopush
32); // autopush after 32 bits

// join the FIFOs to make a single large receive FIFO
//
sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_RX);

// Map the IN pin group to one pin, namely the `pin`
// parameter to this function.
//
sm_config_set_in_pins (&c, pin);

// Map the JMP pin to the `pin` parameter of this function.
//
sm_config_set_jmp_pin (&c, pin);

// Set the clock divider to 10 ticks per 562.5us burst period
//
float div = clock_get_hz (clk_sys) / (10.0 / 526.6e-6);
sm_config_set_clkdiv (&c, div);

// Apply the configuration to the state machine
//
pio_sm_init (pio, sm, offset, &c);

// Set the state machine running
//
pio_sm_set_enabled (pio, sm, true);
}
%}
62 changes: 62 additions & 0 deletions pico_sdk_import.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake

# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()

if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()

if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()

if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()

set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")

if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()

get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()

set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()

set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)

include(${PICO_SDK_INIT_CMAKE_FILE})
Loading

0 comments on commit a254215

Please sign in to comment.