|
| 1 | +//! |
| 2 | +//! Demonstrates use of I2C bus to configure and use FT6x06 touchscreen controller |
| 3 | +//! |
| 4 | +//! Hardware Required: STM32F412G-DISCO board or STM32F413H-DISCO board |
| 5 | +//! |
| 6 | +//! Procedure: Compile this example, load it onto the microcontroller, and run it. |
| 7 | +//! |
| 8 | +//! Example run command: `cargo run --release --features stm32f412,fsmc_lcd --example display_touch` |
| 9 | +//! |
| 10 | +//! Expected behavior: The display draws circle with its center around the touch. The co-ordinates of the touch |
| 11 | +//! are printed on screen. |
| 12 | +
|
| 13 | +#![no_main] |
| 14 | +#![no_std] |
| 15 | +#![allow(unused_variables)] |
| 16 | + |
| 17 | +use cortex_m; |
| 18 | +use cortex_m_rt::entry; |
| 19 | +use rtt_target::{rprintln, rtt_init_print}; |
| 20 | +#[cfg(feature = "stm32f412")] |
| 21 | +use stm32f4xx_hal::fsmc_lcd::ChipSelect1; |
| 22 | +#[cfg(feature = "stm32f413")] |
| 23 | +use stm32f4xx_hal::fsmc_lcd::ChipSelect3; |
| 24 | +use stm32f4xx_hal::{ |
| 25 | + fsmc_lcd::{FsmcLcd, LcdPins, Timing}, |
| 26 | + gpio::Speed, |
| 27 | + pac, |
| 28 | + prelude::*, |
| 29 | + rcc::Rcc, |
| 30 | +}; |
| 31 | + |
| 32 | +use embedded_graphics::{ |
| 33 | + pixelcolor::Rgb565, |
| 34 | + prelude::*, |
| 35 | + primitives::{Circle, PrimitiveStyle}, |
| 36 | +}; |
| 37 | + |
| 38 | +#[cfg(feature = "stm32f413")] |
| 39 | +use stm32f4xx_hal::fmpi2c::FMPI2c; |
| 40 | +#[cfg(feature = "stm32f412")] |
| 41 | +use stm32f4xx_hal::i2c::I2c; |
| 42 | + |
| 43 | +#[allow(unused_imports)] |
| 44 | +use panic_semihosting; |
| 45 | + |
| 46 | +use ft6x06::{long_hard_reset, Ft6X06}; |
| 47 | + |
| 48 | +use st7789::*; |
| 49 | + |
| 50 | +/// A simple example to connect to the FT6x06 crate and get the values for |
| 51 | +/// x and y positions of touch. |
| 52 | +#[entry] |
| 53 | +fn main() -> ! { |
| 54 | + rtt_init_print!(); |
| 55 | + rprintln!("Started"); |
| 56 | + |
| 57 | + let p = pac::Peripherals::take().unwrap(); |
| 58 | + let cp = cortex_m::Peripherals::take().unwrap(); |
| 59 | + |
| 60 | + let rcc: Rcc = p.RCC.constrain(); |
| 61 | + |
| 62 | + let clocks = rcc.cfgr.sysclk(100.MHz()).freeze(); |
| 63 | + let mut delay = cp.SYST.delay(&clocks); |
| 64 | + |
| 65 | + let gpiob = p.GPIOB.split(); |
| 66 | + let gpioc = p.GPIOC.split(); |
| 67 | + let gpiod = p.GPIOD.split(); |
| 68 | + let gpioe = p.GPIOE.split(); |
| 69 | + let gpiof = p.GPIOF.split(); |
| 70 | + let gpiog = p.GPIOG.split(); |
| 71 | + |
| 72 | + // Pins connected to the LCD on the board |
| 73 | + let lcd_pins = LcdPins { |
| 74 | + data: ( |
| 75 | + gpiod.pd14.into_alternate(), |
| 76 | + gpiod.pd15.into_alternate(), |
| 77 | + gpiod.pd0.into_alternate(), |
| 78 | + gpiod.pd1.into_alternate(), |
| 79 | + gpioe.pe7.into_alternate(), |
| 80 | + gpioe.pe8.into_alternate(), |
| 81 | + gpioe.pe9.into_alternate(), |
| 82 | + gpioe.pe10.into_alternate(), |
| 83 | + gpioe.pe11.into_alternate(), |
| 84 | + gpioe.pe12.into_alternate(), |
| 85 | + gpioe.pe13.into_alternate(), |
| 86 | + gpioe.pe14.into_alternate(), |
| 87 | + gpioe.pe15.into_alternate(), |
| 88 | + gpiod.pd8.into_alternate(), |
| 89 | + gpiod.pd9.into_alternate(), |
| 90 | + gpiod.pd10.into_alternate(), |
| 91 | + ), |
| 92 | + address: gpiof.pf0.into_alternate(), |
| 93 | + read_enable: gpiod.pd4.into_alternate(), |
| 94 | + write_enable: gpiod.pd5.into_alternate(), |
| 95 | + #[cfg(feature = "stm32f413")] |
| 96 | + chip_select: ChipSelect3(gpiog.pg10.into_alternate()), |
| 97 | + #[cfg(feature = "stm32f412")] |
| 98 | + chip_select: ChipSelect1(gpiod.pd7.into_alternate()), |
| 99 | + }; |
| 100 | + |
| 101 | + // Enable backlight |
| 102 | + #[cfg(feature = "stm32f413")] |
| 103 | + let mut backlight_control = gpioe.pe5.into_push_pull_output(); |
| 104 | + |
| 105 | + #[cfg(feature = "stm32f412")] |
| 106 | + let mut backlight_control = gpiof.pf5.into_push_pull_output(); |
| 107 | + |
| 108 | + backlight_control.set_high(); |
| 109 | + |
| 110 | + // Setup the RESET pin |
| 111 | + #[cfg(feature = "stm32f413")] |
| 112 | + let mut lcd_reset = gpiob.pb13.into_push_pull_output().speed(Speed::VeryHigh); |
| 113 | + |
| 114 | + #[cfg(feature = "stm32f412")] |
| 115 | + let lcd_reset = gpiod.pd11.into_push_pull_output().speed(Speed::VeryHigh); |
| 116 | + |
| 117 | + #[cfg(feature = "stm32f412")] |
| 118 | + let mut ts_reset = gpiof.pf12.into_push_pull_output().speed(Speed::VeryHigh); |
| 119 | + |
| 120 | + // Workaround on STM32F413: |
| 121 | + // - On the STM32F413 the touchscreen shares the reset GPIO pin w/ the LCD. |
| 122 | + // - The ST7789 driver uses a fast (10uS) reset. |
| 123 | + // - The touchscreen controller needs 5mS: |
| 124 | + // https://www.displayfuture.com/Display/datasheet/controller/FT6206.pdf |
| 125 | + // |
| 126 | + // Perform a longer reset here first. |
| 127 | + // |
| 128 | + #[cfg(feature = "stm32f412")] |
| 129 | + long_hard_reset(&mut ts_reset, &mut delay).expect("long hard reset"); |
| 130 | + #[cfg(feature = "stm32f413")] |
| 131 | + long_hard_reset(&mut lcd_reset, &mut delay).expect("long hard reset"); |
| 132 | + |
| 133 | + // We're not using the "tearing" signal from the display |
| 134 | + let mut _te = gpiob.pb14.into_floating_input(); |
| 135 | + |
| 136 | + // Set up timing |
| 137 | + let write_timing = Timing::default().data(3).address_setup(3).bus_turnaround(0); |
| 138 | + let read_timing = Timing::default().data(8).address_setup(8).bus_turnaround(0); |
| 139 | + |
| 140 | + // Initialise FSMC memory provider |
| 141 | + let (_fsmc, interface) = FsmcLcd::new(p.FSMC, lcd_pins, &read_timing, &write_timing); |
| 142 | + |
| 143 | + // Pass display-interface instance ST7789 driver to setup a new display |
| 144 | + let mut disp = ST7789::new( |
| 145 | + interface, |
| 146 | + Some(lcd_reset), |
| 147 | + Some(backlight_control), |
| 148 | + 240, |
| 149 | + 240, |
| 150 | + ); |
| 151 | + |
| 152 | + // Initialise the display and clear the screen |
| 153 | + disp.init(&mut delay).unwrap(); |
| 154 | + disp.clear(Rgb565::BLACK).unwrap(); |
| 155 | + |
| 156 | + // Orientation set is default. The touch coordinates data changes as per orientation. |
| 157 | + // The touch coordinates and shapes are adjusted accordingly. |
| 158 | + rprintln!("The orientation set is {}", disp.orientation() as u8); |
| 159 | + |
| 160 | + // Intializing the i2c bus for touchscreen |
| 161 | + rprintln!("Connecting to I2c"); |
| 162 | + |
| 163 | + // Declare the pins for i2c address bus on each board. |
| 164 | + // STM32F412 uses I2c1 type for i2c bus. |
| 165 | + // The pins are mentioned in documentation -um2135-discovery-kit-with-stm32f412zg-mcu-stmicroelectronics |
| 166 | + #[cfg(feature = "stm32f412")] |
| 167 | + let mut i2c = { |
| 168 | + I2c::new( |
| 169 | + p.I2C1, |
| 170 | + ( |
| 171 | + gpiob.pb6.into_alternate_open_drain(), //scl pin |
| 172 | + gpiob.pb7.into_alternate_open_drain(), //sda pin |
| 173 | + ), |
| 174 | + 400.kHz(), |
| 175 | + &clocks, |
| 176 | + ) |
| 177 | + }; |
| 178 | + |
| 179 | + // STM32F413 uses FMPI2C1 type. |
| 180 | + // The pins are mentioned in documentation -um2135-discovery-kit-with-stm32f413zh-mcu-stmicroelectronics |
| 181 | + #[cfg(feature = "stm32f413")] |
| 182 | + let mut i2c = { |
| 183 | + FMPI2c::new( |
| 184 | + p.FMPI2C1, |
| 185 | + ( |
| 186 | + gpioc.pc6.into_alternate_open_drain(), // scl pin |
| 187 | + gpioc.pc7.into_alternate_open_drain(), // sda pin |
| 188 | + ), |
| 189 | + 400.kHz(), |
| 190 | + ) |
| 191 | + }; |
| 192 | + |
| 193 | + #[cfg(feature = "stm32f412")] |
| 194 | + let ts_int = gpiog.pg5.into_pull_down_input(); |
| 195 | + #[cfg(feature = "stm32f413")] |
| 196 | + let ts_int = gpioc.pc1.into_pull_down_input(); |
| 197 | + |
| 198 | + // Create a struct of ft6x06 driver for touchscreen. |
| 199 | + let mut touch = Ft6X06::new(&i2c, 0x38, ts_int).unwrap(); |
| 200 | + |
| 201 | + // Run internal calibration of touchscreen |
| 202 | + let tsc = touch.ts_calibration(&mut i2c, &mut delay); |
| 203 | + match tsc { |
| 204 | + Err(e) => rprintln!("Error {} from ts_calibration", e), |
| 205 | + Ok(u) => rprintln!("ts_calibration returned {}", u), |
| 206 | + } |
| 207 | + rprintln!("If nothing happens - touch the screen!"); |
| 208 | + |
| 209 | + // Loop to get the touch data |
| 210 | + loop { |
| 211 | + let t = touch.detect_touch(&mut i2c); |
| 212 | + let mut num: u8 = 0; |
| 213 | + match t { |
| 214 | + Err(_e) => rprintln!("Error from fetching number of touches"), |
| 215 | + Ok(n) => { |
| 216 | + num = n; |
| 217 | + if num != 0 { |
| 218 | + rprintln!("Number of touches: {}", num) |
| 219 | + }; |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + if num > 0 { |
| 224 | + let t = touch.get_touch(&mut i2c, 1); |
| 225 | + |
| 226 | + match t { |
| 227 | + Err(_e) => rprintln!("Error fetching touch data"), |
| 228 | + Ok(n) => { |
| 229 | + // Prints the touch coordinates. |
| 230 | + rprintln!( |
| 231 | + "Touch: {:>3}x{:>3} - weight: {:>3} misc: {}", |
| 232 | + n.x, |
| 233 | + n.y, |
| 234 | + n.weight, |
| 235 | + n.misc |
| 236 | + ); |
| 237 | + |
| 238 | + // Circle with 1 pixel wide white stroke with top-left point at (10, 20) with a diameter of 3 |
| 239 | + // The touchscreen coordinates are flipped and have an offset. The following conversion gives correct orientation. |
| 240 | + Circle::new(Point::new(i32::from(n.y), 240 - i32::from(n.x)), 20) |
| 241 | + .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) |
| 242 | + .draw(&mut disp) |
| 243 | + .unwrap(); |
| 244 | + } |
| 245 | + } |
| 246 | + } |
| 247 | + } |
| 248 | +} |
0 commit comments