Add i2c split keyboard support for rp2040
This commit is contained in:
parent
573d63a180
commit
e8d2aca2e9
11 changed files with 279 additions and 4 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use nix
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -116,3 +116,4 @@ compile_commands.json
|
|||
# VIA(L) files that don't belong in QMK repo
|
||||
via*.json
|
||||
/keyboards/**/keymaps/vial/*
|
||||
.direnv
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define SPLIT_HAND_PIN GP21
|
||||
#define SPLIT_HAND_PIN_LOW_IS_LEFT
|
||||
#define SPLIT_USB_DETECT
|
||||
|
||||
/*
|
||||
* Feature disable options
|
||||
* These options are also useful to firmware size reduction.
|
||||
|
|
74
keyboards/rkb1/i2c_master.c
Normal file
74
keyboards/rkb1/i2c_master.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
// rp2040 implementation of i2c_master.c
|
||||
#include "i2c_master.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include <string.h>
|
||||
|
||||
void cos_i2c_init(void) {
|
||||
static bool is_initialized = false;
|
||||
if (!is_initialized) {
|
||||
is_initialized = true;
|
||||
|
||||
gpio_set_function(24, GPIO_FUNC_I2C);
|
||||
gpio_set_function(25, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(24);
|
||||
gpio_pull_up(25);
|
||||
}
|
||||
}
|
||||
|
||||
static bool enabled = false;
|
||||
static void init_i2c(void) {
|
||||
if(!enabled) {
|
||||
i2c_init(i2c0, 100 * 1000); // 100kb/s
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t i2c_address;
|
||||
|
||||
static i2c_status_t pico_to_qmk(int result) {
|
||||
if(result == PICO_ERROR_TIMEOUT)
|
||||
return I2C_STATUS_TIMEOUT;
|
||||
if(result < 0)
|
||||
return I2C_STATUS_ERROR;
|
||||
return I2C_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
i2c_status_t cos_i2c_start(uint8_t address) {
|
||||
i2c_address = address;
|
||||
init_i2c();
|
||||
return I2C_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
i2c_status_t cos_i2c_transmit(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) {
|
||||
cos_i2c_start(address);
|
||||
return pico_to_qmk(i2c_write_timeout_us(i2c0, address, data, (size_t)length, false, (uint32_t)timeout * 1000));
|
||||
}
|
||||
|
||||
i2c_status_t cos_i2c_receive(uint8_t address, uint8_t * data, uint16_t length, uint16_t timeout) {
|
||||
cos_i2c_start(address);
|
||||
return pico_to_qmk(i2c_read_timeout_us(i2c0, address, data, (size_t)length, false, (uint32_t)timeout * 1000));
|
||||
}
|
||||
|
||||
i2c_status_t cos_i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
|
||||
cos_i2c_start(devaddr);
|
||||
uint8_t complete_packet[length + 1];
|
||||
memcpy(complete_packet + 1, data, (size_t)length);
|
||||
complete_packet[0] = regaddr;
|
||||
|
||||
return cos_i2c_transmit(devaddr, complete_packet, length + 1, timeout);
|
||||
}
|
||||
|
||||
i2c_status_t cos_i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout){
|
||||
cos_i2c_start(devaddr);
|
||||
// TODO: uses 2x timeout
|
||||
i2c_status_t reg_result = cos_i2c_transmit(devaddr, ®addr, 1, timeout);
|
||||
if(reg_result != I2C_STATUS_SUCCESS)
|
||||
return reg_result;
|
||||
return cos_i2c_receive(devaddr, data, length, timeout);
|
||||
}
|
||||
|
||||
void cos_i2c_stop(void) {
|
||||
enabled = false;
|
||||
i2c_deinit(i2c0);
|
||||
}
|
119
keyboards/rkb1/i2c_slave.c
Normal file
119
keyboards/rkb1/i2c_slave.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include "i2c_slave.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/irq.h"
|
||||
#include <string.h>
|
||||
#include "transactions.h"
|
||||
|
||||
// Code taken from here: https://github.com/harsha-vk/weeder_bot/blob/0bb2ac3f627dc2b3867480d210c0536ae0f684a5/assets/raspberry_pi_pico_code/src/i2c_slave.cpp
|
||||
|
||||
static volatile bool is_callback_executor = false;
|
||||
volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
|
||||
static volatile uint8_t buffer_address;
|
||||
static volatile bool sub_has_register_set = false;
|
||||
|
||||
static volatile bool transfer_in_progress = false;
|
||||
enum i2c_event_t {
|
||||
I2C_SUB_RECEIVE,
|
||||
I2C_SUB_REQUEST,
|
||||
I2C_SUB_FINISH
|
||||
};
|
||||
|
||||
static void i2c_sub_handler(enum i2c_event_t event) {
|
||||
//uint8_t ack = 1;
|
||||
switch(event) {
|
||||
case I2C_SUB_RECEIVE: {
|
||||
while(i2c_get_read_available(i2c0)) {
|
||||
if(!sub_has_register_set) {
|
||||
i2c_read_raw_blocking(i2c0, (uint8_t *)&buffer_address, 1);
|
||||
if (buffer_address >= I2C_SLAVE_REG_COUNT) {
|
||||
//ack = 0;
|
||||
buffer_address = 0;
|
||||
}
|
||||
sub_has_register_set = true;
|
||||
is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
|
||||
} else {
|
||||
// find out how many bytes we can safely read
|
||||
uint32_t bytes_left = I2C_SLAVE_REG_COUNT - buffer_address;
|
||||
uint32_t bytes_to_read = i2c_get_read_available(i2c0);
|
||||
uint32_t num_bytes = (bytes_left < bytes_to_read) ? bytes_left : bytes_to_read;
|
||||
i2c_read_raw_blocking(i2c0, (uint8_t *)i2c_slave_reg + buffer_address, num_bytes);
|
||||
buffer_address += num_bytes;
|
||||
if(buffer_address >= I2C_SLAVE_REG_COUNT)
|
||||
buffer_address = 0;
|
||||
// If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
|
||||
if (is_callback_executor) {
|
||||
split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
|
||||
if (trans->slave_callback) {
|
||||
trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case I2C_SUB_REQUEST: {
|
||||
i2c_write_raw_blocking(i2c0, (uint8_t*)i2c_slave_reg + buffer_address, 1);
|
||||
buffer_address++;
|
||||
break;
|
||||
}
|
||||
case I2C_SUB_FINISH:
|
||||
sub_has_register_set = false;
|
||||
is_callback_executor = false;
|
||||
break;
|
||||
}
|
||||
// TODO: ack??
|
||||
}
|
||||
|
||||
static void finish_transfer(void) {
|
||||
transfer_in_progress = false;
|
||||
i2c_sub_handler(I2C_SUB_FINISH);
|
||||
}
|
||||
|
||||
static void i2c_handler(void) {
|
||||
i2c_hw_t *hw = i2c_get_hw(i2c0);
|
||||
uint32_t intr_stat = hw->intr_stat;
|
||||
if(intr_stat == 0) return; // spurious
|
||||
if(intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) {
|
||||
hw->clr_tx_abrt;
|
||||
finish_transfer();
|
||||
}
|
||||
if(intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) {
|
||||
hw->clr_start_det;
|
||||
finish_transfer();
|
||||
}
|
||||
if(intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
|
||||
hw->clr_stop_det;
|
||||
finish_transfer();
|
||||
}
|
||||
if(intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) {
|
||||
transfer_in_progress = true;
|
||||
i2c_sub_handler(I2C_SUB_RECEIVE);
|
||||
}
|
||||
if(intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) {
|
||||
hw->clr_rd_req;
|
||||
transfer_in_progress = true;
|
||||
i2c_sub_handler(I2C_SUB_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
void cos_i2c_slave_init(uint8_t address) {
|
||||
i2c_init(i2c0, 100 * 1000);
|
||||
i2c_set_slave_mode(i2c0, true, address);
|
||||
gpio_set_function(24, GPIO_FUNC_I2C);
|
||||
gpio_set_function(25, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(24);
|
||||
gpio_pull_up(25);
|
||||
|
||||
i2c_hw_t *hw = i2c_get_hw(i2c0);
|
||||
hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS | I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS;
|
||||
irq_set_exclusive_handler(I2C0_IRQ, i2c_handler);
|
||||
irq_set_enabled(I2C0_IRQ, true);
|
||||
}
|
||||
|
||||
void cos_i2c_slave_stop(void) {
|
||||
i2c_set_slave_mode(i2c0, false, 0);
|
||||
i2c_deinit(i2c0);
|
||||
}
|
||||
|
||||
|
41
keyboards/rkb1/i2c_slave.h
Normal file
41
keyboards/rkb1/i2c_slave.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* Copyright (C) 2019 Elia Ritterbusch
|
||||
+
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Library made by: g4lvanix
|
||||
* GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
|
||||
|
||||
Info: Inititate the library by giving the required address.
|
||||
Read or write to the necessary buffer according to the opperation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef I2C_SLAVE_REG_COUNT
|
||||
|
||||
# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
|
||||
# include "transport.h"
|
||||
# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)
|
||||
# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
|
||||
# define I2C_SLAVE_REG_COUNT 30
|
||||
# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
|
||||
|
||||
#endif // I2C_SLAVE_REG_COUNT
|
||||
|
||||
_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");
|
||||
|
||||
extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
|
||||
|
||||
void cos_i2c_slave_init(uint8_t address);
|
||||
void cos_i2c_slave_stop(void);
|
|
@ -1 +1,9 @@
|
|||
# This file intentionally left blank
|
||||
SPLIT_KEYBOARD = yes
|
||||
SPLIT_TRANSPORT = custom
|
||||
RP2040_INTRINSICS_ENABLED = yes
|
||||
|
||||
SRC += i2c_slave.c i2c_master.c
|
||||
QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c $(QUANTUM_DIR)/split_common/transactions.c
|
||||
OPT_DEFS += -DSPLIT_COMMON_TRANSACTIONS
|
||||
COMMON_VPATH += $(QUANTUM_PATH)/split_common
|
||||
EXTRAINCDIRS += $(BOARD_PATH)
|
||||
|
|
|
@ -207,4 +207,4 @@ __attribute__((weak)) i2c_status_t i2c_ping_address(uint8_t address, uint16_t ti
|
|||
// This approach may produce false negative results for I2C devices that do not respond to a register 0 read request.
|
||||
uint8_t data = 0;
|
||||
return i2c_readReg(address, 0, &data, sizeof(data), timeout);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#define _PICO_ASSERT_H
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#define PARAM_ASSERTIONS_ENABLED(x) /* x */
|
||||
#define invalid_params_if(x, test) /* x test */
|
||||
#define valid_params_if(x, test) /* x test */
|
||||
#define hard_assert_if(x, test) /* x test */
|
||||
# include "hardware/flash.h"
|
||||
#endif
|
||||
|
||||
|
@ -30,3 +37,4 @@
|
|||
#ifndef WEAR_LEVELING_RP2040_FLASH_BASE
|
||||
# define WEAR_LEVELING_RP2040_FLASH_BASE ((WEAR_LEVELING_RP2040_FLASH_SIZE) - (WEAR_LEVELING_BACKING_SIZE))
|
||||
#endif
|
||||
|
||||
|
|
22
platforms/chibios/vendors/RP/RP2040.mk
vendored
22
platforms/chibios/vendors/RP/RP2040.mk
vendored
|
@ -31,7 +31,20 @@ PICOSDKSRC = $(PICOSDKROOT)/src/rp2_common/hardware_clocks/clocks.c \
|
|||
$(PICOSDKROOT)/src/rp2_common/hardware_claim/claim.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_watchdog/watchdog.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_xosc/xosc.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/bootrom.c
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_i2c/i2c.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_irq/irq.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_irq/irq_handler_chain.S \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_timer/timer.c \
|
||||
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/bootrom.c \
|
||||
$(PICOSDKROOT)/src/common/pico_time/time.c \
|
||||
$(PICOSDKROOT)/src/common/pico_time/timeout_helper.c \
|
||||
$(PICOSDKROOT)/src/common/pico_util/datetime.c \
|
||||
$(PICOSDKROOT)/src/common/pico_util/pheap.c \
|
||||
$(PICOSDKROOT)/src/common/pico_util/queue.c \
|
||||
$(PICOSDKROOT)/src/common/pico_sync/critical_section.c \
|
||||
$(PICOSDKROOT)/src/common/pico_sync/lock_core.c \
|
||||
$(PICOSDKROOT)/src/common/pico_sync/mutex.c \
|
||||
$(PICOSDKROOT)/src/common/pico_sync/sem.c \
|
||||
|
||||
PICOSDKINC = $(CHIBIOS)//os/various/pico_bindings/dumb/include \
|
||||
$(PICOSDKROOT)/src/common/pico_base/include \
|
||||
|
@ -49,10 +62,15 @@ PICOSDKINC = $(CHIBIOS)//os/various/pico_bindings/dumb/include \
|
|||
$(PICOSDKROOT)/src/rp2_common/hardware_resets/include \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_watchdog/include \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_xosc/include \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_i2c/include \
|
||||
$(PICOSDKROOT)/src/rp2_common/hardware_timer/include \
|
||||
$(PICOSDKROOT)/src/rp2040/hardware_regs/include \
|
||||
$(PICOSDKROOT)/src/rp2040/hardware_structs/include \
|
||||
$(PICOSDKROOT)/src/boards/include \
|
||||
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/include
|
||||
$(PICOSDKROOT)/src/rp2_common/pico_bootrom/include \
|
||||
$(PICOSDKROOT)/src/common/pico_time/include \
|
||||
$(PICOSDKROOT)/src/common/pico_util/include \
|
||||
$(PICOSDKROOT)/src/common/pico_sync/include
|
||||
|
||||
PLATFORM_SRC += $(PICOSDKSRC)
|
||||
EXTRAINCDIRS += $(PICOSDKINC)
|
||||
|
|
|
@ -128,3 +128,4 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])
|
|||
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
||||
transactions_slave(master_matrix, slave_matrix);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue