[{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe goal for this project is to get the display, touch screen, and sd card working on the Pi Pico by calibrating the touchscreen and printing the contents of the sdcard to serial console.\u003c/p\u003e\n\u003cp\u003eLearning how to share multiple SPI peripherals reduces the amount of pins you need which can increase the amount of available pins for other uses.\u0026nbsp; A SPI bus is very similar to an I2C bus except the SPI peripheral has a unique chip select pin assigned to it.\u0026nbsp; I find it easier to think of the chip select pin as a SPI address pin.\u0026nbsp; You can only have 1 address per device with I2C and the same holds true with SPI devices except the address is a physical pin.\u003c/p\u003e\n\u003cp\u003eBecause SPI peripherals require a physical pin you will be limited to how many you can have based on how many free pins your microcontroller has.\u0026nbsp; What SPI lacks in chain ability it makes up for with speed.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eI2C devices are half-duplex\u003c/li\u003e\n\u003cli\u003eSPI devices are full duplex\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eBecause SPI communication is twice as fast as I2C it makes far more sense to use SPI for displays and sd cards!\u0026nbsp; \u003c/strong\u003eDon't get me wrong, I2C definitely has its place for uses such as:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003etemp sensors\u003c/li\u003e\n\u003cli\u003eoptical sensors\u003c/li\u003e\n\u003cli\u003e7-Segment displays\u003c/li\u003e\n\u003cli\u003e14-Segment displays\u003c/li\u003e\n\u003cli\u003esmall 16x9 matrix LED modules\u003c/li\u003e\n\u003cli\u003eNeopixel strips\u003c/li\u003e\n\u003cli\u003eand chaining a ton of devices on 1 bus without the limitation of physical pins per device.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhen it comes to needing faster communication for a display that has 480x320 (153,600 total) pixels or sd card reading \u0026amp; writing for a single device that's where the SPI protocol outperforms I2C.\u003c/p\u003e\n\u003cp\u003eI recently worked on a project with a Raspberry Pi Pico that needed to have a Touchscreen TFT \u0026amp; SD Card.\u0026nbsp; That's actually 3 peripherals because the touchscreen controller chip requires pins too.\u0026nbsp; In the majority of scenarios when you have a SPI touchscreen you actually have 2 SPI devices, the display and the touchscreen.\u003c/p\u003e\n\u003cp\u003eIn the Raspberry Pi Pico pinout diagram there are 2 separate SPI buses.\u0026nbsp; SPI0 and SPI1.\u0026nbsp; SPI0 peripherals cannot communicate with peripherals on the SPI1 bus and vice versa. A peripheral would be anything on the SPI protocol such as a display, sdcard, temp sensor, etc...\u0026nbsp; They are highlighted below with magenta labels. Please notice that each SPI bus is prepended with SPI0 or SPI1.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5526","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/822/original/raspberry_pi_Pico-R3-Pinout-narrow.png?1713040729","metadata":{"caption":"Raspberry Pi Pico pinout diagram"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eHere is the wiring setup using an Adafruit Proto Picow Doubler.\u0026nbsp; The doubler offers a maximum of 3 input headers per physical pin.\u0026nbsp; That means you can share up to 3 SPI devices per pin with their doubler. This is a very handy feature as I didn't have to use a breadboard to prototype this project, very cool.\u0026nbsp; The reset button also came in handy during prototyping.\u0026nbsp; If you have a Pi Pico I highly recommend getting one of these.\u0026nbsp; They're like Feather doublers but for the Pico.\u0026nbsp; \u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/823/original/IMG_1544.jpg?1713041292","metadata":{"caption":"Adafruit Pi Pico Doubler"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003cp\u003eIt's almost impossible to follow wire routing from a picture so here is a diagram of the pinout I'm using.\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/840/original/Pico_Doubler_TFT_Pinout.jpg?1714033620","metadata":{"caption":"Precise Project Pinout"}},{"element_type":"product","content":"https://www.adafruit.com/product/5906","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eI'm using a Chinese 3.5\" ST7796 TFT with built-in SDCard.\u0026nbsp; You can use an Adafruit 3.5\" TFT Featherwing with minimal code changes.\u003c/p\u003e\n\u003cp\u003eHere is the back of the Chinese TFT.\u0026nbsp; This is typical of TFT's with touchscreens and built-in SD Cards.\u0026nbsp; There are a lot of pins. If you're a beginner it might seem daunting at first and that's ok, learning this stuff takes time.\u003c/p\u003e\n\u003cp\u003eThey mix and match their silkscreen labeling which can be easily confusing.\u0026nbsp; For example T_DIN and T_DO are the same thing as MOSI and MISO. All you need to know is that T_DIN = MOSI \u0026amp; T_DO = MISO. The reason for this is because MOSI \u0026amp; MISO naming conventions are being phased out in favor of DIN \u0026amp; DOUT, mixing the silkscreen naming conventions on 1 PCB is abnormal.\u003c/p\u003e\n\u003cp\u003eYou'll often see touchscreen silkscreen labels use a naming convention of T, TS, or Touch.\u0026nbsp; You can use the IRQ for touch like the Adafruit 3.5\" TFT Featherwing does, but I'm not doing that here, I'm not using the IRQ pin at all.\u003c/p\u003e\n\u003cp\u003eYou can optionally choose to wire the LED/LITE pin to a PWM pin on the Pi Pico for brightness control.\u0026nbsp; To always have the display at full brightness I'm connecting the LED pin directly to 3.3V OUT on the Pi Pico.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/825/original/IMG_1545.jpg?1713042044","metadata":{"caption":"Back of 3.5\" ST7796 TFT (only the PCB is 4.0\" false advertising)"}},{"element_type":"parts","content":"\n  \u003cdiv class=\"parts-details\"\u003e\n    \u003cdiv\u003e\n      \u003cspan class=\"parts-quantity\"\u003e1\u003c/span\u003e\u003cspan\u003e x \u003c/span\u003e\n        \u003ca href=\"https://www.aliexpress.us/item/2251832829271342.html\" class=\"parts-name\" target=\"_blank\"\u003e3.5\" ST7796S TFT\u003c/a\u003e\n      \u003cdiv class=\"parts-description\"\u003e3.5\" Touchscreen TFT\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"parts-action\"\u003e\n  \u003cdiv\u003e\n      \u003ca href=\"https://www.aliexpress.us/item/2251832829271342.html\" class=\"parts-url btn-secondary\" target=\"_blank\"\u003eBuy Now\u003c/a\u003e\n\n  \u003c/div\u003e\n\u003c/div\u003e\n\n  \u003cdiv class=\"clearfix\"\u003e\u003c/div\u003e\n","metadata":{"name":"3.5\" ST7796S TFT","url":"https://www.aliexpress.us/item/2251832829271342.html","description":"3.5\" Touchscreen TFT","quantity":"1"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n        \u003cp\u003eThe goal with SCK, MOSI, and MISO for SPI bus sharing is to splice all SCK pins together, all MOSI pins together, and all MISO pins together.\u0026nbsp; Since I'm using a doubler there is no literal splicing of wires, just using multiple headers to the same pin, the same as you would do with a breadboard.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGP2 (SCK)\u003c/li\u003e\n\u003cli\u003eGP3 (MOSI)\u003c/li\u003e\n\u003cli\u003eGP4 (MISO)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEach peripheral is then given its own unique physical pin for chip select addressing.\u0026nbsp;\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGP21 (TFT CS)\u003c/li\u003e\n\u003cli\u003eGP17 (SD CS)\u003c/li\u003e\n\u003cli\u003eGP14 (TS CS)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou might notice that TS_CS is on SPI bus 1 not 0 with the rest of the pins.\u0026nbsp; You can put CS pins anywhere but they must have a unique pin that cannot be shared.\u0026nbsp; Since I'm using SD_CS on GP17 which is SPI0CS then I cannot use any other magenta pin with the same SPI0CS label. So I chose a random pin on SPI1.\u003c/p\u003e\n\u003cp\u003eData Command (DC) can be on any free pin and TFT Reset is optional.\u0026nbsp; The TFT screen can still reset via software command using Data Command (DC), so it's not completely necessary to use a hardware reset pin.\u003c/p\u003e\n\u003cp\u003eYou might be tempted to hookup VCC or LITE to the 5V VBUS pin. It's a better idea in a 3.3V logic system to use the 3.3V OUT pin especially with a display that has no datasheet or schematics. Most Circuit Python microcontrollers are 3.3V logic. The 3.3V OUT pin is power only, there is no data going through a power pin.\u003c/p\u003e\n      \n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/826/original/RaspberryPiPico_Touchscreen_SDCard_layout.jpg?1713042792","metadata":{"caption":"SPI bus sharing diagram for the Raspberry Pi Pico"}},{"element_type":"alert","content":"\u003cp\u003eWatch out for the 3.3V EN pin right above 3.3V pin. Many beginners mistakenly use 3.3V EN instead of the 3.3V OUT pin. If you want 3.3V power then use the 3.3V OUT pin.\u003c/p\u003e","metadata":{"class":"element row-fluid alert-element build-alert alert-warning","markdown":"Watch out for the 3.3V EN pin right above 3.3V pin. Many beginners mistakenly use 3.3V EN instead of the 3.3V OUT pin. If you want 3.3V power then use the 3.3V OUT pin.","alert_type":"warning"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe RUN pin is nice for soldering a toggle switch for a reset button. In fact that's how the reset pin works on the Adafruit Picow Bell Doubler... and any other add-on board with a reset button for the Pi Pico. You can test it by taking a piece of wire, touching one end to the RUN pin, and the other end to any GND pin. \u0026nbsp;It will reset the Pico... which is always a good thing to know.\u003c/p\u003e\n\u003cp\u003eNow that we've got all the learning steps out of the way we can play with some code!\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2024 DJDevon3\n# SPDX-License-Identifier: MIT\n# Coded for Circuit Python 9.x\n\"\"\"Raspberry Pi Pico with Touchscreen TFT \u0026 SDCard Example\"\"\"\n\nimport time\nimport os\nimport board\nimport busio\nimport terminalio\nimport displayio\nfrom adafruit_display_text import label\nimport adafruit_imageload\nimport sdcardio\nimport storage\nfrom circuitpython_st7796s import ST7796S\nfrom circuitpython_xpt2046 import Touch\n\ndisplayio.release_displays()\n\nspi = busio.SPI(board.GP2, MOSI=board.GP3, MISO=board.GP4)\ntft_cs = board.GP21\ntft_dc = board.GP22\ntft_rst = board.GP20\nts_cs = board.GP14\nsd_cs = board.GP17\n\n# Initialize SPI SDCard prior to other SPI peripherals!\ntry:\n    print(\"Attempting to mount sd card\")\n    sdcard = sdcardio.SDCard(spi, sd_cs) \n    vfs = storage.VfsFat(sdcard)\n    virtual_root = \"/sd\"\n    storage.mount(vfs, virtual_root)\n    print(os.listdir(\"/sd\"))\nexcept Exception as e:\n    print(\"no sd card:\", e)\n    print(\"continuing\")\n\n# 4.0\" ST7796S Display\nDISPLAY_WIDTH = 480\nDISPLAY_HEIGHT = 320\nDISPLAY_ROTATION = 180\n\n# Touch calibration\nTOUCH_X_MIN = 100\nTOUCH_X_MAX = 1996\nTOUCH_Y_MIN = 100\nTOUCH_Y_MAX = 1996\n\ndisplay_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst)\ndisplay = ST7796S(display_bus, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, rotation=DISPLAY_ROTATION)\n\n# Instantiate the touchpad\ntouch = Touch(\n    spi=spi,\n    cs=ts_cs,\n    width=DISPLAY_WIDTH,\n    height=DISPLAY_HEIGHT,\n    rotation=DISPLAY_ROTATION,\n    x_min=TOUCH_X_MIN,\n    x_max=TOUCH_X_MAX,\n    y_min=TOUCH_Y_MIN,\n    y_max=TOUCH_Y_MAX,\n)\n\n# Quick Colors for Labels\nTEXT_BLACK = 0x000000\nTEXT_BLUE = 0x0000FF\nTEXT_CYAN = 0x00FFFF\nTEXT_GRAY = 0x8B8B8B\nTEXT_GREEN = 0x00FF00\nTEXT_LIGHTBLUE = 0x90C7FF\nTEXT_MAGENTA = 0xFF00FF\nTEXT_ORANGE = 0xFFA500\nTEXT_PURPLE = 0x800080\nTEXT_RED = 0xFF0000\nTEXT_WHITE = 0xFFFFFF\nTEXT_YELLOW = 0xFFFF00\n\ndef make_my_label(font, anchor_point, anchored_position, scale, color):\n    func_label = label.Label(font)\n    func_label.anchor_point = anchor_point\n    func_label.anchored_position = anchored_position\n    func_label.scale = scale\n    func_label.color = color\n    return func_label\n\ninstructions_label = make_my_label(\n    terminalio.FONT, (0.5, 0.5), (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 4), 2, TEXT_WHITE\n)\nx_min_label = make_my_label(\n    terminalio.FONT, (0.0, 0.0), (10, DISPLAY_HEIGHT/2), 2, TEXT_WHITE\n)\nx_max_label = make_my_label(\n    terminalio.FONT, (1.0, 0.0), (DISPLAY_WIDTH - 10, DISPLAY_HEIGHT/2), 2, TEXT_WHITE\n)\ny_min_label = make_my_label(\n    terminalio.FONT, (0.5, 0.0), (DISPLAY_WIDTH / 2, 10), 2, TEXT_WHITE\n)\ny_max_label = make_my_label(\n    terminalio.FONT, (0.5, 1.0), (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT - 10), 2, TEXT_WHITE\n)\n\nsprite_sheet, palette = adafruit_imageload.load(\n    \"icons/rocket.bmp\",\n    bitmap=displayio.Bitmap,\n    palette=displayio.Palette,\n)\npalette.make_transparent(0)\nrocket_icon = displayio.TileGrid(\n    sprite_sheet,\n    pixel_shader=palette,\n    x=DISPLAY_WIDTH - 80,\n    y=2,\n    width=1,\n    height=1,\n    tile_width=80,\n    tile_height=82,\n    default_tile=0,\n)\n\n# Create Display Groups\nmain_group = displayio.Group()\nmain_group.append(rocket_icon)\nmain_group.append(x_min_label)\nmain_group.append(x_max_label)\nmain_group.append(y_min_label)\nmain_group.append(y_max_label)\nmain_group.append(instructions_label)\ndisplay.root_group = main_group\n\nprint(\"Go Ahead - Touch the Screen - Make My Day!\")\n\nx = y = 0\nx_min = y_min = x_max = y_max = min(DISPLAY_WIDTH, DISPLAY_HEIGHT) // 2\nx_min_label.text = f\"X-Min:{x_min}\"\nx_max_label.text = f\"X-Max:{x_max}\"\ny_min_label.text = f\"Y-Min:{y_min}\"\ny_max_label.text = f\"Y-Max:{y_max}\"\ninstructions_label.text = f\"draw swirlies on corners to calibrate\"\n\nwhile True:\n    x = touch.raw_touch()\n    if x is not None:\n        x_min = min(x_min, x[0])\n        x_max = max(x_max, x[0])\n        y_min = min(y_min, x[1])\n        y_max = max(y_max, x[1])\n        print(f\"(({x_min}, {x_max}), ({y_min}, {y_max}))\")\n        x_min_label.text = f\"X-Min:{x_min}\"\n        x_max_label.text = f\"X-Max:{x_max}\"\n        y_min_label.text = f\"Y-Min:{y_min}\"\n        y_max_label.text = f\"Y-Max:{y_max}\"\n        time.sleep(0.05)","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eAfter wiring and code it should present a test calibration screen.\u0026nbsp; If you scroll a stylus (or fingernail) across the screen it will display calibration values for you to plug in at the top of the script for more accurate touch results.\u003c/p\u003e\n\u003cp\u003eHere's a serial output example\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"Attempting to mount sd card\n['System Volume Information', 'Github_Avatars']  # \u003c-- Showing SD Card contents\nGo Ahead - Touch the Screen - Make My Day!\n((160, 1883), (160, 240))\n((160, 1907), (160, 240))\n((160, 1949), (160, 240))\n((160, 1949), (160, 240))\n((160, 1956), (160, 240))\n((160, 1956), (160, 240))\n((160, 1956), (160, 240))\n((160, 1956), (160, 240))\n((160, 1956), (160, 240))","metadata":{"language":"terminal","linenums":false}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/824/original/IMG_1543.JPG?1713041472","metadata":{"caption":"3.5\" ST7796 Touchscreen TFT"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eWe now have the SD Card contents printed to serial and touchscreen calibration.\u0026nbsp; Everything works!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhy is knowing how to share a SPI bus with peripherals such a big deal?\u0026nbsp;\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eYou will eventually come upon a situation where a microcontroller doesn't have enough pins for your project, you will run out of pins someday, and when that happens I hope you remember that you can share SCK, MOSI, and MISO lines for SPI devices.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhat is the least amount of pins needed for a SPI display, SPI Touchscreen, and SPI SD Card?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e9 pins\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e1 physical pin for 3V3 power\u003c/li\u003e\n\u003cli\u003e1 physical pin for common ground\u003c/li\u003e\n\u003cli\u003e1 physical pin for TFT Data Command\u003c/li\u003e\n\u003cli\u003e1 physical pin for all MOSI's\u003c/li\u003e\n\u003cli\u003e1 physical pin for all MISO's\u003c/li\u003e\n\u003cli\u003e1 physical pin for all SCK's\u003c/li\u003e\n\u003cli\u003e3 physical pins for chip select for the TFT, Touchscreen, SD Card\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou can accomplish this same project with an Adafruit QT Py and have 4 GPIO pins left over including I2C for Stemma capable sensors and devices.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/827/original/IMG_1546.jpg?1713045324","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eNow you can see why it's important to learn this. Yes, you can run all of this on a QT Py with creative use of SPI bus sharing. :)\u003c/p\u003e\n\u003cp\u003eNow that the display, touchscreen, and sd card are working you're ready to embark upon bigger and better projects!\u003c/p\u003e\n\u003cp\u003eIf you have any questions please seek support in the Adafruit Forums or Adafruit Discord.\u003c/p\u003e\n\u003cp\u003eGithub Repository: \u003ca href=\"https://github.com/DJDevon3/My_Circuit_Python_Projects/tree/main/Boards/raspberrypi/Raspberry%20Pi%20Pico/3.5%20ST7796S%20TFT/Touchscreen%20and%20SDCard%20Example\" target=\"_blank\"\u003eDJDevon3's Rasberry Pi Pico Touchscreen \u0026amp; SDCard Example\u003c/a\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}}]