📄 alt_touchscreen.c
字号:
*y = assemble_sample ((sm->rxdata_values)[3], (sm->rxdata_values)[4]);}staticvoid pen_adc_data_init (alt_touchscreen_pen_adc_data* dat){ dat->x = 0; dat->y = 0;}static void pen_adc_data_update (alt_touchscreen_pen_adc_data* dat, int x, int y){ dat->x = x; dat->y = y;}////////////////// The SPI command-values.//// The AD7843 has these command-bits://// # Name Meaning X-samp Y-samp // +-+------+------------------------------+----------+-----------+// |7| Start| Every cmd starts with 1 | 1 | 1 |// |6| A2 | 0 (x) or 1 (y) sample | 0 | 1 |// |5| A1 | Use bonus-inputs (not screen)| 0 | 0 |// |4| A0 | For us: Just ~A1 | 1 | 1 |// |3| SER | Singe-Ended (1), or diff (0) | 0 | 0 |// |2| Mode | 0=12-bit 1=8-bit resolution | 0 | 0 |// |1| PD1 | Chip always powered-up | 1 | 1 |// |0| PD0 | Disable "penIRQ" feature | 0 | 0 |// +-+------+------------------------------+----------+-----------+// // We want to use the chip in 12-bit mode and we don't want it to// power-down between samples (we want to leave the resistive// screen "driven" between samples to avoid settling-time issues).//// Sampling the screen in differential-mode gives true-relative// data that's probably more noise-immune. Sounds good.//// The Pen IRQ issue// The "Pen IRQ" issue is a different can of worms. An // interrupt when the pen arrives *seems* like both a nice feature// and a good idea. It's neither. Since this entire system is// now an interrupt-driven sampling system; and since you can// register a civilized application-context callback for// responding to pen up/down events, a hardware // interrupt on pen-down is actually now actively-useless. Dealing// with it would make this software vastly more complicated, and// for absolutely no benefit.//// HOWEVER....//// We "hijack" the so-called "Pen IRQ" feature as just a polled// pen-detection feature. We have to. There's no other// reliable way to tell if the pen is down. Looking at sensor// data and guessing is just no way to run a// railroad--especially not when there's a perfectly-good// digital bit sitting right there that tells you reliably// whether the pen is present. So we leave the so-called "Pen// IRQ" feature enabled--and then don't take interrupts from it.//// Of course: After every "real" command we have to send 8 bits of// pure-zero to reclaim the four "leftover" bits of our 12-bit sample//////////////////////////////////////////////////////////////////#define ALT_TOUCHSCREEN_X_SAMPLE_CMD 0x92#define ALT_TOUCHSCREEN_Y_SAMPLE_CMD 0xD2#define ALT_TOUCHSCREEN_ZERO_FILL_CMD 0x00////////////////////////////////////////////////////////////////// hardware_retrieve_rxdata //// Get pending data from the rxdata register in the SPI peripheral.// You have to have some reason to believe that there IS pending// data. If not, this routine will return garbage. //// One way to be sure there's pending data: Only call this routine// from an interrupt-handler that's triggered by the RRDY interrupt.//////////////////////////////////////////////////////////////////staticalt_u8 hardware_retrieve_rxdata (alt_touchscreen_hardware* hw){ return (alt_u8) IORD_ALTERA_AVALON_SPI_RXDATA (hw->spi_controller_base);}////////////////////////////////////////////////////////////////// hardware_stop//// Disables interrupts from the peripheral and un-registers the ISR// handler. //////////////////////////////////////////////////////////////////staticvoid hardware_stop (alt_touchscreen_hardware* hw){ IOWR_ALTERA_AVALON_SPI_CONTROL (hw->spi_controller_base, 0); // Un-register our ISR. alt_irq_register (hw->spi_controller_irq_number, NULL, NULL);}staticint hardware_pen_detect (alt_touchscreen_hardware* hw){ // Pen-signal is active-low from AD7843 chip return (IORD_ALTERA_AVALON_PIO_DATA(hw->pen_detect_pio_base) == 0); }staticvoid hardware_send_spi_command (alt_touchscreen_hardware* hw, alt_u8 command, int link_with_next_command){ // Assert continuous-select BEFORE we do the command. if (link_with_next_command) IOWR_ALTERA_AVALON_SPI_CONTROL (hw->spi_controller_base, ALTERA_AVALON_SPI_CONTROL_IRRDY_MSK | ALTERA_AVALON_SPI_CONTROL_SSO_MSK ); IOWR_ALTERA_AVALON_SPI_TXDATA (hw->spi_controller_base, command); // De-assert continuous-select AFTER we do the command. if (!link_with_next_command) IOWR_ALTERA_AVALON_SPI_CONTROL (hw->spi_controller_base, ALTERA_AVALON_SPI_CONTROL_IRRDY_MSK );}staticvoid sample_machine_update_from_rxdata (alt_touchscreen_sample_machine* sm, alt_u8 rx_data){ (sm->rxdata_values) [(sm->sample_phase)++] = rx_data;}staticint sample_machine_done (alt_touchscreen_sample_machine* sm){ return sm->sample_phase >= NUM_SAMPLE_MACHINE_PHASES;}staticint sample_machine_ready (alt_touchscreen_sample_machine* sm){ return sm->sample_phase == 0;}staticalt_u8 sample_machine_next_command (alt_touchscreen_sample_machine* sm){ return (sm->scheduled_commands) [sm->sample_phase];}staticint sample_machine_link_next_command (alt_touchscreen_sample_machine* sm){ return (sm->link_with_next_command) [sm->sample_phase];}staticvoid sample_machine_reset (alt_touchscreen_sample_machine* sm){ sm->sample_phase = 0;}staticvoid sample_machine_init (alt_touchscreen_sample_machine* sm){ int i; sample_machine_reset (sm); for (i = 0; i < NUM_SAMPLE_MACHINE_PHASES; i++) (sm->rxdata_values) [i] = 0; (sm->scheduled_commands) [0] = ALT_TOUCHSCREEN_X_SAMPLE_CMD; (sm->scheduled_commands) [1] = ALT_TOUCHSCREEN_ZERO_FILL_CMD; (sm->scheduled_commands) [2] = ALT_TOUCHSCREEN_Y_SAMPLE_CMD; (sm->scheduled_commands) [3] = ALT_TOUCHSCREEN_ZERO_FILL_CMD; (sm->scheduled_commands) [4] = ALT_TOUCHSCREEN_ZERO_FILL_CMD; (sm->link_with_next_command) [0] = 1; (sm->link_with_next_command) [1] = 1; (sm->link_with_next_command) [2] = 1; (sm->link_with_next_command) [3] = 1; (sm->link_with_next_command) [4] = 0;} staticvoid spi_isr_context_init (alt_touchscreen_spi_isr_context* context, alt_touchscreen_hardware* hw, alt_touchscreen_sample_machine* machine, alt_touchscreen_pen_adc_data* adc_data, alt_touchscreen_pen_state* pen_state, alt_touchscreen_event_flag_group* event_flags){ context->hw = hw; context->sample_machine = machine; context->adc_data = adc_data; context->pen_state = pen_state; context->event_flags = event_flags;}staticvoid alarm_context_init (alt_touchscreen_alarm_context* context, int samples_per_second, alt_touchscreen_hardware* hw, alt_touchscreen_sample_machine* machine, alt_touchscreen_pen_state* pen_state, alt_touchscreen_event_flag_group* event_flags){ // Compute this only once here, so we don't do a divide in the // timer ISR. context->nticks = alt_ticks_per_second() / samples_per_second; context->hw = hw; context->sample_machine = machine; context->pen_state = pen_state; context->event_flags = event_flags;}//////////////////////////////////////////////////////////////////// SPI ISR callback//// This is the heart of the touchscreen sampling system.//// This routine deals with incoming data arriving on the touchscreen// SPI port--and chooses which command to send next.//// This is the "mastermind" of the whole screen-sampling// process...and yet, it is event-driven. This implies some sort // of sequencing state-machine of a perfectly-conventional sort.//// The ISR is actually two nested state-machines.//// The "outer" state-machine (implemented right here in this// function) keeps track of which "sample phase" we're in. // If we don't have a complete sample, then there isn't much to do.//// When we've finally got a complete new sample, we call (as a// subroutine) the "inner" state-machine, which knows, basically,// what's the next thing to do with the sample.// ////////////////////////////////////////////////////////////////staticvoid spi_isr (void* context, alt_u32 interrupt_number){ alt_touchscreen_spi_isr_context* spi_isr_context = (alt_touchscreen_spi_isr_context*) context; int pen_detect; alt_touchscreen_hardware* hardware = spi_isr_context->hw; alt_touchscreen_sample_machine* machine = spi_isr_context->sample_machine; alt_touchscreen_pen_adc_data* pen_data = spi_isr_context->adc_data; alt_touchscreen_pen_state* pen_state = spi_isr_context->pen_state; alt_touchscreen_event_flag_group* flags = spi_isr_context->event_flags; alt_u8 rx_data = hardware_retrieve_rxdata (hardware); sample_machine_update_from_rxdata (machine, rx_data); if (sample_machine_done (machine)) { pen_detect = hardware_pen_detect (hardware); pen_state_update_from_detect_sample (pen_state, pen_detect); event_flag_group_update_from_pen_state (flags, pen_state); if (pen_detect) { int x; int y; sample_machine_assemble_x_y (machine, &x, &y); pen_adc_data_update(pen_data, x, y); } sample_machine_reset (machine); } else { hardware_send_spi_command (hardware, sample_machine_next_command (machine), sample_machine_link_next_command (machine)); }}//////////////////////////////////////////////////////////////////// init_hardware//// This does a little more than just putting the SPI hardware into a// known state. It://// * Initializes the SPI-peripheral, // * Sends a command to configure the AD7843 the way we like it.// -- in particular, we NEED the "PEN_IRQ" feature enabled,// or else we'll never see the pen arrive!// * WAITS AROUND for the first "throw-away" data to come back.// * Registers the ISR routine.// * Enables interrupts on the hardware.// // Returns zero if successful.//////////////////////////////////////////////////////////////////staticint hardware_init (alt_touchscreen_hardware* hw, alt_u32 spi_controller_base, alt_u32 spi_controller_irq_number, alt_u32 pen_detect_pio_base, alt_touchscreen_spi_isr_context* context){ int error_code; int tx_done = 0; int spi_retry_count = 0; // Trivial setup of just-my-variables: hw->spi_controller_base = spi_controller_base; hw->spi_controller_irq_number = spi_controller_irq_number; hw->pen_detect_pio_base = pen_detect_pio_base; // Take no chances. Disable interrupts; put the peripheral in a // safe state. IOWR_ALTERA_AVALON_SPI_CONTROL (spi_controller_base, 0); // Clear any lingering garbage from the RX-register. IORD_ALTERA_AVALON_SPI_RXDATA(spi_controller_base); // Writing to the status-register clears some of the error-condition // bits (if they happen to have gotten set somehow). IOWR_ALTERA_AVALON_SPI_STATUS(spi_controller_base, 0); // Select slave zero. // The "select" register is a bit-mask. A "1" in any bit-position // means "please select the corresponding slave." We only expect // to have one slave, which is slave number zero. So we write a "1" // into bit zero to select it. // This is a little counter-intuitive, because we're writing a 1 to // select slave zero. Still: That's how it works. // IOWR_ALTERA_AVALON_SPI_SLAVE_SEL (spi_controller_base, 1); // Write a sampling-command to the AD7843 just to turn-on the // pen-detection feature. We wait-around-for, and then throw away, // the returning data. Note that the SPI is SLOOOW, so this might // add many, many microseconds to the callers (presumably // initialization) code. // // We place a timeout on this, because it would be unseemly to // lock-up the caller's code mysteriously at start-up if there's // some kind of catastrophic problem with the SPI peripheral. // IOWR_ALTERA_AVALON_SPI_TXDATA (spi_controller_base, ALT_TOUCHSCREEN_X_SAMPLE_CMD ); do { tx_done = (IORD_ALTERA_AVALON_SPI_STATUS (spi_controller_base)) & (ALTERA_AVALON_SPI_STATUS_RRDY_MSK ) ; usleep (1000); // 1ms of waiting-around. if (spi_retry_count++ > 20) return -1; } while (!tx_done); // Read and discard the garbage-value in the RXDATA register IORD_ALTERA_AVALON_SPI_RXDATA(spi_controller_base); // Before we enable interrupts, it would be a good idea to install // our interrupt-handler. Otherwise: Who would handle them? // error_code = alt_irq_register (hw->spi_controller_irq_number, (void*) context, spi_isr); if (error_code) return 1; // Now we've sent the first command and (only incidentally) // cleared-out the recieve-buffer. // // Directly and belligerently write the SPI control-register to // * Continuously-select the slave. // * Enable interrupts on RX-complete (RRDY) // IOWR_ALTERA_AVALON_SPI_CONTROL (spi_controller_base, ALTERA_AVALON_SPI_CONTROL_IRRDY_MSK | ALTERA_AVALON_SPI_CONTROL_SSO_MSK ); return 0; // Happy.}//////////////////////////////////////////////////////////////////// get_coherent_pen_data//// This is the only legal way to get sensor-data in the application// context. Otherwise, you might get interrupted in the middle and// get something unexpected. Did I say "might?" This is an embedded// system. After millions of cycles, you certainly will.//// This returns the sample-count (so software can tell if it got // a new sample).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -