📄 alt_touchscreen.c
字号:
//////////////////////////////////////////////////////////////////staticunsigned int get_coherent_pen_data (alt_touchscreen_pen_state* pen_state, alt_touchscreen_pen_adc_data* pen_adc_data, alt_touchscreen_axis_swap_choice swap_xy, int* pen_down, int* x_adc_sample, int* y_adc_sample){ int x; int y; int sample_number; // Look at the data over and over again until we DIDN'T get an // interrupt while we were in the middle of it. do { sample_number = pen_state->sample_number; *pen_down = pen_state->pen_down; x = pen_adc_data->x; y = pen_adc_data->y; } while (sample_number != pen_state->sample_number); // If some goober wired-up the ADC wrong (as on the EEK), then you // can un-do this in software. Yay, software--savior of goobers // everywhere. if (swap_xy == ALT_TOUCHSCREEN_SWAP_XY) { *x_adc_sample = y; *y_adc_sample = x; } else { *x_adc_sample = x; *y_adc_sample = y; } return sample_number;}////////////////////////////////////////////////////////////////// Timer (alarm) callback//// If you arrange for this thing to get called at ~60Hz, then the // screen will be sampled at (roughly) a quarter of that rate,// because it takes two samples to get each of X and Y.//// This function just dispatches commands to the SPI port, // and acknowledges that it did so by setting the// "transmit_request" variable to "0."// It doesn't do anything else. It doesn't even know what the// commands mean. It just sends the commands it's asked to send; // acknowledges that they were sent (basically: so it doesn't send// them twice), and exits.//////////////////////////////////////////////////////////////////staticalt_u32 alarm_callback (void* context){ alt_touchscreen_alarm_context* alarm_context = (alt_touchscreen_alarm_context*) context; alt_touchscreen_hardware* hardware = alarm_context->hw; alt_touchscreen_sample_machine* machine = alarm_context->sample_machine; alt_touchscreen_pen_state* pen_state = alarm_context->pen_state; alt_touchscreen_event_flag_group* flags = alarm_context->event_flags; int pen_detect; // If the sample-machine isn't ready, it could only be because it's // still busy. If it's busy, we don't want // to go around starting any action of any kind. // if (!sample_machine_ready(machine)) { // Note that this should never happen. // This means that we're "falling behind," and the sample-machine // isn't done with the last sample when the alarm goes off asking // for the next sample. // // Make a note of this in a debug flag, //screen->sampling_running_behind_alarm++; return alarm_context->nticks; } // We have to read the pen-state inbetween samples...because that's // the way the AD7843 works. "Inbetween samples" is right now, // before we kick-off another sample sequence. // Update our flags & states & such with this new pen-down data: // pen_detect = hardware_pen_detect (hardware); if (pen_detect) { // Launch a sample-sequence only if the pen is down. hardware_send_spi_command (hardware, sample_machine_next_command (machine), sample_machine_link_next_command (machine)); } else { // If the pen is up, this is actually important information. // there's no reason to launch another sample...but if we don't // update the pen-state, our clients won't know that the pen has // been lifted. And, obviously: They need to know. pen_state_update_from_detect_sample (pen_state, pen_detect); event_flag_group_update_from_pen_state (flags, pen_state); } return alarm_context->nticks;}//////////////////////////////////////////////////////////////////// alt_touchscreen_get_pen//// Public API//// Provides the caller with a full, coherent sample of the current// pen-state. Returns the "serial number" of the x,y,pen sample// acquired (so apps can see when the data changes).//// If pen_down == 0, then x & y are meaningless.//// This DOES NOT cause any actual sampling of the hardware--that// happens in the background. This executes very quickly because// (really) it just updates a couple of variables and does a little// scaling arithmetic.//////////////////////////////////////////////////////////////////unsigned int alt_touchscreen_get_pen (alt_touchscreen* screen, int* pen_down, int* x, int* y){ unsigned int x_adc_sample = 0; unsigned int y_adc_sample = 0; unsigned int sample_number = 0; sample_number = get_coherent_pen_data (&(screen->pen_state), &(screen->pen_adc_data), screen->swap_xy, pen_down, &x_adc_sample, &y_adc_sample ); // Note that we have to scale even if the pen isn't down. // The value you get is the LAST LOCATION the pen was seen. surface_scaler_compute_output (&(screen->surface_scaler), x_adc_sample, y_adc_sample, x, y ); return sample_number;}////////////////////////////////////////////////////////////////// // alt_touchscreen_stop()//// Public API.//// disables ISRs, unregisters alarms, and un-registers callbacks.// and returns touchscreen state-variables to some// harmless condition.// ////////////////////////////////////////////////////////////////void alt_touchscreen_stop(alt_touchscreen* screen){ // Turn off alarms alt_alarm_stop (&(screen->alarm)); // Disable IRQs in the SPI core itself. hardware_stop (&(screen->hardware)); callback_registry_init (&(screen->callback_registry));} //////////////////////////////////////////////////////////////////// alt_touchscreen_register_callback_func//// Public API//// You pass-in a pointer to your callback-function, and indicate// whether you want to register it for pen-up or pen-down events.//// Pass NULL to un-register a callback.//// returns 0 on success.//// You can only register one callback for each event type. You must// un-register it before you regster another one. If you try to// register multiple callbacks for the same event-type, this function// will return a nonzero error code (and the preexisting callback will// still be registered).//// alt_touchscreen_stop() unregisters all callbacks.////// Implementation:// This is just a wrapper around the registry-management function // The wrapper hides the "registry" details from the caller, whilst// preserving the modular testability of the "pure" registry functionality.////////////////////////////////////////////////////////////////////int alt_touchscreen_register_callback_func ( alt_touchscreen* screen, alt_touchscreen_callback_trigger callback_reason, alt_touchscreen_event_callback_func callback_func, void* context ){ return callback_registry_register_callback (&(screen->callback_registry), callback_reason, callback_func, context);} //////////////////////////////////////////////////////////////////// alt_touchscreen_event_loop_update//// Public API//// You call this function from your pen-management thread or, if you// don't have an OS, whenever you can from your main while(1) loop. //// This function DOES NOT drive the sensor or interact with the// sensor at all.//// This is just a convenience-function for dispatching your// registered callbacks. //// This may seem inconvenient....but it's not *very* inconvenient;// and this way your callbacks run in application-context instead of// ISR.//// This function returns nothing.//// Implementation:// This is just a wrapper around the registry-management function // The wrapper hides the "registry" details from the caller, whilst// preserving the modular testability of the "pure" registry functionality.// //////////////////////////////////////////////////////////////////void alt_touchscreen_event_loop_update (alt_touchscreen* screen){ alt_touchscreen_scaled_pen_data pen_data; alt_touchscreen_get_pen( screen, (&pen_data.pen_down), (&pen_data.x), (&pen_data.y)); callback_registry_dispatch_from_event_flag_group (&(screen->callback_registry), &(screen->event_flags ), &(pen_data) );}//////////////////////////////////////////////////////////////////// alt_touchscreen_init//// Public API. Starts the touchscreen sampling machinery//// returns zero if successful.////////////////////////////////////////////////////////////////int alt_touchscreen_init (alt_touchscreen* screen, alt_u32 spi_controller_base, alt_u32 spi_controller_irq_number, alt_u32 pen_detect_pio_base, int samples_per_second, alt_touchscreen_axis_swap_choice swap_xy){ int error = 0; sample_machine_init (&(screen->sample_machine)); pen_state_init (&(screen->pen_state )); event_flag_group_init (&(screen->event_flags)); pen_adc_data_init (&(screen->pen_adc_data)); callback_registry_init (&(screen->callback_registry)); // It feels a little silly to do all of this "initialization of // structures which just point to other structures," But this is // The Way to inforce purity & separatin, giving functions only and // exactly the minimal information they need to do their jobs. // Decoupling in the name of testability and all that, eh what? // spi_isr_context_init (&(screen->spi_isr_context), &(screen->hardware), &(screen->sample_machine), &(screen->pen_adc_data), &(screen->pen_state ), &(screen->event_flags)); alarm_context_init (&(screen->alarm_context), samples_per_second, &(screen->hardware), &(screen->sample_machine), &(screen->pen_state ), &(screen->event_flags)); // Set some "harmless" scaling numbers. These will just give // the identity-transform on the A-to-D data. Users have to call // explicit scale-setting functions if they want to. // screen->swap_xy = swap_xy; alt_touchscreen_calibrate_lower_left (screen, 0, 0, 0, 0 ); alt_touchscreen_calibrate_upper_right (screen, 4095, 4095, 4095, 4095 ); // Now clear-out the SPI controller and put it in a known state: // This has the side-effect of registering the SPI interrupt // handler. error = hardware_init (&(screen->hardware), spi_controller_base, spi_controller_irq_number, pen_detect_pio_base, &(screen->spi_isr_context)); if (error) { alt_touchscreen_stop(screen); return error; } // Set an alarm for regular sampling. error = alt_alarm_start (&(screen->alarm), screen->alarm_context.nticks, alarm_callback, (void*) (&(screen->alarm_context))); if (error) { // Couldn't set alarm. // Shut-down the entire touchscreen and return a failure code. alt_touchscreen_stop(screen); return error; } return 0; // All happy; no error.}//////////////////////////////////////////////////////////////////// alt_touchscreen_calibrate_upper_right //// --and--//// alt_touchscreen_calibrate_lower_left//// Public API//// The easiest way to do this is write your application and use the// default settings and print-out the X/Y coordinates continuously.//// First: Make sure you un-swap X and Y if they're swapped. You do// this when you initialize the touchscreen.//// Next: Put your pen in the upper-right and lower-left corners.// write-down the X- and Y-values. You'll use these as the// "adc_value" numbers to pass into these routines.//// The "screen coordinates" are whatever your application wants to// assign to those two corners of the screen. //// "Up/down" and "left/right" are whatever you consider them to be// when you're holding the pen in one of these corners. Just plug// the numbers in an you'll get what you want.// ////////////////////////////////////////////////////////////////void alt_touchscreen_calibrate_upper_right ( alt_touchscreen* screen, unsigned int x_adc_value, unsigned int y_adc_value, int x_screen_coordinate, int y_screen_coordinate ) { surface_scaler_set_upper_right (&(screen->surface_scaler), x_adc_value, y_adc_value, x_screen_coordinate, y_screen_coordinate);}void alt_touchscreen_calibrate_lower_left( alt_touchscreen* screen, unsigned int x_adc_value, unsigned int y_adc_value, int x_screen_coordinate, int y_screen_coordinate ) { surface_scaler_set_lower_left (&(screen->surface_scaler), x_adc_value, y_adc_value, x_screen_coordinate, y_screen_coordinate);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -