diff options
author | Vic (Chun-Ju) Yang <victoryang@chromium.org> | 2014-04-22 10:05:48 +0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-04-30 09:49:01 +0000 |
commit | 1b7573c3e9484b35ee62c1160719076e8e9d8837 (patch) | |
tree | 29fc82e326c65ddadebc9c41dc9dcea9962b3bb0 | |
parent | 39588b067fe2411a326fcec23a5df6584f20fab7 (diff) | |
download | chrome-ec-1b7573c3e9484b35ee62c1160719076e8e9d8837.tar.gz |
Keyborg: implement matrix scanning
This implements dual chip matrix scanning. Now the scan result is only
dumped to debug output.
BUG=None
TEST=Put a finger on the panel and see its shape.
BRANCH=None
Change-Id: I015c901b42e24fe4a6249c12c37bc5bfcb308c9f
Signed-off-by: Vic (Chun-Ju) Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/196468
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | board/keyborg/board.c | 119 | ||||
-rw-r--r-- | board/keyborg/build.mk | 2 | ||||
-rw-r--r-- | board/keyborg/hardware.c | 12 | ||||
-rw-r--r-- | board/keyborg/spi_comm.c | 3 | ||||
-rw-r--r-- | board/keyborg/spi_comm.h | 1 | ||||
-rw-r--r-- | board/keyborg/touch_scan.c | 262 | ||||
-rw-r--r-- | board/keyborg/touch_scan.h | 60 | ||||
-rw-r--r-- | chip/stm32/registers.h | 23 |
8 files changed, 479 insertions, 3 deletions
diff --git a/board/keyborg/board.c b/board/keyborg/board.c index 7616a0aaac..f609395316 100644 --- a/board/keyborg/board.c +++ b/board/keyborg/board.c @@ -12,12 +12,123 @@ #include "spi_comm.h" #include "system.h" #include "task.h" +#include "touch_scan.h" #include "util.h" +const struct ts_pin row_pins[] = { + {TS_GPIO_E, 0}, /* R1 */ + {TS_GPIO_E, 4}, /* R2 */ + {TS_GPIO_E, 8}, /* R3 */ + {TS_GPIO_E, 1}, /* R4 */ + {TS_GPIO_E, 11}, /* R5 */ + {TS_GPIO_E, 12}, /* R6 */ + {TS_GPIO_E, 15}, /* R7 */ + {TS_GPIO_E, 13}, /* R8 */ + {TS_GPIO_D, 3}, /* R9 */ + {TS_GPIO_D, 4}, /* R10 */ + {TS_GPIO_D, 5}, /* R11 */ + {TS_GPIO_D, 0}, /* R12 */ + {TS_GPIO_D, 6}, /* R13 */ + {TS_GPIO_D, 8}, /* R14 */ + {TS_GPIO_D, 11}, /* R15 */ + {TS_GPIO_D, 10}, /* R16 */ + {TS_GPIO_D, 12}, /* R17 */ + {TS_GPIO_D, 13}, /* R18 */ + {TS_GPIO_D, 14}, /* R19 */ + {TS_GPIO_D, 15}, /* R20 */ + {TS_GPIO_C, 8}, /* R21 */ + {TS_GPIO_C, 7}, /* R22 */ + {TS_GPIO_C, 15}, /* R23 */ + {TS_GPIO_E, 6}, /* R24 */ + {TS_GPIO_E, 5}, /* R25 */ + {TS_GPIO_E, 2}, /* R26 */ + {TS_GPIO_E, 3}, /* R27 */ + {TS_GPIO_E, 10}, /* R28 */ + {TS_GPIO_E, 9}, /* R29 */ + {TS_GPIO_E, 14}, /* R30 */ + {TS_GPIO_E, 7}, /* R31 */ + {TS_GPIO_D, 2}, /* R32 */ + {TS_GPIO_D, 7}, /* R33 */ + {TS_GPIO_D, 1}, /* R34 */ + {TS_GPIO_D, 9}, /* R35 */ + {TS_GPIO_C, 5}, /* R36 */ + {TS_GPIO_C, 6}, /* R37 */ + {TS_GPIO_C, 10}, /* R38 */ + {TS_GPIO_C, 13}, /* R39 */ + {TS_GPIO_C, 14}, /* R40 */ + {TS_GPIO_C, 12}, /* R41 */ +}; +BUILD_ASSERT(ARRAY_SIZE(row_pins) == ROW_COUNT); + +const struct ts_pin col_pins[] = { + {TS_GPIO_B, 5}, /* C1 */ + {TS_GPIO_H, 1}, /* C2 */ + {TS_GPIO_H, 0}, /* C3 */ + {TS_GPIO_H, 5}, /* C4 */ + {TS_GPIO_H, 10}, /* C5 */ + {TS_GPIO_H, 6}, /* C6 */ + {TS_GPIO_H, 4}, /* C7 */ + {TS_GPIO_H, 3}, /* C8 */ + {TS_GPIO_H, 9}, /* C9 */ + {TS_GPIO_H, 12}, /* C10 */ + {TS_GPIO_H, 11}, /* C11 */ + {TS_GPIO_H, 15}, /* C12 */ + {TS_GPIO_H, 2}, /* C13 */ + {TS_GPIO_H, 14}, /* C14 */ + {TS_GPIO_G, 5}, /* C15 */ + {TS_GPIO_G, 9}, /* C16 */ + {TS_GPIO_G, 4}, /* C17 */ + {TS_GPIO_G, 15}, /* C18 */ + {TS_GPIO_G, 10}, /* C19 */ + {TS_GPIO_G, 12}, /* C20 */ + {TS_GPIO_G, 0}, /* C21 */ + {TS_GPIO_G, 11}, /* C22 */ + {TS_GPIO_B, 0}, /* C23 */ + {TS_GPIO_G, 2}, /* C24 */ + {TS_GPIO_G, 1}, /* C25 */ + {TS_GPIO_A, 10}, /* Fake C26. C26 is used by debugger. */ + {TS_GPIO_A, 10}, /* Fake C27. C27 is used by debugger. */ + {TS_GPIO_B, 3}, /* C28 */ + {TS_GPIO_A, 10}, /* Fake C29. C29 is used as UART Tx. */ + {TS_GPIO_B, 8}, /* C30 */ + {TS_GPIO_A, 10}, /* C31 */ + {TS_GPIO_B, 1}, /* C32 */ + {TS_GPIO_G, 13}, /* C33 */ + {TS_GPIO_B, 7}, /* C34 */ + {TS_GPIO_B, 2}, /* C35 */ + {TS_GPIO_G, 14}, /* C36 */ + {TS_GPIO_G, 3}, /* C37 */ + {TS_GPIO_G, 7}, /* C38 */ + {TS_GPIO_H, 13}, /* C39 */ + {TS_GPIO_H, 7}, /* C40 */ + {TS_GPIO_B, 4}, /* C41 */ + {TS_GPIO_H, 8}, /* C42 */ + {TS_GPIO_B, 6}, /* C43 */ + {TS_GPIO_B, 9}, /* C44 */ + {TS_GPIO_I, 10}, /* C45 */ + {TS_GPIO_I, 11}, /* C46 */ + {TS_GPIO_I, 9}, /* C47 */ + {TS_GPIO_G, 8}, /* C48 */ + {TS_GPIO_G, 6}, /* C49 */ + {TS_GPIO_I, 4}, /* C50 */ + {TS_GPIO_I, 3}, /* C51 */ + {TS_GPIO_I, 5}, /* C52 */ + {TS_GPIO_I, 14}, /* C53 */ + {TS_GPIO_I, 12}, /* C54 */ + {TS_GPIO_I, 8}, /* C55 */ + {TS_GPIO_I, 6}, /* C56 */ + {TS_GPIO_I, 15}, /* C57 */ + {TS_GPIO_I, 0}, /* C58 */ + {TS_GPIO_I, 13}, /* C59 */ + {TS_GPIO_I, 7}, /* C60 */ +}; +BUILD_ASSERT(ARRAY_SIZE(col_pins) == COL_COUNT); + int main(void) { int i = 0; hardware_init(); + touch_scan_init(); debug_printf("Keyborg starting...\n"); master_slave_init(); @@ -43,6 +154,14 @@ int main(void) debug_printf("Passed\n"); else debug_printf("Failed\n"); + task_wait_event(SECOND); + debug_printf("Scan..."); + if (touch_scan_full_matrix() == EC_SUCCESS) + debug_printf("Passed\n"); + else { + debug_printf("Failed\n"); + task_wait_event(-1); + } } } } diff --git a/board/keyborg/build.mk b/board/keyborg/build.mk index c0ee425972..b31dafce38 100644 --- a/board/keyborg/build.mk +++ b/board/keyborg/build.mk @@ -9,5 +9,5 @@ CHIP:=stm32 CHIP_FAMILY:=stm32f CHIP_VARIANT:=stm32ts60 -board-y=board.o hardware.o runtime.o master_slave.o spi_comm.o +board-y=board.o hardware.o runtime.o master_slave.o spi_comm.o touch_scan.o board-$(CONFIG_DEBUG_PRINTF)+=debug.o diff --git a/board/keyborg/hardware.c b/board/keyborg/hardware.c index 3d140533f2..6651cf5b46 100644 --- a/board/keyborg/hardware.c +++ b/board/keyborg/hardware.c @@ -9,6 +9,7 @@ #include "registers.h" #include "task.h" #include "timer.h" +#include "touch_scan.h" #include "util.h" static void clock_init(void) @@ -113,8 +114,8 @@ static void adc_init(void) /* Set right alignment */ STM32_ADC_CR2(id) &= ~(1 << 11); - /* Set sampling time to 28.5 cycles */ - STM32_ADC_SMPR2(id) = 0x3; + /* Set sampling time */ + STM32_ADC_SMPR2(id) = ADC_SMPR_VAL; /* Select AIN0 */ STM32_ADC_SQR3(id) &= ~0x1f; @@ -168,6 +169,12 @@ static void irq_init(void) asm("cpsie i"); } +static void pmse_init(void) +{ + /* Use 10K-ohm pull down */ + STM32_PMSE_CR |= (1 << 13); +} + void hardware_init(void) { power_init(); @@ -176,4 +183,5 @@ void hardware_init(void) timers_init(); adc_init(); irq_init(); + pmse_init(); } diff --git a/board/keyborg/spi_comm.c b/board/keyborg/spi_comm.c index 95f3ac9762..8d459d3ae5 100644 --- a/board/keyborg/spi_comm.c +++ b/board/keyborg/spi_comm.c @@ -13,6 +13,7 @@ #include "spi_comm.h" #include "task.h" #include "timer.h" +#include "touch_scan.h" #include "util.h" #define DUMMY_DATA 0xdd @@ -431,6 +432,8 @@ static void spi_nss_interrupt(void) if (cmd->cmd_sts == TS_CMD_HELLO) spi_slave_hello_back(cmd); + else if (cmd->cmd_sts == TS_CMD_FULL_SCAN) + touch_scan_slave_start(); else spi_slave_nack(); } diff --git a/board/keyborg/spi_comm.h b/board/keyborg/spi_comm.h index 4a1abb632a..8000503e8f 100644 --- a/board/keyborg/spi_comm.h +++ b/board/keyborg/spi_comm.h @@ -10,6 +10,7 @@ enum ts_command { TS_CMD_HELLO = 0, + TS_CMD_FULL_SCAN, }; struct spi_comm_packet { diff --git a/board/keyborg/touch_scan.c b/board/keyborg/touch_scan.c new file mode 100644 index 0000000000..3867a4ee96 --- /dev/null +++ b/board/keyborg/touch_scan.c @@ -0,0 +1,262 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Touch scanning module */ + +#include "common.h" +#include "console.h" +#include "debug.h" +#include "dma.h" +#include "gpio.h" +#include "hooks.h" +#include "master_slave.h" +#include "registers.h" +#include "spi_comm.h" +#include "timer.h" +#include "touch_scan.h" +#include "util.h" + +#define TS_PIN_TO_CR(p) ((((p).port_id + 1) << 16) | (1 << (p).pin)) +#define TS_GPIO_TO_BASE(p) (0x40010800 + (p) * 0x400) + +/* + * Storage for partial touch scan data. This will be smaller once + * we figure out how to encode it. + */ +static uint8_t touch_data[COL_COUNT + 10][ROW_COUNT * 2]; + +static uint8_t buf[ROW_COUNT * 2]; + +static uint32_t mccr_list[COL_COUNT]; +static uint32_t mrcr_list[ROW_COUNT]; + +static void set_gpio(const struct ts_pin pin, enum pin_type type) +{ + uint32_t addr, mode, mask; + uint32_t port = TS_GPIO_TO_BASE(pin.port_id); + uint32_t pmask = 1 << pin.pin; + + if (pmask & 0xff) { + addr = port; + mode = pmask; + } else { + addr = port + 0x04; + mode = pmask >> 8; + } + mode = mode * mode * mode * mode * 0xf; + + mask = REG32(addr) & ~mode; + + if (type == PIN_COL) { + /* Alternate output open-drain */ + mask |= 0xdddddddd & mode; + } else if (type == PIN_PD) { + mask |= 0x88888888 & mode; + STM32_GPIO_BSRR(port) = pmask << 16; + } else if (type == PIN_ROW) { + /* Nothing for PIN_ROW. Already analog input. */ + } + + REG32(addr) = mask; +} + +void touch_scan_init(void) +{ + int i; + + for (i = 0; i < ROW_COUNT; ++i) { + set_gpio(col_pins[i], PIN_ROW); + STM32_PMSE_PxPMR(row_pins[i].port_id) |= 1 << row_pins[i].pin; + } + for (i = 0; i < COL_COUNT; ++i) + set_gpio(col_pins[i], PIN_PD); + + for (i = 0; i < ROW_COUNT; ++i) + mrcr_list[i] = TS_PIN_TO_CR(row_pins[i]); + for (i = 0; i < COL_COUNT; ++i) + mccr_list[i] = TS_PIN_TO_CR(col_pins[i]); +} + +static void start_adc_sample(int id, int wait_cycle) +{ + /* Clear EOC and STRT bit */ + STM32_ADC_SR(id) &= ~((1 << 1) | (1 << 4)); + + /* Start conversion */ + STM32_ADC_CR2(id) |= (1 << 0); + + /* Wait for conversion start */ + while (!(STM32_ADC_SR(id) & (1 << 4))) + ; + + /* Each iteration takes 3 CPU cycles */ + asm("1: subs %0, #1\n" + " bne 1b\n" :: "r"(wait_cycle / 3)); +} + +#if ADC_SMPL_CYCLE_2 < ADC_CONV_CYCLE_2 * 2 +static uint16_t flush_adc(int id) +{ + while (!(STM32_ADC_SR(id) & (1 << 1))) + ; + return STM32_ADC_DR(id) & ADC_READ_MAX; +} +#else +#define flush_adc(x) STM32_ADC_DR(x) +#endif + +static void enable_col(int idx, int enabled) +{ + if (enabled) { + set_gpio(col_pins[idx], PIN_COL); + STM32_PMSE_PxPMR(col_pins[idx].port_id) |= + 1 << col_pins[idx].pin; + } else { + set_gpio(col_pins[idx], PIN_PD); + STM32_PMSE_PxPMR(col_pins[idx].port_id) &= + ~(1 << col_pins[idx].pin); + } +} + +void scan_column(uint8_t *data) +{ + int i; + + STM32_PMSE_MRCR = mrcr_list[0]; + start_adc_sample(0, ADC_LONG_CPU_CYCLE); + STM32_PMSE_MRCR = mrcr_list[1]; + start_adc_sample(1, ADC_LONG_CPU_CYCLE); + + for (i = 2; i < ROW_COUNT; ++i) { + data[i - 2] = ADC_DATA_WINDOW(flush_adc(i & 1)); + STM32_PMSE_MRCR = mrcr_list[i]; + start_adc_sample(i & 1, ADC_SHORT_CPU_CYCLE); + } + + while (!(STM32_ADC_SR(ROW_COUNT & 1) & (1 << 1))) + ; + data[ROW_COUNT - 2] = ADC_DATA_WINDOW(flush_adc(ROW_COUNT & 1)); + while (!(STM32_ADC_SR((ROW_COUNT & 1) ^ 1) & (1 << 1))) + ; + data[ROW_COUNT - 1] = ADC_DATA_WINDOW(flush_adc((ROW_COUNT & 1) ^ 1)); +} + +void touch_scan_slave_start(void) +{ + int col, i; + struct spi_comm_packet *resp = (struct spi_comm_packet *)buf; + + for (col = 0; col < COL_COUNT * 2; ++col) { + if (col >= COL_COUNT) { + enable_col(col - COL_COUNT, 1); + STM32_PMSE_MCCR = mccr_list[col - COL_COUNT]; + } + + if (master_slave_sync(20) != EC_SUCCESS) + return; + + scan_column(resp->data); + resp->cmd_sts = EC_SUCCESS; + + /* Trim trailing zeros. */ + for (i = 0; i < ROW_COUNT; ++i) + if (resp->data[i] >= THRESHOLD) + resp->size = i; + + /* Flush the last response */ + if (col != 0) + spi_slave_send_response_flush(); + + if (master_slave_sync(20) != EC_SUCCESS) + return; + + /* Start sending the response for the current column */ + spi_slave_send_response_async(resp); + + if (col >= COL_COUNT) { + enable_col(col - COL_COUNT, 0); + STM32_PMSE_MCCR = 0; + } + } + spi_slave_send_response_flush(); + master_slave_sync(20); +} + +int touch_scan_full_matrix(void) +{ + struct spi_comm_packet cmd; + const struct spi_comm_packet *resp; + int col, row; + timestamp_t st = get_time(); + uint8_t *dptr = NULL, *last_dptr = NULL; + + cmd.cmd_sts = TS_CMD_FULL_SCAN; + cmd.size = 0; + + if (spi_master_send_command(&cmd)) + return EC_ERROR_UNKNOWN; + + for (col = 0; col < COL_COUNT * 2; ++col) { + if (col < COL_COUNT) { + enable_col(col, 1); + STM32_PMSE_MCCR = mccr_list[col]; + } + + if (master_slave_sync(20) != EC_SUCCESS) + return EC_ERROR_UNKNOWN; + + last_dptr = dptr; + if (col < COL_COUNT + 10) + dptr = touch_data[col]; + else + dptr = buf; + + scan_column(dptr + ROW_COUNT); + + if (col > 0) { + /* Flush the data from the slave for the last column */ + resp = spi_master_wait_response_done(); + if (resp == NULL) + return EC_ERROR_UNKNOWN; + memcpy(last_dptr, resp->data, resp->size); + memset(last_dptr + resp->size, 0, + ROW_COUNT - resp->size); + } + + if (master_slave_sync(20) != EC_SUCCESS) + return EC_ERROR_UNKNOWN; + + /* Start receiving data for the current column */ + if (spi_master_wait_response_async() != EC_SUCCESS) + return EC_ERROR_UNKNOWN; + + if (col < COL_COUNT) { + enable_col(col, 0); + STM32_PMSE_MCCR = 0; + } + } + + resp = spi_master_wait_response_done(); + if (resp == NULL) + return EC_ERROR_UNKNOWN; + memcpy(last_dptr, resp->data, resp->size); + memset(last_dptr + resp->size, 0, ROW_COUNT - resp->size); + + master_slave_sync(20); + + debug_printf("Sampling took %d us\n", get_time().val - st.val); + + for (row = 0; row < ROW_COUNT * 2; ++row) { + for (col = 0; col < COL_COUNT + 10; ++col) { + if (touch_data[col][row] < THRESHOLD) + debug_printf(" - "); + else + debug_printf("%3d ", touch_data[col][row]); + } + debug_printf("\n"); + } + + return EC_SUCCESS; +} diff --git a/board/keyborg/touch_scan.h b/board/keyborg/touch_scan.h new file mode 100644 index 0000000000..022c2b7d3e --- /dev/null +++ b/board/keyborg/touch_scan.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Touch scanning module */ + +#ifndef __BOARD_KEYBORG_TOUCH_SCAN_H +#define __BOARD_KEYBORG_TOUCH_SCAN_H + +enum pin_type { + PIN_ROW, + PIN_COL, + PIN_PD, +}; + +/* 8-bit window */ +#define ADC_WINDOW_POS 2 +#define ADC_DATA_WINDOW(x) ((x) >> ADC_WINDOW_POS) + +/* Threshold for each cell */ +#define THRESHOLD 35 + +/* ADC speed */ +#define ADC_SMPR_VAL 0x3 /* 28.5 cycles */ +#define ADC_SMPL_CYCLE_2 57 +#define ADC_CONV_CYCLE_2 25 + +/* CPU clock is 4 times faster than ADC clock */ +#define ADC_LONG_CPU_CYCLE (ADC_SMPL_CYCLE_2 * 2) +#define ADC_SHORT_CPU_CYCLE ((ADC_SMPL_CYCLE_2 - ADC_CONV_CYCLE_2) * 2) + +struct ts_pin { + uint8_t port_id; /* GPIO_A = 0, GPIO_B = 1, ... */ + uint8_t pin; +}; + +#define TS_GPIO_A 0 +#define TS_GPIO_B 1 +#define TS_GPIO_C 2 +#define TS_GPIO_D 3 +#define TS_GPIO_E 4 +#define TS_GPIO_F 5 +#define TS_GPIO_G 6 +#define TS_GPIO_H 7 +#define TS_GPIO_I 8 + +extern const struct ts_pin row_pins[]; +extern const struct ts_pin col_pins[]; + +#define ROW_COUNT 41 +#define COL_COUNT 60 + +void touch_scan_init(void); + +void touch_scan_slave_start(void); + +int touch_scan_full_matrix(void); + +#endif /* __BOARD_KEYBORG_TOUCH_SCAN_H */ diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index c6b2acef09..3741a38c69 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -1166,6 +1166,29 @@ typedef volatile struct stm32_dma_regs stm32_dma_regs_t; #define STM32_CRC_CR_REV_IN_WORD (3 << 5) #define STM32_CRC_CR_REV_OUT (1 << 7) +/* --- PMSE --- */ +#define STM32_PMSE_BASE 0x40013400 + +#define STM32_PMSE_ARCR REG32(STM32_PMSE_BASE + 0x0) +#define STM32_PMSE_ACCR REG32(STM32_PMSE_BASE + 0x4) +#define STM32_PMSE_CR REG32(STM32_PMSE_BASE + 0x8) +#define STM32_PMSE_CRTDR REG32(STM32_PMSE_BASE + 0x14) +#define STM32_PMSE_IER REG32(STM32_PMSE_BASE + 0x18) +#define STM32_PMSE_SR REG32(STM32_PMSE_BASE + 0x1c) +#define STM32_PMSE_IFCR REG32(STM32_PMSE_BASE + 0x20) +#define STM32_PMSE_PxPMR(x) REG32(STM32_PMSE_BASE + 0x2c + (x) * 4) +#define STM32_PMSE_PAPMR REG32(STM32_PMSE_BASE + 0x2c) +#define STM32_PMSE_PBPMR REG32(STM32_PMSE_BASE + 0x30) +#define STM32_PMSE_PCPMR REG32(STM32_PMSE_BASE + 0x34) +#define STM32_PMSE_PDPMR REG32(STM32_PMSE_BASE + 0x38) +#define STM32_PMSE_PEPMR REG32(STM32_PMSE_BASE + 0x3c) +#define STM32_PMSE_PFPMR REG32(STM32_PMSE_BASE + 0x40) +#define STM32_PMSE_PGPMR REG32(STM32_PMSE_BASE + 0x44) +#define STM32_PMSE_PHPMR REG32(STM32_PMSE_BASE + 0x48) +#define STM32_PMSE_PIPMR REG32(STM32_PMSE_BASE + 0x4c) +#define STM32_PMSE_MRCR REG32(STM32_PMSE_BASE + 0x100) +#define STM32_PMSE_MCCR REG32(STM32_PMSE_BASE + 0x104) + /* --- MISC --- */ #define STM32_CEC_BASE 0x40007800 /* STM32F100 only */ |