📄 alt_touchscreen.c
字号:
/******************************************************************************* ** License Agreement ** ** Copyright (c) 2007 Altera Corporation, San Jose, California, USA. ** All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and associated documentation files (the "Software"), ** to deal in the Software without restriction, including without limitation ** the rights to use, copy, modify, merge, publish, distribute, sublicense, ** and/or sell copies of the Software, and to permit persons to whom the ** Software is furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** This agreement shall be governed in all respects by the laws of the State ** of California and by the laws of the United States of America. ** *******************************************************************************//***************************************************************** * * alt_touchscreen * * A large comment atop the header-file describes this * module, its API, and its operation in gruesome detail. * ****************************************************************/#include "alt_touchscreen.h"#include "altera_avalon_spi_regs.h"#include "altera_avalon_pio_regs.h"#include "sys/alt_irq.h"#include <stdio.h>#include <unistd.h>staticint axis_scaler_compute_output (alt_touchscreen_axis_scaler* as, int input){ int output; int numerator = (input - as->input_A) * (as->output_B - as->output_A); // ----------------------------------------------------- int denominator = (as->input_B - as->input_A); output = (numerator / denominator) + as->output_A; return output;}staticvoid surface_scaler_compute_output (alt_touchscreen_surface_scaler* ss, int input_x, int input_y, int* output_x, int* output_y){ *output_x = axis_scaler_compute_output (&(ss->x), input_x); *output_y = axis_scaler_compute_output (&(ss->y), input_y);} staticvoid surface_scaler_set_lower_left (alt_touchscreen_surface_scaler* ss, int input_x, int input_y, int output_x, int output_y){ ss->x.input_A = input_x; ss->y.input_A = input_y; ss->x.output_A = output_x; ss->y.output_A = output_y;}staticvoid surface_scaler_set_upper_right (alt_touchscreen_surface_scaler* ss, int input_x, int input_y, int output_x, int output_y){ ss->x.input_B = input_x; ss->y.input_B = input_y; ss->x.output_B = output_x; ss->y.output_B = output_y;}////////////////////////////////////////////////////////////////// pen_state_init//////////////////////////////////////////////////////////////////staticvoid pen_state_init (alt_touchscreen_pen_state* ps){ ps->pen_down = 0; ps->previous_pen_down = 0; ps->pen_detect_counter = 0; ps->sample_number = 0;}////////////////////////////////////////////////////////////////// pen_state_update_from_detect_sample//// You pass-in the current reading from the pen-detect PIO, // And this makes the pen up/down determination, plus keeps // history necessary for detecting transitions.//// You're supposed to call this whenever://// 1) You've just gathered a new sample from the ADC and checked// the pen-state, or...//// 2) You've responded to a timer-callback and, before taking a// sample, you've noticed that the pen is up. //// Case (1) is boring. Of course you want to update the pen-state// after gathering a fresh X/Y sample. Case (2) is more subtle. You// only want to update the pen-state in the timer-callback if the pen// is UP. If you detect the pen is down, you don't want to go// promising anyone any sample-data (an implicit promise when you// declare "The pen is down!") until you've actually read the ADCs to// figure-out where the pen is. In short: Don't call this if you've// noticed the pen is down, but you haven't actually taken a sample yet.//// Implementation note:// This actually waits for the second pen-detection event before// declaring that "The pen is down." I honestly don't see why this// is ncessary--there must be some subtlety in the program-logic that// I've missed. I do know this: If you don't put in a one-count// delay, then the first pen-location your application sees is an OLD// x-y value every time the pen goes down. Adding a one-count delay// in this routine fixes that problem. Mysteriously. I'm sure it// all makes sense, I just can't explain it.//// NOTE: the caller must take care to grab a sample from the PIO at the// right time. The PIO does not contain a valid value when any// SPI-commands are in-process to the ADC. Read the ADC datasheet// for a complete description.//////////////////////////////////////////////////////////////////staticvoid pen_state_update_from_detect_sample (alt_touchscreen_pen_state* ps, int pen_detect_sample) { ps->sample_number++; if (pen_detect_sample) ps->pen_detect_counter++; else ps->pen_detect_counter = 0; ps->previous_pen_down = ps->pen_down; ps->pen_down = ps->pen_detect_counter > 1;}////////////////////////////////////////////////////////////////// event_flag_group_init//// Simple: initializes the event-flag structure.//////////////////////////////////////////////////////////////////staticvoid event_flag_group_init (alt_touchscreen_event_flag_group* efg){ efg->pen_up_event_flag = 0; efg->pen_move_event_flag = 0; efg->pen_down_event_flag = 0;}////////////////////////////////////////////////////////////////// event_flag_group_update_from_pen_state//// An "event" is a change from one state to another. This routine // can only ever SET flags. They are CLEARED when they're// acknowledged by the receiver in the application context. This// routine is supposed to be called in the interrput context. //// Although it's worth considering: Should this actually be called// in the application context? I think not...but it's a subtle// point and, says me, worth considering carefully.//////////////////////////////////////////////////////////////////staticvoid event_flag_group_update_from_pen_state (alt_touchscreen_event_flag_group* efg, alt_touchscreen_pen_state* ps){ efg->pen_up_event_flag |= (!ps->pen_down && ps->previous_pen_down); efg->pen_down_event_flag |= ( ps->pen_down && !ps->previous_pen_down); efg->pen_move_event_flag |= ps->pen_down;}////////////////////////////////////////////////////////////////// callback_registry_entry_init//// Simple: initializes the event-callback structure.//////////////////////////////////////////////////////////////////staticvoid callback_registry_entry_init(alt_touchscreen_callback_registry_entry* cbre) { cbre->trigger = ALT_TOUCHSCREEN_CALLBACK_NEVER; cbre->callback_func = NULL; cbre->context = NULL;}////////////////////////////////////////////////////////////////// callback_registry_entry_test_and_do //// Check my trigger-condition against the provided trigger-condition// If it's my trigger-type...then call my callback-function.//////////////////////////////////////////////////////////////////staticvoid callback_registry_entry_do_on_trigger ( alt_touchscreen_callback_registry_entry* cbre, alt_touchscreen_callback_trigger trigger_kind, alt_touchscreen_scaled_pen_data* pen_data){ if ((cbre->callback_func != NULL ) && (cbre->trigger == trigger_kind) ) (cbre->callback_func)(pen_data->pen_down, pen_data->x, pen_data->y, cbre->context);}////////////////////////////////////////////////////////////////// callback_registry_entry_is_registered//// Query function: Returns true if this entry is properly-registered,// false if it's empty (unregistered).//////////////////////////////////////////////////////////////////staticint callback_registry_entry_is_registered ( alt_touchscreen_callback_registry_entry* cbre){ return (cbre->trigger == ALT_TOUCHSCREEN_CALLBACK_NEVER);}////////////////////////////////////////////////////////////////// callback_registry_init//// Simple: initializes the event-callback registry (array).// Just stuffs it full of initialized registry-entries.//////////////////////////////////////////////////////////////////staticvoid callback_registry_init (alt_touchscreen_callback_registry* cbr) { int i = 0; // Get a pointer to the first registry-entry: alt_touchscreen_callback_registry_entry* cbre = &((cbr->registry)[0]); for (i = 0; i < ALT_TOUCHSCREEN_CALLBACK_REGISTRY_NUM_ENTRIES; i++) callback_registry_entry_init (cbre++);}////////////////////////////////////////////////////////////////// callback_registry_dispatch_one_type//// Check all registered callbacks against the provided trigger-condition// and event-flag. //// If the event-flag is zero, obviously we don't want to call anybody.//// The event-flag is acknowledged (set to zero) after all callbacks.//////////////////////////////////////////////////////////////////staticvoid callback_registry_dispatch_one_type ( alt_touchscreen_callback_registry* cbr, alt_touchscreen_callback_trigger trigger_kind, volatile int* trigger_flag, alt_touchscreen_scaled_pen_data* pen_data){ int i; alt_touchscreen_callback_registry_entry* cbre = &((cbr->registry)[0]); // If the condition isn't even asserted, don't bother with anything else. if (*trigger_flag == 0) return; // We've seen the flag, so acknowledge (clear) it. *trigger_flag = 0; for (i = 0; i < ALT_TOUCHSCREEN_CALLBACK_REGISTRY_NUM_ENTRIES; i++) callback_registry_entry_do_on_trigger (cbre++, trigger_kind, pen_data); }////////////////////////////////////////////////////////////////// callback_dispatch_from_event_flags//// Dispatches all the callbacks in the registry based on all the // events in the event-flag group.//// The event-flags are cleared after the callbacks, even if none.//////////////////////////////////////////////////////////////////staticvoid callback_registry_dispatch_from_event_flag_group ( alt_touchscreen_callback_registry* cbr, alt_touchscreen_event_flag_group* efg, alt_touchscreen_scaled_pen_data* pen_data){ callback_registry_dispatch_one_type (cbr, ALT_TOUCHSCREEN_CALLBACK_ON_PEN_UP, &(efg->pen_up_event_flag ), pen_data); callback_registry_dispatch_one_type (cbr, ALT_TOUCHSCREEN_CALLBACK_ON_PEN_DOWN, &(efg->pen_down_event_flag), pen_data); callback_registry_dispatch_one_type (cbr, ALT_TOUCHSCREEN_CALLBACK_ON_PEN_MOVE, &(efg->pen_move_event_flag), pen_data);}////////////////////////////////////////////////////////////////// callback_registry_get_empty_entry //// Returns a pointer to the next empty entry in the callback-registry,// or NULL if there are no empty entries.//////////////////////////////////////////////////////////////////staticalt_touchscreen_callback_registry_entry* callback_registry_get_empty_entry (alt_touchscreen_callback_registry* cbr){ int i; alt_touchscreen_callback_registry_entry* cbre = &((cbr->registry)[0]); for (i = 0; i < ALT_TOUCHSCREEN_CALLBACK_REGISTRY_NUM_ENTRIES; i++) { if (callback_registry_entry_is_registered (cbre)) return cbre; cbre++; } return NULL; // No empty entries found.}////////////////////////////////////////////////////////////////// callback_registry_register_callback// // Registers the requested callback-type.// // Returns zero if successful, nonzero on failure.//// Failure happens if you try to register too many callbacks.//////////////////////////////////////////////////////////////////staticint callback_registry_register_callback ( alt_touchscreen_callback_registry* cbr, alt_touchscreen_callback_trigger reason, alt_touchscreen_event_callback_func callback_func, void* context){ alt_touchscreen_callback_registry_entry* cbre = callback_registry_get_empty_entry (cbr); if (cbre == NULL) return -1; cbre->trigger = reason; cbre->callback_func = callback_func; cbre->context = context; return 0;}////////////////////////////////////////////////////////////////// assemble_sample//// You have to read the 8-bit SPI rx-data register twice to get a// full sample (because samples are 12 bits). //// The first 7 bits get stored in the screen's "partial_data_sample"// field. This combines those with a (presumptive) successive value// from the rx-data register to form a full sample.//////////////////////////////////////////////////////////////////staticinline int assemble_sample (alt_u8 first_rxdata_value, alt_u8 second_rxdata_value){ // Top-7 bits come from previous sample, bottom 5 from this one. return ((first_rxdata_value << 5) ) | ((second_rxdata_value >> 3) & 0x1F) ;}static void sample_machine_assemble_x_y (alt_touchscreen_sample_machine* sm, int* x,int* y){ *x = assemble_sample ((sm->rxdata_values)[1], (sm->rxdata_values)[2]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -