From 9d18e10afb2439a6a9ba6978a799259746a837b7 Mon Sep 17 00:00:00 2001 From: Kevin Townsend Date: Tue, 3 May 2011 13:34:19 +0200 Subject: [PATCH] Initial commit --- ChangeLog.txt | 384 ++ License.txt | 27 + Makefile | 180 + Readme.txt | 11 + build/codelite/LPC1343 Workspace.tags | Bin 0 -> 99328 bytes build/codelite/LPC1343 Workspace.workspace | 12 + .../LPC1343 Workspace.workspace.session | 28 + build/codelite/LPC1343_CodeBase.project | 369 ++ build/codelite/tags | 4 + build/crossworks/LPC1343_CodeBase.hzp | 356 ++ build/crossworks/LPC1343_CodeBase.hzs | 61 + build/crossworks/flash_placement.xml | 31 + core/adc/adc.c | 228 ++ core/adc/adc.h | 47 + core/cmd/cmd.c | 296 ++ core/cmd/cmd.h | 60 + core/cpu/cpu.c | 169 + core/cpu/cpu.h | 76 + core/gpio/gpio.c | 550 +++ core/gpio/gpio.h | 112 + core/i2c/i2c.c | 353 ++ core/i2c/i2c.h | 83 + core/iap/iap.c | 57 + core/iap/iap.h | 60 + core/libc/ctype.c | 104 + core/libc/stdio.c | 475 +++ core/libc/string.c | 328 ++ core/pmu/pmu.c | 419 ++ core/pmu/pmu.h | 50 + core/pwm/pwm.c | 254 ++ core/pwm/pwm.h | 50 + core/pwm/pwm_100us_50percent.png | Bin 0 -> 4984 bytes core/rom_drivers.h | 58 + core/ssp/ssp.c | 299 ++ core/ssp/ssp.h | 85 + core/systick/systick.c | 229 ++ core/systick/systick.h | 50 + core/timer16/timer16.c | 505 +++ core/timer16/timer16.h | 59 + core/timer32/timer32.c | 344 ++ core/timer32/timer32.h | 66 + core/uart/uart.c | 347 ++ core/uart/uart.h | 78 + core/uart/uart_buf.c | 149 + core/usbcdc/cdc.h | 236 ++ core/usbcdc/cdc_buf.c | 161 + core/usbcdc/cdc_buf.h | 29 + core/usbcdc/cdcuser.c | 351 ++ core/usbcdc/cdcuser.h | 63 + core/usbcdc/config.h | 40 + core/usbcdc/usb.h | 228 ++ core/usbcdc/usbcfg.h | 157 + core/usbcdc/usbcore.c | 1058 +++++ core/usbcdc/usbcore.h | 88 + core/usbcdc/usbdesc.c | 202 + core/usbcdc/usbdesc.h | 35 + core/usbcdc/usbhw.c | 606 +++ core/usbcdc/usbhw.h | 62 + core/usbcdc/usbreg.h | 134 + core/usbcdc/usbuser.c | 208 + core/usbcdc/usbuser.h | 57 + core/usbhid-rom/usb.h | 240 ++ core/usbhid-rom/usbconfig.c | 115 + core/usbhid-rom/usbconfig.h | 66 + core/usbhid-rom/usbhid.c | 222 ++ core/usbhid-rom/usbhid.h | 46 + core/wdt/wdt.c | 152 + core/wdt/wdt.h | 47 + drivers/chibi/chb.c | 229 ++ drivers/chibi/chb.h | 100 + drivers/chibi/chb_buf.c | 89 + drivers/chibi/chb_buf.h | 44 + drivers/chibi/chb_drvr.c | 917 +++++ drivers/chibi/chb_drvr.h | 374 ++ drivers/chibi/chb_eeprom.c | 70 + drivers/chibi/chb_eeprom.h | 50 + drivers/chibi/chb_spi.c | 77 + drivers/chibi/chb_spi.h | 62 + drivers/chibi/types.h | 54 + drivers/dac/mcp4725/mcp4725.c | 158 + drivers/dac/mcp4725/mcp4725.h | 51 + drivers/eeprom/at25040/at25040.c | 297 ++ drivers/eeprom/at25040/at25040.h | 85 + drivers/eeprom/eeprom.c | 410 ++ drivers/eeprom/eeprom.h | 61 + drivers/eeprom/mcp24aa/mcp24aa.c | 339 ++ drivers/eeprom/mcp24aa/mcp24aa.h | 67 + drivers/fatfs/ccsbcs.c | 540 +++ drivers/fatfs/diskio.h | 81 + drivers/fatfs/ff.c | 3154 +++++++++++++++ drivers/fatfs/ff.h | 596 +++ drivers/fatfs/ffconf.h | 167 + drivers/fatfs/integer.h | 34 + drivers/fatfs/mmc.c | 684 ++++ drivers/lcd/bitmap/readme.txt | 12 + drivers/lcd/bitmap/ssd1306/ssd1306.c | 404 ++ drivers/lcd/bitmap/ssd1306/ssd1306.h | 95 + drivers/lcd/bitmap/st7565/st7565.c | 445 +++ drivers/lcd/bitmap/st7565/st7565.h | 109 + drivers/lcd/icons16.h | 62 + drivers/lcd/smallfonts.c | 556 +++ drivers/lcd/smallfonts.h | 68 + drivers/lcd/tft/bmp.c | 351 ++ drivers/lcd/tft/bmp.h | 150 + drivers/lcd/tft/colors.h | 94 + drivers/lcd/tft/dialogues/alphanumeric.c | 364 ++ drivers/lcd/tft/dialogues/alphanumeric.h | 43 + drivers/lcd/tft/drawing.c | 1205 ++++++ drivers/lcd/tft/drawing.h | 107 + drivers/lcd/tft/fonts/bitmapfonts.h | 67 + drivers/lcd/tft/fonts/dejavusans9.c | 826 ++++ drivers/lcd/tft/fonts/dejavusans9.h | 10 + drivers/lcd/tft/fonts/dejavusansbold9.c | 913 +++++ drivers/lcd/tft/fonts/dejavusansbold9.h | 10 + drivers/lcd/tft/fonts/dejavusanscondensed9.c | 774 ++++ drivers/lcd/tft/fonts/dejavusanscondensed9.h | 10 + drivers/lcd/tft/fonts/dejavusansmono8.c | 1069 +++++ drivers/lcd/tft/fonts/dejavusansmono8.h | 10 + drivers/lcd/tft/fonts/dejavusansmonobold8.c | 1069 +++++ drivers/lcd/tft/fonts/dejavusansmonobold8.h | 10 + drivers/lcd/tft/fonts/veramono11.c | 1164 ++++++ drivers/lcd/tft/fonts/veramono11.h | 11 + drivers/lcd/tft/fonts/veramono9.c | 1069 +++++ drivers/lcd/tft/fonts/veramono9.h | 11 + drivers/lcd/tft/fonts/veramonobold11.c | 1164 ++++++ drivers/lcd/tft/fonts/veramonobold11.h | 11 + drivers/lcd/tft/fonts/veramonobold9.c | 1069 +++++ drivers/lcd/tft/fonts/veramonobold9.h | 11 + drivers/lcd/tft/hw/ILI9325.c | 624 +++ drivers/lcd/tft/hw/ILI9325.h | 185 + drivers/lcd/tft/hw/ILI9328.c | 616 +++ drivers/lcd/tft/hw/ILI9328.h | 185 + drivers/lcd/tft/hw/readme.txt | 12 + drivers/lcd/tft/hw/st7735.c | 444 +++ drivers/lcd/tft/hw/st7735.h | 113 + drivers/lcd/tft/hw/st7783.c | 506 +++ drivers/lcd/tft/hw/st7783.h | 131 + drivers/lcd/tft/hw/template.c | 208 + drivers/lcd/tft/lcd.h | 81 + drivers/lcd/tft/readme.txt | 34 + drivers/lcd/tft/touchscreen.c | 609 +++ drivers/lcd/tft/touchscreen.h | 109 + drivers/motor/stepper/stepper.c | 300 ++ drivers/motor/stepper/stepper.h | 61 + drivers/rsa/rsa.c | 128 + drivers/rsa/rsa.h | 51 + .../sensors/analogjoystick/analogjoystick.c | 138 + .../sensors/analogjoystick/analogjoystick.h | 69 + drivers/sensors/lm75b/lm75b.c | 208 + drivers/sensors/lm75b/lm75b.h | 70 + drivers/sensors/pn532/pn532.c | 152 + drivers/sensors/pn532/pn532.h | 65 + drivers/sensors/pn532/pn532_drvr.h | 103 + drivers/sensors/pn532/pn532_drvr_spi.c | 410 ++ drivers/sensors/pn532/pn532_drvr_uart.c | 276 ++ drivers/sensors/pn532/pn532_mifare.c | 92 + drivers/sensors/pn532/pn532_mifare.h | 123 + drivers/sensors/tcs3414/tcs3414.c | 260 ++ drivers/sensors/tcs3414/tcs3414.h | 130 + drivers/sensors/tsl2561/tsl2561.c | 364 ++ drivers/sensors/tsl2561/tsl2561.h | 163 + lpc134x-vcom.inf | 65 + lpc134x.h | 3534 +++++++++++++++++ lpc1xxx/LPC11xx_handlers.c | 170 + lpc1xxx/LPC13xx_handlers.c | 193 + lpc1xxx/LPC1xxx_startup.c | 65 + lpc1xxx/linkscript.ld | 77 + lpcrc | Bin 0 -> 7384 bytes lpcrc.exe | Bin 0 -> 47342 bytes main.c | 141 + project/cmd_tbl.h | 150 + project/commands.c | 120 + project/commands.h | 44 + project/commands/cmd_chibi_addr.c | 90 + project/commands/cmd_chibi_tx.c | 90 + project/commands/cmd_i2ceeprom_read.c | 76 + project/commands/cmd_i2ceeprom_write.c | 100 + project/commands/cmd_lm75b_gettemp.c | 67 + project/commands/cmd_sd_dir.c | 144 + project/commands/cmd_sysinfo.c | 126 + project/commands/cmd_uart.c | 88 + project/commands/drawing/cmd_bmp.c | 99 + project/commands/drawing/cmd_button.c | 100 + project/commands/drawing/cmd_calibrate.c | 63 + project/commands/drawing/cmd_circle.c | 102 + project/commands/drawing/cmd_clear.c | 74 + project/commands/drawing/cmd_gettext.c | 64 + project/commands/drawing/cmd_line.c | 85 + project/commands/drawing/cmd_orientation.c | 83 + project/commands/drawing/cmd_pixel.c | 92 + project/commands/drawing/cmd_progress.c | 85 + project/commands/drawing/cmd_rectangle.c | 97 + project/commands/drawing/cmd_text.c | 84 + project/commands/drawing/cmd_textw.c | 82 + project/commands/drawing/cmd_tsthreshhold.c | 83 + project/commands/drawing/cmd_tswait.c | 94 + project/readme.txt | 32 + projectconfig.h | 884 +++++ sysdefs.h | 65 + sysinit.c | 278 ++ sysinit.h | 50 + tools/colors_h.png | Bin 0 -> 25564 bytes tools/dotfactory/DotFactorySettings.png | Bin 0 -> 34083 bytes .../DotFactorySettings_FixedWidth.png | Bin 0 -> 25428 bytes tools/dotfactory/OutputConfigs.xml | 49 + tools/dotfactory/TheDotFactory-src-0.0.9.7z | Bin 0 -> 46783 bytes tools/dotfactory/TheDotFactory.exe | Bin 0 -> 76288 bytes tools/dotfactory/readme.txt | 14 + tools/examples/basics/blinky/main.c | 72 + tools/examples/basics/blinky/readme.txt | 1 + .../examples/basics/blinky_nonblocking/main.c | 94 + .../basics/blinky_nonblocking/readme.txt | 5 + tools/examples/basics/pwm_piezobuzzer/main.c | 110 + .../basics/pwm_piezobuzzer/readme.txt | 12 + tools/examples/chibi/receive/main.c | 117 + tools/examples/chibi/receive/readme.txt | 3 + tools/examples/chibi/sniffer_wsbridge/main.c | 146 + .../chibi/sniffer_wsbridge/readme.txt | 9 + tools/examples/chibi/transmit/main.c | 92 + tools/examples/chibi/transmit/readme.txt | 2 + tools/examples/default/main.c | 110 + tools/examples/default/readme.txt | 3 + .../lcd/tft/basic_ui/basic_ui_screenshot.png | Bin 0 -> 3947 bytes tools/examples/lcd/tft/basic_ui/main.c | 215 + tools/examples/lcd/tft/drawing_basic/main.c | 182 + tools/examples/lcd/tft/oscilloscope/main.c | 273 ++ .../examples/lcd/tft/oscilloscope/readme.txt | 38 + tools/examples/lcd/tft/touchscreen/main.c | 81 + tools/examples/lcd/tft/touchscreen/readme.txt | 11 + tools/examples/readme.txt | 4 + .../sensors/pn532/ISO14443A_ID/main.c | 141 + .../sensors/pn532/ISO14443A_ID/readme.txt | 30 + tools/examples/sensors/tsl2561/main.c | 70 + tools/examples/sensors/tsl2561/readme.txt | 2 + tools/lpcrc/Makefile | 12 + tools/lpcrc/bin/lpcrc-linux | Bin 0 -> 7384 bytes tools/lpcrc/bin/lpcrc.exe | Bin 0 -> 47342 bytes tools/lpcrc/lpcrc.c | 97 + tools/readme.txt | 59 + .../Breakout_TFTLCD_ILI9325_v1.3.png | Bin 0 -> 58881 bytes tools/schematics/LPC1343_BaseBoard_v1.6.png | Bin 0 -> 86252 bytes .../schematics/LPC1343_StandAloneLCD_v1.2.png | Bin 0 -> 84664 bytes .../schematics/LPC1343_StandAloneLCD_v1.3.png | Bin 0 -> 85012 bytes tools/testfirmware/blinky.bin | Bin 0 -> 6412 bytes tools/testfirmware/uartcli.bin | Bin 0 -> 9240 bytes tools/testfirmware/usbcli.bin | Bin 0 -> 13026 bytes tools/wsbridge/v0.50/Exe/Linux/wsbridge | Bin 0 -> 13509 bytes tools/wsbridge/v0.50/Exe/Win/wsbridge.exe | Bin 0 -> 8192 bytes tools/wsbridge/v0.50/Linux/Makefile | 17 + tools/wsbridge/v0.50/Linux/main.c | 419 ++ tools/wsbridge/v0.50/Win/wsbridge.sln | 20 + tools/wsbridge/v0.50/Win/wsbridge.suo | Bin 0 -> 12800 bytes tools/wsbridge/v0.50/Win/wsbridge/Program.cs | 305 ++ .../Win/wsbridge/Properties/AssemblyInfo.cs | 36 + .../v0.50/Win/wsbridge/wsbridge.csproj | 96 + .../v0.50/Win/wsbridge/wsbridge.csproj.user | 15 + 256 files changed, 52157 insertions(+) create mode 100644 ChangeLog.txt create mode 100644 License.txt create mode 100644 Makefile create mode 100644 Readme.txt create mode 100644 build/codelite/LPC1343 Workspace.tags create mode 100644 build/codelite/LPC1343 Workspace.workspace create mode 100644 build/codelite/LPC1343 Workspace.workspace.session create mode 100644 build/codelite/LPC1343_CodeBase.project create mode 100644 build/codelite/tags create mode 100644 build/crossworks/LPC1343_CodeBase.hzp create mode 100644 build/crossworks/LPC1343_CodeBase.hzs create mode 100644 build/crossworks/flash_placement.xml create mode 100644 core/adc/adc.c create mode 100644 core/adc/adc.h create mode 100644 core/cmd/cmd.c create mode 100644 core/cmd/cmd.h create mode 100644 core/cpu/cpu.c create mode 100644 core/cpu/cpu.h create mode 100644 core/gpio/gpio.c create mode 100644 core/gpio/gpio.h create mode 100644 core/i2c/i2c.c create mode 100644 core/i2c/i2c.h create mode 100644 core/iap/iap.c create mode 100644 core/iap/iap.h create mode 100644 core/libc/ctype.c create mode 100644 core/libc/stdio.c create mode 100644 core/libc/string.c create mode 100644 core/pmu/pmu.c create mode 100644 core/pmu/pmu.h create mode 100644 core/pwm/pwm.c create mode 100644 core/pwm/pwm.h create mode 100644 core/pwm/pwm_100us_50percent.png create mode 100644 core/rom_drivers.h create mode 100644 core/ssp/ssp.c create mode 100644 core/ssp/ssp.h create mode 100644 core/systick/systick.c create mode 100644 core/systick/systick.h create mode 100644 core/timer16/timer16.c create mode 100644 core/timer16/timer16.h create mode 100644 core/timer32/timer32.c create mode 100644 core/timer32/timer32.h create mode 100644 core/uart/uart.c create mode 100644 core/uart/uart.h create mode 100644 core/uart/uart_buf.c create mode 100644 core/usbcdc/cdc.h create mode 100644 core/usbcdc/cdc_buf.c create mode 100644 core/usbcdc/cdc_buf.h create mode 100644 core/usbcdc/cdcuser.c create mode 100644 core/usbcdc/cdcuser.h create mode 100644 core/usbcdc/config.h create mode 100644 core/usbcdc/usb.h create mode 100644 core/usbcdc/usbcfg.h create mode 100644 core/usbcdc/usbcore.c create mode 100644 core/usbcdc/usbcore.h create mode 100644 core/usbcdc/usbdesc.c create mode 100644 core/usbcdc/usbdesc.h create mode 100644 core/usbcdc/usbhw.c create mode 100644 core/usbcdc/usbhw.h create mode 100644 core/usbcdc/usbreg.h create mode 100644 core/usbcdc/usbuser.c create mode 100644 core/usbcdc/usbuser.h create mode 100644 core/usbhid-rom/usb.h create mode 100644 core/usbhid-rom/usbconfig.c create mode 100644 core/usbhid-rom/usbconfig.h create mode 100644 core/usbhid-rom/usbhid.c create mode 100644 core/usbhid-rom/usbhid.h create mode 100644 core/wdt/wdt.c create mode 100644 core/wdt/wdt.h create mode 100644 drivers/chibi/chb.c create mode 100644 drivers/chibi/chb.h create mode 100644 drivers/chibi/chb_buf.c create mode 100644 drivers/chibi/chb_buf.h create mode 100644 drivers/chibi/chb_drvr.c create mode 100644 drivers/chibi/chb_drvr.h create mode 100644 drivers/chibi/chb_eeprom.c create mode 100644 drivers/chibi/chb_eeprom.h create mode 100644 drivers/chibi/chb_spi.c create mode 100644 drivers/chibi/chb_spi.h create mode 100644 drivers/chibi/types.h create mode 100644 drivers/dac/mcp4725/mcp4725.c create mode 100644 drivers/dac/mcp4725/mcp4725.h create mode 100644 drivers/eeprom/at25040/at25040.c create mode 100644 drivers/eeprom/at25040/at25040.h create mode 100644 drivers/eeprom/eeprom.c create mode 100644 drivers/eeprom/eeprom.h create mode 100644 drivers/eeprom/mcp24aa/mcp24aa.c create mode 100644 drivers/eeprom/mcp24aa/mcp24aa.h create mode 100644 drivers/fatfs/ccsbcs.c create mode 100644 drivers/fatfs/diskio.h create mode 100644 drivers/fatfs/ff.c create mode 100644 drivers/fatfs/ff.h create mode 100644 drivers/fatfs/ffconf.h create mode 100644 drivers/fatfs/integer.h create mode 100644 drivers/fatfs/mmc.c create mode 100644 drivers/lcd/bitmap/readme.txt create mode 100644 drivers/lcd/bitmap/ssd1306/ssd1306.c create mode 100644 drivers/lcd/bitmap/ssd1306/ssd1306.h create mode 100644 drivers/lcd/bitmap/st7565/st7565.c create mode 100644 drivers/lcd/bitmap/st7565/st7565.h create mode 100644 drivers/lcd/icons16.h create mode 100644 drivers/lcd/smallfonts.c create mode 100644 drivers/lcd/smallfonts.h create mode 100644 drivers/lcd/tft/bmp.c create mode 100644 drivers/lcd/tft/bmp.h create mode 100644 drivers/lcd/tft/colors.h create mode 100644 drivers/lcd/tft/dialogues/alphanumeric.c create mode 100644 drivers/lcd/tft/dialogues/alphanumeric.h create mode 100644 drivers/lcd/tft/drawing.c create mode 100644 drivers/lcd/tft/drawing.h create mode 100644 drivers/lcd/tft/fonts/bitmapfonts.h create mode 100644 drivers/lcd/tft/fonts/dejavusans9.c create mode 100644 drivers/lcd/tft/fonts/dejavusans9.h create mode 100644 drivers/lcd/tft/fonts/dejavusansbold9.c create mode 100644 drivers/lcd/tft/fonts/dejavusansbold9.h create mode 100644 drivers/lcd/tft/fonts/dejavusanscondensed9.c create mode 100644 drivers/lcd/tft/fonts/dejavusanscondensed9.h create mode 100644 drivers/lcd/tft/fonts/dejavusansmono8.c create mode 100644 drivers/lcd/tft/fonts/dejavusansmono8.h create mode 100644 drivers/lcd/tft/fonts/dejavusansmonobold8.c create mode 100644 drivers/lcd/tft/fonts/dejavusansmonobold8.h create mode 100644 drivers/lcd/tft/fonts/veramono11.c create mode 100644 drivers/lcd/tft/fonts/veramono11.h create mode 100644 drivers/lcd/tft/fonts/veramono9.c create mode 100644 drivers/lcd/tft/fonts/veramono9.h create mode 100644 drivers/lcd/tft/fonts/veramonobold11.c create mode 100644 drivers/lcd/tft/fonts/veramonobold11.h create mode 100644 drivers/lcd/tft/fonts/veramonobold9.c create mode 100644 drivers/lcd/tft/fonts/veramonobold9.h create mode 100644 drivers/lcd/tft/hw/ILI9325.c create mode 100644 drivers/lcd/tft/hw/ILI9325.h create mode 100644 drivers/lcd/tft/hw/ILI9328.c create mode 100644 drivers/lcd/tft/hw/ILI9328.h create mode 100644 drivers/lcd/tft/hw/readme.txt create mode 100644 drivers/lcd/tft/hw/st7735.c create mode 100644 drivers/lcd/tft/hw/st7735.h create mode 100644 drivers/lcd/tft/hw/st7783.c create mode 100644 drivers/lcd/tft/hw/st7783.h create mode 100644 drivers/lcd/tft/hw/template.c create mode 100644 drivers/lcd/tft/lcd.h create mode 100644 drivers/lcd/tft/readme.txt create mode 100644 drivers/lcd/tft/touchscreen.c create mode 100644 drivers/lcd/tft/touchscreen.h create mode 100644 drivers/motor/stepper/stepper.c create mode 100644 drivers/motor/stepper/stepper.h create mode 100644 drivers/rsa/rsa.c create mode 100644 drivers/rsa/rsa.h create mode 100644 drivers/sensors/analogjoystick/analogjoystick.c create mode 100644 drivers/sensors/analogjoystick/analogjoystick.h create mode 100644 drivers/sensors/lm75b/lm75b.c create mode 100644 drivers/sensors/lm75b/lm75b.h create mode 100644 drivers/sensors/pn532/pn532.c create mode 100644 drivers/sensors/pn532/pn532.h create mode 100644 drivers/sensors/pn532/pn532_drvr.h create mode 100644 drivers/sensors/pn532/pn532_drvr_spi.c create mode 100644 drivers/sensors/pn532/pn532_drvr_uart.c create mode 100644 drivers/sensors/pn532/pn532_mifare.c create mode 100644 drivers/sensors/pn532/pn532_mifare.h create mode 100644 drivers/sensors/tcs3414/tcs3414.c create mode 100644 drivers/sensors/tcs3414/tcs3414.h create mode 100644 drivers/sensors/tsl2561/tsl2561.c create mode 100644 drivers/sensors/tsl2561/tsl2561.h create mode 100644 lpc134x-vcom.inf create mode 100644 lpc134x.h create mode 100644 lpc1xxx/LPC11xx_handlers.c create mode 100644 lpc1xxx/LPC13xx_handlers.c create mode 100644 lpc1xxx/LPC1xxx_startup.c create mode 100644 lpc1xxx/linkscript.ld create mode 100644 lpcrc create mode 100644 lpcrc.exe create mode 100644 main.c create mode 100644 project/cmd_tbl.h create mode 100644 project/commands.c create mode 100644 project/commands.h create mode 100644 project/commands/cmd_chibi_addr.c create mode 100644 project/commands/cmd_chibi_tx.c create mode 100644 project/commands/cmd_i2ceeprom_read.c create mode 100644 project/commands/cmd_i2ceeprom_write.c create mode 100644 project/commands/cmd_lm75b_gettemp.c create mode 100644 project/commands/cmd_sd_dir.c create mode 100644 project/commands/cmd_sysinfo.c create mode 100644 project/commands/cmd_uart.c create mode 100644 project/commands/drawing/cmd_bmp.c create mode 100644 project/commands/drawing/cmd_button.c create mode 100644 project/commands/drawing/cmd_calibrate.c create mode 100644 project/commands/drawing/cmd_circle.c create mode 100644 project/commands/drawing/cmd_clear.c create mode 100644 project/commands/drawing/cmd_gettext.c create mode 100644 project/commands/drawing/cmd_line.c create mode 100644 project/commands/drawing/cmd_orientation.c create mode 100644 project/commands/drawing/cmd_pixel.c create mode 100644 project/commands/drawing/cmd_progress.c create mode 100644 project/commands/drawing/cmd_rectangle.c create mode 100644 project/commands/drawing/cmd_text.c create mode 100644 project/commands/drawing/cmd_textw.c create mode 100644 project/commands/drawing/cmd_tsthreshhold.c create mode 100644 project/commands/drawing/cmd_tswait.c create mode 100644 project/readme.txt create mode 100644 projectconfig.h create mode 100644 sysdefs.h create mode 100644 sysinit.c create mode 100644 sysinit.h create mode 100644 tools/colors_h.png create mode 100644 tools/dotfactory/DotFactorySettings.png create mode 100644 tools/dotfactory/DotFactorySettings_FixedWidth.png create mode 100644 tools/dotfactory/OutputConfigs.xml create mode 100644 tools/dotfactory/TheDotFactory-src-0.0.9.7z create mode 100644 tools/dotfactory/TheDotFactory.exe create mode 100644 tools/dotfactory/readme.txt create mode 100644 tools/examples/basics/blinky/main.c create mode 100644 tools/examples/basics/blinky/readme.txt create mode 100644 tools/examples/basics/blinky_nonblocking/main.c create mode 100644 tools/examples/basics/blinky_nonblocking/readme.txt create mode 100644 tools/examples/basics/pwm_piezobuzzer/main.c create mode 100644 tools/examples/basics/pwm_piezobuzzer/readme.txt create mode 100644 tools/examples/chibi/receive/main.c create mode 100644 tools/examples/chibi/receive/readme.txt create mode 100644 tools/examples/chibi/sniffer_wsbridge/main.c create mode 100644 tools/examples/chibi/sniffer_wsbridge/readme.txt create mode 100644 tools/examples/chibi/transmit/main.c create mode 100644 tools/examples/chibi/transmit/readme.txt create mode 100644 tools/examples/default/main.c create mode 100644 tools/examples/default/readme.txt create mode 100644 tools/examples/lcd/tft/basic_ui/basic_ui_screenshot.png create mode 100644 tools/examples/lcd/tft/basic_ui/main.c create mode 100644 tools/examples/lcd/tft/drawing_basic/main.c create mode 100644 tools/examples/lcd/tft/oscilloscope/main.c create mode 100644 tools/examples/lcd/tft/oscilloscope/readme.txt create mode 100644 tools/examples/lcd/tft/touchscreen/main.c create mode 100644 tools/examples/lcd/tft/touchscreen/readme.txt create mode 100644 tools/examples/readme.txt create mode 100644 tools/examples/sensors/pn532/ISO14443A_ID/main.c create mode 100644 tools/examples/sensors/pn532/ISO14443A_ID/readme.txt create mode 100644 tools/examples/sensors/tsl2561/main.c create mode 100644 tools/examples/sensors/tsl2561/readme.txt create mode 100644 tools/lpcrc/Makefile create mode 100644 tools/lpcrc/bin/lpcrc-linux create mode 100644 tools/lpcrc/bin/lpcrc.exe create mode 100644 tools/lpcrc/lpcrc.c create mode 100644 tools/readme.txt create mode 100644 tools/schematics/Breakout_TFTLCD_ILI9325_v1.3.png create mode 100644 tools/schematics/LPC1343_BaseBoard_v1.6.png create mode 100644 tools/schematics/LPC1343_StandAloneLCD_v1.2.png create mode 100644 tools/schematics/LPC1343_StandAloneLCD_v1.3.png create mode 100644 tools/testfirmware/blinky.bin create mode 100644 tools/testfirmware/uartcli.bin create mode 100644 tools/testfirmware/usbcli.bin create mode 100644 tools/wsbridge/v0.50/Exe/Linux/wsbridge create mode 100644 tools/wsbridge/v0.50/Exe/Win/wsbridge.exe create mode 100644 tools/wsbridge/v0.50/Linux/Makefile create mode 100644 tools/wsbridge/v0.50/Linux/main.c create mode 100644 tools/wsbridge/v0.50/Win/wsbridge.sln create mode 100644 tools/wsbridge/v0.50/Win/wsbridge.suo create mode 100644 tools/wsbridge/v0.50/Win/wsbridge/Program.cs create mode 100644 tools/wsbridge/v0.50/Win/wsbridge/Properties/AssemblyInfo.cs create mode 100644 tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj create mode 100644 tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj.user diff --git a/ChangeLog.txt b/ChangeLog.txt new file mode 100644 index 0000000..3280b0d --- /dev/null +++ b/ChangeLog.txt @@ -0,0 +1,384 @@ +v0.9.2 - Ongoing +================ + +- Added lcdDrawPixels() to lcd.h and the current lcd + drivers to render rows of raw lcd data much quicker + than addressing consecutive pixels individually. + Can be used with windowing in future for fast + animation, continuously writing raw pixels in a + 32x32 pixel frame, etc. +- Fixed ili9328SetWindow() and ili9325SetWindow(). + They can now be used for fast screen paints of + restricted areas of the screen, such as animations + where raw pixel data is constantly fed to the LCD. +- Fixed pixel overflow bugs in drawCircleFilled(). Any + pixels that exceed screen limits will now be dropped, + though at the expense of slightly slower code. +- Added drawLineDotted() to drawing.c to draw lines with + a fixed pattern of empty and solid pixels +- Added __resetBootloader() macro to lpc134x.h to + reset and enter the USB bootloader (LPC1343 Reference + Design Base Board only since this depends on the + existance of the RC combination on the ISP pin). + To reset and executre code normally, the watchdog + time can be used (see "core/wdt"). +- Added AIRCR register to lpc134x.h (Application Interrupt + and Reset Control Register). +- Added drawIcon16() to drawing.c to render monochrome + 16x16 pixel icons from a byte array. +- Added a handful of common 16x16 icon definitions in + /drivers/lcd/icons16.h ... adapted from Gentleface + Toolbar Icon Set. For license terms see + http://www.gentleface.com/free_icon_set.html +- Added a few basic RGB565 color schemes to colors.h and + an image file showing the colors in /tools/colors_h.png +- Updated alphanumeric dialogue form to use new buttons. +- Rewrote drawButton() to use rounded rectangle and + all colors are now passed in as parameters. Also + modified the button command in the CLI to match this. +- Rewrote drawProgressBar() to take advantage of the + new rounded rectangle function (below). Also + modified the progress bar command in the CLI to + allow more colors to be indicated when rendering. +- Added drawRectangleRounded() to drawing.c (filled + rectangle with configurable rounded corners). +- Added drawArrow() to drawing.c to make small rectangles + for left/right menu items, etc. +- Completely rewrote the touch screen calibration code to + be much more accurate. + +v0.9.0 - 28 March 2011 +====================== + +- Calibration process no longer starts automatically when + calibration data is not found in EEPROM. tsCalibrate() + now needs to called manually or from the CLI. +- Fixed a bug in cmd_tswait.c when no delay was passed. +- Added CLI command to set touch screen threshold + to register a touch event (since this can vary from + screen to screen). Type 'x' at the CLI to get/set + the value. The setting is persisted to EEPROM, and if + no value is found on EEPROM CFG_TFTLCD_TS_DEFAULTTHRESHOLD + from projectconfig.h is used. +- Adjusted touch screen code to check ADC values twice + and compare. If mismatch occurs, and error is returned. + This provides far more consistent TS values at the + expense of having to check the error results. See + 'alphanumeric.c' for an example of doing this + correctly. +- Added seperate driver for ILI9328 based displays, even + though it seems to be identical to the ILI9325. Files + were seperated in case the initialisation needs to be + tweeked later or if incompatibilities are found. +- Added the ability to write the contents of the LCD + to a bitmap image, though it's very slow reading data + pixel by pixel and converting it to 24-bit RGB values. + Performance can definately be improved. +- Added 'drawRGB565toBGRA32' to drawing.c to convert + RGB565 colors back to the canonical BGRA32 format +- Added gain controls to TSL2561 driver to increase or + decrease sensitivity. Should be easy to implement + auto-gain functionality now. +- Changed USB VID to a valid value. Source files and + lpc134x-vcom.inf updated accordingly. +- Moved USB VID and PID to projectconfig.h +- Added '-mcpu' flag to the linker in Makefile (the wrong + libm was being linked because of this). + + WARNING + -------------------------------------------------------- +- Some config settings in EEPROM were relocated in v0.90+ + of the LPC1343 Code Base to take into account the new + touch screen calibration code. See projectconfig.h + for more details of the new EEPROM layout. + +v0.8.6 - 21 March 2011 +====================== + +- Added DWT section to LPC134x.h (see Cortex-M3 + Technical Reference Manual section 8.3) +- Added CPU_RESET_CYCLECOUNTER to cpu.c to do rough + in-code performance checks (counts CPU cycles) +- Added '-lm' to linker in Makefile for math.c support +- Increased MAX_STRING_SIZE from 100 to 255 in stdio.c +- Added driver for the TCS3414 RGB color sensor +- Added driver for the TSL2561 digital light sensor +- Added CFG_USBCDC_BUFFERSIZE to projectconfig.h + instead of cdc_buf.h. +- Added a PWM example using a piezo buzzer (see + 'tools/examples/basics/pwm_piezobuzzer' for details) +- Added ctype.c to 'core/libc' +- Changed Crossworks project to use libc files from + 'core/libc' and modified the USB CDC printf to be + much faster by redirecting inside 'puts' rather than + '__putchar' (see 'sysinit.c'). +- Fixed a bug in LPC134x.h ... USB_CMDCODE write and read + masks were reversed +- Added a simple example of reading the card ID from an + ISO14443A (Mifare Classic, etc.) card using the PN532 + ('/tools/examples/sensors/pn532/ISO14443A_ID'). +- Added an 'lcd' folder to 'tools/examples' showing how to use + the generic lcd drawing routines, the touch-screen, etc. +- Rewrote part of the touch screen calibration code to get more + accurate results. Each point is now tested twice and the + results are averaged. Values beyond a certain limit are also + rejected to ignore wacky readings. +- Added a preliminary PN532 (NFC/RFID) driver. Currently UART + only, and only allows simple commands to be sent and received. + By default, all data is fed out to the console via printf. + Will be elaborated and expanded in future releases. +- Cleaned up the ILI9325 TFT LCD driver a bit, adding a proper + enum for the commands, etc. + + +v0.8.5 - 7 March 2011 +===================== + +- Fixed a problem with GDB script when deploying firmware + with Segger J-Link in CodeLite. Code will now deploy + and start executing properly when F5 is pressed (assuming + the Segger GDB Server software is running in the background). +- Interrupts are now disabled while feeding the watchdog as per + a warning in the usermanual. +- Changed fonts for TTF LCDs. DejaVu Sans 9 is now used by default + in the LPC1343 Code Base. The following open source fonts have + been converted using The Dot Factory (see '/tools/dotfactory') and + can be found in '/drivers/lcd/tft/fonts': + + FONT NAME WIDTH + ------------------------------- ---------- + - Bitstream Vera Mono 9 Fixed (8) + - Bitstream Vera Mono 9 Bold Fixed (8) + - Bitstream Vera Mono 11 Fixed (9) + - Bitstream Vera Mono 11 Bold Fixed (9) + - DejaVu Sans 9 Variable + - DejaVu Sans 9 Bold Variable + - DejaVu Sans Condensed 9 Variable + - DejaVu Sans Mono 8 Fixed (8) + - DejaVu Sans Mono 8 Bold Fixed (8) + +- Modified projectconfig.h to select the target board and configure + the project accordingly. This isn't ideal, but it's a lot more + maintainable than have a seperate code base for every board since + they share 95% of the same code. +- Modified WDT code to use the WDT oscillator (rather than the + external crystal) +- Fixed a bug when waking up from sleep in Chibi. Removed systick + delay when toggling reset and slptr pins since this was causing + a blocking condition with the wakeup ISR. +- Added an example of how to retrieve the MCU's unique + 128-bit serial number using IAP (core/iap) +- Added an option to projectconfig.h to select which pin to use for + SCK on SSP0, since the QFN33 package doesn't have all the same + pins. +- Updated Chibi to v0.91 (adds sleep method and promiscuous mode to allow + Chibi to act as an 802.15.4 packet sniffer, etc.). +- Added lcdProperties_t to lcd.h to indicate the properties of different + LCDs (to determine if there is a touch screen present, whether the + screen orientation can be changed, etc.) +- Added basic ST7735 LCD driver (128x160 pixel TFT LCD using 6-pin + SPI-esque interface). Work in progress (no orientation support, etc.). +- Added some simple examples in the 'tools/examples' folder. + +v0.80 - 27 Jan 2011 +=================== + +- Added optimised lcdDrawVLine using orientation + though this should only be used on lines + greate than 20 pixels or so to see any real + advantage +- Added cmd_uart to allow the HW UART speed + to be set in EEPROM (between 9600 and + 115200). Use the 'U' command. +- Updated Crossworks projects files to use + custom printf in 'core/libc' (gains about + 1.3KB in release mode) +- Added driver for SSD1306 OLED displays + to '/drivers/bitmap/ssd1306' +- Changed commands in the CLI to single + characters and shortened descriptions + to save flash space (~0.7KB) +- Updated ILI9325 to allow the screen + orientation to be changed to portrait + of landscape mode. +- Added cmd_orientation to change the LCD + orientation from the CLI +- Fixed a bug in the PMU Wakeup interrupt + handler +- Added a circular buffer for outgoing CDC + data (cdc_buf.c) since you can only feed one + frame per ms (max 64 bytes). The speed needs + to be improved finding the right balance + between caching data and sending it to the EP, + but this should avoid any dropped characters. + More testing will be needed to improve this. +- Fixed a bug with the USB CDC buffer +- Added SILENTMODE to the CLI to disable the + command prompt and echo of input characters + (see CFG_INTERFACE_SILENTMODE). Useful + when another MCU is use the CLI rather than + a human. +- Added IRQ pin to the CLI to indicate when a + command is being processed (pin = low) and + when it has completed or a new command can + be processed (pin = high). +- Added 'cmd_tswait' to wait for a touch event +- Removed 'drivers/nfc/pn532' until proper drivers + can be written +- Added 'cmd_text' and 'cmd_textw' to the CLI to + render text on the LCD and get the width in + pixels of the specified text before rendering. + +v0.70 - 18 Dec 2010 +=================== + +- Added 'calibrate' and 'gettext' commands to the CLI +- Added a dialogue box for the TFT LCD/Touchscreen + to input alpha-numeric data and return the + results as a string ('drivers/lcd/tft/dialogues/*') +- Added touch screen calibration code and UI + ('drivers/lcd/tft/touchscreen.c') +- Fixed a bug in ILI9325.c to make sure that the LCD + is reset during initialisation +- ** IMPORTANT ** Changed 'core/i2c/*' to fix a number + of bugs (thanks Rob65!) ... though this means all + i2c peripherals and drivers will need to be tested. +- Added 'colorscheme_t' to drawing.h to try to use a + standard color palette across GUI controls and make + it easy to change later. Currently only the button + and progress bar make use of this. +- Added 'lcdBacklightOn()' and 'lcdBacklightOff()' to + lcd.h +- Added 'CFG_TFTLCD_TS_THRESHOLD' to projectconfig.h + to configure touch-screen sensitivity when waiting + for a touch event +- Modified 'core/gpio/gpio.c' to reduce the compiled code + size. Repetitive switches where only the registers + change were modified to use temporary registers and + execute the code only once. +- Added the source and binaries for TheDotFactory to + 'tools/dotfactory' for convenience sake. See: + http://www.pavius.net/downloads/tools/53-the-dot-factory +- Removed 'drivers/lcd/tft/tscalibration.c' and moved + the relevant code to touchscreen.c +- Removed the imgconv tool from 'tools/imgconv' (see + point below for more information) +- Removed the previous custom image code and replaced + it with 24-bit Windows bitmap files. This is a bit + slower (since 24-bit data needs to be converted to + RGB565 for each pixel and rows are rendered bottom + up making pixel drawing harder to optimise) but + converting and eventually saving images is easier. + The current code is functional but needs to be + heavily optimised. +- Made some minor optimisations to the data and cmd + functions in the ILI9325 driver. Other functions + need to be optimised as well, but these two should + be close to optimal now. Currently 25 cycles + compiled with -Os, 36 cycles with no optimisation. +- Added 'projects/command/drawing/cmd_bmp.c' to load + 24-bit Windows bitmap images from the SD card and + display them on the TFT LCD. +- Added some schematics to '/tools/schematics' to + show the assumed pin configurations + +v0.60 - 8 December 2010 +======================= + +- Added a number of CLI commands for the TFT LCD + (see 'project/commands/drawing') +- Modified FCLK_FAST() in 'drivers/fatfs/mmc.c' to set + the max speed to 6.0MHz for improved reliability. + Tested with a 4GB microSD card, 300KB/s can be read + using a 512 byte read buffer. +- Modified Chibi to use 16-bit timer 0 for us delays + (more accurate) +- Added a number of commands to + '/projects/commands/drawing' for basic TFT LCD + operations. Removed 'lcd-test.c' and 'lcd-fill.c'. +- Added a flag to projectconfig.h to make FATFS read-only + (to save a couple KB when space is tight). +- Moved 'project/eeprom.*' to 'drivers/eeprom' to add a + level of abstraction to eeprom access throughout the code +- Added a table to projectconfig.h to manage eeprom + addresses. First 256 byte of eeprom should be reserved + for this. +- Added 'drivers/lcd/tft/colors.h' to keep all common + 16-bit RGB565 color definitions in one location +- Added some sample images to 'tools/imgconv' that can + be used to render a basic clock on a TFT LCD screen + (using drawImageFromFile in 'drivers/lcd/tft/drawing.c') +- Modified the makefile to automatically run './lpcrc' + post-build +- Added drawButton() to 'drivers/lcd/tft/drawing.c' +- Updated 'drivers/lcd/tft/touchscreen.c' to do basic + debouncing. tsReadZ() was added to detect pressure on + the touch screen, and tsWaitForEvent() will only exit + when the pressure passes a minimum threshold. (Thanks + to Limor for pointing out some code by Rossum for this.) +- Fixed some off-by-one pixel errors in cmd-test and + elsewhere + +v0.51 - 27 November 2010 +======================== + +- Added simple RSA encryption/decryption driver. Currently limited to + using 64-bit and 32-bit numbers in key pairs, which isn't terribly secure, + but allows much smaller code size than if huge number support was included. +- Added a driver for the MCP4725 I2C DAC (see "drivers/dac/mcp4725"). +- Moved 'rom_drivers.h' from 'core/usbhid-rom' to 'core' since it can be used + for usbmsd-rom as well if it is implemented at a later date. +- ili9325ReadData() added to ili9325.c by Adafruit Industries (thanks!) +- Modified FCLK_FAST() in 'drivers/fatfs/mmc.c' to set fast SPI speed + to 18MHz instead of the previous 4MHz. This allows for slightly faster + rendering of bitmap images from SD cards, amongst other things. +- Fixed ILI9325 driver to use 0,0 based pixels and not 1x1, meaning the + pixel co-ordinates are 0..239 for X and 0..319 for Y. There may still be + some off-by-one pixel issues that need to be further tested. + +v0.50 - 26 October 2010 +======================= + +- Moved command.c and cmd_tbl.h to /project to try to keep all + project-specific files in one location to improve code-sharing and make + upgrading easier +- Created seperate .c files for every command for the command-line interface + to make it easier to maintain and share code between projects. All commands + listed in cmd_tbl.h are now stored in their own files in /project/commands + (ex.: "/project/commands/cmd_hello.c", etc.) +- Added a CLI command to demonstrate how to draw to the ILI9325 TFT LCD +- Added a CLI command to show the contents of any directory from the SD card + (/project/commmands/cmd_sd_dir.c) +- Added a CLI command showing how to enter deep-sleep and exit with a SW + wakeup (/project/commands/cmd_deepsleep.c). Please note that the wakeup + will not work if you are using USB for the CLI. To see the wakeup message, + you must use UART for the CLI, which is selected by uncommenting + CFG_PRINTF_UART in projectconfig.h +- Fixed pmu.c to set the core clock back to an appropriate state after wakeup +- Added simple wrapper for eeprom access (projects/eeprom.c) +- Reorganised drivers/lcd to take into account non TFT LCD displays. +- Added a preliminary (experimental) driver for 128x64 pixel ST7565-based + bitmap LCDs (ex: Adafruit Industries - http://bit.ly/9xj5vw) ... based on a + driver from Limor Fried at Adafruit +- Numerous bug fixes added by Roel Verdult (Linux compatibility, GCC warnings, etc.) +- Added stub files for PN532 driver (/drivers/nfc/pn532), and added CFG_PN532 to + projectconfig.h as an option +- Added a simple pwm example using 16-bit timer 1 and p1.9 (MAT0) for the output. PWM can be used + to drive stepper motors, or to dim LEDs by adjusting the duty-cycle, etc. The example can be + found in "/core/pwm" +- Added a bi-polar stepper motor driver based on the Arduino library. Basic position data was added + to detece deviation from the 'Home' position and to retrieve the spindle's current angle. See + "drivers/motor/stepper" for more information. +- Added test firmware for the UART and USBCDC CLI (tools/testfirmware) +- Added a table to projectconfig.h to try to keep track of pin and peripherals + usage by drivers and the included SW examples. +- Reorganised some pins used by drivers to avoid pin conflicts. +- Modified core/timer16/timer16.c to allow PWM output to be stopped after a + certain number of pulses have been sent out. + + KNOWN ISSUES + +- The SPI code needs to be modified to allow several SPI devices to be safely + connected on the same bus. At present, this has not been tested since the + SSP/SPI blocks are used exclusively in the current drivers. + \ No newline at end of file diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..655e9a4 --- /dev/null +++ b/License.txt @@ -0,0 +1,27 @@ +Software License Agreement (BSD License) + +Unless otherwise noted, Copyright (c) 2010, microBuilder SARL. +Portions Copyright (c) 2010 Roel Verdult. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..57e7680 --- /dev/null +++ b/Makefile @@ -0,0 +1,180 @@ +########################################################################## +# User configuration and firmware specific object files +########################################################################## + +# The target, flash and ram of the LPC1xxx microprocessor. +# Use for the target the value: LPC11xx, LPC13xx or LPC17xx +TARGET = LPC13xx +FLASH = 32K +SRAM = 8K + +# For USB HID support the LPC134x reserves 384 bytes from the sram, +# if you don't want to use the USB features, just use 0 here. +SRAM_USB = 384 + +VPATH = +OBJS = main.o + +########################################################################## +# Project-specific files +########################################################################## + +VPATH += project +OBJS += commands.o + +VPATH += project/commands +OBJS += cmd_chibi_addr.o cmd_chibi_tx.o cmd_uart.o +OBJS += cmd_i2ceeprom_read.o cmd_i2ceeprom_write.o cmd_lm75b_gettemp.o +OBJS += cmd_sysinfo.o cmd_sd_dir.o cmd_tswait.o cmd_orientation.o +OBJS += cmd_tsthreshhold.o + +VPATH += project/commands/drawing +OBJS += cmd_button.o cmd_circle.o cmd_clear.o cmd_line.o cmd_pixel.o +OBJS += cmd_progress.o cmd_bmp.o cmd_gettext.o cmd_calibrate.o +OBJS += cmd_text.o cmd_textw.o cmd_rectangle.o + +########################################################################## +# Optional driver files +########################################################################## + +# Chibi Light-Weight Wireless Stack (AT86RF212) +VPATH += drivers/chibi +OBJS += chb.o chb_buf.o chb_drvr.o chb_eeprom.o chb_spi.o + +# 4K EEPROM +VPATH += drivers/eeprom drivers/eeprom/mcp24aa +OBJS += eeprom.o mcp24aa.o + +# LM75B temperature sensor +VPATH += drivers/sensors/lm75b +OBJS += lm75b.o + +# TFT LCD support +VPATH += drivers/lcd/tft drivers/lcd/tft/hw drivers/lcd/tft/fonts +VPATH += drivers/lcd/tft/dialogues +OBJS += drawing.o touchscreen.o bmp.o alphanumeric.o +OBJS += dejavusans9.o dejavusansbold9.o dejavusanscondensed9.o +OBJS += dejavusansmono8.o dejavusansmonobold8.o +OBJS += veramono9.o veramonobold9.o veramono11.o veramonobold11.o +# LCD Driver (Only one can be included at a time!) +OBJS += ILI9328.o +# OBJS += ILI9325.o +# OBJS += st7735.o +# OBJS += st7783.o + +# Bitmap/Monochrome LCD support (ST7565, SSD1306, etc.) +VPATH += drivers/lcd drivers/lcd/bitmap/st7565 +VPATH += drivers/lcd/bitmap/ssd1306 +OBJS += smallfonts.o st7565.o ssd1306.o + +# ChaN FatFS and SD card support +VPATH += drivers/fatfs +OBJS += ff.o mmc.o + +# Motors +VPATH += drivers/motor/stepper +OBJS += stepper.o + +# RSA Encryption/Descryption +VPATH += drivers/rsa +OBJS += rsa.o + +# DAC +VPATH += drivers/dac/mcp4725 +OBJS += mcp4725.o + +# RFID/NFC +VPATH += drivers/sensors/pn532 +OBJS += pn532.o pn532_drvr_uart.o + +# TAOS Light Sensors +VPATH += drivers/sensors/tcs3414 drivers/sensors/tsl2561 +OBJS += tcs3414.o tsl2561.o + +########################################################################## +# Library files +########################################################################## + +VPATH += core core/adc core/cmd core/cpu core/gpio core/i2c core/pmu +VPATH += core/ssp core/systick core/timer16 core/timer32 core/uart +VPATH += core/usbhid-rom core/libc core/wdt core/usbcdc core/pwm +VPATH += core/IAP +OBJS += adc.o cpu.o cmd.o gpio.o i2c.o pmu.o ssp.o systick.o timer16.o +OBJS += timer32.o uart.o uart_buf.o usbconfig.o usbhid.o stdio.o string.o +OBJS += wdt.o cdcuser.o cdc_buf.o usbcore.o usbdesc.o usbhw.o usbuser.o +OBJS += sysinit.o pwm.o iap.o + +########################################################################## +# GNU GCC compiler prefix and location +########################################################################## + +CROSS_COMPILE = arm-none-eabi- +AS = $(CROSS_COMPILE)gcc +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)gcc +SIZE = $(CROSS_COMPILE)size +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +OUTFILE = firmware +LPCRC = ./lpcrc + +########################################################################## +# GNU GCC compiler flags +########################################################################## +ROOT_PATH = . +INCLUDE_PATHS = -I$(ROOT_PATH) -I$(ROOT_PATH)/project + +########################################################################## +# Startup files +########################################################################## + +LD_PATH = lpc1xxx +LD_SCRIPT = $(LD_PATH)/linkscript.ld +LD_TEMP = $(LD_PATH)/memory.ld + +ifeq (LPC11xx,$(TARGET)) + CORTEX_TYPE=m0 +else + CORTEX_TYPE=m3 +endif + +CPU_TYPE = cortex-$(CORTEX_TYPE) +VPATH += lpc1xxx +OBJS += $(TARGET)_handlers.o LPC1xxx_startup.o + +########################################################################## +# Compiler settings, parameters and flags +########################################################################## + +CFLAGS = -c -g -Os $(INCLUDE_PATHS) -Wall -mthumb -ffunction-sections -fdata-sections -fmessage-length=0 -mcpu=$(CPU_TYPE) -DTARGET=$(TARGET) -fno-builtin +ASFLAGS = -c -g -Os $(INCLUDE_PATHS) -Wall -mthumb -ffunction-sections -fdata-sections -fmessage-length=0 -mcpu=$(CPU_TYPE) -D__ASSEMBLY__ -x assembler-with-cpp +LDFLAGS = -nostartfiles -mthumb -mcpu=$(CPU_TYPE) -Wl,--gc-sections +LDLIBS = -lm +OCFLAGS = --strip-unneeded + +all: firmware + +%.o : %.c + $(CC) $(CFLAGS) -o $@ $< + +%.o : %.s + $(AS) $(ASFLAGS) -o $@ $< + +firmware: $(OBJS) $(SYS_OBJS) + -@echo "MEMORY" > $(LD_TEMP) + -@echo "{" >> $(LD_TEMP) + -@echo " flash(rx): ORIGIN = 0x00000000, LENGTH = $(FLASH)" >> $(LD_TEMP) + -@echo " sram(rwx): ORIGIN = 0x10000000+$(SRAM_USB), LENGTH = $(SRAM)-$(SRAM_USB)" >> $(LD_TEMP) + -@echo "}" >> $(LD_TEMP) + -@echo "INCLUDE $(LD_SCRIPT)" >> $(LD_TEMP) + $(LD) $(LDFLAGS) -T $(LD_TEMP) -o $(OUTFILE).elf $(OBJS) $(LDLIBS) + -@echo "" + $(SIZE) $(OUTFILE).elf + -@echo "" + $(OBJCOPY) $(OCFLAGS) -O binary $(OUTFILE).elf $(OUTFILE).bin + $(OBJCOPY) $(OCFLAGS) -O ihex $(OUTFILE).elf $(OUTFILE).hex + -@echo "" + $(LPCRC) firmware.bin + +clean: + rm -f $(OBJS) $(LD_TEMP) $(OUTFILE).elf $(OUTFILE).bin $(OUTFILE).hex diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..b058eab --- /dev/null +++ b/Readme.txt @@ -0,0 +1,11 @@ +This software is based on the freely available LPC1343 Reference Design available +at www.microBuilder.eu, and is licensed under the BSD license (see license.txt +for details). + +The latest version of the software is always available at the following url: + +http://www.microbuilder.eu/Projects/LPC1343ReferenceDesign/LPC1343CodeBase.aspx + +Online documentation is available at: + +http://www.microbuilder.eu/Projects/LPC1343ReferenceDesign/CodeBaseDocumentation.aspx \ No newline at end of file diff --git a/build/codelite/LPC1343 Workspace.tags b/build/codelite/LPC1343 Workspace.tags new file mode 100644 index 0000000000000000000000000000000000000000..e2fc66b2428d50de71e3dc724e41f211e1a9e1de GIT binary patch literal 99328 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 + + + + + + + + + + + diff --git a/build/codelite/LPC1343 Workspace.workspace.session b/build/codelite/LPC1343 Workspace.workspace.session new file mode 100644 index 0000000..d0bdde6 --- /dev/null +++ b/build/codelite/LPC1343 Workspace.workspace.session @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/codelite/LPC1343_CodeBase.project b/build/codelite/LPC1343_CodeBase.project new file mode 100644 index 0000000..a7dade6 --- /dev/null +++ b/build/codelite/LPC1343_CodeBase.project @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # Make sure that we are using SWD +monitor interface SWD + +# Set monitor to little endian +monitor endian little + +# Set monitor speed +monitor speed 1000 + +# Reset device +monitor reset + +# Set device ID to LPC1343 +monitor flash device = LPC1343 + +# Enable flash download +monitor flash download = 1 + +# Transfer the firmware to the device +load "../../firmware.elf" + +# Initializing PC and stack pointer +monitor reg r13 = (0x00000000) +monitor reg pc = (0x00000004) + + + + + + + + make clean + make + + + + None + $(WorkspacePath)/../../ + + + + + + + + + + + + + + + + + + + + + + + make clean + make + + + + None + $(WorkspacePath) + + + + + + + + + + + + + + + + + diff --git a/build/codelite/tags b/build/codelite/tags new file mode 100644 index 0000000..f672c5a --- /dev/null +++ b/build/codelite/tags @@ -0,0 +1,4 @@ +rx_data C:\Documents and Settings\Kevin\My Documents\My Dropbox\microBuilder\Code\LPC1343\LPC1343_CodeBase\main.c /^static chb_rx_data_t rx_data;$/;" variable line:66 +main C:\Documents and Settings\Kevin\My Documents\My Dropbox\microBuilder\Code\LPC1343\LPC1343_CodeBase\main.c /^int main (void)$/;" function line:70 signature:(void) returns:int +__putchar C:\Documents and Settings\Kevin\My Documents\My Dropbox\microBuilder\Code\LPC1343\LPC1343_CodeBase\main.c /^void __putchar(char c) $/;" function line:186 signature:(char c) returns:void +puts C:\Documents and Settings\Kevin\My Documents\My Dropbox\microBuilder\Code\LPC1343\LPC1343_CodeBase\main.c /^int puts ( const char * str )$/;" function line:191 signature:( const char * str ) returns:int diff --git a/build/crossworks/LPC1343_CodeBase.hzp b/build/crossworks/LPC1343_CodeBase.hzp new file mode 100644 index 0000000..0bab7e7 --- /dev/null +++ b/build/crossworks/LPC1343_CodeBase.hzp @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/crossworks/LPC1343_CodeBase.hzs b/build/crossworks/LPC1343_CodeBase.hzs new file mode 100644 index 0000000..0ebdfbc --- /dev/null +++ b/build/crossworks/LPC1343_CodeBase.hzs @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/crossworks/flash_placement.xml b/build/crossworks/flash_placement.xml new file mode 100644 index 0000000..963e460 --- /dev/null +++ b/build/crossworks/flash_placement.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/adc/adc.c b/core/adc/adc.c new file mode 100644 index 0000000..fdf2d30 --- /dev/null +++ b/core/adc/adc.c @@ -0,0 +1,228 @@ +/**************************************************************************/ +/*! + @file adc.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section Description + + SW-based single-channel A/D conversion. If you wish to convert + multiple ADC channels simultaneously, this code will need to be + modified to work in BURST mode. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "core/adc/adc.h" + + void main (void) + { + cpuInit(); + adcInit(); + + uint32_t results = 0; + while(1) + { + // Get A/D conversion results from A/D channel 0 + results = adcRead(0); + } + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ +/**************************************************************************/ + +#include "adc.h" + +static bool _adcInitialised = false; +static uint8_t _adcLastChannel = 0; + +/**************************************************************************/ +/*! + @brief Returns the conversion results on the specified ADC channel. + + This function will manually start an A/D conversion on a single + channel and return the results. + + @param[in] channelNum + The A/D channel [0..7] that will be used during the A/D + conversion. (Note that only A/D channel's 0..3 are + configured by default in adcInit.) + + @return 0 if an overrun error occured, otherwise a 10-bit value + containing the A/D conversion results. + @warning Only AD channels 0..3 are configured for A/D in adcInit. + If you wish to use A/D pins 4..7 they will also need to + be added to the adcInit function. +*/ +/**************************************************************************/ +uint32_t adcRead (uint8_t channelNum) +{ + if (!_adcInitialised) adcInit(); + + uint32_t regVal, adcData; + + /* make sure that channel number is 0..7 */ + if ( channelNum >= 8 ) + { + // ToDo: Change this to throw an exception back + channelNum = 0; + } + + /* Deselect all channels */ + ADC_AD0CR &= ~ADC_AD0CR_SEL_MASK; + + /* Start converting now on the appropriate channel */ + ADC_AD0CR |= ADC_AD0CR_START_STARTNOW | (1 << channelNum); + + /* wait until end of A/D convert */ + while ( 1 ) + { + // Get data register results for the requested channel + switch (channelNum) + { + case 0: + regVal = (*(pREG32(ADC_AD0DR0))); + break; + case 1: + regVal = (*(pREG32(ADC_AD0DR1))); + break; + case 2: + regVal = (*(pREG32(ADC_AD0DR2))); + break; + case 3: + regVal = (*(pREG32(ADC_AD0DR3))); + break; + case 4: + regVal = (*(pREG32(ADC_AD0DR4))); + break; + case 5: + regVal = (*(pREG32(ADC_AD0DR5))); + break; + case 6: + regVal = (*(pREG32(ADC_AD0DR6))); + break; + case 7: + regVal = (*(pREG32(ADC_AD0DR7))); + break; + default: + regVal = (*(pREG32(ADC_AD0DR0))); + break; + } + + /* read result of A/D conversion */ + if (regVal & ADC_DR_DONE) + { + break; + } + } + + /* stop ADC */ + ADC_AD0CR &= ~ADC_AD0CR_START_MASK; + + /* return 0 if an overrun occurred */ + if ( regVal & ADC_DR_OVERRUN ) + { + return (0); + } + + /* return conversion results */ + adcData = (regVal >> 6) & 0x3FF; + return (adcData); +} + +/**************************************************************************/ +/*! + @brief Initialises the A/D converter and configures channels 0..3 + for 10-bit, SW-controlled A/D conversion. + + @return Nothing +*/ +/**************************************************************************/ +void adcInit (void) +{ + /* Disable Power down bit to the ADC block. */ + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_ADC); + + /* Enable AHB clock to the ADC. */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_ADC); + + /* Digital pins need to have the 'analog' bit set in addition + to changing their pin function */ + + /* Set AD0 to analog input */ + IOCON_JTAG_TDI_PIO0_11 &= ~(IOCON_JTAG_TDI_PIO0_11_ADMODE_MASK | + IOCON_JTAG_TDI_PIO0_11_FUNC_MASK | + IOCON_JTAG_TDI_PIO0_11_MODE_MASK); + IOCON_JTAG_TDI_PIO0_11 |= (IOCON_JTAG_TDI_PIO0_11_FUNC_AD0 & + IOCON_JTAG_TDI_PIO0_11_ADMODE_ANALOG); + + /* Set AD1 to analog input */ + IOCON_JTAG_TMS_PIO1_0 &= ~(IOCON_JTAG_TMS_PIO1_0_ADMODE_MASK | + IOCON_JTAG_TMS_PIO1_0_FUNC_MASK | + IOCON_JTAG_TMS_PIO1_0_MODE_MASK); + IOCON_JTAG_TMS_PIO1_0 |= (IOCON_JTAG_TMS_PIO1_0_FUNC_AD1 & + IOCON_JTAG_TMS_PIO1_0_ADMODE_ANALOG); + + /* Set AD2 to analog input */ + IOCON_JTAG_TDO_PIO1_1 &= ~(IOCON_JTAG_TDO_PIO1_1_ADMODE_MASK | + IOCON_JTAG_TDO_PIO1_1_FUNC_MASK | + IOCON_JTAG_TDO_PIO1_1_MODE_MASK); + IOCON_JTAG_TDO_PIO1_1 |= (IOCON_JTAG_TDO_PIO1_1_FUNC_AD2 & + IOCON_JTAG_TDO_PIO1_1_ADMODE_ANALOG); + + /* Set AD3 to analog input */ + IOCON_JTAG_nTRST_PIO1_2 &= ~(IOCON_JTAG_nTRST_PIO1_2_ADMODE_MASK | + IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK | + IOCON_JTAG_nTRST_PIO1_2_MODE_MASK); + IOCON_JTAG_nTRST_PIO1_2 |= (IOCON_JTAG_nTRST_PIO1_2_FUNC_AD3 & + IOCON_JTAG_nTRST_PIO1_2_ADMODE_ANALOG); + + /* Note that in SW mode only one channel can be selected at a time (AD0 in this case) + To select multiple channels, ADC_AD0CR_BURST_HWSCANMODE must be used */ + ADC_AD0CR = (ADC_AD0CR_SEL_AD0 | /* SEL=1,select channel 0 on ADC0 */ + (((CFG_CPU_CCLK / SCB_SYSAHBCLKDIV) / 1000000 - 1 ) << 8) | /* CLKDIV = Fpclk / 1000000 - 1 */ + ADC_AD0CR_BURST_SWMODE | /* BURST = 0, no BURST, software controlled */ + ADC_AD0CR_CLKS_10BITS | /* CLKS = 0, 11 clocks/10 bits */ + ADC_AD0CR_START_NOSTART | /* START = 0 A/D conversion stops */ + ADC_AD0CR_EDGE_RISING); /* EDGE = 0 (CAP/MAT signal falling, trigger A/D conversion) */ + + /* Set initialisation flag */ + _adcInitialised = true; + + /* Set last channel flag to 0 (initialised above) */ + _adcLastChannel = 0; + + return; +} diff --git a/core/adc/adc.h b/core/adc/adc.h new file mode 100644 index 0000000..29e168d --- /dev/null +++ b/core/adc/adc.h @@ -0,0 +1,47 @@ +/**************************************************************************/ +/*! + @file adc.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/**************************************************************************/ + +#ifndef _ADC_H_ +#define _ADC_H_ + +#include "projectconfig.h" + +uint32_t adcRead (uint8_t channelNum); +void adcInit (void); + +#endif diff --git a/core/cmd/cmd.c b/core/cmd/cmd.c new file mode 100644 index 0000000..ee5ccf4 --- /dev/null +++ b/core/cmd/cmd.c @@ -0,0 +1,296 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. +*******************************************************************/ + +/**************************************************************************/ +/*! + @file cmd.c + @author Christopher Wang (Freaklabs) + Modified by: K. Townsend (microBuilder.eu) + @date 19 May 2010 + + Original code taken from the FreakUSB Open Source USB Device Stack + http://freaklabs.org/index.php/FreakUSB-Open-Source-USB-Device-Stack.html + + If it works well, you can thank Akiba at Freaklabs. If it fails + miserably, you can blame me (since parts of it it were rather + ungraciously modified). :-) + +*/ +/**************************************************************************/ + +#include +#include + +#include "cmd.h" +#include "project/cmd_tbl.h" + +#ifdef CFG_PRINTF_UART +#include "core/uart/uart.h" +#endif + +#ifdef CFG_PRINTF_USBCDC + #include "core/usbcdc/cdcuser.h" + static char usbcdcBuf [32]; +#endif + +#if CFG_INTERFACE_ENABLEIRQ == 1 + #include "core/gpio/gpio.h" +#endif + +static uint8_t msg[CFG_INTERFACE_MAXMSGSIZE]; +static uint8_t *msg_ptr; + +/**************************************************************************/ +/*! + @brief Polls the relevant incoming message queue to see if anything + is waiting to be processed. +*/ +/**************************************************************************/ +void cmdPoll() +{ + #if defined CFG_PRINTF_UART + while (uartRxBufferDataPending()) + { + uint8_t c = uartRxBufferRead(); + cmdRx(c); + } + #endif + + #if defined CFG_PRINTF_USBCDC + int numBytesToRead, numBytesRead, numAvailByte; + + CDC_OutBufAvailChar (&numAvailByte); + if (numAvailByte > 0) + { + numBytesToRead = numAvailByte > 32 ? 32 : numAvailByte; + numBytesRead = CDC_RdOutBuf (&usbcdcBuf[0], &numBytesToRead); + int i; + for (i = 0; i < numBytesRead; i++) + { + cmdRx(usbcdcBuf[i]); + } + } + #endif +} + +/**************************************************************************/ +/*! + @brief Handles a single incoming character. If a new line is + detected, the entire command will be passed to the command + parser. If a text character is detected, it will be added to + the message buffer until a new line is detected (up to the + maximum queue size, CFG_INTERFACE_MAXMSGSIZE). + + @param[in] c + The character to parse. +*/ +/**************************************************************************/ +void cmdRx(uint8_t c) +{ + // read out the data in the buffer and echo it back to the host. + switch (c) + { + case '\r': + case '\n': + // terminate the msg and reset the msg ptr. then send + // it to the handler for processing. + *msg_ptr = '\0'; + #if CFG_INTERFACE_SILENTMODE == 0 + printf("%s", CFG_PRINTF_NEWLINE); + #endif + cmdParse((char *)msg); + msg_ptr = msg; + break; + + case '\b': + #if CFG_INTERFACE_SILENTMODE == 0 + printf("%c",c); + #endif + if (msg_ptr > msg) + { + msg_ptr--; + } + break; + + default: + #if CFG_INTERFACE_SILENTMODE == 0 + printf("%c",c); + #endif + *msg_ptr++ = c; + break; + } +} + +/**************************************************************************/ +/*! + @brief Displays the command prompt. The text that appears is defined + in projectconfig.h. +*/ +/**************************************************************************/ +static void cmdMenu() +{ + #if CFG_INTERFACE_SILENTMODE == 0 + printf(CFG_PRINTF_NEWLINE); + printf(CFG_INTERFACE_PROMPT); + #endif +} + +/**************************************************************************/ +/*! + @brief Parse the command line. This function tokenizes the command + input, then searches for the command table entry associated + with the commmand. Once found, it will jump to the + corresponding function. + + @param[in] cmd + The entire command string to be parsed +*/ +/**************************************************************************/ +void cmdParse(char *cmd) +{ + size_t argc, i = 0; + char *argv[30]; + + argv[i] = strtok(cmd, " "); + do + { + argv[++i] = strtok(NULL, " "); + } while ((i < 30) && (argv[i] != NULL)); + + argc = i; + for (i=0; i < CMD_COUNT; i++) + { + if (!strcmp(argv[0], cmd_tbl[i].command)) + { + if ((argc == 2) && !strcmp (argv [1], "?")) + { + // Display parameter help menu on 'command ?' + printf ("%s%s%s", cmd_tbl[i].description, CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + printf ("%s%s", cmd_tbl[i].parameters, CFG_PRINTF_NEWLINE); + } + else if ((argc - 1) < cmd_tbl[i].minArgs) + { + // Too few arguments supplied + printf ("Too few arguments (%d expected)%s", cmd_tbl[i].minArgs, CFG_PRINTF_NEWLINE); + printf ("%sType '%s ?' for more information%s%s", CFG_PRINTF_NEWLINE, cmd_tbl[i].command, CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + } + else if ((argc - 1) > cmd_tbl[i].maxArgs) + { + // Too many arguments supplied + printf ("Too many arguments (%d maximum)%s", cmd_tbl[i].maxArgs, CFG_PRINTF_NEWLINE); + printf ("%sType '%s ?' for more information%s%s", CFG_PRINTF_NEWLINE, cmd_tbl[i].command, CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + } + else + { + #if CFG_INTERFACE_ENABLEIRQ != 0 + // Set the IRQ pin high at start of a command + gpioSetValue(CFG_INTERFACE_IRQPORT, CFG_INTERFACE_IRQPIN, 1); + #endif + // Dispatch command to the appropriate function + cmd_tbl[i].func(argc - 1, &argv [1]); + #if CFG_INTERFACE_ENABLEIRQ != 0 + // Set the IRQ pin low to signal the end of a command + gpioSetValue(CFG_INTERFACE_IRQPORT, CFG_INTERFACE_IRQPIN, 0); + #endif + } + + // Refresh the command prompt + cmdMenu(); + return; + } + } + printf("Command not recognized: '%s'%s%s", cmd, CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + #if CFG_INTERFACE_SILENTMODE == 0 + printf("Type '?' for a list of all available commands%s", CFG_PRINTF_NEWLINE); + #endif + + cmdMenu(); +} + +/**************************************************************************/ +/*! + @brief Initialises the command line using the appropriate interface +*/ +/**************************************************************************/ +void cmdInit() +{ + #if defined CFG_INTERFACE && defined CFG_INTERFACE_UART + // Check if UART is already initialised + uart_pcb_t *pcb = uartGetPCB(); + if (!pcb->initialised) + { + uartInit(CFG_UART_BAUDRATE); + } + #endif + + #if CFG_INTERFACE_ENABLEIRQ != 0 + // Set IRQ pin as output + gpioSetDir(CFG_INTERFACE_IRQPORT, CFG_INTERFACE_IRQPIN, gpioDirection_Output); + gpioSetValue(CFG_INTERFACE_IRQPORT, CFG_INTERFACE_IRQPIN, 1); + #endif + + // init the msg ptr + msg_ptr = msg; + + // Show the menu + cmdMenu(); + + // Set the IRQ pin low by default + #if CFG_INTERFACE_ENABLEIRQ != 0 + gpioSetValue(CFG_INTERFACE_IRQPORT, CFG_INTERFACE_IRQPIN, 0); + #endif +} + +/**************************************************************************/ +/*! + 'help' command handler +*/ +/**************************************************************************/ +void cmd_help(uint8_t argc, char **argv) +{ + size_t i; + + printf("Command Description%s", CFG_PRINTF_NEWLINE); + printf("------- -----------%s", CFG_PRINTF_NEWLINE); + + // Display full command list + for (i=0; i < CMD_COUNT; i++) + { + if (!cmd_tbl[i].hidden) + { + printf ("%-10s %s%s", cmd_tbl[i].command, cmd_tbl[i].description, CFG_PRINTF_NEWLINE); + } + } + + printf("%sCommand parameters can be seen by entering: ?%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); +} diff --git a/core/cmd/cmd.h b/core/cmd/cmd.h new file mode 100644 index 0000000..7dde782 --- /dev/null +++ b/core/cmd/cmd.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/*! + @file cmd.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __CMD_H__ +#define __CMD_H__ + +#include "projectconfig.h" + +typedef struct +{ + char *command; + uint8_t minArgs; + uint8_t maxArgs; + uint8_t hidden; + void (*func)(uint8_t argc, char **argv); + const char *description; + const char *parameters; +} cmd_t; + +void cmdPoll(); +void cmdRx(uint8_t c); +void cmdParse(char *cmd); +void cmdInit(); + +#endif \ No newline at end of file diff --git a/core/cpu/cpu.c b/core/cpu/cpu.c new file mode 100644 index 0000000..03c6db6 --- /dev/null +++ b/core/cpu/cpu.c @@ -0,0 +1,169 @@ +/**************************************************************************/ +/*! + @file cpu.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Initialises the CPU and any core clocks. By default, the core clock + is set to run at 72MHz. In order to reduce power consumption all pins + are set to GPIO and input by cpuInit. + + @section EXAMPLE + @code + #include "lpc134x.h" + #include "core/cpu/cpu.h" + + int main (void) + { + // Initialise the CPU and setup the PLL + cpuInit(); + + while(1) + { + } + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "cpu.h" +#include "core/gpio/gpio.h" + +/**************************************************************************/ +/*! + @brief Configures the main clock/PLL + + The speed at which the MCU operates is set here using the SCB_PLLCTRL + register, and the SCB_PLLCLKSEL register can be used to select which + oscillator to use to generate the system clocks (the internal 12MHz + oscillator or an external crystal). + + @param[in] multiplier + The PLL multiplier + +*/ +/**************************************************************************/ +void cpuPllSetup (cpuMultiplier_t multiplier) +{ + uint32_t i; + + // Power up system oscillator + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_SYSOSC_MASK); + + // Setup the crystal input (bypass disabled, 1-20MHz crystal) + SCB_SYSOSCCTRL = (SCB_SYSOSCCTRL_BYPASS_DISABLED | SCB_SYSOSCCTRL_FREQRANGE_1TO20MHZ); + + for (i = 0; i < 200; i++) + { + __asm volatile ("NOP"); + } + + // Configure PLL + SCB_PLLCLKSEL = SCB_CLKSEL_SOURCE_MAINOSC; // Select external crystal as PLL clock source + SCB_PLLCLKUEN = SCB_PLLCLKUEN_UPDATE; // Update clock source + SCB_PLLCLKUEN = SCB_PLLCLKUEN_DISABLE; // Toggle update register once + SCB_PLLCLKUEN = SCB_PLLCLKUEN_UPDATE; // Update clock source again + + // Wait until the clock is updated + while (!(SCB_PLLCLKUEN & SCB_PLLCLKUEN_UPDATE)); + + // Set clock speed + switch (multiplier) + { + case CPU_MULTIPLIER_2: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_2 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + case CPU_MULTIPLIER_3: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_3 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + case CPU_MULTIPLIER_4: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_4 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + case CPU_MULTIPLIER_5: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_5 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + case CPU_MULTIPLIER_6: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_6 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + case CPU_MULTIPLIER_1: + default: + SCB_PLLCTRL = (SCB_PLLCTRL_MULT_1 | (1 << SCB_PLLCTRL_DIV_BIT)); + break; + } + + // Enable system PLL + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_SYSPLL_MASK); + + // Wait for PLL to lock + while (!(SCB_PLLSTAT & SCB_PLLSTAT_LOCK)); + + // Setup main clock (use PLL output) + SCB_MAINCLKSEL = SCB_MAINCLKSEL_SOURCE_SYSPLLCLKOUT; + SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; // Update clock source + SCB_MAINCLKUEN = SCB_MAINCLKUEN_DISABLE; // Toggle update register once + SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; + + // Wait until the clock is updated + while (!(SCB_MAINCLKUEN & SCB_MAINCLKUEN_UPDATE)); + + // Disable USB clock by default (enabled in USB code) + SCB_PDRUNCFG |= (SCB_PDSLEEPCFG_USBPAD_PD); // Power-down USB PHY + SCB_PDRUNCFG |= (SCB_PDSLEEPCFG_USBPLL_PD); // Power-down USB PLL + + // Set system AHB clock + SCB_SYSAHBCLKDIV = SCB_SYSAHBCLKDIV_DIV1; + + // Enabled IOCON clock for I/O related peripherals + SCB_SYSAHBCLKCTRL |= SCB_SYSAHBCLKCTRL_IOCON; +} + +/**************************************************************************/ +/*! + @brief Initialises the CPU, setting up the PLL, etc. +*/ +/**************************************************************************/ +void cpuInit (void) +{ + gpioInit(); + + // Set all GPIO pins to input by default + GPIO_GPIO0DIR &= ~(GPIO_IO_ALL); + GPIO_GPIO1DIR &= ~(GPIO_IO_ALL); + GPIO_GPIO2DIR &= ~(GPIO_IO_ALL); + GPIO_GPIO3DIR &= ~(GPIO_IO_ALL); + + // Setup PLL (etc.) + cpuPllSetup(CPU_MULTIPLIER_6); +} diff --git a/core/cpu/cpu.h b/core/cpu/cpu.h new file mode 100644 index 0000000..f2e12ac --- /dev/null +++ b/core/cpu/cpu.h @@ -0,0 +1,76 @@ +/**************************************************************************/ +/*! + @file cpu.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "projectconfig.h" + +// Macro to initialise, reset and enable the cycle counter. +// This can be used for rough timing and performance tests +// by resetting the cycle counter before a function, and +// then reading the value after with "int count = DWT_CYCCNT" +// +// CPU_RESET_CYCLECOUNTER; +// ... do something +// int count = DWT_CYCCNT; +// +#define CPU_RESET_CYCLECOUNTER do { SCB_DEMCR = SCB_DEMCR | 0x01000000; \ + DWT_CYCCNT = 0; \ + DWT_CTRL = DWT_CTRL | 1 ; } while(0) + +/**************************************************************************/ +/*! + @brief Indicates the value for the PLL multiplier +*/ +/**************************************************************************/ +typedef enum +{ + CPU_MULTIPLIER_1 = 0, + CPU_MULTIPLIER_2, + CPU_MULTIPLIER_3, + CPU_MULTIPLIER_4, + CPU_MULTIPLIER_5, + CPU_MULTIPLIER_6 +} +cpuMultiplier_t; + +void cpuPllSetup (cpuMultiplier_t multiplier); +void cpuInit (void); + +#endif \ No newline at end of file diff --git a/core/gpio/gpio.c b/core/gpio/gpio.c new file mode 100644 index 0000000..3809a5e --- /dev/null +++ b/core/gpio/gpio.c @@ -0,0 +1,550 @@ +/**************************************************************************/ +/*! + @file gpio.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Controls the general purpose digital IO. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "gpio.h" + +#ifdef CFG_CHIBI +#include "drivers/chibi/chb_drvr.h" +volatile uint32_t chibi_counter = 0; +#endif + +static bool _gpioInitialised = false; + +/**************************************************************************/ +/*! + @brief IRQ Handler for GPIO port 0 (currently checks pin 0.1) +*/ +/**************************************************************************/ +void PIOINT0_IRQHandler(void) +{ + uint32_t regVal; + + regVal = gpioIntStatus(0, 1); + if (regVal) + { + gpioIntClear(0, 1); + } + return; +} + +/**************************************************************************/ +/*! + @brief IRQ Handler for GPIO port 1 (currently checks pin 1.1) +*/ +/**************************************************************************/ +void PIOINT1_IRQHandler(void) +{ + uint32_t regVal; + +#ifdef CFG_CHIBI + // Check for interrupt on 1.8 + regVal = gpioIntStatus(1, 8); + if (regVal) + { + chibi_counter++; + chb_ISR_Handler(); + gpioIntClear(1, 8); + } +#else + regVal = gpioIntStatus(1, 1); + if ( regVal ) + { + gpioIntClear(1, 1); + } +#endif + + return; +} + +/**************************************************************************/ +/*! + @brief IRQ Handler for GPIO port 2 (currently checks pin 2.1) +*/ +/**************************************************************************/ +void PIOINT2_IRQHandler(void) +{ + uint32_t regVal; + + regVal = gpioIntStatus(2, 1); + if ( regVal ) + { + gpioIntClear(2, 1); + } + return; +} + +/**************************************************************************/ +/*! + @brief IRQ Handler for GPIO port 3 (currently checks pin 3.1) +*/ +/**************************************************************************/ +void PIOINT3_IRQHandler(void) +{ + uint32_t regVal; + + regVal = gpioIntStatus(3, 1); + if ( regVal ) + { + gpioIntClear(3, 1); + } + return; +} + +/**************************************************************************/ +/*! + @brief Initialises GPIO and enables the GPIO interrupt + handler for all GPIO ports. +*/ +/**************************************************************************/ +void gpioInit (void) +{ + /* Enable AHB clock to the GPIO domain. */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_GPIO); + + /* Set up NVIC when I/O pins are configured as external interrupts. */ + NVIC_EnableIRQ(EINT0_IRQn); + NVIC_EnableIRQ(EINT1_IRQn); + NVIC_EnableIRQ(EINT2_IRQn); + NVIC_EnableIRQ(EINT3_IRQn); + + /* Set initialisation flag */ + _gpioInitialised = true; + + return; +} + +/**************************************************************************/ +/*! + @brief Sets the direction (input/output) for a specific port pin + + @param[in] portNum + The port number (0..3) + @param[in] bitPos + The bit position (0..11) + @param[in] dir + The pin direction (gpioDirection_Input or + gpioDirection_Output) +*/ +/**************************************************************************/ +void gpioSetDir (uint32_t portNum, uint32_t bitPos, gpioDirection_t dir) +{ + if (!_gpioInitialised) gpioInit(); + + // Get the appropriate register (handled this way to optimise code size) + REG32 *gpiodir = &GPIO_GPIO0DIR; + switch (portNum) + { + case 0: + gpiodir = &GPIO_GPIO0DIR; + break; + case 1: + gpiodir = &GPIO_GPIO1DIR; + break; + case 2: + gpiodir = &GPIO_GPIO2DIR; + break; + case 3: + gpiodir = &GPIO_GPIO3DIR; + break; + } + + // Toggle dir + dir == gpioDirection_Output ? (*gpiodir |= (1 << bitPos)) : (*gpiodir &= ~(1 << bitPos)); +} + +/**************************************************************************/ +/*! + @brief Gets the value for a specific port pin + + @param[in] portNum + The port number (0..3) + @param[in] bitPos + The bit position (0..31) + + @return The current value for the specified port pin (0..1) +*/ +/**************************************************************************/ +uint32_t gpioGetValue (uint32_t portNum, uint32_t bitPos) +{ + if (!_gpioInitialised) gpioInit(); + + uint32_t value = 0; + + switch (portNum) + { + case 0: + value = (GPIO_GPIO0DATA & (1 << bitPos)) ? 1 : 0; + break; + case 1: + value = (GPIO_GPIO1DATA & (1 << bitPos)) ? 1 : 0; + break; + case 2: + value = (GPIO_GPIO2DATA & (1 << bitPos)) ? 1 : 0; + break; + case 3: + value = (GPIO_GPIO3DATA & (1 << bitPos)) ? 1 : 0; + break; + default: + break; + } + + return value; +} + +/**************************************************************************/ +/*! + @brief Sets the value for a specific port pin (only relevant when a + pin is configured as output). + + @param[in] portNum + The port number (0..3) + @param[in] bitPos + The bit position (0..31) + @param[in] bitValue + The value to set for the specified bit (0..1). 0 will set + the pin low and 1 will set the pin high. +*/ +/**************************************************************************/ +void gpioSetValue (uint32_t portNum, uint32_t bitPos, uint32_t bitVal) +{ + if (!_gpioInitialised) gpioInit(); + + // Get the appropriate register (handled this way to optimise code size) + REG32 *gpiodata = &GPIO_GPIO0DATA; + switch (portNum) + { + case 0: + gpiodata = &GPIO_GPIO0DATA; + break; + case 1: + gpiodata = &GPIO_GPIO1DATA; + break; + case 2: + gpiodata = &GPIO_GPIO2DATA; + break; + case 3: + gpiodata = &GPIO_GPIO3DATA; + break; + } + + // Toggle value + bitVal == 1 ? (*gpiodata |= (1 << bitPos)) : (*gpiodata &= ~(1 << bitPos)); +} + +/**************************************************************************/ +/*! + @brief Sets the interrupt sense, event, etc. + + @param[in] portNum + The port number (0..3) + @param[in] bitPos + The bit position (0..31) + @param[in] sense + Whether the interrupt should be configured as edge or level + sensitive. + @param[in] edge + Whether one edge or both trigger an interrupt. + @param[in] event + Whether the rising or the falling edge (high or low) + should be used to trigger the interrupt. + + @section Example + + @code + // Initialise gpio + gpioInit(); + // Set GPIO1.8 to input + gpioSetDir(1, 8, gpioDirection_Input); + // Disable the internal pullup/down resistor on P1.8 + gpioSetPullup (&IOCON_PIO1_8, gpioPullupMode_Inactive); + // Setup an interrupt on GPIO1.8 + gpioSetInterrupt(1, // Port + 8, // Pin + gpioInterruptSense_Edge, // Edge/Level Sensitive + gpioInterruptEdge_Single, // Single/Double Edge + gpioInterruptEvent_ActiveHigh); // Rising/Falling + // Enable the interrupt + gpioIntEnable(1, 8); + @endcode +*/ +/**************************************************************************/ +void gpioSetInterrupt (uint32_t portNum, uint32_t bitPos, gpioInterruptSense_t sense, gpioInterruptEdge_t edge, gpioInterruptEvent_t event) +{ + if (!_gpioInitialised) gpioInit(); + + // Get the appropriate register (handled this way to optimise code size) + REG32 *gpiois = &GPIO_GPIO0IS; + REG32 *gpioibe = &GPIO_GPIO0IBE; + REG32 *gpioiev = &GPIO_GPIO0IEV; + switch (portNum) + { + case 0: + gpiois = &GPIO_GPIO0IS; + gpioibe = &GPIO_GPIO0IBE; + gpioiev = &GPIO_GPIO0IEV; + break; + case 1: + gpiois = &GPIO_GPIO1IS; + gpioibe = &GPIO_GPIO1IBE; + gpioiev = &GPIO_GPIO1IEV; + break; + case 2: + gpiois = &GPIO_GPIO2IS; + gpioibe = &GPIO_GPIO2IBE; + gpioiev = &GPIO_GPIO2IEV; + break; + case 3: + gpiois = &GPIO_GPIO3IS; + gpioibe = &GPIO_GPIO3IBE; + gpioiev = &GPIO_GPIO3IEV; + break; + } + + if (gpioInterruptSense_Edge) + { + *gpiois &= ~(0x1<= MAX_TIMEOUT); +} + +/***************************************************************************** +** Function name: I2CInit +** +** Descriptions: Initialize I2C controller +** +** parameters: I2c mode is either MASTER or SLAVE +** Returned value: true or false, return false if the I2C +** interrupt handler was not installed correctly +** +*****************************************************************************/ +uint32_t i2cInit( uint32_t I2cMode ) +{ + SCB_PRESETCTRL |= (0x1<<1); + + // Enable I2C clock + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_I2C); + + // Configure pin 0.4 for SCL + IOCON_PIO0_4 &= ~(IOCON_PIO0_4_FUNC_MASK | IOCON_PIO0_4_I2CMODE_MASK); + IOCON_PIO0_4 |= (IOCON_PIO0_4_FUNC_I2CSCL); + + // Configure pin 0.5 for SDA + IOCON_PIO0_5 &= ~(IOCON_PIO0_5_FUNC_MASK | IOCON_PIO0_5_I2CMODE_MASK); + IOCON_PIO0_5 |= IOCON_PIO0_5_FUNC_I2CSDA; + + // Clear flags + I2C_I2CCONCLR = I2C_I2CCONCLR_AAC | + I2C_I2CCONCLR_SIC | + I2C_I2CCONCLR_STAC | + I2C_I2CCONCLR_I2ENC; + + // See p.128 for appropriate values for SCLL and SCLH +#if I2C_FAST_MODE_PLUS + IOCON_PIO0_4 |= (IOCON_PIO0_4_I2CMODE_FASTPLUSI2C); + IOCON_PIO0_5 |= (IOCON_PIO0_5_I2CMODE_FASTPLUSI2C); + I2C_I2CSCLL = I2C_SCLL_HS_SCLL; + I2C_I2CSCLH = I2C_SCLH_HS_SCLH; +#else + I2C_I2CSCLL = I2SCLL_SCLL; + I2C_I2CSCLH = I2SCLH_SCLH; +#endif + + if ( I2cMode == I2CSLAVE ) + { + I2C_I2CADR0 = SLAVE_ADDR; + } + + /* Enable the I2C Interrupt */ + NVIC_EnableIRQ(I2C_IRQn); + I2C_I2CCONSET = I2C_I2CCONSET_I2EN; + + return( TRUE ); +} + +/***************************************************************************** +** Function name: I2CEngine +** +** Descriptions: The routine to complete a I2C transaction +** from start to stop. All the intermitten +** steps are handled in the interrupt handler. +** Before this routine is called, the read +** length, write length and I2C master buffer +** need to be filled. +** +** parameters: None +** Returned value: Any of the I2CSTATE_... values. See i2c.h +** +*****************************************************************************/ +uint32_t i2cEngine( void ) +{ + I2CMasterState = I2CSTATE_IDLE; + RdIndex = 0; + WrIndex = 0; + if ( I2CStart() != TRUE ) + { + I2CStop(); + return ( FALSE ); + } + + /* wait until the state is a terminal state */ + while (I2CMasterState < 0x100); + + return ( I2CMasterState ); +} + +/****************************************************************************** +** End Of File +******************************************************************************/ + diff --git a/core/i2c/i2c.h b/core/i2c/i2c.h new file mode 100644 index 0000000..9ed49be --- /dev/null +++ b/core/i2c/i2c.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * i2c.h: Header file for NXP LPC11xx Family Microprocessors + * + * Copyright(C) 2006, NXP Semiconductor + * Parts of this code are (C) 2010, MyVoice CAD/CAM Services + * All rights reserved. + * + * History + * 2006.07.19 ver 1.00 Preliminary version, first Release + * 2010.07.19 ver 1.10 Rob Jansen - MyVoice CAD/CAM Services + * Updated to reflect new code + * +******************************************************************************/ +#ifndef __I2C_H +#define __I2C_H + +#include "projectconfig.h" + +/* + * These are states returned by the I2CEngine: + * + * IDLE - is never returned but only used internally + * PENDING - is never returned but only used internally in the I2C functions + * ACK - The transaction finished and the slave returned ACK (on all bytes) + * NACK - The transaction is aborted since the slave returned a NACK + * SLA_NACK - The transaction is aborted since the slave returned a NACK on the SLA + * this can be intentional (e.g. an 24LC08 EEPROM states it is busy) + * or the slave is not available/accessible at all. + * ARB_LOSS - Arbitration loss during any part of the transaction. + * This could only happen in a multi master system or could also + * identify a hardware problem in the system. + */ +#define I2CSTATE_IDLE 0x000 +#define I2CSTATE_PENDING 0x001 +#define I2CSTATE_ACK 0x101 +#define I2CSTATE_NACK 0x102 +#define I2CSTATE_SLA_NACK 0x103 +#define I2CSTATE_ARB_LOSS 0x104 + +#define FAST_MODE_PLUS 0 + +#define I2C_BUFSIZE 6 +#define MAX_TIMEOUT 0x00FFFFFF + +#define I2CMASTER 0x01 +#define I2CSLAVE 0x02 + +#define SLAVE_ADDR 0xA0 +#define READ_WRITE 0x01 + +#define RD_BIT 0x01 + +#define I2CONSET_I2EN 0x00000040 /* I2C Control Set Register */ +#define I2CONSET_AA 0x00000004 +#define I2CONSET_SI 0x00000008 +#define I2CONSET_STO 0x00000010 +#define I2CONSET_STA 0x00000020 + +#define I2CONCLR_AAC 0x00000004 /* I2C Control clear Register */ +#define I2CONCLR_SIC 0x00000008 +#define I2CONCLR_STAC 0x00000020 +#define I2CONCLR_I2ENC 0x00000040 + +#define I2DAT_I2C 0x00000000 /* I2C Data Reg */ +#define I2ADR_I2C 0x00000000 /* I2C Slave Address Reg */ +#define I2SCLH_SCLH 120 /* I2C SCL Duty Cycle High Reg */ +#define I2SCLL_SCLL 120 /* I2C SCL Duty Cycle Low Reg */ +#define I2SCLH_HS_SCLH 0x00000020 /* Fast Plus I2C SCL Duty Cycle High Reg */ +#define I2SCLL_HS_SCLL 0x00000020 /* Fast Plus I2C SCL Duty Cycle Low Reg */ + + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +extern void I2C_IRQHandler( void ); +extern uint32_t i2cInit( uint32_t I2cMode ); +extern uint32_t i2cEngine( void ); + +#endif /* end __I2C_H */ +/**************************************************************************** +** End Of File +*****************************************************************************/ diff --git a/core/iap/iap.c b/core/iap/iap.c new file mode 100644 index 0000000..dba85b1 --- /dev/null +++ b/core/iap/iap.c @@ -0,0 +1,57 @@ +/**************************************************************************/ +/*! + @file iap.c + Source: http://knowledgebase.nxp.com/showthread.php?t=1594 +*/ +/**************************************************************************/ +#include "iap.h" + +IAP_return_t iap_return; + +#define IAP_ADDRESS 0x1FFF1FF1 +uint32_t param_table[5]; + +/**************************************************************************/ +/*! + Sends the IAP command and stores the result +*/ +/**************************************************************************/ +void iap_entry(uint32_t param_tab[], uint32_t result_tab[]) +{ + void (*iap)(uint32_t[], uint32_t[]); + iap = (void (*)(uint32_t[], uint32_t[]))IAP_ADDRESS; + iap(param_tab,result_tab); +} + +/**************************************************************************/ +/*! + Returns the CPU's unique 128-bit serial number (4 words long) + + @section Example + + @code + #include "core/iap/iap.h" + + IAP_return_t iap_return; + iap_return = iapReadSerialNumber(); + + if (iap_return.ReturnCode == 0) + { + printf("Serial Number: %08X %08X %08X %08X %s", + iap_return.Result[0], + iap_return.Result[1], + iap_return.Result[2], + iap_return.Result[3], + CFG_PRINTF_NEWLINE); + } + @endcode +*/ +/**************************************************************************/ +IAP_return_t iapReadSerialNumber(void) +{ + // ToDo: Why does IAP sometime cause the application to halt when read??? + param_table[0] = IAP_CMD_READUID; + iap_entry(param_table,(uint32_t*)(&iap_return)); + return iap_return; +} + diff --git a/core/iap/iap.h b/core/iap/iap.h new file mode 100644 index 0000000..7bc643e --- /dev/null +++ b/core/iap/iap.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/*! + @file iap.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _IAP_H_ +#define _IAP_H_ + +#include "projectconfig.h" + +#define IAP_CMD_PREPARESECTORFORWRITE (50) +#define IAP_CMD_COPYRAMTOFLASH (51) +#define CAP_CMD_ERASESECTORS (52) +#define IAP_CMD_BLANKCHECKSECTOR (53) +#define IAP_CMD_READPARTID (54) +#define IAP_CMD_READBOOTCODEVERSION (55) +#define IAP_CMD_COMPARE (56) +#define IAP_CMD_REINVOKEISP (57) +#define IAP_CMD_READUID (58) + +typedef struct +{ + unsigned int ReturnCode; + unsigned int Result[4]; +} IAP_return_t; + +IAP_return_t iapReadSerialNumber(void); + +#endif \ No newline at end of file diff --git a/core/libc/ctype.c b/core/libc/ctype.c new file mode 100644 index 0000000..086065e --- /dev/null +++ b/core/libc/ctype.c @@ -0,0 +1,104 @@ +/* + * This file is part of the libpayload project. + * + * Copyright (C) 2008 Uwe Hermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +int isalpha(int c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + +int isascii(int c) +{ + return (c >= 0 && c <= 127); +} + +int isblank(int c) +{ + return (c == ' ' || c == '\t'); +} + +int iscntrl(int c) +{ + return (c <= 31 || c == 127); +} + +int isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +int isalnum(int c) +{ + return isalpha(c) || isdigit(c); +} + +int isgraph(int c) +{ + return (c >= 33 && c <= 126); +} + +int islower(int c) +{ + return (c >= 'a' && c <= 'z'); +} + +int isprint(int c) +{ + return (c >= 32 && c <= 126); +} + + +int isspace(int c) +{ + return (c == ' ' || (c >= '\t' || c <= '\r')); +} + +int isupper(int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +int tolower(int c) +{ + return (c >= 'A' && c <= 'Z') ? (c + 32) : c; +} + +int toupper(int c) +{ + return (c >= 'a' && c <= 'z') ? (c - 32) : c; +} + +int isxdigit(int c) +{ + return isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'z'); +} + +int ispunct(int c) +{ + return isprint(c) && !isspace(c) && !isalnum(c); +} diff --git a/core/libc/stdio.c b/core/libc/stdio.c new file mode 100644 index 0000000..84d7db8 --- /dev/null +++ b/core/libc/stdio.c @@ -0,0 +1,475 @@ +/* + * Software License Agreement (BSD License) + * + * Based on original stdio.c released by Atmel + * Copyright (c) 2008, Atmel Corporation + * All rights reserved. + * + * Modified by Roel Verdult, Copyright (c) 2010 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Local Definitions +//------------------------------------------------------------------------------ + +// Maximum string size allowed (in bytes). +#define MAX_STRING_SIZE 255 + +//------------------------------------------------------------------------------ +// Global Variables +//------------------------------------------------------------------------------ + +// Required for proper compilation. +//struct _reent r = {0, (FILE*) 0, (FILE*) 1, (FILE*) 0}; +//struct _reent *_impure_ptr = &r; + +//------------------------------------------------------------------------------ +// Local Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Writes a character inside the given string. Returns 1. +// \param pStr Storage string. +// \param c Character to write. +//------------------------------------------------------------------------------ +signed int append_char(char *pStr, char c) +{ + *pStr = c; + return 1; +} + +//------------------------------------------------------------------------------ +// Writes a string inside the given string. +// Returns the size of the written +// string. +// \param pStr Storage string. +// \param pSource Source string. +//------------------------------------------------------------------------------ +signed int PutString(char *pStr, char fill, signed int width, const char *pSource) +{ + signed int num = 0; + + while (*pSource != 0) { + + *pStr++ = *pSource++; + num++; + } + + width -= num; + while (width > 0) { + + *pStr++ = fill; + num++; + width--; + } + + return num; +} + +//------------------------------------------------------------------------------ +// Writes an unsigned int inside the given string, using the provided fill & +// width parameters. +// Returns the size in characters of the written integer. +// \param pStr Storage string. +// \param fill Fill character. +// \param width Minimum integer width. +// \param value Integer value. +//------------------------------------------------------------------------------ +signed int PutUnsignedInt( + char *pStr, + char fill, + signed int width, + unsigned int value) +{ + signed int num = 0; + + // Take current digit into account when calculating width + width--; + + // Recursively write upper digits + if ((value / 10) > 0) { + + num = PutUnsignedInt(pStr, fill, width, value / 10); + pStr += num; + } + // Write filler characters + else { + + while (width > 0) { + + append_char(pStr, fill); + pStr++; + num++; + width--; + } + } + + // Write lower digit + num += append_char(pStr, (value % 10) + '0'); + + return num; +} + +//------------------------------------------------------------------------------ +// Writes a signed int inside the given string, using the provided fill & width +// parameters. +// Returns the size of the written integer. +// \param pStr Storage string. +// \param fill Fill character. +// \param width Minimum integer width. +// \param value Signed integer value. +//------------------------------------------------------------------------------ +signed int PutSignedInt( + char *pStr, + char fill, + signed int width, + signed int value) +{ + signed int num = 0; + unsigned int absolute; + + // Compute absolute value + if (value < 0) { + + absolute = -value; + } + else { + + absolute = value; + } + + // Take current digit into account when calculating width + width--; + + // Recursively write upper digits + if ((absolute / 10) > 0) { + + if (value < 0) { + + num = PutSignedInt(pStr, fill, width, -(absolute / 10)); + } + else { + + num = PutSignedInt(pStr, fill, width, absolute / 10); + } + pStr += num; + } + else { + + // Reserve space for sign + if (value < 0) { + + width--; + } + + // Write filler characters + while (width > 0) { + + append_char(pStr, fill); + pStr++; + num++; + width--; + } + + // Write sign + if (value < 0) { + + num += append_char(pStr, '-'); + pStr++; + } + } + + // Write lower digit + num += append_char(pStr, (absolute % 10) + '0'); + + return num; +} + +//------------------------------------------------------------------------------ +// Writes an hexadecimal value into a string, using the given fill, width & +// capital parameters. +// Returns the number of char written. +// \param pStr Storage string. +// \param fill Fill character. +// \param width Minimum integer width. +// \param maj Indicates if the letters must be printed in lower- or upper-case. +// \param value Hexadecimal value. +//------------------------------------------------------------------------------ +signed int PutHexa( + char *pStr, + char fill, + signed int width, + unsigned char maj, + unsigned int value) +{ + signed int num = 0; + + // Decrement width + width--; + + // Recursively output upper digits + if ((value >> 4) > 0) { + + num += PutHexa(pStr, fill, width, maj, value >> 4); + pStr += num; + } + // Write filler chars + else { + + while (width > 0) { + + append_char(pStr, fill); + pStr++; + num++; + width--; + } + } + + // Write current digit + if ((value & 0xF) < 10) { + + append_char(pStr, (value & 0xF) + '0'); + } + else if (maj) { + + append_char(pStr, (value & 0xF) - 10 + 'A'); + } + else { + + append_char(pStr, (value & 0xF) - 10 + 'a'); + } + num++; + + return num; +} + +//------------------------------------------------------------------------------ +// Global Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Stores the result of a formatted string into another string. Format +/// arguments are given in a va_list instance. +/// Return the number of characters written. +/// \param pStr Destination string. +/// \param length Length of Destination string. +/// \param pFormat Format string. +/// \param ap Argument list. +//------------------------------------------------------------------------------ +signed int vsnprintf(char *pStr, size_t length, const char *pFormat, va_list ap) +{ + char fill; + unsigned char width; + signed int num = 0; + signed int size = 0; + + // Clear the string + if (pStr) { + + *pStr = 0; + } + + // Phase string + while (*pFormat != 0 && size < length) { + + // Normal character + if (*pFormat != '%') { + + *pStr++ = *pFormat++; + size++; + } + // Escaped '%' + else if (*(pFormat+1) == '%') { + + *pStr++ = '%'; + pFormat += 2; + size++; + } + // Token delimiter + else { + + fill = ' '; + width = 0; + pFormat++; + + // Parse filler + if (*pFormat == '0') { + + fill = '0'; + pFormat++; + } + + // Ignore justifier + if (*pFormat == '-') { + pFormat++; + } + + // Parse width + while ((*pFormat >= '0') && (*pFormat <= '9')) { + + width = (width*10) + *pFormat-'0'; + pFormat++; + } + + // Check if there is enough space + if (size + width > length) { + + width = length - size; + } + + // Parse type + switch (*pFormat) { + case 'd': + case 'i': num = PutSignedInt(pStr, fill, width, va_arg(ap, signed int)); break; + case 'u': num = PutUnsignedInt(pStr, fill, width, va_arg(ap, unsigned int)); break; + case 'x': num = PutHexa(pStr, fill, width, 0, va_arg(ap, unsigned int)); break; + case 'X': num = PutHexa(pStr, fill, width, 1, va_arg(ap, unsigned int)); break; + case 's': num = PutString(pStr, fill, width, va_arg(ap, char *)); break; + case 'c': num = append_char(pStr, va_arg(ap, unsigned int)); break; + default: + return EOF; + } + + pFormat++; + pStr += num; + size += num; + } + } + + // NULL-terminated (final \0 is not counted) + if (size < length) { + + *pStr = 0; + } + else { + + *(--pStr) = 0; + size--; + } + + return size; +} + +//------------------------------------------------------------------------------ +/// Stores the result of a formatted string into another string. Format +/// arguments are given in a va_list instance. +/// Return the number of characters written. +/// \param pString Destination string. +/// \param length Length of Destination string. +/// \param pFormat Format string. +/// \param ... Other arguments +//------------------------------------------------------------------------------ +signed int snprintf(char *pString, size_t length, const char *pFormat, ...) +{ + va_list ap; + signed int rc; + + va_start(ap, pFormat); + rc = vsnprintf(pString, length, pFormat, ap); + va_end(ap); + + return rc; +} + +//------------------------------------------------------------------------------ +/// Stores the result of a formatted string into another string. Format +/// arguments are given in a va_list instance. +/// Return the number of characters written. +/// \param pString Destination string. +/// \param pFormat Format string. +/// \param ap Argument list. +//------------------------------------------------------------------------------ +signed int vsprintf(char *pString, const char *pFormat, va_list ap) +{ + return vsnprintf(pString, MAX_STRING_SIZE, pFormat, ap); +} + +//------------------------------------------------------------------------------ +/// Outputs a formatted string on the DBGU stream. Format arguments are given +/// in a va_list instance. +/// \param pFormat Format string +/// \param ap Argument list. +//------------------------------------------------------------------------------ +signed int vprintf(const char *pFormat, va_list ap) +{ + char pStr[MAX_STRING_SIZE]; + char pError[] = "stdio.c: increase MAX_STRING_SIZE\r\n"; + + // Write formatted string in buffer + if (vsprintf(pStr, pFormat, ap) >= MAX_STRING_SIZE) { + + puts(pError); + while (1); // Increase MAX_STRING_SIZE + } + + // Display string + return puts(pStr); +} + +//------------------------------------------------------------------------------ +/// Outputs a formatted string on the DBGU stream, using a variable number of +/// arguments. +/// \param pFormat Format string. +//------------------------------------------------------------------------------ +signed int printf(const char *pFormat, ...) +{ + va_list ap; + signed int result; + + // Forward call to vprintf + va_start(ap, pFormat); + result = vprintf(pFormat, ap); + va_end(ap); + + return result; +} + + +//------------------------------------------------------------------------------ +/// Writes a formatted string inside another string. +/// \param pStr Storage string. +/// \param pFormat Format string. +//------------------------------------------------------------------------------ +signed int sprintf(char *pStr, const char *pFormat, ...) +{ + va_list ap; + signed int result; + + // Forward call to vsprintf + va_start(ap, pFormat); + result = vsprintf(pStr, pFormat, ap); + va_end(ap); + + return result; +} \ No newline at end of file diff --git a/core/libc/string.c b/core/libc/string.c new file mode 100644 index 0000000..3216009 --- /dev/null +++ b/core/libc/string.c @@ -0,0 +1,328 @@ +/* + * Software License Agreement (BSD License) + * + * Based on original stdio.c released by Atmel + * Copyright (c) 2008, Atmel Corporation + * All rights reserved. + * + * Modified by Roel Verdult, Copyright (c) 2010 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Global Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Copies data from a source buffer into a destination buffer. The two buffers +/// must NOT overlap. Returns the destination buffer. +/// \param pDestination Destination buffer. +/// \param pSource Source buffer. +/// \param num Number of bytes to copy. +//------------------------------------------------------------------------------ +void * memcpy(void *pDestination, const void *pSource, size_t num) +{ + unsigned char *pByteDestination; + unsigned char *pByteSource; + unsigned int *pAlignedSource = (unsigned int *) pSource; + unsigned int *pAlignedDestination = (unsigned int *) pDestination; + + // If num is more than 4 bytes, and both dest. and source are aligned, + // then copy dwords + if ((((unsigned int) pAlignedDestination & 0x3) == 0) + && (((unsigned int) pAlignedSource & 0x3) == 0) + && (num >= 4)) { + + while (num >= 4) { + + *pAlignedDestination++ = *pAlignedSource++; + num -= 4; + } + } + + // Copy remaining bytes + pByteDestination = (unsigned char *) pAlignedDestination; + pByteSource = (unsigned char *) pAlignedSource; + while (num--) { + + *pByteDestination++ = *pByteSource++; + } + + return pDestination; +} + +//------------------------------------------------------------------------------ +/// Fills a memory region with the given value. Returns a pointer to the +/// memory region. +/// \param pBuffer Pointer to the start of the memory region to fill +/// \param value Value to fill the region with +/// \param num Size to fill in bytes +//------------------------------------------------------------------------------ +void * memset(void *pBuffer, int value, size_t num) +{ + unsigned char *pByteDestination; + unsigned int *pAlignedDestination = (unsigned int *) pBuffer; + unsigned int alignedValue = (value << 24) | (value << 16) | (value << 8) | value; + + // Set words if possible + if ((((unsigned int) pAlignedDestination & 0x3) == 0) && (num >= 4)) { + while (num >= 4) { + *pAlignedDestination++ = alignedValue; + num -= 4; + } + } + // Set remaining bytes + pByteDestination = (unsigned char *) pAlignedDestination; + while (num--) { + *pByteDestination++ = value; + } + return pBuffer; +} + +void* memmove(void *s1, const void *s2, size_t n) +{ + char *s=(char*)s2, *d=(char*)s1; + + if(d > s){ + s+=n-1; + d+=n-1; + while(n){ + *d--=*s--; + n--; + } + }else if(d < s) + while(n){ + *d++=*s++; + n--; + } + return s1; +} + +int memcmp(const void *av, const void *bv, size_t len) +{ + const unsigned char *a = av; + const unsigned char *b = bv; + size_t i; + + for (i=0; i0 if 1st string > 2nd string +/// Return <0 if 1st string < 2nd string +/// \param pString1 Pointer to the start of the 1st string. +/// \param pString2 Pointer to the start of the 2nd string. +/// \param count Number of bytes that should be compared. +//----------------------------------------------------------------------------- +int strncmp(const char *pString1, const char *pString2, size_t count) +{ + int r; + + while(count) { + r = *pString1 - *pString2; + if (r == 0) { + if (*pString1 == 0) { + break; + } + pString1++; + pString2++; + count--; + continue; + } + return r; + } + return 0; +} + +//----------------------------------------------------------------------------- +/// Copy the first number of bytes from source string to destination string +/// Return the pointer to the destination string. +/// \param pDestination Pointer to the start of destination string. +/// \param pSource Pointer to the start of the source string. +/// \param count Number of bytes that should be copied. +//----------------------------------------------------------------------------- +char * strncpy(char *pDestination, const char *pSource, size_t count) +{ + char *pSaveDest = pDestination; + + while (count) { + *pDestination = *pSource; + if (*pSource == 0) { + break; + } + pDestination++; + pSource++; + count--; + } + return pSaveDest; +} + +// Following code is based on the BSD licensed code released by UoC +// Copyright (c) 1988 Regents of the University of California + +int strcmp(const char *s1, const char *s2) +{ + while (*s1 == *s2++) + if (*s1++ == 0) + return (0); + return (*(unsigned char *)s1 - *(unsigned char *)--s2); +} + +char *strtok(char *s, const char *delim) +{ + static char *last; + return strtok_r(s, delim, &last); +} + +char *strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/core/pmu/pmu.c b/core/pmu/pmu.c new file mode 100644 index 0000000..d90d228 --- /dev/null +++ b/core/pmu/pmu.c @@ -0,0 +1,419 @@ +/**************************************************************************/ +/*! + @file pmu.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Controls the power management features of the LPC1343, allowing you + to enter sleep/deep-sleep or deep power-down mode. + + For examples of how to enter either mode, see the comments for the + functions pmuSleep(), pmuDeepSleep() and pmuPowerDown(). + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "core/gpio/gpio.h" +#include "core/cpu/cpu.h" +#include "core/timer32/timer32.h" +#include "pmu.h" + +#ifdef CFG_CHIBI + #include "drivers/chibi/chb_drvr.h" +#endif +#define PMU_WDTCLOCKSPEED_HZ 7812 + +void pmuSetupHW(void); +void pmuRestoreHW(void); + + +/**************************************************************************/ +/*! + Wakeup interrupt handler +*/ +/**************************************************************************/ +void WAKEUP_IRQHandler(void) +{ + uint32_t regVal; + + // Reconfigure system clock/PLL + cpuPllSetup(CPU_MULTIPLIER_6); + + // Clear match bit on timer + TMR_TMR32B0EMR = 0; + + // Clear pending bits + SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK; + + // Clear SLEEPDEEP bit + SCB_SCR &= ~SCB_SCR_SLEEPDEEP; + + // Disable the deep sleep timer + TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED; + + /* This handler takes care of all the port pins if they + are configured as wakeup source. */ + regVal = SCB_STARTSRP0; + if (regVal != 0) + { + SCB_STARTRSRP0CLR = regVal; + } + + // Reconfigure CT32B0 + timer32Init(0, TIMER32_DEFAULTINTERVAL); + timer32Enable(0); + + // Perform peripheral specific and custom wakeup tasks + pmuRestoreHW(); + + /* See tracker for bug report. */ + __asm volatile ("NOP"); + + return; +} + +/**************************************************************************/ +/*! + Setup the clock for the watchdog timer. The default is 7.8125kHz. +*/ +/**************************************************************************/ +static void pmuWDTClockInit (void) +{ + /* Enable WDT clock */ + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC); + + /* Configure watchdog clock */ + /* Freq. = 0.5MHz, div = 64: WDT_OSC = 7.8125kHz */ + SCB_WDTOSCCTRL = SCB_WDTOSCCTRL_FREQSEL_0_5MHZ | + SCB_WDTOSCCTRL_DIVSEL_DIV64; + + // Switch main clock to WDT output + SCB_MAINCLKSEL = SCB_MAINCLKSEL_SOURCE_WDTOSC; + SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; // Update clock source + SCB_MAINCLKUEN = SCB_MAINCLKUEN_DISABLE; // Toggle update register once + SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; + + // Wait until the clock is updated + while (!(SCB_MAINCLKUEN & SCB_MAINCLKUEN_UPDATE)); +} + +/**************************************************************************/ +/*! + @brief Initialises the power management unit +*/ +/**************************************************************************/ +void pmuInit( void ) +{ + /* Enable all clocks, even those turned off at power up. */ + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC_MASK | + SCB_PDRUNCFG_SYSOSC_MASK | + SCB_PDRUNCFG_ADC_MASK); + + return; +} + +/**************************************************************************/ +/*! + @brief Puts select peripherals in sleep mode. + + This function will put the device into sleep mode. Most gpio pins + can be used to wake the device up, but the pins must first be + configured for this in pmuInit. + + @section Example + + @code + // Configure wakeup sources before going into sleep/deep-sleep. + // By default, pin 0.1 is configured as wakeup source (falling edge) + pmuInit(); + + // Enter sleep mode + pmuSleep(); + @endcode +*/ +/**************************************************************************/ +void pmuSleep() +{ + SCB_PDAWAKECFG = SCB_PDRUNCFG; + __asm volatile ("WFI"); + return; +} + +/**************************************************************************/ +/*! + @brief Turns off select peripherals and puts the device in deep-sleep + mode. + + The device can be configured to wakeup from deep-sleep mode after a + specified delay by supplying a non-zero value to the wakeupSeconds + parameter. This will configure CT32B0 to toggle pin 0.1 (CT32B0_MAT2) + after x seconds, waking the device up. The timer will be configured + to run off the WDT OSC while in deep-sleep mode, meaning that WDTOSC + should not be powered off (using the sleepCtrl parameter) when a + wakeup delay is specified. + + The sleepCtrl parameter is used to indicate which peripherals should + be put in sleep mode (see the SCB_PDSLEEPCFG register for details). + + @param[in] sleepCtrl + The bits to set in the SCB_PDSLEEPCFG register. This + controls which peripherals will be put in sleep mode. + @param[in] wakeupSeconds + The number of seconds to wait until the device will + wakeup. If you do not wish to wakeup after a specific + delay, enter a value of 0. + + @code + uint32_t pmuRegVal; + + // Initialise power management unit + pmuInit(); + + // Put peripherals into sleep mode + pmuRegVal = SCB_PDSLEEPCFG_IRCOUT_PD | + SCB_PDSLEEPCFG_IRC_PD | + SCB_PDSLEEPCFG_FLASH_PD | + SCB_PDSLEEPCFG_USBPLL_PD | + SCB_PDSLEEPCFG_SYSPLL_PD | + SCB_PDSLEEPCFG_SYSOSC_PD | + SCB_PDSLEEPCFG_ADC_PD | + SCB_PDSLEEPCFG_BOD_PD; + + // Enter deep sleep mode (wakeup after 5 seconds) + // By default, pin 0.1 is configured as wakeup source + pmuDeepSleep(pmuRegVal, 5); + @endcode +*/ +/**************************************************************************/ +void pmuDeepSleep(uint32_t sleepCtrl, uint32_t wakeupSeconds) +{ + // Setup the board for deep sleep mode, shutting down certain + // peripherals and remapping pins for lower power + pmuSetupHW(); + SCB_PDAWAKECFG = SCB_PDRUNCFG; + sleepCtrl |= (1 << 9) | (1 << 11); + SCB_PDSLEEPCFG = sleepCtrl; + SCB_SCR |= SCB_SCR_SLEEPDEEP; + + /* Configure system to run from WDT and set TMR32B0 for wakeup */ + if (wakeupSeconds > 0) + { + // Make sure WDTOSC isn't disabled in PDSLEEPCFG + SCB_PDSLEEPCFG &= ~(SCB_PDSLEEPCFG_WDTOSC_PD); + + // Disable 32-bit timer 0 if currently in use + TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED; + + // Disable internal pullup on 0.1 + gpioSetPullup(&IOCON_PIO0_1, gpioPullupMode_Inactive); + + /* Enable the clock for CT32B0 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B0); + + /* Configure 0.1 as Timer0_32 MAT2 */ + IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK; + IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CT32B0_MAT2; + + /* Set appropriate timer delay */ + TMR_TMR32B0MR0 = PMU_WDTCLOCKSPEED_HZ * wakeupSeconds; + + /* Configure match control register to raise an interrupt and reset on MR0 */ + TMR_TMR32B0MCR |= (TMR_TMR32B0MCR_MR0_INT_ENABLED | TMR_TMR32B0MCR_MR0_RESET_ENABLED); + + /* Configure external match register to set 0.1 high on match */ + TMR_TMR32B0EMR &= ~(0xFF<<4); // Clear EMR config bits + TMR_TMR32B0EMR |= TMR_TMR32B0EMR_EMC2_HIGH; // Set MAT2 (0.1) high on match + + /* Enable wakeup interrupts (any I/O pin can be used as a wakeup source) */ + //NVIC_EnableIRQ(WAKEUP0_IRQn); // P0.0 + NVIC_EnableIRQ(WAKEUP1_IRQn); // P0.1 (CT32B0_MAT2) + //NVIC_EnableIRQ(WAKEUP2_IRQn); // P0.2 + //NVIC_EnableIRQ(WAKEUP3_IRQn); // P0.3 + //NVIC_EnableIRQ(WAKEUP4_IRQn); // P0.4 + //NVIC_EnableIRQ(WAKEUP5_IRQn); // P0.5 + //NVIC_EnableIRQ(WAKEUP6_IRQn); // P0.6 + //NVIC_EnableIRQ(WAKEUP7_IRQn); // P0.7 + //NVIC_EnableIRQ(WAKEUP8_IRQn); // P0.8 + //NVIC_EnableIRQ(WAKEUP9_IRQn); // P0.9 + //NVIC_EnableIRQ(WAKEUP10_IRQn); // P0.10 + //NVIC_EnableIRQ(WAKEUP11_IRQn); // P0.11 + //NVIC_EnableIRQ(WAKEUP12_IRQn); // P1.0 + //NVIC_EnableIRQ(WAKEUP13_IRQn); // P1.1 + //NVIC_EnableIRQ(WAKEUP14_IRQn); // P1.2 + //NVIC_EnableIRQ(WAKEUP15_IRQn); // P1.3 + //NVIC_EnableIRQ(WAKEUP16_IRQn); // P1.4 + //NVIC_EnableIRQ(WAKEUP17_IRQn); // P1.5 + //NVIC_EnableIRQ(WAKEUP18_IRQn); // P1.6 + //NVIC_EnableIRQ(WAKEUP19_IRQn); // P1.7 + //NVIC_EnableIRQ(WAKEUP20_IRQn); // P1.8 + //NVIC_EnableIRQ(WAKEUP21_IRQn); // P1.9 + //NVIC_EnableIRQ(WAKEUP22_IRQn); // P1.10 + //NVIC_EnableIRQ(WAKEUP23_IRQn); // P1.11 + //NVIC_EnableIRQ(WAKEUP24_IRQn); // P2.0 + //NVIC_EnableIRQ(WAKEUP25_IRQn); // P2.1 + //NVIC_EnableIRQ(WAKEUP26_IRQn); // P2.2 + //NVIC_EnableIRQ(WAKEUP27_IRQn); // P2.3 + //NVIC_EnableIRQ(WAKEUP28_IRQn); // P2.4 + //NVIC_EnableIRQ(WAKEUP29_IRQn); // P2.5 + //NVIC_EnableIRQ(WAKEUP30_IRQn); // P2.6 + //NVIC_EnableIRQ(WAKEUP31_IRQn); // P2.7 + //NVIC_EnableIRQ(WAKEUP32_IRQn); // P2.8 + //NVIC_EnableIRQ(WAKEUP33_IRQn); // P2.9 + //NVIC_EnableIRQ(WAKEUP34_IRQn); // P2.10 + //NVIC_EnableIRQ(WAKEUP35_IRQn); // P2.11 + //NVIC_EnableIRQ(WAKEUP36_IRQn); // P3.0 + //NVIC_EnableIRQ(WAKEUP37_IRQn); // P3.1 + //NVIC_EnableIRQ(WAKEUP38_IRQn); // P3.2 + //NVIC_EnableIRQ(WAKEUP39_IRQn); // P3.3 + + /* Use RISING EDGE for wakeup detection. */ + SCB_STARTAPRP0 |= SCB_STARTAPRP0_APRPIO0_1; + + /* Clear all wakeup sources */ + SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK; + + /* Enable Port 0.1 as wakeup source. */ + SCB_STARTERP0 |= SCB_STARTERP0_ERPIO0_1; + + // Reconfigure clock to run from WDTOSC + pmuWDTClockInit(); + + /* Start the timer */ + TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_ENABLED; + } + + __asm volatile ("WFI"); + return; +} + +/**************************************************************************/ +/*! + @brief Puts the device in deep power-down mode. + + This function will configure the PMU control register and enter + deep power-down mode. Pre-determined values are stored in the four + general-purpose registers (PMU_GPREG0..3), which can be used to persist + any essential system settings while the device is in deep power-down + mode, so long as 3.3V is still available. + + @warning The only way to wake a device up from deep power-down mode + is to set a low-level on P1.4. If 3.3V power is lost, the + values stored in the four general-purpose registers will + also be lost. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "core/pmu/pmu.h" + + int main(void) + { + cpuInit(); + pmuInit(); + + // Enter power-down mode + pmuPowerDown(); + + while(1) + { + // Device was woken up by WAKEUP pin + } + } + @endcode +*/ +/**************************************************************************/ +void pmuPowerDown( void ) +{ + uint32_t regVal; + + // Make sure HW and external devices are in low power mode + pmuSetupHW(); + + if ( (PMU_PMUCTRL & ((0x1<<8) | (PMU_PMUCTRL_DPDFLAG))) != 0x0 ) + { + /* Check sleep and deep power down bits. If sleep and/or + deep power down mode are entered, clear the PCON bits. */ + regVal = PMU_PMUCTRL; + regVal |= ((0x1<<8) | + (PMU_PMUCTRL_DPDEN_SLEEP) | + (PMU_PMUCTRL_DPDFLAG)); + PMU_PMUCTRL = regVal; + + if ( (PMU_GPREG0 != 0x12345678)||(PMU_GPREG1 != 0x87654321) + ||(PMU_GPREG2 != 0x56781234)||(PMU_GPREG3 != 0x43218765) ) + { + while (1); + } + } + else + { + /* If in neither sleep nor deep-sleep mode, enter deep power down mode. */ + PMU_GPREG0 = 0x12345678; + PMU_GPREG1 = 0x87654321; + PMU_GPREG2 = 0x56781234; + PMU_GPREG3 = 0x43218765; + SCB_SCR |= SCB_SCR_SLEEPDEEP; + PMU_PMUCTRL = PMU_PMUCTRL_DPDEN_DEEPPOWERDOWN; + __asm volatile ("WFI"); + } + return; +} + +/**************************************************************************/ +/*! + @brief Configures parts and system peripherals to use lower power + before entering sleep mode +*/ +/**************************************************************************/ +void pmuSetupHW(void) +{ + #ifdef CFG_CHIBI + chb_sleep(TRUE); + #endif +} + +/**************************************************************************/ +/*! + @brief Restores parts and system peripherals to an appropriate + state after waking up from deep-sleep mode +*/ +/**************************************************************************/ +void pmuRestoreHW(void) +{ + #ifdef CFG_CHIBI + // Wakeup Chibi/Transceiver + chb_sleep(FALSE); + #endif +} diff --git a/core/pmu/pmu.h b/core/pmu/pmu.h new file mode 100644 index 0000000..58504e6 --- /dev/null +++ b/core/pmu/pmu.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/*! + @file pmu.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __PMU_H__ +#define __PMU_H__ + +#include "projectconfig.h" + +void WAKEUP_IRQHandler( void ); +void pmuInit( void ); +void pmuSleep( void ); +void pmuDeepSleep(uint32_t sleepCtrl, uint32_t wakeupSeconds); +void pmuPowerDown( void ); + +#endif diff --git a/core/pwm/pwm.c b/core/pwm/pwm.c new file mode 100644 index 0000000..b94329f --- /dev/null +++ b/core/pwm/pwm.c @@ -0,0 +1,254 @@ +/**************************************************************************/ +/*! + @file pwm.c + @author K. Townsend (microBuilder.eu) + + @brief Simple PWM example that can be used to control a motor, dim + an LED, etc. Uses 16-bit Timer 1 and P1.9 for PWM output. + + @section Example + + @code + #include "core/pwm/pwm.h" + ... + + // Initialises PWM output on 16-bit Timer 1 and + // sets MAT0 (P1.9) as output + pwmInit(); + + // Setup the pulse-width and duty-cycle + pwmSetDutyCycle(50); // Set 50% duty cycle + pwmSetFrequencyInMicroseconds(100); // 100 millisecond pulse width + + // Enable PWM output for exactly 50 pulses + pwmStartFixed(50); + + // Alternatively, enable PWM output indefinately + pwmStart(); + + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "pwm.h" + +uint32_t pwmPulseWidth = CFG_PWM_DEFAULT_PULSEWIDTH; +uint32_t pwmDutyCycle = CFG_PWM_DEFAULT_DUTYCYCLE; + +// pwmMaxPulses is used by TIMER16_1_IRQHandler to turn PWM off after +// a specified number of pulses have been sent. This only relevant when +// pwmStartFixed() is used. +volatile uint32_t pwmMaxPulses = 0; + +/**************************************************************************/ +/*! + Initialises 16-bit Timer 1, and configures the MAT0 output (pin 1.9) + to send the PWM output signal. +*/ +/**************************************************************************/ +void pwmInit(void) +{ + /* Enable the clock for CT16B1 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B1); + + /* Configure PIO1.9 as Timer1_16 MAT0 Output */ + /* Alternatively, PIO1.10 (MAT1) can also be used though + the initialisation code will need to be modified */ + IOCON_PIO1_9 &= ~IOCON_PIO1_9_FUNC_MASK; + IOCON_PIO1_9 |= IOCON_PIO1_9_FUNC_CT16B1_MAT0; + + /* Set default pulse width (MR3)*/ + TMR_TMR16B1MR3 = pwmPulseWidth; + + /* Set default duty cycle (MR0) */ + TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmDutyCycle)) / 100; + + /* Configure match control register to reset on MR3 */ + TMR_TMR16B1MCR = (TMR_TMR16B1MCR_MR3_RESET_ENABLED); + + /* External Match Register Settings for PWM */ + TMR_TMR16B1EMR = TMR_TMR16B1EMR_EMC0_TOGGLE | TMR_TMR16B1EMR_EM0; + + /* Disable Timer1 by default (enabled by pwmStart of pwmStartFixed) */ + TMR_TMR16B1TCR &= ~TMR_TMR16B1TCR_COUNTERENABLE_MASK; + + /* Enable PWM0 and PWM3 */ + TMR_TMR16B1PWMC = TMR_TMR16B1PWMC_PWM0_ENABLED | TMR_TMR16B1PWMC_PWM3_ENABLED; + + /* Make sure that the timer interrupt is enabled */ + NVIC_EnableIRQ(TIMER_16_1_IRQn); +} + +/**************************************************************************/ +/*! + Starts the PWM output +*/ +/**************************************************************************/ +void pwmStart(void) +{ + /* Disable interrupt on MR3 in case it was enabled by pwmStartFixed() */ + TMR_TMR16B1MCR &= ~(TMR_TMR16B1MCR_MR3_INT_MASK); + + /* Enable Timer1 */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; +} + +/**************************************************************************/ +/*! + Stops the PWM output +*/ +/**************************************************************************/ +void pwmStop(void) +{ + /* Disable Timer1 */ + TMR_TMR16B1TCR &= ~(TMR_TMR16B1TCR_COUNTERENABLE_MASK); +} + +/**************************************************************************/ +/*! + Starts the PWM output, and stops after the specified number of + pulses. + + @param[in] pulses + The number of pulses to generate before disabling the + PWM output. The output is actually disabled in the + timer ISR. + + @warning The PWM output is actually stopped inside the 16-bit + timer ISR in "core/timer16/timer16.h". +*/ +/**************************************************************************/ +void pwmStartFixed(uint32_t pulses) +{ + pwmMaxPulses = pulses; + + /* Configure match control register to also raise an interrupt on MR3 */ + TMR_TMR16B1MCR |= (TMR_TMR16B1MCR_MR3_INT_ENABLED); + + /* Enable Timer1 (it will eventually be disabled in the ISR) */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; +} + +/**************************************************************************/ +/*! + Sets the signal's duty cycle in percent (1-100). + + @param[in] percentage + The duty-cycle in percentage (the amount of time that + the signal is 'high' relative to the time its 'low'). + + @returns -1 if an invalid percentage was supplied. Value must be + between 1 and 100. +*/ +/**************************************************************************/ +int pwmSetDutyCycle(uint32_t percentage) +{ + if ((percentage < 1) || (percentage > 100)) + { + /* Duty Cycle must be a value between 1 and 100 */ + return -1; + } + + /* Set Duty Cycle (MR0) */ + TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - (pwmDutyCycle = percentage))) / 100; + + return 0; +} + +/**************************************************************************/ +/*! + Sets the signal's frequency/pulse-width to the specified number + of ticks. + + @param[in] ticks + The duration in clock ticks of each full pulse. + + @returns -1 if a zero-value was provided for ticks. +*/ +/**************************************************************************/ +int pwmSetFrequencyInTicks(uint16_t ticks) +{ + if (ticks < 1) + { + return -1; + } + + /* Set Pulse Width (MR3)*/ + TMR_TMR16B1MR3 = (pwmPulseWidth = ticks); + + /* Adjust Duty Cycle (MR0) */ + TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmDutyCycle)) / 100; + + return 0; +} + +/**************************************************************************/ +/*! + Sets the signal's frequency/pulse-width to the specified number + of microseconds. + + @param[in] us + The duration in microseconds of each full pulse. + + @returns -1 if the supplied value exceeds the limits of the 16-bit + timer, or if a zero-value was provided. + + @Warning Because a 16-bit timer is used here by default, the + maximum frequency is quite small. Running at 36MHz, the + largest possible pulse-width/frequency is ~1,82mS or + 1820 microSeconds. At 12MHz its 5461 uS, and at 48MHz + its 1365 uS. +*/ +/**************************************************************************/ +int pwmSetFrequencyInMicroseconds(uint16_t us) +{ + if (us < 1) + { + return -1; + } + + uint32_t ticks = (((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 1000000) * us); + if (ticks > 0xFFFF) + { + /* Delay exceeds the upper limit for the 16-bit timer */ + return -1; + } + + /* Set Pulse Width (MR3)*/ + TMR_TMR16B1MR3 = (pwmPulseWidth = ticks); + + /* Adjust Duty Cycle (MR0) */ + TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmDutyCycle)) / 100; + + return 0; +} + + diff --git a/core/pwm/pwm.h b/core/pwm/pwm.h new file mode 100644 index 0000000..e03214f --- /dev/null +++ b/core/pwm/pwm.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/*! + @file pwm.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _PWM_H_ +#define _PWM_H_ + +#include "projectconfig.h" + +void pwmInit( void ); +void pwmStart( void ); +void pwmStop( void ); +void pwmStartFixed( uint32_t pulses ); +int pwmSetDutyCycle( uint32_t percentage ); +int pwmSetFrequencyInTicks( uint16_t ticks ); +int pwmSetFrequencyInMicroseconds(uint16_t us ); + +#endif \ No newline at end of file diff --git a/core/pwm/pwm_100us_50percent.png b/core/pwm/pwm_100us_50percent.png new file mode 100644 index 0000000000000000000000000000000000000000..efed5f4fb97916ba7f0e859a1a869dc207b50943 GIT binary patch literal 4984 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 XoT9mhiw4d@RFun5&rG*M8yWpCoq##` literal 0 HcmV?d00001 diff --git a/core/rom_drivers.h b/core/rom_drivers.h new file mode 100644 index 0000000..f1c4184 --- /dev/null +++ b/core/rom_drivers.h @@ -0,0 +1,58 @@ +/**************************************************************************/ +/*! + @file rom_drivers.h + @author NXP Semiconductor +*/ +/**************************************************************************/ + +#ifndef ROM_DRIVERS_H_ +#define ROM_DRIVERS_H_ + +#include "sysdefs.h" + +typedef struct _USB_DEVICE_INFO +{ + uint16_t DevType; + uint32_t DevDetailPtr; +} USB_DEV_INFO; + +typedef struct _USBD +{ + void (*init_clk_pins)(void); + void (*isr)(void); + void (*init)( USB_DEV_INFO * DevInfoPtr ); + void (*connect)(uint32_t con); +} USBD; + +typedef struct _ROM +{ + const USBD * pUSBD; +} ROM; + +typedef struct _MSC_DEVICE_INFO { + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint32_t StrDescPtr; + uint32_t MSCInquiryStr; + uint32_t BlockCount; + uint32_t BlockSize; + uint32_t MemorySize; + void (*MSC_Write)( uint32_t offset, uint8_t src[], uint32_t length); + void (*MSC_Read)( uint32_t offset, uint8_t dst[], uint32_t length); +} MSC_DEVICE_INFO; + +typedef struct _HID_DEVICE_INFO +{ + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint32_t StrDescPtr; + uint8_t InReportCount; + uint8_t OutReportCount; + uint8_t SampleInterval; + void (*InReport)(uint8_t src[], uint32_t length); + void (*OutReport)(uint8_t dst[], uint32_t length); +} HID_DEVICE_INFO; + +#endif diff --git a/core/ssp/ssp.c b/core/ssp/ssp.c new file mode 100644 index 0000000..284945c --- /dev/null +++ b/core/ssp/ssp.c @@ -0,0 +1,299 @@ +/**************************************************************************/ +/*! + @file ssp.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Generic code for SSP/SPI communications. By default, the SSP block + is initialised in SPI master mode. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "core/ssp/ssp.h" + ... + cpuInit(); + sspInit(0, sspClockPolarity_High, sspClockPhase_RisingEdge); + ... + uint8_t request[SSP_FIFOSIZE]; + uint8_t response[SSP_FIFOSIZE]; + + // Send 0x9C to the slave device and wait for a response + request[0] = 0x80 | 0x1C; + // Toggle the select pin + ssp0Select(); + // Send 1 byte from the request buffer + sspSend(0, (uint8_t *)&request, 1); + // Receive 1 byte into the response buffer + sspReceive(0, (uint8_t *)&response, 1); + // Reset the select pin + ssp0Deselect(); + // Print the results + debug_printf("Ox%x ", response[0]); + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "ssp.h" +#include "core/gpio/gpio.h" + +/* Statistics for all interrupts */ +volatile uint32_t interruptRxStat = 0; +volatile uint32_t interruptOverRunStat = 0; +volatile uint32_t interruptRxTimeoutStat = 0; + +/**************************************************************************/ +/*! + @brief SSP0 interrupt handler for SPI communication + + The algorithm is, if RXFIFO is at least half full, + start receive until it's empty; if TXFIFO is at least + half empty, start transmit until it's full. + This will maximize the use of both FIFOs and performance. +*/ +/**************************************************************************/ +void SSP_IRQHandler (void) +{ + uint32_t regValue; + + regValue = SSP_SSP0MIS; + + /* Check for overrun interrupt */ + if ( regValue & SSP_SSP0MIS_RORMIS_FRMRCVD ) + { + interruptOverRunStat++; + SSP_SSP0ICR = SSP_SSP0ICR_RORIC_CLEAR; // Clear interrupt + } + + /* Check for timeout interrupt */ + if ( regValue & SSP_SSP0MIS_RTMIS_NOTEMPTY ) + { + interruptRxTimeoutStat++; + SSP_SSP0ICR = SSP_SSP0ICR_RTIC_CLEAR; // Clear interrupt + } + + /* Check if Rx buffer is at least half-full */ + if ( regValue & SSP_SSP0MIS_RXMIS_HALFFULL ) + { + // ToDo: Receive until it's empty + interruptRxStat++; + } + return; +} + +/**************************************************************************/ +/*! + @brief Initialises the SSP0 port + + By default, SSP0 is set to SPI frame-format with 8-bit data. Pin 2.11 + is routed to serve as serial clock (SCK), and SSEL (0.2) is set to + GPIO to allow manual control of when the SPI port is enabled or + disabled. Overrun and timeout interrupts are both enabled. + + @param[in] portNum + The SPI port to use (0..1) + @param[in] polarity + Indicates whether the clock should be held high + (sspClockPolarity_High) or low (sspClockPolarity_Low) + when inactive. + @param[in] phase + Indicates whether new bits start on the leading + (sspClockPhase_RisingEdge) or falling + (sspClockPhase_FallingEdge) edge of clock transitions. + + @note sspSelect() and sspDeselect() macros have been defined in + ssp.h to control the SSEL line without having to know the + specific pin being used. +*/ +/**************************************************************************/ +void sspInit (uint8_t portNum, sspClockPolarity_t polarity, sspClockPhase_t phase) +{ + gpioInit(); + + if (portNum == 0) + { + /* Reset SSP */ + SCB_PRESETCTRL &= ~SCB_PRESETCTRL_SSP0_MASK; + SCB_PRESETCTRL |= SCB_PRESETCTRL_SSP0_RESETDISABLED; + + /* Enable AHB clock to the SSP domain. */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_SSP0); + + /* Divide by 1 (SSPCLKDIV also enables to SSP CLK) */ + SCB_SSP0CLKDIV = SCB_SSP0CLKDIV_DIV1; + + /* Set P0.8 to SSP MISO */ + IOCON_PIO0_8 &= ~IOCON_PIO0_8_FUNC_MASK; + IOCON_PIO0_8 |= IOCON_PIO0_8_FUNC_MISO0; + + /* Set P0.9 to SSP MOSI */ + IOCON_PIO0_9 &= ~IOCON_PIO0_9_FUNC_MASK; + IOCON_PIO0_9 |= IOCON_PIO0_9_FUNC_MOSI0; + + /* Set 2.11 to SSP SCK (0.6 and 0.10 can also be used) */ + #ifdef CFG_SSP0_SCKPIN_2_11 + IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO2_11; + IOCON_PIO2_11 = IOCON_PIO2_11_FUNC_SCK0; + #endif + + /* Set 0.6 to SSP SCK (2.11 and 0.10 can also be used) */ + #ifdef CFG_SSP0_SCKPIN_0_6 + IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO0_6; + IOCON_PIO0_6 = IOCON_PIO0_6_FUNC_SCK; + #endif + + /* Set P0.2/SSEL to GPIO output and high */ + IOCON_PIO0_2 &= ~IOCON_PIO0_2_FUNC_MASK; + IOCON_PIO0_2 |= IOCON_PIO0_2_FUNC_GPIO; + gpioSetDir(SSP0_CSPORT, SSP0_CSPIN, 1); + gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 1); + gpioSetPullup(&IOCON_PIO0_2, gpioPullupMode_Inactive); // Board has external pull-up + + /* If SSP0CLKDIV = DIV1 -- (PCLK / (CPSDVSR × [SCR+1])) = (72,000,000 / (2 x [8 + 1])) = 4.0 MHz */ + uint32_t configReg = ( SSP_SSP0CR0_DSS_8BIT // Data size = 8-bit + | SSP_SSP0CR0_FRF_SPI // Frame format = SPI + | SSP_SSP0CR0_SCR_8); // Serial clock rate = 8 + + // Set clock polarity + if (polarity == sspClockPolarity_High) + configReg |= SSP_SSP0CR0_CPOL_HIGH; // Clock polarity = High between frames + else + configReg &= ~SSP_SSP0CR0_CPOL_MASK; // Clock polarity = Low between frames + + // Set edge transition + if (phase == sspClockPhase_FallingEdge) + configReg |= SSP_SSP0CR0_CPHA_SECOND; // Clock out phase = Trailing edge clock transition + else + configReg &= ~SSP_SSP0CR0_CPHA_MASK; // Clock out phase = Leading edge clock transition + + // Assign config values to SSP0CR0 + SSP_SSP0CR0 = configReg; + + /* Clock prescale register must be even and at least 2 in master mode */ + SSP_SSP0CPSR = SSP_SSP0CPSR_CPSDVSR_DIV2; + + /* Clear the Rx FIFO */ + uint8_t i, Dummy=Dummy; + for ( i = 0; i < SSP_FIFOSIZE; i++ ) + { + Dummy = SSP_SSP0DR; + } + + /* Enable the SSP Interrupt */ + NVIC_EnableIRQ(SSP_IRQn); + + /* Set SSPINMS registers to enable interrupts + * enable all error related interrupts */ + SSP_SSP0IMSC = ( SSP_SSP0IMSC_RORIM_ENBL // Enable overrun interrupt + | SSP_SSP0IMSC_RTIM_ENBL); // Enable timeout interrupt + + /* Enable device and set it to master mode, no loopback */ + SSP_SSP0CR1 = SSP_SSP0CR1_SSE_ENABLED | SSP_SSP0CR1_MS_MASTER | SSP_SSP0CR1_LBM_NORMAL; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Sends a block of data to the SSP0 port + + @param[in] portNum + The SPI port to use (0..1) + @param[in] buf + Pointer to the data buffer + @param[in] length + Block length of the data buffer +*/ +/**************************************************************************/ +void sspSend (uint8_t portNum, uint8_t *buf, uint32_t length) +{ + uint32_t i; + uint8_t Dummy = Dummy; + + if (portNum == 0) + { + for (i = 0; i < length; i++) + { + /* Move on only if NOT busy and TX FIFO not full. */ + while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_NOTFULL | SSP_SSP0SR_BSY_BUSY)) != SSP_SSP0SR_TNF_NOTFULL); + SSP_SSP0DR = *buf; + buf++; + + while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY ); + /* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO + on MISO. Otherwise, when SSP0Receive() is called, previous data byte + is left in the FIFO. */ + Dummy = SSP_SSP0DR; + } + } + + return; +} + +/**************************************************************************/ +/*! + @brief Receives a block of data from the SSP0 port + + @param[in] portNum + The SPI port to use (0..1) + @param[in] buf + Pointer to the data buffer + @param[in] length + Block length of the data buffer +*/ +/**************************************************************************/ +void sspReceive(uint8_t portNum, uint8_t *buf, uint32_t length) +{ + uint32_t i; + + if (portNum == 0) + { + for ( i = 0; i < length; i++ ) + { + /* As long as the receive FIFO is not empty, data can be received. */ + SSP_SSP0DR = 0xFF; + + /* Wait until the Busy bit is cleared */ + while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY ); + + *buf = SSP_SSP0DR; + buf++; + } + } + + return; +} + diff --git a/core/ssp/ssp.h b/core/ssp/ssp.h new file mode 100644 index 0000000..3deab4e --- /dev/null +++ b/core/ssp/ssp.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/*! + @file ssp.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _SSP_H_ +#define _SSP_H_ + +#include "projectconfig.h" +#include "core/gpio/gpio.h" + +#define SSP_FIFOSIZE 8 /* SPI read and write buffer size */ +#define SSP_MAX_TIMEOUT 0xFF + +#define SSP0_CSPORT 0 +#define SSP0_CSPIN 2 + +/* Macro definitions to enable and disable SPI */ +#define ssp0Select() do {gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 0);} while (0) +#define ssp0Deselect() do {gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 1);} while (0) + +/**************************************************************************/ +/*! + Indicates whether the clock should be high or low between frames. +*/ +/**************************************************************************/ +typedef enum sspClockPolarity_e +{ + sspClockPolarity_Low = 0, + sspClockPolarity_High +} +sspClockPolarity_t; + +/**************************************************************************/ +/*! + Indicates whether the bits start at the rising or falling edge of + the clock transition. +*/ +/**************************************************************************/ +typedef enum sspClockPhase_e +{ + sspClockPhase_RisingEdge = 0, + sspClockPhase_FallingEdge +} +sspClockPhase_t; + +extern void SSP_IRQHandler (void); +void sspInit (uint8_t portNum, sspClockPolarity_t polarity, sspClockPhase_t phase); +void sspSend (uint8_t portNum, uint8_t *buf, uint32_t length); +void sspReceive (uint8_t portNum, uint8_t *buf, uint32_t length); + +#endif diff --git a/core/systick/systick.c b/core/systick/systick.c new file mode 100644 index 0000000..ebf50b8 --- /dev/null +++ b/core/systick/systick.c @@ -0,0 +1,229 @@ +/**************************************************************************/ +/*! + @file systick.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Controls the 24-bit 'system tick' clock, which can be used as a + generic timer or to control time sharing with an embedded real-time + operating system (such as FreeRTOS). + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "core/systick/systick.h" + + void main (void) + { + cpuInit(); + + // Start systick timer with one tick every 10ms + systickInit(10); + + while(1) + { + } + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "systick.h" + +#ifdef CFG_SDCARD +#include "drivers/fatfs/diskio.h" +volatile uint32_t fatTicks = 0; +#endif + +volatile uint32_t systickTicks = 0; // 1ms tick counter +volatile uint32_t systickRollovers = 0; + +/**************************************************************************/ +/*! + @brief Systick interrupt handler +*/ +/**************************************************************************/ +void SysTick_Handler (void) +{ + systickTicks++; + + // Increment rollover counter + if (systickTicks == 0xFFFFFFFF) systickRollovers++; + + #ifdef CFG_SDCARD + fatTicks++; + if (fatTicks == 10) + { + fatTicks = 0; + disk_timerproc(); + } + #endif +} + +/**************************************************************************/ +/*! + @brief Configures the systick timer + + @param[in] ticks + The number of clock cycles between each tick of the + systick timer. for example, 'CFG_CPU_CCLK / 1000' = + 1 millisecond. This value must not exceed 0x00FFFFFF. +*/ +/**************************************************************************/ +static uint32_t systickConfig(uint32_t ticks) +{ + // Check if 'ticks' is greater than maximum value + if (ticks > SYSTICK_STRELOAD_MASK) + { + return (1); + } + + // Reset counter + systickTicks = 0; + + // Set reload register + SYSTICK_STRELOAD = (ticks & SYSTICK_STRELOAD_MASK) - 1; + + // Load the systick counter value + SYSTICK_STCURR = 0; + + // Enable systick IRQ and timer + SYSTICK_STCTRL = SYSTICK_STCTRL_CLKSOURCE | + SYSTICK_STCTRL_TICKINT | + SYSTICK_STCTRL_ENABLE; + + return (0); +} + +/**************************************************************************/ +/*! + @brief Initialises the systick timer + + @param[in] delayMs + The number of milliseconds between each tick of the systick + timer. + + @note The shortest possible delay is 1 millisecond, which will + allow fine grained delays, but will cause more load on the + system than a 10mS delay. The resolution of the systick + timer needs to be balanced with the amount of processing + time you can spare. The delay should really only be set + to 1 mS if you genuinely have a need for 1mS delays, + otherwise a higher value like 5 or 10 mS is probably + more appropriate. +*/ +/**************************************************************************/ +void systickInit (uint32_t delayMs) +{ + systickConfig ((CFG_CPU_CCLK / 1000) * delayMs); +} + +/**************************************************************************/ +/*! + @brief Causes a blocking delay for 'delayTicks' ticks on the + systick timer. For example: systickDelay(100) would cause + a blocking delay for 100 ticks of the systick timer. + + @param[in] delayTicks + The number of systick ticks to cause a blocking delay for + + @Note This function takes into account the fact that the tick + counter may eventually roll over to 0 once it reaches + 0xFFFFFFFF. +*/ +/**************************************************************************/ +void systickDelay (uint32_t delayTicks) +{ + uint32_t curTicks; + curTicks = systickTicks; + + // Make sure delay is at least 1 tick in case of division, etc. + if (delayTicks == 0) delayTicks = 1; + + if (curTicks > 0xFFFFFFFF - delayTicks) + { + // Rollover will occur during delay + while (systickTicks >= curTicks) + { + while (systickTicks < (delayTicks - (0xFFFFFFFF - curTicks))); + } + } + else + { + while ((systickTicks - curTicks) < delayTicks); + } +} + +/**************************************************************************/ +/*! + @brief Returns the current value of the systick timer counter. + This value is incremented by one every time an interrupt + fires for the systick timer. +*/ +/**************************************************************************/ +uint32_t systickGetTicks(void) +{ + return systickTicks; +} + +/**************************************************************************/ +/*! + @brief Returns the current value of the systick timer rollover + counter. This value is incremented by one every time the + tick counter rolls over from 0xFFFFFFFF to 0. +*/ +/**************************************************************************/ +uint32_t systickGetRollovers(void) +{ + return systickRollovers; +} + +/**************************************************************************/ +/*! + @brief Returns the approximate number of seconds that the + systick timer has been running. +*/ +/**************************************************************************/ +uint32_t systickGetSecondsActive(void) +{ + uint32_t currentTick = systickTicks; + uint32_t rollovers = systickRollovers; + uint32_t secsActive = currentTick / (1000 / CFG_SYSTICK_DELAY_IN_MS); + secsActive += rollovers * (0xFFFFFFFF / (1000 / CFG_SYSTICK_DELAY_IN_MS)); + + return secsActive; +} + diff --git a/core/systick/systick.h b/core/systick/systick.h new file mode 100644 index 0000000..a184a3d --- /dev/null +++ b/core/systick/systick.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/*! + @file systick.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _SYSTICK_H_ +#define _SYSTICK_H_ + +#include "projectconfig.h" + +void systickInit (uint32_t delayMs); +void systickDelay (uint32_t delayTicks); +uint32_t systickGetTicks(void); +uint32_t systickGetRollovers(void); +uint32_t systickGetSecondsActive(void); + +#endif \ No newline at end of file diff --git a/core/timer16/timer16.c b/core/timer16/timer16.c new file mode 100644 index 0000000..38fca82 --- /dev/null +++ b/core/timer16/timer16.c @@ -0,0 +1,505 @@ +/**************************************************************************/ +/*! + @file timer16.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Generic code for both 16-bit timers. + + @warning 16-bit timers are limited to roughly ~0.91ms (or 910uS) on + a system running at 72MHz since: + @code + 1 mS = CFG_CPU_CCLK / 1000 + = 72000000 / 1000 + = 72000 'ticks' + @endcode + Meaning that 0xFFFF (65535) 'ticks' = 0.910208 milliseconds + or 910 microseconds. + + @section Example + + @code + #include "/core/cpu/cpu.h" + #include "/core/timer16/timer16.h" + + // Instantiated in timer16.h + extern volatile uint32_t timer16_0_counter; + ... + cpuInit(); + + // Initialise timer0 with a delay of 0xFFFF, which will cause the + // timer interrupt to fire every 65535 ticks and increment + // timer16_0_counter by 1 + timer16Init(0, 0xFFFF); + + // Enable the timer + timer16Enable(0); + + // At this point timer16_0_counter should start incrementing by 1 + // every 65535 ticks + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "timer16.h" + +volatile uint32_t timer16_0_counter = 0; +volatile uint32_t timer16_1_counter = 0; + +#ifdef CFG_PWM + volatile uint32_t pwmCounter = 0; + extern volatile uint32_t pwmMaxPulses; // See drivers/pwm/pwm.c +#endif + +/**************************************************************************/ +/*! + @brief Causes a blocking delay for the specified number of + clock ticks. + + @note The exact duration of this delay depends on the speed of the + system clock, but it will invariably be short because of the + 16-bit limitation. For example, on a system with a 72MHz + clock, a 1mS delay would be equal to 72,000 ticks, which is + already above the maximum 16-bit value of 65,535. Thus, the + maximum delay measured in mS with a 72MHz clock is ~0.91mS. + + @param[in] timerNum + The 16-bit timer to user (0..1) + @param[in] delayInTicks + The number of clock ticks to delay (0..65534) + + @section Example + + @code + #include "/core/cpu/cpu.h" + #include "/core/timer16/timer16.h" + + int main(void) + { + cpuInit(); + + // Initialise timer 0 ... delay is provided but not used here + timer16Init(0, 0xFFFF); + + // Enable the timer + timer16Enable(0); + + while(1) + { + // Cause blocking delay for 36000 ticks (0.5mS @ 72MHz) + // Note: The delay must be 65534 or less (16-bit value) + timer16DelayTicks(0, 36000); + } + } + @endcode +*/ +/**************************************************************************/ +void timer16DelayTicks(uint8_t timerNum, uint16_t delayInTicks) +{ + // ToDo: Verify incoming value + + if (timerNum == 0) + { + /* Reset the timer */ + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERRESET_ENABLED; + + /* Set the prescaler to zero */ + TMR_TMR16B0PR = 0x00; + + TMR_TMR16B0MR0 = delayInTicks; + + /* Reset all interrupts */ + TMR_TMR16B0IR = TMR_TMR16B0IR_MASK_ALL; + + /* Stop timer on match (MR0) */ + TMR_TMR16B0MCR = TMR_TMR16B0MCR_MR0_STOP_ENABLED; + + /* Start timer */ + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; + + /* Wait until the delay time has elapsed */ + while (TMR_TMR16B0TCR & TMR_TMR16B0TCR_COUNTERENABLE_ENABLED); + } + + else if (timerNum == 1) + { + /* Reset the timer */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERRESET_ENABLED; + + /* Set the prescaler to zero */ + TMR_TMR16B1PR = 0x00; + + TMR_TMR16B1MR0 = delayInTicks; + + /* Reset all interrupts */ + TMR_TMR16B1IR = TMR_TMR16B1IR_MASK_ALL; + + /* Stop timer on match (MR0) */ + TMR_TMR16B1MCR = TMR_TMR16B1MCR_MR0_STOP_ENABLED; + + /* Start timer */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; + + /* Wait until the delay time has elapsed */ + while (TMR_TMR16B1TCR & TMR_TMR16B1TCR_COUNTERENABLE_ENABLED); + } + + return; +} + +/**************************************************************************/ +/*! + @brief Causes a blocking delay for the specified number of + microseconds + + @warning The maximum delay in uS will depend on the clock speed, + but running at 72MHz the maximum delay (MR = 0xFFFF) + would be 910uS (0xFFFF / 72 = 910), or 0.91 milliseconds. + + @param[in] timerNum + The 16-bit timer to user (0..1) + @param[in] delayInUs + The number of microseconds to wait + + @section Example + + @code + #include "/core/cpu/cpu.h" + #include "/core/timer16/timer16.h" + + int main(void) + { + cpuInit(); + + // Initialise timer 0 ... delay is provided but not used here + timer16Init(0, 0xFFFF); + + // Enable the timer + timer16Enable(0); + + while(1) + { + // Cause blocking delay for 500 microseconds (0.5mS) + timer16DelayUS(0, 500); + } + } + @endcode +*/ +/**************************************************************************/ +void timer16DelayUS(uint8_t timerNum, uint16_t delayInUS) +{ + // ToDo: Check if the appropriate timer is enabled first? + + if (timerNum == 0) + { + /* Reset the timer */ + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERRESET_ENABLED; + + /* Set the prescaler to zero */ + TMR_TMR16B0PR = 0x00; + + TMR_TMR16B0MR0 = delayInUS * ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV)/1000000); + + /* Reset all interrupts */ + TMR_TMR16B0IR = TMR_TMR16B0IR_MASK_ALL; + + /* Stop timer on match (MR0) */ + TMR_TMR16B0MCR = TMR_TMR16B0MCR_MR0_STOP_ENABLED; + + /* Start timer */ + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; + + /* Wait until the delay time has elapsed */ + while (TMR_TMR16B0TCR & TMR_TMR16B0TCR_COUNTERENABLE_ENABLED); + } + + else if (timerNum == 1) + { + /* Reset the timer */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERRESET_ENABLED; + + /* Set the prescaler to zero */ + TMR_TMR16B1PR = 0x00; + + TMR_TMR16B1MR0 = delayInUS * ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV)/1000000); + + /* Reset all interrupts */ + TMR_TMR16B1IR = TMR_TMR16B1IR_MASK_ALL; + + /* Stop timer on match (MR0) */ + TMR_TMR16B1MCR = TMR_TMR16B1MCR_MR0_STOP_ENABLED; + + /* Start timer */ + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; + + /* Wait until the delay time has elapsed */ + while (TMR_TMR16B1TCR & TMR_TMR16B1TCR_COUNTERENABLE_ENABLED); + } + + return; +} + +/**************************************************************************/ +/*! + @brief Interrupt handler for 16-bit timer 0 +*/ +/**************************************************************************/ +void TIMER16_0_IRQHandler(void) +{ + /* Clear the interrupt flag */ + TMR_TMR16B0IR = TMR_TMR16B0IR_MR0; + + /* Increment timer counter by 1 (it will automatically roll back to 0) */ + timer16_0_counter++; + return; +} + +/**************************************************************************/ +/*! + @brief Interrupt handler for 16-bit timer 1 +*/ +/**************************************************************************/ +void TIMER16_1_IRQHandler(void) +{ + /* Clear the interrupt flag */ + TMR_TMR16B1IR = TMR_TMR16B1IR_MR0; + + /* Increment timer counter by 1 (it will automatically roll back to 0) */ + timer16_1_counter++; + + #ifdef CFG_PWM + /* Check if the PWM output should be disabled after pwmMaxPulses pulses */ + /* See "drivers/pwm/pwm.c" */ + if (TMR_TMR16B1IR & TMR_TMR16B1IR_MR3) + { + /* Clear the interrupt flag */ + TMR_TMR16B1IR = TMR_TMR16B1IR_MR3; + + if (pwmMaxPulses > 0) + { + pwmCounter++; + if (pwmCounter == pwmMaxPulses) + { + /* Disable interrupt on MR3 */ + TMR_TMR16B1MCR &= ~(TMR_TMR16B1MCR_MR3_INT_MASK); + /* Disable Timer */ + TMR_TMR16B1TCR &= ~(TMR_TMR16B1TCR_COUNTERENABLE_MASK); + /* Reset the counter variables */ + pwmCounter = 0; + pwmMaxPulses = 0; + } + } + } + #endif + + return; +} + +/**************************************************************************/ +/*! + @brief Enables the specified timer + + @param[in] timerNum + The 16-bit timer to enable (0..1) +*/ +/**************************************************************************/ +void timer16Enable(uint8_t timerNum) +{ + if ( timerNum == 0 ) + { + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; + } + + else if (timerNum == 1) + { + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Disables the specified timer + + @param[in] timerNum + The 16-bit timer to disable (0..1) +*/ +/**************************************************************************/ +void timer16Disable(uint8_t timerNum) +{ + if ( timerNum == 0 ) + { + TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_DISABLED; + } + + else if (timerNum == 1) + { + TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_DISABLED; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Resets the specified timer + + @param[in] timerNum + The 16-bit timer to reset (0..1) +*/ +/**************************************************************************/ +void timer16Reset(uint8_t timerNum) +{ + uint32_t regVal; + + if ( timerNum == 0 ) + { + regVal = TMR_TMR16B0TCR; + regVal |= TMR_TMR16B0TCR_COUNTERRESET_ENABLED; + TMR_TMR16B0TCR = regVal; + } + + else if (timerNum == 1) + { + regVal = TMR_TMR16B1TCR; + regVal |= TMR_TMR16B1TCR_COUNTERRESET_ENABLED; + TMR_TMR16B1TCR = regVal; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Initialises the specified 16-bit timer, sets the timer + interval, resets the timer, and configures the interrupt + handler. + + Initialises a 16-bit timer with the supplied timer interval (the + amount of time that passes between each timer 'tick'). Every time that + this interval elapses, the timer's interrupt will be fired and the + appropriate counter variable will be incremented by one (For example, + with CT16B0, 'timer16_0_counter' would be incremented). + + @param[in] timerNum + The 16-bit timer to initiliase (0..1) + @param[in] timerInterval + The number of clock 'ticks' between resets (0..65534) + + @warning Care needs to be taken when configuring the timers since + the pins are all multiplexed with other peripherals. This + code is provided as a starting point, but it will need to + be adjusted according to your own situation and + pin/peripheral requirements +*/ +/**************************************************************************/ +void timer16Init(uint8_t timerNum, uint16_t timerInterval) +{ + // If timerInterval is invalid, use the default value + if (timerInterval < 1) + { + timerInterval = TIMER16_DEFAULTINTERVAL; + } + + if ( timerNum == 0 ) + { + /* Enable the clock for CT16B0 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B0); + + /* The physical pins associated with CT16B0 are not enabled by + default in order to avoid conflicts with other peripherals. + Pin 0.10 (CT16B0_MAT2), for example, can not be used while + debugging with a hardware debugger. If you require one or + more of these pins, simply uncomment the code below */ + + /* Configure PIO0.2 as Timer0_16 CAP0 */ + // IOCON_PIO0_2 &= ~IOCON_PIO0_2_FUNC_MASK; + // IOCON_PIO0_2 |= IOCON_PIO0_2_FUNC_CT16B0_CAP0; + + /* Configure PIO0.8 as Timer0_16 MAT0 */ + // IOCON_PIO0_8 &= ~IOCON_PIO0_8_FUNC_MASK; + // IOCON_PIO0_8 |= IOCON_PIO0_8_FUNC_CT16B0_MAT0; + + /* Configure PIO0.9 as Timer0_16 MAT1 */ + // IOCON_PIO0_9 &= ~IOCON_PIO0_9_FUNC_MASK; + // IOCON_PIO0_9 |= IOCON_PIO0_9_FUNC_CT16B0_MAT1; + + /* Configure PIO0.10 as Timer0_16 MAT3 */ + // IOCON_JTAG_TCK_PIO0_10 &= ~IOCON_JTAG_TCK_PIO0_10_FUNC_MASK; + // IOCON_JTAG_TCK_PIO0_10 |= IOCON_JTAG_TCK_PIO0_10_FUNC_CT16B0_MAT2; + + timer16_0_counter = 0; + TMR_TMR16B0MR0 = timerInterval; + + /* Configure match control register to raise an interrupt and reset on MR0 */ + TMR_TMR16B0MCR = (TMR_TMR16B0MCR_MR0_INT_ENABLED | TMR_TMR16B0MCR_MR0_RESET_ENABLED); + + /* Enable the TIMER0 interrupt */ + NVIC_EnableIRQ(TIMER_16_0_IRQn); + } + + else if ( timerNum == 1 ) + { + /* Enable the clock for CT16B1 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B1); + + /* The physical pins associated with CT16B0 are not enabled by + default in order to avoid conflicts with other peripherals. + Pin 0.10 (CT16B0_MAT2), for example, can not be used while + debugging with a hardware debugger. If you require one or + more of these pins, simply uncomment the code below */ + + /* Configure PIO1.8 as Timer1_16 CAP0 */ + // IOCON_PIO1_8 &= ~IOCON_PIO1_8_FUNC_MASK; + // IOCON_PIO1_8 |= IOCON_PIO1_8_FUNC_CT16B1_CAP0; + + /* Configure PIO1.9 as Timer1_16 MAT0 */ + // IOCON_PIO1_9 &= ~IOCON_PIO1_9_FUNC_MASK; + // IOCON_PIO1_9 |= IOCON_PIO1_9_FUNC_CT16B1_MAT0; + + /* Configure PIO1.10 as Timer1_16 MAT1 */ + // IOCON_PIO1_10 &= ~IOCON_PIO1_10_FUNC_MASK; + // IOCON_PIO1_10 |= IOCON_PIO1_10_FUNC_CT16B1_MAT1; + + timer16_1_counter = 0; + TMR_TMR16B1MR0 = timerInterval; + + /* Configure match control register to raise an interrupt and reset on MR0 */ + TMR_TMR16B1MCR = (TMR_TMR16B1MCR_MR0_INT_ENABLED | TMR_TMR16B1MCR_MR0_RESET_ENABLED); + + /* Enable the TIMER1 Interrupt */ + NVIC_EnableIRQ(TIMER_16_1_IRQn); + } + return; +} diff --git a/core/timer16/timer16.h b/core/timer16/timer16.h new file mode 100644 index 0000000..2130b69 --- /dev/null +++ b/core/timer16/timer16.h @@ -0,0 +1,59 @@ +/**************************************************************************/ +/*! + @file timer16.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __TIMER16_H__ +#define __TIMER16_H__ + +#include "projectconfig.h" + +#define TIMER16_DEFAULTINTERVAL (0xFFFF) // ~0.91mS @ 72MHz, ~1.37mS @ 48MHz + +#define TIMER16_CCLK_100US ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 10000) +#define TIMER16_CCLK_1MS ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 1000) + +void TIMER16_0_IRQHandler(void); +void TIMER16_1_IRQHandler(void); + +void timer16DelayTicks(uint8_t timerNum, uint16_t delayInTicks); +void timer16DelayUS(uint8_t timerNum, uint16_t delayInUS); +void timer16Enable(uint8_t timerNum); +void timer16Disable(uint8_t timerNum); +void timer16Reset(uint8_t timerNum); +void timer16Init(uint8_t timerNum, uint16_t timerInterval); + +#endif diff --git a/core/timer32/timer32.c b/core/timer32/timer32.c new file mode 100644 index 0000000..b768905 --- /dev/null +++ b/core/timer32/timer32.c @@ -0,0 +1,344 @@ +/**************************************************************************/ +/*! + @file timer32.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Generic code for 32-bit timers. By default, the timers are configured + to generate an interrupt once every 100 microseconds, incrementing a + global variable once per tick. + + @warning Please note that the ROM-based USB drivers on the LPC1343 + require the use of 32-bit Timer 1. If you plan on using the + ROM-based USB functionality, you should restrict your timer + usage to 32-bit timer 0. + + @section Example + + @code + #include "/core/cpu/cpu.h" + #include "/core/timer32/timer32.h" + ... + cpuInit(); + + // Initialise 32-bit timer 0 with 100uS ticks + timer32Init(0, TIMER32_DEFAULTINTERVAL); + + // Enable timer 0 + timer32Enable(0); + + // Cause a blocking delay for 1 second (1000mS) + timer32Delay(0, TIMER32_DELAY_1MS * 1000); + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "timer32.h" + +volatile uint32_t timer32_0_counter = 0; +volatile uint32_t timer32_1_counter = 0; + +/**************************************************************************/ +/*! + @brief Causes a blocking delay for the specified number of + timer ticks. The duration of each 'tick' is determined by + the 'timerInterval' property supplied to timer32Init. + + @param[in] timerNum + The 32-bit timer to user (0..1) + @param[in] delay + The number of counter increments to wait +*/ +/**************************************************************************/ +void timer32Delay(uint8_t timerNum, uint32_t delay) +{ + uint32_t curTicks; + + if (timerNum == 0) + { + curTicks = timer32_0_counter; + if (curTicks > 0xFFFFFFFF - delay) + { + // Rollover will occur during delay + while (timer32_0_counter >= curTicks) + { + while (timer32_0_counter < (delay - (0xFFFFFFFF - curTicks))); + } + } + else + { + while ((timer32_0_counter - curTicks) < delay); + } + } + + else if (timerNum == 1) + { + curTicks = timer32_1_counter; + if (curTicks > 0xFFFFFFFF - delay) + { + // Rollover will occur during delay + while (timer32_1_counter >= curTicks) + { + while (timer32_1_counter < (delay - (0xFFFFFFFF - curTicks))); + } + } + else + { + while ((timer32_1_counter - curTicks) < delay); + } + } + + return; +} + +/**************************************************************************/ +/*! + @brief Interrupt handler for 32-bit timer 0 +*/ +/**************************************************************************/ +void TIMER32_0_IRQHandler(void) +{ + /* Clear the interrupt flag */ + TMR_TMR32B0IR = TMR_TMR32B0IR_MR0; + + /* If you wish to perform some action after each timer 'tick' (such as + incrementing a counter variable) you can do so here */ + timer32_0_counter++; + + return; +} + +/**************************************************************************/ +/*! + @brief Interrupt handler for 32-bit timer 1 +*/ +/**************************************************************************/ +void TIMER32_1_IRQHandler(void) +{ + /* Clear the interrupt flag */ + TMR_TMR32B1IR = TMR_TMR32B1IR_MR0; + + /* If you wish to perform some action after each timer 'tick' (such as + incrementing a counter variable) you can do so here */ + timer32_1_counter++; + + return; +} + +/**************************************************************************/ +/*! + @brief Enables the specified timer + + @param[in] timerNum + The 32-bit timer to enable (0..1) +*/ +/**************************************************************************/ +void timer32Enable(uint8_t timerNum) +{ + if ( timerNum == 0 ) + { + TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_ENABLED; + } + + else if (timerNum == 1) + { + TMR_TMR32B1TCR = TMR_TMR32B1TCR_COUNTERENABLE_ENABLED; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Disables the specified timer + + @param[in] timerNum + The 32-bit timer to disable (0..1) +*/ +/**************************************************************************/ +void timer32Disable(uint8_t timerNum) +{ + if ( timerNum == 0 ) + { + TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED; + } + + else if (timerNum == 1) + { + TMR_TMR32B1TCR = TMR_TMR32B1TCR_COUNTERENABLE_DISABLED; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Resets the specified timer + + @param[in] timerNum + The 32-bit timer to reset (0..1) +*/ +/**************************************************************************/ +void timer32Reset(uint8_t timerNum) +{ + uint32_t regVal; + + if ( timerNum == 0 ) + { + regVal = TMR_TMR32B0TCR; + regVal |= TMR_TMR32B0TCR_COUNTERRESET_ENABLED; + TMR_TMR32B0TCR = regVal; + } + + else if (timerNum == 1) + { + regVal = TMR_TMR32B1TCR; + regVal |= TMR_TMR32B1TCR_COUNTERRESET_ENABLED; + TMR_TMR32B1TCR = regVal; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Initialises the specified 32-bit timer, and configures the + timer to raise an interrupt and reset on match on MR0. + + @param[in] timerNum + The 32-bit timer to initiliase (0..1) + @param[in] timerInterval + The number of clock 'ticks' between resets (0..0xFFFFFFFF) + + @note Care needs to be taken when configuring the timers since the + pins are all multiplexed with other peripherals. This code is + provided as a starting point, but it will need to be adjusted + according to your own situation and pin/peripheral requirements +*/ +/**************************************************************************/ +void timer32Init(uint8_t timerNum, uint32_t timerInterval) +{ + // If timerInterval is invalid, use the default value + if (timerInterval < 1) + { + timerInterval = TIMER32_DEFAULTINTERVAL; + } + + if ( timerNum == 0 ) + { + /* Enable the clock for CT32B0 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B0); + + /* The physical pins associated with CT32B0 are not enabled by + default in order to avoid conflicts with other peripherals. If + you wish to use any of the pin-dependant functionality, simply + uncomment the appropriate lines below. */ + + /* Configure PIO1.5 as Timer0_32 CAP0 */ + // IOCON_PIO1_5 &= ~IOCON_PIO1_5_FUNC_MASK; + // IOCON_PIO1_5 |= IOCON_PIO1_5_FUNC_CT32B0_CAP0; + + /* Configure PIO1.6 as Timer0_32 MAT0 */ + // IOCON_PIO1_6 &= ~IOCON_PIO1_6_FUNC_MASK; + // IOCON_PIO1_6 |= IOCON_PIO1_6_FUNC_CT32B0_MAT0; + + /* Configure PIO1.7 as Timer0_32 MAT1 */ + // IOCON_PIO1_7 &= ~IOCON_PIO1_7_FUNC_MASK; + // IOCON_PIO1_7 |= IOCON_PIO1_7_FUNC_CT32B0_MAT1; + + /* Configure PIO0.1 as Timer0_32 MAT2 */ + // IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK; + // IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CT32B0_MAT2; + + /* Configure PIO0.11 as Timer0_32 MAT3 */ + /* Note: This pint can not be used with JTAG/SWD */ + // IOCON_JTAG_TDI_PIO0_11 &= ~IOCON_JTAG_TDI_PIO0_11_FUNC_MASK; + // IOCON_JTAG_TDI_PIO0_11 |= IOCON_JTAG_TDI_PIO0_11_FUNC_CT32B0_MAT3; + + timer32_0_counter = 0; + TMR_TMR32B0MR0 = timerInterval; + + /* Configure match control register to raise an interrupt and reset on MR0 */ + TMR_TMR32B0MCR = (TMR_TMR32B0MCR_MR0_INT_ENABLED | TMR_TMR32B0MCR_MR0_RESET_ENABLED); + + /* Enable the TIMER0 interrupt */ + NVIC_EnableIRQ(TIMER_32_0_IRQn); + } + + else if ( timerNum == 1 ) + { + /* Enable the clock for CT32B1 */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B1); + + /* The physical pins associated with CT32B0 are not enabled by + default in order to avoid conflicts with other peripherals. */ + + /* Configure PIO1.0 as Timer1_32 CAP0 */ + /* Note: This pint can not be used with JTAG/SWD */ + // IOCON_JTAG_TMS_PIO1_0 &= ~IOCON_JTAG_TMS_PIO1_0_FUNC_MASK; + // IOCON_JTAG_TMS_PIO1_0 |= IOCON_JTAG_TMS_PIO1_0_FUNC_CT32B1_CAP0; + + /* Configure PIO1.1 as Timer1_32 MAT0 */ + /* Note: This pint can not be used with JTAG/SWD */ + // IOCON_JTAG_TDO_PIO1_1 &= ~IOCON_JTAG_TDO_PIO1_1_FUNC_MASK; + // IOCON_JTAG_TDO_PIO1_1 |= IOCON_JTAG_TDO_PIO1_1_FUNC_CT32B1_MAT0; + + /* Configure PIO1.2 as Timer1_32 MAT1 */ + /* Note: This pint can not be used with JTAG/SWD */ + // IOCON_JTAG_nTRST_PIO1_2 &= ~IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK; + // IOCON_JTAG_nTRST_PIO1_2 |= IOCON_JTAG_nTRST_PIO1_2_FUNC_CT32B1_MAT1; + + /* Configure PIO1.3 as Timer1_32 MAT2 */ + /* Note: This pint can not be used with JTAG/SWD */ + // IOCON_SWDIO_PIO1_3 &= ~IOCON_SWDIO_PIO1_3_FUNC_MASK; + // IOCON_SWDIO_PIO1_3 |= IOCON_SWDIO_PIO1_3_FUNC_CT32B1_MAT2; + + /* Configure PIO1.4 as Timer1_32 MAT3 */ + // IOCON_PIO1_4 &= ~IOCON_PIO1_4_FUNC_MASK; + // IOCON_PIO1_4 |= IOCON_PIO1_4_FUNC_CT32B1_MAT3; + + timer32_1_counter = 0; + TMR_TMR32B1MR0 = timerInterval; + + /* Configure match control register to raise an interrupt and reset on MR0 */ + TMR_TMR32B1MCR = (TMR_TMR32B1MCR_MR0_INT_ENABLED | TMR_TMR32B1MCR_MR0_RESET_ENABLED); + + /* Enable the TIMER1 Interrupt */ + NVIC_EnableIRQ(TIMER_32_1_IRQn); + } + return; +} + + diff --git a/core/timer32/timer32.h b/core/timer32/timer32.h new file mode 100644 index 0000000..b7135a8 --- /dev/null +++ b/core/timer32/timer32.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/*! + @file timer32.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __TIMER32_H__ +#define __TIMER32_H__ + +#include "projectconfig.h" + +#define TIMER32_CCLK_1US ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 1000000) +#define TIMER32_CCLK_10US ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 100000) +#define TIMER32_CCLK_100US ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 10000) +#define TIMER32_CCLK_1MS ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 1000) +#define TIMER32_CCLK_10MS ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 100) +#define TIMER32_CCLK_100MS ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 10) +#define TIMER32_CCLK_1S (CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) +#define TIMER32_DEFAULTINTERVAL (TIMER32_CCLK_100US) + +#define TIMER32_DELAY_100US (1) // 100uS delay = 1 tick +#define TIMER32_DELAY_1MS (10) // 1mS delay = 10 ticks +#define TIMER32_DELAY_1S (10000) // 1S delay = 10000 ticks + +void TIMER32_0_IRQHandler(void); +void TIMER32_1_IRQHandler(void); + +void timer32Delay(uint8_t timerNum, uint32_t delay); +void timer32Enable(uint8_t timerNum); +void timer32Disable(uint8_t timerNum); +void timer32Reset(uint8_t timerNum); +void timer32Init(uint8_t timerNum, uint32_t timerInterval); + +#endif diff --git a/core/uart/uart.c b/core/uart/uart.c new file mode 100644 index 0000000..57589ff --- /dev/null +++ b/core/uart/uart.c @@ -0,0 +1,347 @@ +/**************************************************************************/ +/*! + @file uart.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Generic code for UART-based communication. Incoming text is stored + in a FIFO Queue for safer processing. + + @section Example: Sending text via UART + + @code + #include "core/cpu/cpu.h" + #include "core/uart/uart.h" + ... + #define UARTBUFFERSIZE 5 + ... + cpuInit(); + uartInit(57600); + ... + uint8_t uartBuffer[UARTBUFFERSIZE] = { 'T', 'e', 's', 't', '\n' }; + + // Send contents of uartBuffer + uartSend((uint8_t *)uartBuffer, UARTBUFFERSIZE); + @endcode + + @section Example: Reading from UART + + @code + + #include "core/cpu/cpu.h" + #include "core/uart/uart.h" + + cpuInit(); + uartInit(57600); + + // Get a reference to the UART control block + uart_pcb_t *pcb = uartGetPCB(); + + // Read any text available in the queue + while (uartRxBufferDataPending()) + { + // Read the first available character + uint8_t c = uartRxBufferRead(); + + // read out the data in the buffer and echo it back to the host. + switch (c) + { + case '\r': + printf("\n\r"); + break; + default: + printf("%c", c); + break; + } + } + + #endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include + +#include "uart.h" + +#ifdef CFG_INTERFACE_UART + #include "core/cmd/cmd.h" +#endif + +/**************************************************************************/ +/*! + UART protocol control block, which is used to safely access the + RX FIFO buffer from elsewhere in the code. This should be accessed + through 'uartGetPCB()'. +*/ +/**************************************************************************/ +static uart_pcb_t pcb; + +/**************************************************************************/ +/*! + IRQ to handle incoming data, etc. +*/ +/**************************************************************************/ +void UART_IRQHandler(void) +{ + uint8_t IIRValue, LSRValue; + uint8_t Dummy = Dummy; + + IIRValue = UART_U0IIR; + IIRValue &= ~(UART_U0IIR_IntStatus_MASK); /* skip pending bit in IIR */ + IIRValue &= UART_U0IIR_IntId_MASK; /* check bit 1~3, interrupt identification */ + + // 1.) Check receiver line status + if (IIRValue == UART_U0IIR_IntId_RLS) + { + LSRValue = UART_U0LSR; + // Check for errors + if (LSRValue & (UART_U0LSR_OE | UART_U0LSR_PE | UART_U0LSR_FE | UART_U0LSR_RXFE | UART_U0LSR_BI)) + { + /* There are errors or break interrupt */ + /* Read LSR will clear the interrupt */ + pcb.status = LSRValue; + Dummy = UART_U0RBR; /* Dummy read on RX to clear interrupt, then bail out */ + return; + } + // No error and receive data is ready + if (LSRValue & UART_U0LSR_RDR_DATA) + { + /* If no error on RLS, normal ready, save into the data buffer. */ + /* Note: read RBR will clear the interrupt */ + uartRxBufferWrite(UART_U0RBR); + } + } + + // 2.) Check receive data available + else if (IIRValue == UART_U0IIR_IntId_RDA) + { + // Add incoming text to UART buffer + uartRxBufferWrite(UART_U0RBR); + } + + // 3.) Check character timeout indicator + else if (IIRValue == UART_U0IIR_IntId_CTI) + { + /* Bit 9 as the CTI error */ + pcb.status |= 0x100; + } + + // 4.) Check THRE (transmit holding register empty) + else if (IIRValue == UART_U0IIR_IntId_THRE) + { + /* Check status in the LSR to see if valid data in U0THR or not */ + LSRValue = UART_U0LSR; + if (LSRValue & UART_U0LSR_THRE) + { + pcb.pending_tx_data = 0; + } + else + { + pcb.pending_tx_data= 1; + } + } + return; +} + +/**************************************************************************/ +/*! + @brief Get a pointer to the UART's protocol control block, which can + be used to control the RX FIFO buffer and check whether UART + has already been initialised or not. + + @section Example + + @code + // Make sure that UART is initialised + uart_pcb_t *pcb = uartGetPCB(); + if (!pcb->initialised) + { + uartInit(CFG_UART_BAUDRATE); + } + @endcode + +*/ +/**************************************************************************/ +uart_pcb_t *uartGetPCB() +{ + return &pcb; +} + +/**************************************************************************/ +/*! + @brief Initialises UART at the specified baud rate. + + @param[in] baudRate + The baud rate to use when configuring the UART. +*/ +/**************************************************************************/ +void uartInit(uint32_t baudrate) +{ + uint32_t fDiv; + uint32_t regVal; + + NVIC_DisableIRQ(UART_IRQn); + + // Clear protocol control blocks + memset(&pcb, 0, sizeof(uart_pcb_t)); + pcb.pending_tx_data = 0; + uartRxBufferInit(); + + /* Set 1.6 UART RXD */ + IOCON_PIO1_6 &= ~IOCON_PIO1_6_FUNC_MASK; + IOCON_PIO1_6 |= IOCON_PIO1_6_FUNC_UART_RXD; + + /* Set 1.7 UART TXD */ + IOCON_PIO1_7 &= ~IOCON_PIO1_7_FUNC_MASK; + IOCON_PIO1_7 |= IOCON_PIO1_7_FUNC_UART_TXD; + + /* Enable UART clock */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_UART); + SCB_UARTCLKDIV = SCB_UARTCLKDIV_DIV1; /* divided by 1 */ + + /* 8 bits, no Parity, 1 Stop bit */ + UART_U0LCR = (UART_U0LCR_Word_Length_Select_8Chars | + UART_U0LCR_Stop_Bit_Select_1Bits | + UART_U0LCR_Parity_Disabled | + UART_U0LCR_Parity_Select_OddParity | + UART_U0LCR_Break_Control_Disabled | + UART_U0LCR_Divisor_Latch_Access_Enabled); + + /* Baud rate */ + regVal = SCB_UARTCLKDIV; + fDiv = (((CFG_CPU_CCLK * SCB_SYSAHBCLKDIV)/regVal)/16)/baudrate; + + UART_U0DLM = fDiv / 256; + UART_U0DLL = fDiv % 256; + + /* Set DLAB back to 0 */ + UART_U0LCR = (UART_U0LCR_Word_Length_Select_8Chars | + UART_U0LCR_Stop_Bit_Select_1Bits | + UART_U0LCR_Parity_Disabled | + UART_U0LCR_Parity_Select_OddParity | + UART_U0LCR_Break_Control_Disabled | + UART_U0LCR_Divisor_Latch_Access_Disabled); + + /* Enable and reset TX and RX FIFO. */ + UART_U0FCR = (UART_U0FCR_FIFO_Enabled | + UART_U0FCR_Rx_FIFO_Reset | + UART_U0FCR_Tx_FIFO_Reset); + + /* Read to clear the line status. */ + regVal = UART_U0LSR; + + /* Ensure a clean start, no data in either TX or RX FIFO. */ + while (( UART_U0LSR & (UART_U0LSR_THRE|UART_U0LSR_TEMT)) != (UART_U0LSR_THRE|UART_U0LSR_TEMT) ); + while ( UART_U0LSR & UART_U0LSR_RDR_DATA ) + { + /* Dump data from RX FIFO */ + regVal = UART_U0RBR; + } + + /* Set the initialised flag in the protocol control block */ + pcb.initialised = 1; + pcb.baudrate = baudrate; + + /* Enable the UART Interrupt */ + NVIC_EnableIRQ(UART_IRQn); + UART_U0IER = UART_U0IER_RBR_Interrupt_Enabled | UART_U0IER_RLS_Interrupt_Enabled; + + return; +} + +/**************************************************************************/ +/*! + @brief Sends the contents of supplied text buffer over UART. + + @param[in] bufferPtr + Pointer to the text buffer + @param[in] bufferPtr + The size of the text buffer + + @section Example + + @code + // Set 5-character text buffer + uint8_t uartBuffer[5] = { 'T', 'e', 's', 't', '\n' }; + // Send contents of uartBuffer + uartSend((uint8_t *)uartBuffer, 5); + @endcode + +*/ +/**************************************************************************/ +void uartSend (uint8_t *bufferPtr, uint32_t length) +{ + while (length != 0) + { + /* THRE status, contain valid data */ + while ( !(UART_U0LSR & UART_U0LSR_THRE) ); + UART_U0THR = *bufferPtr; + + bufferPtr++; + length--; + } + + return; +} + +/**************************************************************************/ +/*! + @brief Sends a single byte over UART. + + @param[in] byte + Byte value to send + + @section Example + + @code + // Send 0xFF over UART + uartSendByte(0xFF); + // Send 'B' over UART (note single quotes) + uartSendByte('B'); + @endcode + +*/ +/**************************************************************************/ +void uartSendByte (uint8_t byte) +{ + /* THRE status, contain valid data */ + while ( !(UART_U0LSR & UART_U0LSR_THRE) ); + UART_U0THR = byte; + + return; +} + + + diff --git a/core/uart/uart.h b/core/uart/uart.h new file mode 100644 index 0000000..928b6ce --- /dev/null +++ b/core/uart/uart.h @@ -0,0 +1,78 @@ +/**************************************************************************/ +/*! + @file uart.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __UART_H__ +#define __UART_H__ + +#include "projectconfig.h" + +// Buffer used for circular fifo +typedef struct _uart_buffer_t +{ + uint8_t ep_dir; + volatile uint8_t len; + volatile uint8_t wr_ptr; + volatile uint8_t rd_ptr; + uint8_t buf[CFG_UART_BUFSIZE]; +} uart_buffer_t; + +// UART Protocol control block +typedef struct _uart_pcb_t +{ + BOOL initialised; + uint32_t baudrate; + uint32_t status; + uint32_t pending_tx_data; + uart_buffer_t rxfifo; +} uart_pcb_t; + +void UART_IRQHandler(void); +uart_pcb_t *uartGetPCB(); +void uartInit(uint32_t Baudrate); +void uartSend(uint8_t *BufferPtr, uint32_t Length); +void uartSendByte (uint8_t byte); + +// Rx Buffer access control +void uartRxBufferInit(); +uint8_t uartRxBufferRead(); +void uartRxBufferWrite(uint8_t data); +void uartRxBufferClearFIFO(); +uint8_t uartRxBufferDataPending(); +bool uartRxBufferReadArray(byte_t* rx, size_t* len); + +#endif diff --git a/core/uart/uart_buf.c b/core/uart/uart_buf.c new file mode 100644 index 0000000..f51733e --- /dev/null +++ b/core/uart/uart_buf.c @@ -0,0 +1,149 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. +*******************************************************************/ + +/**************************************************************************/ +/*! + @file uart_buf.c + @author Christopher Wang (Freaklabs) + Modified by: K. Townsend (microBuilder.eu) + @date 19 May 2010 + + Original code taken from the FreakUSB Open Source USB Device Stack + http://freaklabs.org/index.php/FreakUSB-Open-Source-USB-Device-Stack.html + + If it works well, you can thank Akiba at Freaklabs. If it fails + miserably, you can blame me (since parts of it it were rather + ungraciously modified). :-) + +*/ +/**************************************************************************/ + +#include "uart.h" + +/**************************************************************************/ +/*! + Initialises the RX FIFO buffer +*/ +/**************************************************************************/ +void uartRxBufferInit() +{ + uart_pcb_t *pcb = uartGetPCB(); + pcb->rxfifo.len = 0; +} + +/**************************************************************************/ +/*! + Read one byte out of the RX buffer. This function will return the byte + located at the array index of the read pointer, and then increment the + read pointer index. If the read pointer exceeds the maximum buffer + size, it will roll over to zero. +*/ +/**************************************************************************/ +uint8_t uartRxBufferRead() +{ + uart_pcb_t *pcb = uartGetPCB(); + uint8_t data; + + data = pcb->rxfifo.buf[pcb->rxfifo.rd_ptr]; + pcb->rxfifo.rd_ptr = (pcb->rxfifo.rd_ptr + 1) % CFG_UART_BUFSIZE; + pcb->rxfifo.len--; + return data; +} + +/**************************************************************************/ +/*! + Read byte array from uart + */ +/**************************************************************************/ +bool uartRxBufferReadArray(byte_t* rx, size_t* len) +{ + uart_pcb_t *pcb = uartGetPCB(); + *len = 0; + + while(pcb->rxfifo.len != 0) + { + (*rx) = uartRxBufferRead(); + (*len)++; + rx++; + } + + return (*len != 0); +} + +/**************************************************************************/ +/*! + Write one byte into the RX buffer. This function will write one + byte into the array index specified by the write pointer and increment + the write index. If the write index exceeds the max buffer size, then it + will roll over to zero. +*/ +/**************************************************************************/ +void uartRxBufferWrite(uint8_t data) +{ + uart_pcb_t *pcb = uartGetPCB(); + + pcb->rxfifo.buf[pcb->rxfifo.wr_ptr] = data; + pcb->rxfifo.wr_ptr = (pcb->rxfifo.wr_ptr + 1) % CFG_UART_BUFSIZE; + pcb->rxfifo.len++; +} + +/**************************************************************************/ +/*! + Clear the fifo read and write pointers and set the length to zero. +*/ +/**************************************************************************/ +void uartRxBufferClearFIFO() +{ + uart_pcb_t *pcb = uartGetPCB(); + + pcb->rxfifo.rd_ptr = 0; + pcb->rxfifo.wr_ptr = 0; + pcb->rxfifo.len = 0; +} + +/**************************************************************************/ +/*! + Check whether there is any data pending on the RX buffer. +*/ +/**************************************************************************/ +uint8_t uartRxBufferDataPending() +{ + uart_pcb_t *pcb = uartGetPCB(); + + if (pcb->rxfifo.len != 0) + { + return 1; + } + + return 0; +} diff --git a/core/usbcdc/cdc.h b/core/usbcdc/cdc.h new file mode 100644 index 0000000..f7af7d3 --- /dev/null +++ b/core/usbcdc/cdc.h @@ -0,0 +1,236 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: CDC.h + * Purpose: USB Communication Device Class Definitions + * Version: V1.00 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __CDC_H +#define __CDC_H + +/*---------------------------------------------------------------------------- + * Definitions based on usbcdc11.pdf (www.usb.org) + *---------------------------------------------------------------------------*/ +// Communication device class specification version 1.10 +#define CDC_V1_10 0x0110 + +// Communication interface class code +// (usbcdc11.pdf, 4.2, Table 15) +#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02 + +// Communication interface class subclass codes +// (usbcdc11.pdf, 4.3, Table 16) +#define CDC_DIRECT_LINE_CONTROL_MODEL 0x01 +#define CDC_ABSTRACT_CONTROL_MODEL 0x02 +#define CDC_TELEPHONE_CONTROL_MODEL 0x03 +#define CDC_MULTI_CHANNEL_CONTROL_MODEL 0x04 +#define CDC_CAPI_CONTROL_MODEL 0x05 +#define CDC_ETHERNET_NETWORKING_CONTROL_MODEL 0x06 +#define CDC_ATM_NETWORKING_CONTROL_MODEL 0x07 + +// Communication interface class control protocol codes +// (usbcdc11.pdf, 4.4, Table 17) +#define CDC_PROTOCOL_COMMON_AT_COMMANDS 0x01 + +// Data interface class code +// (usbcdc11.pdf, 4.5, Table 18) +#define CDC_DATA_INTERFACE_CLASS 0x0A + +// Data interface class protocol codes +// (usbcdc11.pdf, 4.7, Table 19) +#define CDC_PROTOCOL_ISDN_BRI 0x30 +#define CDC_PROTOCOL_HDLC 0x31 +#define CDC_PROTOCOL_TRANSPARENT 0x32 +#define CDC_PROTOCOL_Q921_MANAGEMENT 0x50 +#define CDC_PROTOCOL_Q921_DATA_LINK 0x51 +#define CDC_PROTOCOL_Q921_MULTIPLEXOR 0x52 +#define CDC_PROTOCOL_V42 0x90 +#define CDC_PROTOCOL_EURO_ISDN 0x91 +#define CDC_PROTOCOL_V24_RATE_ADAPTATION 0x92 +#define CDC_PROTOCOL_CAPI 0x93 +#define CDC_PROTOCOL_HOST_BASED_DRIVER 0xFD +#define CDC_PROTOCOL_DESCRIBED_IN_PUFD 0xFE + +// Type values for bDescriptorType field of functional descriptors +// (usbcdc11.pdf, 5.2.3, Table 24) +#define CDC_CS_INTERFACE 0x24 +#define CDC_CS_ENDPOINT 0x25 + +// Type values for bDescriptorSubtype field of functional descriptors +// (usbcdc11.pdf, 5.2.3, Table 25) +#define CDC_HEADER 0x00 +#define CDC_CALL_MANAGEMENT 0x01 +#define CDC_ABSTRACT_CONTROL_MANAGEMENT 0x02 +#define CDC_DIRECT_LINE_MANAGEMENT 0x03 +#define CDC_TELEPHONE_RINGER 0x04 +#define CDC_REPORTING_CAPABILITIES 0x05 +#define CDC_UNION 0x06 +#define CDC_COUNTRY_SELECTION 0x07 +#define CDC_TELEPHONE_OPERATIONAL_MODES 0x08 +#define CDC_USB_TERMINAL 0x09 +#define CDC_NETWORK_CHANNEL 0x0A +#define CDC_PROTOCOL_UNIT 0x0B +#define CDC_EXTENSION_UNIT 0x0C +#define CDC_MULTI_CHANNEL_MANAGEMENT 0x0D +#define CDC_CAPI_CONTROL_MANAGEMENT 0x0E +#define CDC_ETHERNET_NETWORKING 0x0F +#define CDC_ATM_NETWORKING 0x10 + +// CDC class-specific request codes +// (usbcdc11.pdf, 6.2, Table 46) +// see Table 45 for info about the specific requests. +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define CDC_SET_COMM_FEATURE 0x02 +#define CDC_GET_COMM_FEATURE 0x03 +#define CDC_CLEAR_COMM_FEATURE 0x04 +#define CDC_SET_AUX_LINE_STATE 0x10 +#define CDC_SET_HOOK_STATE 0x11 +#define CDC_PULSE_SETUP 0x12 +#define CDC_SEND_PULSE 0x13 +#define CDC_SET_PULSE_TIME 0x14 +#define CDC_RING_AUX_JACK 0x15 +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 +#define CDC_SEND_BREAK 0x23 +#define CDC_SET_RINGER_PARMS 0x30 +#define CDC_GET_RINGER_PARMS 0x31 +#define CDC_SET_OPERATION_PARMS 0x32 +#define CDC_GET_OPERATION_PARMS 0x33 +#define CDC_SET_LINE_PARMS 0x34 +#define CDC_GET_LINE_PARMS 0x35 +#define CDC_DIAL_DIGITS 0x36 +#define CDC_SET_UNIT_PARAMETER 0x37 +#define CDC_GET_UNIT_PARAMETER 0x38 +#define CDC_CLEAR_UNIT_PARAMETER 0x39 +#define CDC_GET_PROFILE 0x3A +#define CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define CDC_SET_ETHERNET_PMP_FILTER 0x41 +#define CDC_GET_ETHERNET_PMP_FILTER 0x42 +#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define CDC_GET_ETHERNET_STATISTIC 0x44 +#define CDC_SET_ATM_DATA_FORMAT 0x50 +#define CDC_GET_ATM_DEVICE_STATISTICS 0x51 +#define CDC_SET_ATM_DEFAULT_VC 0x52 +#define CDC_GET_ATM_VC_STATISTICS 0x53 + +// Communication feature selector codes +// (usbcdc11.pdf, 6.2.2..6.2.4, Table 47) +#define CDC_ABSTRACT_STATE 0x01 +#define CDC_COUNTRY_SETTING 0x02 + +// Feature Status returned for ABSTRACT_STATE Selector +// (usbcdc11.pdf, 6.2.3, Table 48) +#define CDC_IDLE_SETTING (1 << 0) +#define CDC_DATA_MULTPLEXED_STATE (1 << 1) + + +// Control signal bitmap values for the SetControlLineState request +// (usbcdc11.pdf, 6.2.14, Table 51) +#define CDC_DTE_PRESENT (1 << 0) +#define CDC_ACTIVATE_CARRIER (1 << 1) + +// CDC class-specific notification codes +// (usbcdc11.pdf, 6.3, Table 68) +// see Table 67 for Info about class-specific notifications +#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00 +#define CDC_RESPONSE_AVAILABLE 0x01 +#define CDC_AUX_JACK_HOOK_STATE 0x08 +#define CDC_RING_DETECT 0x09 +#define CDC_NOTIFICATION_SERIAL_STATE 0x20 +#define CDC_CALL_STATE_CHANGE 0x28 +#define CDC_LINE_STATE_CHANGE 0x29 +#define CDC_CONNECTION_SPEED_CHANGE 0x2A + +// UART state bitmap values (Serial state notification). +// (usbcdc11.pdf, 6.3.5, Table 69) +#define CDC_SERIAL_STATE_OVERRUN (1 << 6) // receive data overrun error has occurred +#define CDC_SERIAL_STATE_PARITY (1 << 5) // parity error has occurred +#define CDC_SERIAL_STATE_FRAMING (1 << 4) // framing error has occurred +#define CDC_SERIAL_STATE_RING (1 << 3) // state of ring signal detection +#define CDC_SERIAL_STATE_BREAK (1 << 2) // state of break detection +#define CDC_SERIAL_STATE_TX_CARRIER (1 << 1) // state of transmission carrier +#define CDC_SERIAL_STATE_RX_CARRIER (1 << 0) // state of receiver carrier + + +/*---------------------------------------------------------------------------- + * Structures based on usbcdc11.pdf (www.usb.org) + *---------------------------------------------------------------------------*/ + +// Header functional descriptor +// (usbcdc11.pdf, 5.2.3.1) +// This header must precede any list of class-specific descriptors. +typedef struct _CDC_HEADER_DESCRIPTOR{ + uint8_t bFunctionLength; // size of this descriptor in bytes + uint8_t bDescriptorType; // CS_INTERFACE descriptor type + uint8_t bDescriptorSubtype; // Header functional descriptor subtype + uint16_t bcdCDC; // USB CDC specification release version +} __attribute__((packed)) CDC_HEADER_DESCRIPTOR; + +//Call management functional descriptor +// (usbcdc11.pdf, 5.2.3.2) +// Describes the processing of calls for the communication class interface. +typedef struct _CDC_CALL_MANAGEMENT_DESCRIPTOR { + uint8_t bFunctionLength; // size of this descriptor in bytes + uint8_t bDescriptorType; // CS_INTERFACE descriptor type + uint8_t bDescriptorSubtype; // call management functional descriptor subtype + uint8_t bmCapabilities; // capabilities that this configuration supports + uint8_t bDataInterface; // interface number of the data class interface used for call management (optional) +} __attribute__((packed)) CDC_CALL_MANAGEMENT_DESCRIPTOR; + +// Abstract control management functional descriptor +// (usbcdc11.pdf, 5.2.3.3) +// Describes the command supported by the communication interface class with the Abstract Control Model subclass code. +typedef struct _CDC_ABSTRACT_CONTROL_MANAGEMENT_DESCRIPTOR { + uint8_t bFunctionLength; // size of this descriptor in bytes + uint8_t bDescriptorType; // CS_INTERFACE descriptor type + uint8_t bDescriptorSubtype; // abstract control management functional descriptor subtype + uint8_t bmCapabilities; // capabilities supported by this configuration +} __attribute__((packed)) CDC_ABSTRACT_CONTROL_MANAGEMENT_DESCRIPTOR; + +// Union functional descriptors +// (usbcdc11.pdf, 5.2.3.8) +// Describes the relationship between a group of interfaces that can be considered to form a functional unit. +typedef struct _CDC_UNION_DESCRIPTOR { + uint8_t bFunctionLength; // size of this descriptor in bytes + uint8_t bDescriptorType; // CS_INTERFACE descriptor type + uint8_t bDescriptorSubtype; // union functional descriptor subtype + uint8_t bMasterInterface; // interface number designated as master +} __attribute__((packed)) CDC_UNION_DESCRIPTOR; + +// Union functional descriptors with one slave interface +// (usbcdc11.pdf, 5.2.3.8) +typedef struct _CDC_UNION_1SLAVE_DESCRIPTOR { + CDC_UNION_DESCRIPTOR sUnion; // Union functional descriptor + uint8_t bSlaveInterfaces[1]; // Slave interface 0 +} __attribute__((packed)) CDC_UNION_1SLAVE_DESCRIPTOR; + +// Line coding structure +// Format of the data returned when a GetLineCoding request is received +// (usbcdc11.pdf, 6.2.13) +typedef struct _CDC_LINE_CODING { + uint32_t dwDTERate; // Data terminal rate in bits per second + uint8_t bCharFormat; // Number of stop bits + uint8_t bParityType; // Parity bit type + uint8_t bDataBits; // Number of data bits +} __attribute__((packed)) CDC_LINE_CODING; + +// Notification header +// Data sent on the notification endpoint must follow this header. +// see USB_SETUP_PACKET in file usb.h +typedef USB_SETUP_PACKET CDC_NOTIFICATION_HEADER; + +#endif /* __CDC_H */ + diff --git a/core/usbcdc/cdc_buf.c b/core/usbcdc/cdc_buf.c new file mode 100644 index 0000000..2a7c1d5 --- /dev/null +++ b/core/usbcdc/cdc_buf.c @@ -0,0 +1,161 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. +*******************************************************************/ + +/**************************************************************************/ +/*! + @file cdc_buf.c + @author Christopher Wang (Freaklabs) + Modified by: K. Townsend (microBuilder.eu) + @date 19 May 2010 + + Original code taken from the FreakUSB Open Source USB Device Stack + http://freaklabs.org/index.php/FreakUSB-Open-Source-USB-Device-Stack.html + + If it works well, you can thank Akiba at Freaklabs. If it fails + miserably, you can blame me (since parts of it it were rather + ungraciously modified). :-) + +*/ +/**************************************************************************/ + +#include "cdc_buf.h" + +static cdc_buffer_t cdcfifo; + +/**************************************************************************/ +/*! + Gets a pointer to the fifo buffer +*/ +/**************************************************************************/ +cdc_buffer_t *cdcGetBuffer() +{ + return &cdcfifo; +} + +/**************************************************************************/ +/*! + Initialises the RX FIFO buffer +*/ +/**************************************************************************/ +void cdcBufferInit() +{ + cdcfifo.len = 0; +} + +/**************************************************************************/ +/*! + Read one byte out of the RX buffer. This function will return the byte + located at the array index of the read pointer, and then increment the + read pointer index. If the read pointer exceeds the maximum buffer + size, it will roll over to zero. +*/ +/**************************************************************************/ +uint8_t cdcBufferRead() +{ + uint8_t data; + + data = cdcfifo.buf[cdcfifo.rd_ptr]; + cdcfifo.rd_ptr = (cdcfifo.rd_ptr + 1) % CFG_USBCDC_BUFFERSIZE; + cdcfifo.len--; + return data; +} + +/**************************************************************************/ +/*! + Reads x bytes from cdc buffer + */ +/**************************************************************************/ +uint32_t cdcBufferReadLen(uint8_t* buf, uint32_t len) +{ + uint32_t counter, actual; + counter = actual = 0; + + while(counter != len) + { + // Make sure we don't exceed buffer limits + if (cdcfifo.len > 0) + { + buf[counter] = cdcBufferRead(); + actual++; + counter++; + } + else + { + return actual; + } + } + + return actual; +} + +/**************************************************************************/ +/*! + Write one byte into the RX buffer. This function will write one + byte into the array index specified by the write pointer and increment + the write index. If the write index exceeds the max buffer size, then it + will roll over to zero. +*/ +/**************************************************************************/ +void cdcBufferWrite(uint8_t data) +{ + cdcfifo.buf[cdcfifo.wr_ptr] = data; + cdcfifo.wr_ptr = (cdcfifo.wr_ptr + 1) % CFG_USBCDC_BUFFERSIZE; + cdcfifo.len++; +} + +/**************************************************************************/ +/*! + Clear the fifo read and write pointers and set the length to zero. +*/ +/**************************************************************************/ +void cdcBufferClearFIFO() +{ + cdcfifo.rd_ptr = 0; + cdcfifo.wr_ptr = 0; + cdcfifo.len = 0; +} + +/**************************************************************************/ +/*! + Check whether there is any data pending on the RX buffer. +*/ +/**************************************************************************/ +uint8_t cdcBufferDataPending() +{ + if (cdcfifo.len != 0) + { + return 1; + } + + return 0; +} diff --git a/core/usbcdc/cdc_buf.h b/core/usbcdc/cdc_buf.h new file mode 100644 index 0000000..c5cdfad --- /dev/null +++ b/core/usbcdc/cdc_buf.h @@ -0,0 +1,29 @@ +/*---------------------------------------------------------------------------- + * Name: cdc_buf.h + * Purpose: usb cdc buffer handling + * Version: V1.00 + *---------------------------------------------------------------------------*/ + +#ifndef __CDC_BUF_H__ +#define __CDC_BUF_H__ + +#include "projectconfig.h" + +// Buffer used for circular fifo +typedef struct _cdc_buffer_t +{ + volatile uint8_t len; + volatile uint8_t wr_ptr; + volatile uint8_t rd_ptr; + uint8_t buf[CFG_USBCDC_BUFFERSIZE]; +} cdc_buffer_t; + +cdc_buffer_t * cdcGetBuffer(); +void cdcBufferInit(); +uint8_t cdcBufferRead(); +uint32_t cdcBufferReadLen(uint8_t* buf, uint32_t len); +void cdcBufferWrite(uint8_t data); +void cdcBufferClearFIFO(); +uint8_t cdcBufferDataPending(); + +#endif diff --git a/core/usbcdc/cdcuser.c b/core/usbcdc/cdcuser.c new file mode 100644 index 0000000..d553f47 --- /dev/null +++ b/core/usbcdc/cdcuser.c @@ -0,0 +1,351 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: cdcuser.c + * Purpose: USB Communication Device Class User module + * Version: V1.10 + *---------------------------------------------------------------------------- +* This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#include "projectconfig.h" + +#include "usb.h" +#include "usbhw.h" +#include "usbcfg.h" +#include "usbcore.h" +#include "cdc.h" +#include "cdcuser.h" +#include "cdc_buf.h" + +// unsigned char BulkBufIn [64]; // Buffer to store USB IN packet +unsigned char BulkBufOut [64]; // Buffer to store USB OUT packet +unsigned char NotificationBuf [10]; + +CDC_LINE_CODING CDC_LineCoding = {CFG_USBCDC_BAUDRATE, 0, 0, 8}; +unsigned short CDC_SerialState = 0x0000; +unsigned short CDC_DepInEmpty = 1; // Data IN EP is empty + +/*---------------------------------------------------------------------------- + We need a buffer for incoming data on USB port because USB receives + much faster than UART transmits + *---------------------------------------------------------------------------*/ +/* Buffer masks */ +#define CDC_BUF_SIZE (64) // Output buffer in bytes (power 2) + // large enough for file transfer +#define CDC_BUF_MASK (CDC_BUF_SIZE-1ul) + +/* Buffer read / write macros */ +#define CDC_BUF_RESET(cdcBuf) (cdcBuf.rdIdx = cdcBuf.wrIdx = 0) +#define CDC_BUF_WR(cdcBuf, dataIn) (cdcBuf.data[CDC_BUF_MASK & cdcBuf.wrIdx++] = (dataIn)) +#define CDC_BUF_RD(cdcBuf) (cdcBuf.data[CDC_BUF_MASK & cdcBuf.rdIdx++]) +#define CDC_BUF_EMPTY(cdcBuf) (cdcBuf.rdIdx == cdcBuf.wrIdx) +#define CDC_BUF_FULL(cdcBuf) (cdcBuf.rdIdx == cdcBuf.wrIdx+1) +#define CDC_BUF_COUNT(cdcBuf) (CDC_BUF_MASK & (cdcBuf.wrIdx - cdcBuf.rdIdx)) + + +// CDC output buffer +typedef struct __CDC_BUF_T +{ + unsigned char data[CDC_BUF_SIZE]; + volatile unsigned int wrIdx; + volatile unsigned int rdIdx; +} CDC_BUF_T; + +CDC_BUF_T CDC_OutBuf; // buffer for all CDC Out data + +/*---------------------------------------------------------------------------- + read data from CDC_OutBuf + *---------------------------------------------------------------------------*/ +int CDC_RdOutBuf (char *buffer, const int *length) +{ + int bytesToRead, bytesRead; + + /* Read *length bytes, block if *bytes are not avaialable */ + bytesToRead = *length; + bytesToRead = (bytesToRead < (*length)) ? bytesToRead : (*length); + bytesRead = bytesToRead; + + + // ... add code to check for underrun + + while (bytesToRead--) { + *buffer++ = CDC_BUF_RD(CDC_OutBuf); + } + return (bytesRead); +} + +/*---------------------------------------------------------------------------- + write data to CDC_OutBuf + *---------------------------------------------------------------------------*/ +int CDC_WrOutBuf (const char *buffer, int *length) +{ + int bytesToWrite, bytesWritten; + + // Write *length bytes + bytesToWrite = *length; + bytesWritten = bytesToWrite; + + + // ... add code to check for overwrite + + while (bytesToWrite) { + CDC_BUF_WR(CDC_OutBuf, *buffer++); // Copy Data to buffer + bytesToWrite--; + } + + return (bytesWritten); +} + +/*---------------------------------------------------------------------------- + check if character(s) are available at CDC_OutBuf + *---------------------------------------------------------------------------*/ +int CDC_OutBufAvailChar (int *availChar) +{ + *availChar = CDC_BUF_COUNT(CDC_OutBuf); + + return (0); +} +/* end Buffer handling */ + + +/*---------------------------------------------------------------------------- + CDC Initialisation + Initializes the data structures and serial port + Parameters: None + Return Value: None + *---------------------------------------------------------------------------*/ +void CDC_Init (void) +{ + CDC_DepInEmpty = 1; + CDC_SerialState = CDC_GetSerialState(); + + CDC_BUF_RESET(CDC_OutBuf); + + // Initialise the CDC buffer. This is required to buffer outgoing + // data (MCU to PC) since data can only be sent 64 bytes per frame + // with at least 1ms between frames. To see how the buffer is used, + // see 'puts' in systeminit.c + cdcBufferInit(); +} + + +/*---------------------------------------------------------------------------- + CDC SendEncapsulatedCommand Request Callback + Called automatically on CDC SEND_ENCAPSULATED_COMMAND Request + Parameters: None (global SetupPacket and EP0Buf) + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_SendEncapsulatedCommand (void) +{ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC GetEncapsulatedResponse Request Callback + Called automatically on CDC Get_ENCAPSULATED_RESPONSE Request + Parameters: None (global SetupPacket and EP0Buf) + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_GetEncapsulatedResponse (void) +{ + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC SetCommFeature Request Callback + Called automatically on CDC Set_COMM_FATURE Request + Parameters: FeatureSelector + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_SetCommFeature (unsigned short wFeatureSelector) +{ + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC GetCommFeature Request Callback + Called automatically on CDC Get_COMM_FATURE Request + Parameters: FeatureSelector + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_GetCommFeature (unsigned short wFeatureSelector) +{ + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC ClearCommFeature Request Callback + Called automatically on CDC CLEAR_COMM_FATURE Request + Parameters: FeatureSelector + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_ClearCommFeature (unsigned short wFeatureSelector) +{ + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC SetLineCoding Request Callback + Called automatically on CDC SET_LINE_CODING Request + Parameters: none (global SetupPacket and EP0Buf) + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_SetLineCoding (void) +{ + CDC_LineCoding.dwDTERate = (EP0Buf[0] << 0) + | (EP0Buf[1] << 8) + | (EP0Buf[2] << 16) + | (EP0Buf[3] << 24); + CDC_LineCoding.bCharFormat = EP0Buf[4]; + CDC_LineCoding.bParityType = EP0Buf[5]; + CDC_LineCoding.bDataBits = EP0Buf[6]; + + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC GetLineCoding Request Callback + Called automatically on CDC GET_LINE_CODING Request + Parameters: None (global SetupPacket and EP0Buf) + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_GetLineCoding (void) +{ + EP0Buf[0] = (CDC_LineCoding.dwDTERate >> 0) & 0xFF; + EP0Buf[1] = (CDC_LineCoding.dwDTERate >> 8) & 0xFF; + EP0Buf[2] = (CDC_LineCoding.dwDTERate >> 16) & 0xFF; + EP0Buf[3] = (CDC_LineCoding.dwDTERate >> 24) & 0xFF; + EP0Buf[4] = CDC_LineCoding.bCharFormat; + EP0Buf[5] = CDC_LineCoding.bParityType; + EP0Buf[6] = CDC_LineCoding.bDataBits; + + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC SetControlLineState Request Callback + Called automatically on CDC SET_CONTROL_LINE_STATE Request + Parameters: ControlSignalBitmap + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_SetControlLineState (unsigned short wControlSignalBitmap) { + + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC SendBreak Request Callback + Called automatically on CDC Set_COMM_FATURE Request + Parameters: 0xFFFF start of Break + 0x0000 stop of Break + 0x#### Duration of Break + Return Value: TRUE - Success, FALSE - Error + *---------------------------------------------------------------------------*/ +uint32_t CDC_SendBreak (unsigned short wDurationOfBreak) { + + /* ... add code to handle request */ + return (TRUE); +} + + +/*---------------------------------------------------------------------------- + CDC_BulkIn call on DataIn Request + Parameters: none + Return Value: none + *---------------------------------------------------------------------------*/ +void CDC_BulkIn(void) +{ +// int numBytesRead, numBytesAvail; +// +// // ToDo: Modify BulkIn to send incoming data to USB +// +// ser_AvailChar (&numBytesAvail); +// +// // ... add code to check for overwrite +// +// numBytesRead = ser_Read ((char *)&BulkBufIn[0], &numBytesAvail); +// +// // send over USB +// if (numBytesRead > 0) { +// USB_WriteEP (CDC_DEP_IN, &BulkBufIn[0], numBytesRead); +// } +// else { +// CDC_DepInEmpty = 1; +// } +// +// +} + + +/*---------------------------------------------------------------------------- + CDC_BulkOut call on DataOut Request + Parameters: none + Return Value: none + *---------------------------------------------------------------------------*/ +void CDC_BulkOut(void) +{ + int numBytesRead; + + // get data from USB into intermediate buffer + numBytesRead = USB_ReadEP(CDC_DEP_OUT, &BulkBufOut[0]); + + // ... add code to check for overwrite + + // store data in a buffer to transmit it over serial interface + CDC_WrOutBuf ((char *)&BulkBufOut[0], &numBytesRead); +} + + +/*---------------------------------------------------------------------------- + Get the SERIAL_STATE as defined in usbcdc11.pdf, 6.3.5, Table 69. + Parameters: none + Return Value: SerialState as defined in usbcdc11.pdf + *---------------------------------------------------------------------------*/ +unsigned short CDC_GetSerialState (void) +{ + CDC_SerialState = 0; + + return (CDC_SerialState); +} + + +/*---------------------------------------------------------------------------- + Send the SERIAL_STATE notification as defined in usbcdc11.pdf, 6.3.5. + *---------------------------------------------------------------------------*/ +void CDC_NotificationIn (void) +{ + NotificationBuf[0] = 0xA1; // bmRequestType + NotificationBuf[1] = CDC_NOTIFICATION_SERIAL_STATE; // bNotification (SERIAL_STATE) + NotificationBuf[2] = 0x00; // wValue + NotificationBuf[3] = 0x00; + NotificationBuf[4] = 0x00; // wIndex (Interface #, LSB first) + NotificationBuf[5] = 0x00; + NotificationBuf[6] = 0x02; // wLength (Data length = 2 bytes, LSB first) + NotificationBuf[7] = 0x00; + NotificationBuf[8] = (CDC_SerialState >> 0) & 0xFF; // UART State Bitmap (16bits, LSB first) + NotificationBuf[9] = (CDC_SerialState >> 8) & 0xFF; + + USB_WriteEP (CDC_CEP_IN, &NotificationBuf[0], 10); // send notification +} diff --git a/core/usbcdc/cdcuser.h b/core/usbcdc/cdcuser.h new file mode 100644 index 0000000..55cd910 --- /dev/null +++ b/core/usbcdc/cdcuser.h @@ -0,0 +1,63 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: cdcuser.h + * Purpose: USB Communication Device Class User module Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __CDCUSER_H__ +#define __CDCUSER_H__ + +/* CDC buffer handling */ +extern int CDC_RdOutBuf (char *buffer, const int *length); +extern int CDC_WrOutBuf (const char *buffer, int *length); +extern int CDC_OutBufAvailChar (int *availChar); + + +/* CDC Data In/Out Endpoint Address */ +#define CDC_DEP_IN 0x83 +#define CDC_DEP_OUT 0x03 + +/* CDC Communication In Endpoint Address */ +#define CDC_CEP_IN 0x81 + +/* CDC Requests Callback Functions */ +extern uint32_t CDC_SendEncapsulatedCommand (void); +extern uint32_t CDC_GetEncapsulatedResponse (void); +extern uint32_t CDC_SetCommFeature (unsigned short wFeatureSelector); +extern uint32_t CDC_GetCommFeature (unsigned short wFeatureSelector); +extern uint32_t CDC_ClearCommFeature (unsigned short wFeatureSelector); +extern uint32_t CDC_GetLineCoding (void); +extern uint32_t CDC_SetLineCoding (void); +extern uint32_t CDC_SetControlLineState (unsigned short wControlSignalBitmap); +extern uint32_t CDC_SendBreak (unsigned short wDurationOfBreak); + +/* CDC Bulk Callback Functions */ +extern void CDC_BulkIn (void); +extern void CDC_BulkOut (void); + +/* CDC Notification Callback Function */ +extern void CDC_NotificationIn (void); + +/* CDC Initialization Function */ +extern void CDC_Init (void); + +/* CDC prepare the SERAIAL_STATE */ +extern unsigned short CDC_GetSerialState (void); + +/* flow control */ +extern unsigned short CDC_DepInEmpty; // DataEndPoint IN empty + +#endif /* __CDCUSER_H__ */ + diff --git a/core/usbcdc/config.h b/core/usbcdc/config.h new file mode 100644 index 0000000..fcfee37 --- /dev/null +++ b/core/usbcdc/config.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * config.h: config file for usbcdc example for NXP LPC13xx Family + * Microprocessors + * + * Copyright(C) 2008, NXP Semiconductor + * All rights reserved. + * + * History + * 2008.07.19 ver 1.00 Preliminary version, first Release + * +******************************************************************************/ + +/* +Overview: + This example shows how to use the USB driver to implement a CDC class USB peripheral. + To run this example, you must attach a USB cable to the board. See + the "Getting Started Guide" appendix for details. + +How to use: + Click the debug toolbar button. + Click the go button. + Plug the LPCXpresso's target side into a PC using a USB cable retrofit + or a 3rd party base board. + + * You should be able to see a new COM port on your PC. +*/ + +#include "projectconfig.h" + +#define USB_VENDOR_ID CFG_USB_VID // Vendor ID +#define USB_PROD_ID CFG_USB_PID // Product ID +#define USB_DEVICE 0x0100 // Device ID + +#define LED_PORT 0 // Port for led +#define LED_BIT 7 // Bit on port for led + + +/********************************************************************************* +** End Of File +*********************************************************************************/ diff --git a/core/usbcdc/usb.h b/core/usbcdc/usb.h new file mode 100644 index 0000000..6ba0aeb --- /dev/null +++ b/core/usbcdc/usb.h @@ -0,0 +1,228 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usb.h + * Purpose: USB Definitions + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __USB_H__ +#define __USB_H__ + + +typedef union { + uint16_t W; + struct { + uint8_t L; + uint8_t H; + } __attribute__((packed)) WB; +} __attribute__((packed)) WORD_BYTE; + + +/* bmRequestType.Dir */ +#define REQUEST_HOST_TO_DEVICE 0 +#define REQUEST_DEVICE_TO_HOST 1 + +/* bmRequestType.Type */ +#define REQUEST_STANDARD 0 +#define REQUEST_CLASS 1 +#define REQUEST_VENDOR 2 +#define REQUEST_RESERVED 3 + +/* bmRequestType.Recipient */ +#define REQUEST_TO_DEVICE 0 +#define REQUEST_TO_INTERFACE 1 +#define REQUEST_TO_ENDPOINT 2 +#define REQUEST_TO_OTHER 3 + +/* bmRequestType Definition */ +typedef union _REQUEST_TYPE { + struct _BM { + uint8_t Recipient : 5; + uint8_t Type : 2; + uint8_t Dir : 1; + } __attribute__((packed)) BM; + uint8_t B; +} __attribute__((packed)) REQUEST_TYPE; + +/* USB Standard Request Codes */ +#define USB_REQUEST_GET_STATUS 0 +#define USB_REQUEST_CLEAR_FEATURE 1 +#define USB_REQUEST_SET_FEATURE 3 +#define USB_REQUEST_SET_ADDRESS 5 +#define USB_REQUEST_GET_DESCRIPTOR 6 +#define USB_REQUEST_SET_DESCRIPTOR 7 +#define USB_REQUEST_GET_CONFIGURATION 8 +#define USB_REQUEST_SET_CONFIGURATION 9 +#define USB_REQUEST_GET_INTERFACE 10 +#define USB_REQUEST_SET_INTERFACE 11 +#define USB_REQUEST_SYNC_FRAME 12 + +/* USB GET_STATUS Bit Values */ +#define USB_GETSTATUS_SELF_POWERED 0x01 +#define USB_GETSTATUS_REMOTE_WAKEUP 0x02 +#define USB_GETSTATUS_ENDPOINT_STALL 0x01 + +/* USB Standard Feature selectors */ +#define USB_FEATURE_ENDPOINT_STALL 0 +#define USB_FEATURE_REMOTE_WAKEUP 1 + +/* USB Default Control Pipe Setup Packet */ +typedef struct _USB_SETUP_PACKET { + REQUEST_TYPE bmRequestType; + uint8_t bRequest; + WORD_BYTE wValue; + WORD_BYTE wIndex; + uint16_t wLength; +} __attribute__((packed)) USB_SETUP_PACKET; + + +/* USB Descriptor Types */ +#define USB_DEVICE_DESCRIPTOR_TYPE 1 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 2 +#define USB_STRING_DESCRIPTOR_TYPE 3 +#define USB_INTERFACE_DESCRIPTOR_TYPE 4 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 5 +#define USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE 6 +#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE 7 +#define USB_INTERFACE_POWER_DESCRIPTOR_TYPE 8 +#define USB_OTG_DESCRIPTOR_TYPE 9 +#define USB_DEBUG_DESCRIPTOR_TYPE 10 +#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 11 + +/* USB Device Classes */ +#define USB_DEVICE_CLASS_RESERVED 0x00 +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 +#define USB_DEVICE_CLASS_MONITOR 0x04 +#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05 +#define USB_DEVICE_CLASS_POWER 0x06 +#define USB_DEVICE_CLASS_PRINTER 0x07 +#define USB_DEVICE_CLASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_HUB 0x09 +#define USB_DEVICE_CLASS_MISCELLANEOUS 0xEF +#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF + +/* bmAttributes in Configuration Descriptor */ +#define USB_CONFIG_POWERED_MASK 0x40 +#define USB_CONFIG_BUS_POWERED 0x80 +#define USB_CONFIG_SELF_POWERED 0xC0 +#define USB_CONFIG_REMOTE_WAKEUP 0x20 + +/* bMaxPower in Configuration Descriptor */ +#define USB_CONFIG_POWER_MA(mA) ((mA)/2) + +/* bEndpointAddress in Endpoint Descriptor */ +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00) +#define USB_ENDPOINT_IN(addr) ((addr) | 0x80) + +/* bmAttributes in Endpoint Descriptor */ +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INTERRUPT 0x03 +#define USB_ENDPOINT_SYNC_MASK 0x0C +#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION 0x00 +#define USB_ENDPOINT_SYNC_ASYNCHRONOUS 0x04 +#define USB_ENDPOINT_SYNC_ADAPTIVE 0x08 +#define USB_ENDPOINT_SYNC_SYNCHRONOUS 0x0C +#define USB_ENDPOINT_USAGE_MASK 0x30 +#define USB_ENDPOINT_USAGE_DATA 0x00 +#define USB_ENDPOINT_USAGE_FEEDBACK 0x10 +#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK 0x20 +#define USB_ENDPOINT_USAGE_RESERVED 0x30 + +/* USB Standard Device Descriptor */ +typedef struct _USB_DEVICE_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)) USB_DEVICE_DESCRIPTOR; + +/* USB 2.0 Device Qualifier Descriptor */ +typedef struct _USB_DEVICE_QUALIFIER_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + uint8_t bReserved; +} __attribute__((packed)) USB_DEVICE_QUALIFIER_DESCRIPTOR; + +/* USB Standard Configuration Descriptor */ +typedef struct _USB_CONFIGURATION_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} __attribute__((packed)) USB_CONFIGURATION_DESCRIPTOR; + +/* USB Standard Interface Descriptor */ +typedef struct _USB_INTERFACE_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __attribute__((packed)) USB_INTERFACE_DESCRIPTOR; + +/* USB Standard Endpoint Descriptor */ +typedef struct _USB_ENDPOINT_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} __attribute__((packed)) USB_ENDPOINT_DESCRIPTOR; + +/* USB String Descriptor */ +typedef struct _USB_STRING_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString/*[]*/; +} __attribute__((packed)) USB_STRING_DESCRIPTOR; + +/* USB Common Descriptor */ +typedef struct _USB_COMMON_DESCRIPTOR { + uint8_t bLength; + uint8_t bDescriptorType; +} __attribute__((packed)) USB_COMMON_DESCRIPTOR; + + +#endif /* __USB_H__ */ diff --git a/core/usbcdc/usbcfg.h b/core/usbcdc/usbcfg.h new file mode 100644 index 0000000..cad6c43 --- /dev/null +++ b/core/usbcdc/usbcfg.h @@ -0,0 +1,157 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbcfg.h + * Purpose: USB Custom Configuration + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------- + * History: + * V1.20 Added vendor specific support + * V1.00 Initial Version + *---------------------------------------------------------------------------*/ + +#ifndef __USBCFG_H__ +#define __USBCFG_H__ + + +//*** <<< Use Configuration Wizard in Context Menu >>> *** + + +/* +// USB Configuration +// USB Power +// Default Power Setting +// <0=> Bus-powered +// <1=> Self-powered +// Max Number of Interfaces <1-256> +// Max Number of Endpoints <1-32> +// Max Endpoint 0 Packet Size +// <8=> 8 Bytes <16=> 16 Bytes <32=> 32 Bytes <64=> 64 Bytes +// DMA Transfer +// Use DMA for selected Endpoints +// Endpoint 0 Out +// Endpoint 0 In +// Endpoint 1 Out +// Endpoint 1 In +// Endpoint 2 Out +// Endpoint 2 In +// Endpoint 3 Out +// Endpoint 3 In +// Endpoint 4 Out +// Endpoint 4 In +// +// +*/ + +#define USB_POWER 0 +#define USB_IF_NUM 1 +#define USB_LOGIC_EP_NUM 5 +#define USB_EP_NUM 10 +#define USB_MAX_PACKET0 64 + +/* +// USB Event Handlers +// Device Events +// Power Event +// Reset Event +// Suspend Event +// Resume Event +// Remote Wakeup Event +// Start of Frame Event +// Error Event +// +// Endpoint Events +// Endpoint 0 Event +// Endpoint 1 Event +// Endpoint 2 Event +// Endpoint 3 Event +// Endpoint 4 Event +// Endpoint 5 Event +// Endpoint 6 Event +// Endpoint 7 Event +// Endpoint 8 Event +// Endpoint 9 Event +// Endpoint 10 Event +// Endpoint 11 Event +// Endpoint 12 Event +// Endpoint 13 Event +// Endpoint 14 Event +// Endpoint 15 Event +// +// USB Core Events +// Set Configuration Event +// Set Interface Event +// Set/Clear Feature Event +// +// +*/ + +#define USB_POWER_EVENT 0 +#define USB_RESET_EVENT 1 +#define USB_SUSPEND_EVENT 1 +#define USB_RESUME_EVENT 1 +#define USB_WAKEUP_EVENT 0 +#define USB_SOF_EVENT 1 +#define USB_ERROR_EVENT 0 +#define USB_EP_EVENT 0x000B +#define USB_CONFIGURE_EVENT 1 +#define USB_INTERFACE_EVENT 0 +#define USB_FEATURE_EVENT 0 + + +/* +// USB Class Support +// enables USB Class specific Requests +// Human Interface Device (HID) +// Interface Number <0-255> +// +// Mass Storage +// Interface Number <0-255> +// +// Audio Device +// Control Interface Number <0-255> +// Streaming Interface 1 Number <0-255> +// Streaming Interface 2 Number <0-255> +// +// Communication Device +// Control Interface Number <0-255> +// Bulk Interface Number <0-255> +// Max Communication Device Buffer Size +// <8=> 8 Bytes <16=> 16 Bytes <32=> 32 Bytes <64=> 64 Bytes +// +// +*/ + +#define USB_CLASS 1 +#define USB_HID 0 +#define USB_HID_IF_NUM 0 +#define USB_MSC 0 +#define USB_MSC_IF_NUM 0 +#define USB_AUDIO 0 +#define USB_ADC_CIF_NUM 0 +#define USB_ADC_SIF1_NUM 1 +#define USB_ADC_SIF2_NUM 2 +#define USB_CDC 1 +#define USB_CDC_CIF_NUM 0 +#define USB_CDC_DIF_NUM 1 +#define USB_CDC_BUFSIZE CFG_USBCDC_BUFSIZE + +/* +// USB Vendor Support +// enables USB Vendor specific Requests +// +*/ +#define USB_VENDOR 0 + + +#endif /* __USBCFG_H__ */ diff --git a/core/usbcdc/usbcore.c b/core/usbcdc/usbcore.c new file mode 100644 index 0000000..fe09bbd --- /dev/null +++ b/core/usbcdc/usbcore.c @@ -0,0 +1,1058 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbcore.c + * Purpose: USB Core Module + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------- + * History: + * V1.20 Added vendor specific requests + * Changed string descriptor handling + * Reworked Endpoint0 + * V1.00 Initial Version + *----------------------------------------------------------------------------*/ +#include "projectconfig.h" + +#include "usb.h" +#include "usbcfg.h" +#include "usbhw.h" +#include "usbcore.h" +#include "usbdesc.h" +#include "usbuser.h" + +#if (USB_CLASS) + +#if (USB_AUDIO) +#include "audio.h" +#include "adcuser.h" +#endif + +#if (USB_HID) +#include "hid.h" +#include "hiduser.h" +#endif + +#if (USB_MSC) +#include "msc.h" +#include "mscuser.h" +extern MSC_CSW CSW; +#endif + +#if (USB_CDC) +#include "cdc.h" +#include "cdcuser.h" +#endif + +#endif + +#if (USB_VENDOR) +#include "vendor.h" +#endif + +uint16_t USB_DeviceStatus; +uint8_t USB_DeviceAddress; +volatile uint8_t USB_Configuration; +uint32_t USB_EndPointMask; +uint32_t USB_EndPointHalt; +uint32_t USB_EndPointStall; /* EP must stay stalled */ +uint8_t USB_NumInterfaces; +uint8_t USB_AltSetting[USB_IF_NUM]; + +uint8_t EP0Buf[USB_MAX_PACKET0]; + + +USB_EP_DATA EP0Data; + +USB_SETUP_PACKET SetupPacket; + + +/* + * Reset USB Core + * Parameters: None + * Return Value: None + */ + +void USB_ResetCore (void) { + + USB_DeviceStatus = USB_POWER; + USB_DeviceAddress = 0; + USB_Configuration = 0; + USB_EndPointMask = 0x00010001; + USB_EndPointHalt = 0x00000000; + USB_EndPointStall = 0x00000000; +} + + +/* + * USB Request - Setup Stage + * Parameters: None (global SetupPacket) + * Return Value: None + */ + +void USB_SetupStage (void) { + USB_ReadEP(0x00, (uint8_t *)&SetupPacket); +} + + +/* + * USB Request - Data In Stage + * Parameters: None (global EP0Data) + * Return Value: None + */ + +void USB_DataInStage (void) { + uint32_t cnt; + + if (EP0Data.Count > USB_MAX_PACKET0) { + cnt = USB_MAX_PACKET0; + } else { + cnt = EP0Data.Count; + } + cnt = USB_WriteEP(0x80, EP0Data.pData, cnt); + EP0Data.pData += cnt; + EP0Data.Count -= cnt; +} + + +/* + * USB Request - Data Out Stage + * Parameters: None (global EP0Data) + * Return Value: None + */ + +void USB_DataOutStage (void) { + uint32_t cnt; + + cnt = USB_ReadEP(0x00, EP0Data.pData); + EP0Data.pData += cnt; + EP0Data.Count -= cnt; +} + + +/* + * USB Request - Status In Stage + * Parameters: None + * Return Value: None + */ + +void USB_StatusInStage (void) { + USB_WriteEP(0x80, NULL, 0); +} + + +/* + * USB Request - Status Out Stage + * Parameters: None + * Return Value: None + */ + +void USB_StatusOutStage (void) { + USB_ReadEP(0x00, EP0Buf); +} + + +/* + * Get Status USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqGetStatus (void) { + uint32_t n, m; + uint16_t* ep0 = (uint16_t __attribute__((packed)) *)EP0Buf; + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + EP0Data.pData = (uint8_t *)&USB_DeviceStatus; + break; + case REQUEST_TO_INTERFACE: + if ((USB_Configuration != 0) && (SetupPacket.wIndex.WB.L < USB_NumInterfaces)) { + *ep0 = 0; + EP0Data.pData = EP0Buf; + } else { + return (FALSE); + } + break; + case REQUEST_TO_ENDPOINT: + n = SetupPacket.wIndex.WB.L & 0x8F; + m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n); + if (((USB_Configuration != 0) || ((n & 0x0F) == 0)) && (USB_EndPointMask & m)) { + *ep0 = (USB_EndPointHalt & m) ? 1 : 0; + EP0Data.pData = EP0Buf; + } else { + return (FALSE); + } + break; + default: + return (FALSE); + } + return (TRUE); +} + + +/* + * Set/Clear Feature USB Request + * Parameters: sc: 0 - Clear, 1 - Set + * (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqSetClrFeature (uint32_t sc) { + uint32_t n, m; + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + if (SetupPacket.wValue.W == USB_FEATURE_REMOTE_WAKEUP) { + if (sc) { + USB_WakeUpCfg(TRUE); + USB_DeviceStatus |= USB_GETSTATUS_REMOTE_WAKEUP; + } else { + USB_WakeUpCfg(FALSE); + USB_DeviceStatus &= ~USB_GETSTATUS_REMOTE_WAKEUP; + } + } else { + return (FALSE); + } + break; + case REQUEST_TO_INTERFACE: + return (FALSE); + case REQUEST_TO_ENDPOINT: + n = SetupPacket.wIndex.WB.L & 0x8F; + m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n); + if ((USB_Configuration != 0) && ((n & 0x0F) != 0) && (USB_EndPointMask & m)) { + if (SetupPacket.wValue.W == USB_FEATURE_ENDPOINT_STALL) { + if (sc) { + USB_SetStallEP(n); + USB_EndPointHalt |= m; + } else { + if ((USB_EndPointStall & m) != 0) { + return (TRUE); + } + USB_ClrStallEP(n); +#if (USB_MSC) + if ((n == MSC_EP_IN) && ((USB_EndPointHalt & m) != 0)) { + /* Compliance Test: rewrite CSW after unstall */ + if (CSW.dSignature == MSC_CSW_Signature) { + USB_WriteEP(MSC_EP_IN, (uint8_t *)&CSW, sizeof(CSW)); + } + } +#endif + USB_EndPointHalt &= ~m; + } + } else { + return (FALSE); + } + } else { + return (FALSE); + } + break; + default: + return (FALSE); + } + return (TRUE); +} + + +/* + * Set Address USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqSetAddress (void) { + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L; + break; + default: + return (FALSE); + } + return (TRUE); +} + + +/* + * Get Descriptor USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqGetDescriptor (void) { + uint8_t *pD; + uint32_t len, n; + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + switch (SetupPacket.wValue.WB.H) { + case USB_DEVICE_DESCRIPTOR_TYPE: + EP0Data.pData = (uint8_t *)USB_DeviceDescriptor; + len = USB_DEVICE_DESC_SIZE; + break; + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + pD = (uint8_t *)USB_ConfigDescriptor; + for (n = 0; n != SetupPacket.wValue.WB.L; n++) { + if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength != 0) { + pD += ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; + } + } + if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0) { + return (FALSE); + } + EP0Data.pData = pD; + len = ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; + break; + case USB_STRING_DESCRIPTOR_TYPE: + pD = (uint8_t *)USB_StringDescriptor; + for (n = 0; n != SetupPacket.wValue.WB.L; n++) { + if (((USB_STRING_DESCRIPTOR *)pD)->bLength != 0) { + pD += ((USB_STRING_DESCRIPTOR *)pD)->bLength; + } + } + if (((USB_STRING_DESCRIPTOR *)pD)->bLength == 0) { + return (FALSE); + } + EP0Data.pData = pD; + len = ((USB_STRING_DESCRIPTOR *)EP0Data.pData)->bLength; + break; + default: + return (FALSE); + } + break; + case REQUEST_TO_INTERFACE: + switch (SetupPacket.wValue.WB.H) { +#if USB_HID + case HID_HID_DESCRIPTOR_TYPE: + if (SetupPacket.wIndex.WB.L != USB_HID_IF_NUM) { + return (FALSE); /* Only Single HID Interface is supported */ + } + EP0Data.pData = (uint8_t *)USB_ConfigDescriptor + HID_DESC_OFFSET; + len = HID_DESC_SIZE; + break; + case HID_REPORT_DESCRIPTOR_TYPE: + if (SetupPacket.wIndex.WB.L != USB_HID_IF_NUM) { + return (FALSE); /* Only Single HID Interface is supported */ + } + EP0Data.pData = (uint8_t *)HID_ReportDescriptor; + len = HID_ReportDescSize; + break; + case HID_PHYSICAL_DESCRIPTOR_TYPE: + return (FALSE); /* HID Physical Descriptor is not supported */ +#endif + default: + return (FALSE); + } + break; + default: + return (FALSE); + } + + if (EP0Data.Count > len) { + EP0Data.Count = len; + } + + return (TRUE); +} + +/* + * Get Configuration USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqGetConfiguration (void) { + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + // Added cast to avoid warnings due to USB_Configuration being volatile (KTownsend) + EP0Data.pData = (uint8_t *)&USB_Configuration; + break; + default: + return (FALSE); + } + return (TRUE); +} + +/* + * Set Configuration USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqSetConfiguration (void) { + USB_COMMON_DESCRIPTOR *pD; + uint32_t alt = 0; + uint32_t n, m; + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + + if (SetupPacket.wValue.WB.L) { + pD = (USB_COMMON_DESCRIPTOR *)USB_ConfigDescriptor; + while (pD->bLength) { + switch (pD->bDescriptorType) { + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bConfigurationValue == SetupPacket.wValue.WB.L) { + USB_Configuration = SetupPacket.wValue.WB.L; + USB_NumInterfaces = ((USB_CONFIGURATION_DESCRIPTOR *)pD)->bNumInterfaces; + for (n = 0; n < USB_IF_NUM; n++) { + USB_AltSetting[n] = 0; + } + for (n = 1; n < 16; n++) { + if (USB_EndPointMask & (1 << n)) { + USB_DisableEP(n); + } + if (USB_EndPointMask & ((1 << 16) << n)) { + USB_DisableEP(n | 0x80); + } + } + USB_EndPointMask = 0x00010001; + USB_EndPointHalt = 0x00000000; + USB_EndPointStall= 0x00000000; + USB_Configure(TRUE); + if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_POWERED_MASK) { + USB_DeviceStatus |= USB_GETSTATUS_SELF_POWERED; + } else { + USB_DeviceStatus &= ~USB_GETSTATUS_SELF_POWERED; + } + } else { + UsbAddPtr((void **)&pD, ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength); + continue; + } + break; + case USB_INTERFACE_DESCRIPTOR_TYPE: + alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting; + break; + case USB_ENDPOINT_DESCRIPTOR_TYPE: + if (alt == 0) { + n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8F; + m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n); + USB_EndPointMask |= m; + USB_ConfigEP((USB_ENDPOINT_DESCRIPTOR *)pD); + USB_EnableEP(n); + USB_ResetEP(n); + } + break; + } + UsbAddPtr((void **)&pD, pD->bLength); + } + } + else { + USB_Configuration = 0; + for (n = 1; n < 16; n++) { + if (USB_EndPointMask & (1 << n)) { + USB_DisableEP(n); + } + if (USB_EndPointMask & ((1 << 16) << n)) { + USB_DisableEP(n | 0x80); + } + } + USB_EndPointMask = 0x00010001; + USB_EndPointHalt = 0x00000000; + USB_EndPointStall = 0x00000000; + USB_Configure(FALSE); + } + + if (USB_Configuration != SetupPacket.wValue.WB.L) { + return (FALSE); + } + break; + default: + return (FALSE); + } + return (TRUE); +} + + +/* + * Get Interface USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqGetInterface (void) { + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_INTERFACE: + if ((USB_Configuration != 0) && (SetupPacket.wIndex.WB.L < USB_NumInterfaces)) { + EP0Data.pData = USB_AltSetting + SetupPacket.wIndex.WB.L; + } else { + return (FALSE); + } + break; + default: + return (FALSE); + } + return (TRUE); +} + + +/* + * Set Interface USB Request + * Parameters: None (global SetupPacket) + * Return Value: TRUE - Success, FALSE - Error + */ + +static inline uint32_t USB_ReqSetInterface (void) { + USB_COMMON_DESCRIPTOR *pD; + uint32_t ifn = 0, alt = 0, old = 0, msk = 0; + uint32_t n, m; + uint32_t set; + + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_INTERFACE: + if (USB_Configuration == 0) return (FALSE); + set = FALSE; + pD = (USB_COMMON_DESCRIPTOR *)USB_ConfigDescriptor; + while (pD->bLength) { + switch (pD->bDescriptorType) { + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + if (((USB_CONFIGURATION_DESCRIPTOR *)pD)->bConfigurationValue != USB_Configuration) { + UsbAddPtr((void **)&pD, ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength); + continue; + } + break; + case USB_INTERFACE_DESCRIPTOR_TYPE: + ifn = ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber; + alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting; + msk = 0; + if ((ifn == SetupPacket.wIndex.WB.L) && (alt == SetupPacket.wValue.WB.L)) { + set = TRUE; + old = USB_AltSetting[ifn]; + USB_AltSetting[ifn] = (uint8_t)alt; + } + break; + case USB_ENDPOINT_DESCRIPTOR_TYPE: + if (ifn == SetupPacket.wIndex.WB.L) { + n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8F; + m = (n & 0x80) ? ((1 << 16) << (n & 0x0F)) : (1 << n); + if (alt == SetupPacket.wValue.WB.L) { + USB_EndPointMask |= m; + USB_EndPointHalt &= ~m; + USB_ConfigEP((USB_ENDPOINT_DESCRIPTOR *)pD); + USB_EnableEP(n); + USB_ResetEP(n); + msk |= m; + } + else if ((alt == old) && ((msk & m) == 0)) { + USB_EndPointMask &= ~m; + USB_EndPointHalt &= ~m; + USB_DisableEP(n); + } + } + break; + } + UsbAddPtr((void **)&pD, pD->bLength); + } + break; + default: + return (FALSE); + } + + return (set); +} + +/* + * USB Endpoint 0 Event Callback + * Parameters: event + * Return Value: none + */ + +void USB_EndPoint0 (uint32_t event) { + + switch (event) { + case USB_EVT_SETUP: + USB_SetupStage(); + USB_DirCtrlEP(SetupPacket.bmRequestType.BM.Dir); + EP0Data.Count = SetupPacket.wLength; /* Number of bytes to transfer */ + switch (SetupPacket.bmRequestType.BM.Type) { + + case REQUEST_STANDARD: + switch (SetupPacket.bRequest) { + case USB_REQUEST_GET_STATUS: + if (!USB_ReqGetStatus()) { + goto stall_i; + } + USB_DataInStage(); + break; + + case USB_REQUEST_CLEAR_FEATURE: + if (!USB_ReqSetClrFeature(0)) { + goto stall_i; + } + USB_StatusInStage(); +#if USB_FEATURE_EVENT + USB_Feature_Event(); +#endif + break; + + case USB_REQUEST_SET_FEATURE: + if (!USB_ReqSetClrFeature(1)) { + goto stall_i; + } + USB_StatusInStage(); +#if USB_FEATURE_EVENT + USB_Feature_Event(); +#endif + break; + + case USB_REQUEST_SET_ADDRESS: + if (!USB_ReqSetAddress()) { + goto stall_i; + } + USB_StatusInStage(); + break; + + case USB_REQUEST_GET_DESCRIPTOR: + if (!USB_ReqGetDescriptor()) { + goto stall_i; + } + USB_DataInStage(); + break; + + case USB_REQUEST_SET_DESCRIPTOR: +/*stall_o:*/ USB_SetStallEP(0x00); /* not supported */ + EP0Data.Count = 0; + break; + + case USB_REQUEST_GET_CONFIGURATION: + if (!USB_ReqGetConfiguration()) { + goto stall_i; + } + USB_DataInStage(); + break; + + case USB_REQUEST_SET_CONFIGURATION: + if (!USB_ReqSetConfiguration()) { + goto stall_i; + } + USB_StatusInStage(); +#if USB_CONFIGURE_EVENT + USB_Configure_Event(); +#endif + break; + + case USB_REQUEST_GET_INTERFACE: + if (!USB_ReqGetInterface()) { + goto stall_i; + } + USB_DataInStage(); + break; + + case USB_REQUEST_SET_INTERFACE: + if (!USB_ReqSetInterface()) { + goto stall_i; + } + USB_StatusInStage(); +#if USB_INTERFACE_EVENT + USB_Interface_Event(); +#endif + break; + + default: + goto stall_i; + } + break; /* end case REQUEST_STANDARD */ + +#if USB_CLASS + case REQUEST_CLASS: + switch (SetupPacket.bmRequestType.BM.Recipient) { + + case REQUEST_TO_DEVICE: + goto stall_i; /* not supported */ + + case REQUEST_TO_INTERFACE: +#if USB_HID + if (SetupPacket.wIndex.WB.L == USB_HID_IF_NUM) { /* IF number correct? */ + switch (SetupPacket.bRequest) { + case HID_REQUEST_GET_REPORT: + if (HID_GetReport()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case HID_REQUEST_SET_REPORT: + EP0Data.pData = EP0Buf; /* data to be received */ + goto setup_class_ok; + case HID_REQUEST_GET_IDLE: + if (HID_GetIdle()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case HID_REQUEST_SET_IDLE: + if (HID_SetIdle()) { + USB_StatusInStage(); /* send Acknowledge */ + goto setup_class_ok; + } + break; + case HID_REQUEST_GET_PROTOCOL: + if (HID_GetProtocol()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case HID_REQUEST_SET_PROTOCOL: + if (HID_SetProtocol()) { + USB_StatusInStage(); /* send Acknowledge */ + goto setup_class_ok; + } + break; + } + } +#endif /* USB_HID */ +#if USB_MSC + if (SetupPacket.wIndex.WB.L == USB_MSC_IF_NUM) { /* IF number correct? */ + switch (SetupPacket.bRequest) { + case MSC_REQUEST_RESET: + if ((SetupPacket.wValue.W == 0) && /* RESET with invalid parameters -> STALL */ + (SetupPacket.wLength == 0)) { + if (MSC_Reset()) { + USB_StatusInStage(); + goto setup_class_ok; + } + } + break; + case MSC_REQUEST_GET_MAX_LUN: + if ((SetupPacket.wValue.W == 0) && /* GET_MAX_LUN with invalid parameters -> STALL */ + (SetupPacket.wLength == 1)) { + if (MSC_GetMaxLUN()) { + EP0Data.pData = EP0Buf; + USB_DataInStage(); + goto setup_class_ok; + } + } + break; + } + } +#endif /* USB_MSC */ +#if USB_AUDIO + if ((SetupPacket.wIndex.WB.L == USB_ADC_CIF_NUM) || /* IF number correct? */ + (SetupPacket.wIndex.WB.L == USB_ADC_SIF1_NUM) || + (SetupPacket.wIndex.WB.L == USB_ADC_SIF2_NUM)) { + switch (SetupPacket.bRequest) { + case AUDIO_REQUEST_GET_CUR: + case AUDIO_REQUEST_GET_MIN: + case AUDIO_REQUEST_GET_MAX: + case AUDIO_REQUEST_GET_RES: + if (ADC_IF_GetRequest()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case AUDIO_REQUEST_SET_CUR: +// case AUDIO_REQUEST_SET_MIN: +// case AUDIO_REQUEST_SET_MAX: +// case AUDIO_REQUEST_SET_RES: + EP0Data.pData = EP0Buf; /* data to be received */ + goto setup_class_ok; + } + } +#endif /* USB_AUDIO */ +#if USB_CDC + if ((SetupPacket.wIndex.WB.L == USB_CDC_CIF_NUM) || /* IF number correct? */ + (SetupPacket.wIndex.WB.L == USB_CDC_DIF_NUM)) { + switch (SetupPacket.bRequest) { + case CDC_SEND_ENCAPSULATED_COMMAND: + EP0Data.pData = EP0Buf; /* data to be received, see USB_EVT_OUT */ + goto setup_class_ok; + case CDC_GET_ENCAPSULATED_RESPONSE: + if (CDC_GetEncapsulatedResponse()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case CDC_SET_COMM_FEATURE: + EP0Data.pData = EP0Buf; /* data to be received, see USB_EVT_OUT */ + goto setup_class_ok; + case CDC_GET_COMM_FEATURE: + if (CDC_GetCommFeature(SetupPacket.wValue.W)) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case CDC_CLEAR_COMM_FEATURE: + if (CDC_ClearCommFeature(SetupPacket.wValue.W)) { + USB_StatusInStage(); /* send Acknowledge */ + goto setup_class_ok; + } + break; + case CDC_SET_LINE_CODING: + EP0Data.pData = EP0Buf; /* data to be received, see USB_EVT_OUT */ + goto setup_class_ok; + case CDC_GET_LINE_CODING: + if (CDC_GetLineCoding()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case CDC_SET_CONTROL_LINE_STATE: + if (CDC_SetControlLineState(SetupPacket.wValue.W)) { + USB_StatusInStage(); /* send Acknowledge */ + goto setup_class_ok; + } + break; + case CDC_SEND_BREAK: + if (CDC_SendBreak(SetupPacket.wValue.W)) { + USB_StatusInStage(); /* send Acknowledge */ + goto setup_class_ok; + } + break; + } + } +#endif /* USB_CDC */ + goto stall_i; /* not supported */ + /* end case REQUEST_TO_INTERFACE */ + + case REQUEST_TO_ENDPOINT: +#if USB_AUDIO + switch (SetupPacket.bRequest) { + case AUDIO_REQUEST_GET_CUR: + case AUDIO_REQUEST_GET_MIN: + case AUDIO_REQUEST_GET_MAX: + case AUDIO_REQUEST_GET_RES: + if (ADC_EP_GetRequest()) { + EP0Data.pData = EP0Buf; /* point to data to be sent */ + USB_DataInStage(); /* send requested data */ + goto setup_class_ok; + } + break; + case AUDIO_REQUEST_SET_CUR: +// case AUDIO_REQUEST_SET_MIN: +// case AUDIO_REQUEST_SET_MAX: +// case AUDIO_REQUEST_SET_RES: + EP0Data.pData = EP0Buf; /* data to be received */ + goto setup_class_ok; + } +#endif /* USB_AUDIO */ + goto stall_i; + /* end case REQUEST_TO_ENDPOINT */ + + default: + goto stall_i; + } +setup_class_ok: /* request finished successfully */ + break; /* end case REQUEST_CLASS */ +#endif /* USB_CLASS */ + +#if USB_VENDOR + case REQUEST_VENDOR: + switch (SetupPacket.bmRequestType.BM.Recipient) { + + case REQUEST_TO_DEVICE: + if (!USB_ReqVendorDev(TRUE)) { + goto stall_i; /* not supported */ + } + break; + + case REQUEST_TO_INTERFACE: + if (!USB_ReqVendorIF(TRUE)) { + goto stall_i; /* not supported */ + } + break; + + case REQUEST_TO_ENDPOINT: + if (!USB_ReqVendorEP(TRUE)) { + goto stall_i; /* not supported */ + } + break; + + default: + goto stall_i; + } + + if (SetupPacket.wLength) { + if (SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) { + USB_DataInStage(); + } + } else { + USB_StatusInStage(); + } + + break; /* end case REQUEST_VENDOR */ +#endif /* USB_VENDOR */ + + default: +stall_i: USB_SetStallEP(0x80); + EP0Data.Count = 0; + break; + } + break; /* end case USB_EVT_SETUP */ + + case USB_EVT_OUT: + if (SetupPacket.bmRequestType.BM.Dir == REQUEST_HOST_TO_DEVICE) { + if (EP0Data.Count) { /* still data to receive ? */ + USB_DataOutStage(); /* receive data */ + if (EP0Data.Count == 0) { /* data complete ? */ + switch (SetupPacket.bmRequestType.BM.Type) { + + case REQUEST_STANDARD: + goto stall_i; /* not supported */ + +#if (USB_CLASS) + case REQUEST_CLASS: + switch (SetupPacket.bmRequestType.BM.Recipient) { + case REQUEST_TO_DEVICE: + goto stall_i; /* not supported */ + + case REQUEST_TO_INTERFACE: +#if USB_HID + if (SetupPacket.wIndex.WB.L == USB_HID_IF_NUM) { /* IF number correct? */ + switch (SetupPacket.bRequest) { + case HID_REQUEST_SET_REPORT: + if (HID_SetReport()) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + } + } +#endif /* USB_HID */ +#if USB_AUDIO + if ((SetupPacket.wIndex.WB.L == USB_ADC_CIF_NUM) || /* IF number correct? */ + (SetupPacket.wIndex.WB.L == USB_ADC_SIF1_NUM) || + (SetupPacket.wIndex.WB.L == USB_ADC_SIF2_NUM)) { + switch (SetupPacket.bRequest) { + case AUDIO_REQUEST_SET_CUR: +// case AUDIO_REQUEST_SET_MIN: +// case AUDIO_REQUEST_SET_MAX: +// case AUDIO_REQUEST_SET_RES: + if (ADC_IF_SetRequest()) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + } + } +#endif /* USB_AUDIO */ +#if USB_CDC + if ((SetupPacket.wIndex.WB.L == USB_CDC_CIF_NUM) || /* IF number correct? */ + (SetupPacket.wIndex.WB.L == USB_CDC_DIF_NUM)) { + switch (SetupPacket.bRequest) { + case CDC_SEND_ENCAPSULATED_COMMAND: + if (CDC_SendEncapsulatedCommand()) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + case CDC_SET_COMM_FEATURE: + if (CDC_SetCommFeature(SetupPacket.wValue.W)) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + case CDC_SET_LINE_CODING: + if (CDC_SetLineCoding()) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + } + } +#endif /* USB_CDC */ + goto stall_i; + /* end case REQUEST_TO_INTERFACE */ + + case REQUEST_TO_ENDPOINT: +#if USB_AUDIO + switch (SetupPacket.bRequest) { + case AUDIO_REQUEST_SET_CUR: +// case AUDIO_REQUEST_SET_MIN: +// case AUDIO_REQUEST_SET_MAX: +// case AUDIO_REQUEST_SET_RES: + if (ADC_EP_SetRequest()) { + USB_StatusInStage(); /* send Acknowledge */ + goto out_class_ok; + } + break; + } +#endif /* USB_AUDIO */ + goto stall_i; + /* end case REQUEST_TO_ENDPOINT */ + + default: + goto stall_i; + } +out_class_ok: /* request finished successfully */ + break; /* end case REQUEST_CLASS */ +#endif /* USB_CLASS */ + +#if USB_VENDOR + case REQUEST_VENDOR: + switch (SetupPacket.bmRequestType.BM.Recipient) { + + case REQUEST_TO_DEVICE: + if (!USB_ReqVendorDev(FALSE)) { + goto stall_i; /* not supported */ + } + break; + + case REQUEST_TO_INTERFACE: + if (!USB_ReqVendorIF(FALSE)) { + goto stall_i; /* not supported */ + } + break; + + case REQUEST_TO_ENDPOINT: + if (!USB_ReqVendorEP(FALSE)) { + goto stall_i; /* not supported */ + } + break; + + default: + goto stall_i; + } + + USB_StatusInStage(); + + break; /* end case REQUEST_VENDOR */ +#endif /* USB_VENDOR */ + + default: + goto stall_i; + } + } + } + } else { + USB_StatusOutStage(); /* receive Acknowledge */ + } + break; /* end case USB_EVT_OUT */ + + case USB_EVT_IN : + if (SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) { + USB_DataInStage(); /* send data */ + } else { + if (USB_DeviceAddress & 0x80) { + USB_DeviceAddress &= 0x7F; + USB_SetAddress(USB_DeviceAddress); + } + } + break; /* end case USB_EVT_IN */ + + case USB_EVT_OUT_STALL: + USB_ClrStallEP(0x00); + break; + + case USB_EVT_IN_STALL: + USB_ClrStallEP(0x80); + break; + + } +} diff --git a/core/usbcdc/usbcore.h b/core/usbcdc/usbcore.h new file mode 100644 index 0000000..5098be0 --- /dev/null +++ b/core/usbcdc/usbcore.h @@ -0,0 +1,88 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbcore.h + * Purpose: USB Core Definitions + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __USBCORE_H__ +#define __USBCORE_H__ + +#include "usbcfg.h" + +/* USB Endpoint Data Structure */ +typedef struct _USB_EP_DATA { + uint8_t *pData; + uint16_t Count; +} USB_EP_DATA; + +/* USB Core Global Variables */ +extern uint16_t USB_DeviceStatus; +extern uint8_t USB_DeviceAddress; +volatile extern uint8_t USB_Configuration; +extern uint32_t USB_EndPointMask; +extern uint32_t USB_EndPointHalt; +extern uint32_t USB_EndPointStall; +extern uint8_t USB_AltSetting[USB_IF_NUM]; + + +/* USB Endpoint 0 Buffer */ +extern uint8_t EP0Buf[USB_MAX_PACKET0]; + +/* USB Endpoint 0 Data Info */ +extern USB_EP_DATA EP0Data; + +/* USB Setup Packet */ +extern USB_SETUP_PACKET SetupPacket; + +/* USB Core Functions */ +extern void USB_ResetCore (void); + +/* Newer C compilers make it really difficult to add + * an integer to a pointer */ +static inline void UsbAddPtr(void **vpptr, uint32_t n); + +/* + * Add a number of bytes to a pointer's address + * Harder than you might think. Some compilers say: + * Expected an lvalue -- Assignment expects its first operand to be + * an lvalue. Please note that a cast removes the lvaluedness of an + * expression. + * + * vpptr = void pointer to pointer + * n = number of bytes to add to pointer + * Call looks like: AddPtr((void **)&myPointer, 8); + */ +static inline void UsbAddPtr(void **vpptr, uint32_t n) +{ + /* Declare a pointer to a pointer to a byte. Only a byte pointer + * can be incremented by a number of bytes. Other pointers will + * increment by a multiple of what they point to. + */ + uint8_t **bpptr; + + /* Convert our void pointer to a pointer to a byte pointer to a pointer */ + bpptr = (uint8_t **)vpptr; + + /* Add 'n' bytes to our pointer value */ + (*bpptr) += n; +} + + + + + + + +#endif /* __USBCORE_H__ */ diff --git a/core/usbcdc/usbdesc.c b/core/usbcdc/usbdesc.c new file mode 100644 index 0000000..6642e60 --- /dev/null +++ b/core/usbcdc/usbdesc.c @@ -0,0 +1,202 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbdesc.c + * Purpose: USB Descriptors + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------- + * History: + * V1.20 Changed string descriptor handling + * V1.00 Initial Version + *---------------------------------------------------------------------------*/ +#include "projectconfig.h" +#include "usb.h" +#include "cdc.h" +#include "usbcfg.h" +#include "usbdesc.h" +#include "config.h" + + +/* USB Standard Device Descriptor */ +const uint8_t USB_DeviceDescriptor[] = { + USB_DEVICE_DESC_SIZE, /* bLength */ + USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL(0x0200), /* 2.0 */ /* bcdUSB */ + USB_DEVICE_CLASS_COMMUNICATIONS, /* bDeviceClass CDC*/ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_MAX_PACKET0, /* bMaxPacketSize0 */ + WBVAL(USB_VENDOR_ID), /* idVendor */ + WBVAL(USB_PROD_ID), /* idProduct */ + WBVAL(USB_DEVICE), /* 1.00 */ /* bcdDevice */ + 0x01, /* iManufacturer */ + 0x02, /* iProduct */ + 0x03, /* iSerialNumber */ + 0x01 /* bNumConfigurations: one possible configuration*/ +}; + +/* USB Configuration Descriptor */ +/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ +const uint8_t USB_ConfigDescriptor[] = { +/* Configuration 1 */ + USB_CONFIGUARTION_DESC_SIZE, /* bLength */ + USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL( /* wTotalLength */ + 1*USB_CONFIGUARTION_DESC_SIZE + + 1*USB_INTERFACE_DESC_SIZE + /* communication interface */ + 0x0013 + /* CDC functions */ + 1*USB_ENDPOINT_DESC_SIZE + /* interrupt endpoint */ + 1*USB_INTERFACE_DESC_SIZE + /* data interface */ + 2*USB_ENDPOINT_DESC_SIZE /* bulk endpoints */ + ), + 0x02, /* bNumInterfaces */ + 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ + 0x00, /* iConfiguration: no string to describe this configuration */ + USB_CONFIG_BUS_POWERED /*|*/ /* bmAttributes */ +/*USB_CONFIG_REMOTE_WAKEUP*/, + USB_CONFIG_POWER_MA(100), /* bMaxPower, device power consumption is 100 mA */ +/* Interface 0, Alternate Setting 0, Communication class interface descriptor */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_CDC_CIF_NUM, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints: One endpoint used */ + CDC_COMMUNICATION_INTERFACE_CLASS, /* bInterfaceClass: Communication Interface Class */ + CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass: Abstract Control Model */ + 0x00, /* bInterfaceProtocol: no protocol used */ + 0x5E, /* iInterface: */ +/*Header Functional Descriptor*/ + 0x05, /* bLength: Endpoint Descriptor size */ + CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ + CDC_HEADER, /* bDescriptorSubtype: Header Func Desc */ + WBVAL(CDC_V1_10), /* 1.10 */ /* bcdCDC */ +/*Call Management Functional Descriptor*/ + 0x05, /* bFunctionLength */ + CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ + CDC_CALL_MANAGEMENT, /* bDescriptorSubtype: Call Management Func Desc */ + 0x01, /* bmCapabilities: device handles call management */ + 0x01, /* bDataInterface: CDC data IF ID */ +/*Abstract Control Management Functional Descriptor*/ + 0x04, /* bFunctionLength */ + CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ + CDC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype: Abstract Control Management desc */ + 0x02, /* bmCapabilities: SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported */ +/*Union Functional Descriptor*/ + 0x05, /* bFunctionLength */ + CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */ + CDC_UNION, /* bDescriptorSubtype: Union func desc */ + USB_CDC_CIF_NUM, /* bMasterInterface: Communication class interface is master */ + USB_CDC_DIF_NUM, /* bSlaveInterface0: Data class interface is slave 0 */ +/*Endpoint 1 Descriptor*/ /* event notification (optional) */ + USB_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_IN(1), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ + WBVAL(0x0010), /* wMaxPacketSize */ + 0x02, /* 2ms */ /* bInterval */ +/* Interface 1, Alternate Setting 0, Data class interface descriptor*/ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_CDC_DIF_NUM, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: no alternate setting */ + 0x02, /* bNumEndpoints: two endpoints used */ + CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass: Data Interface Class */ + 0x00, /* bInterfaceSubClass: no subclass available */ + 0x00, /* bInterfaceProtocol: no protocol used */ + 0x5E, /* iInterface: */ +/* Endpoint, EP3 Bulk Out */ + USB_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(3), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ + WBVAL(64), /* wMaxPacketSize */ + 0x00, /* bInterval: ignore for Bulk transfer */ +/* Endpoint, EP3 Bulk In */ + USB_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_IN(3), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ + WBVAL(64), /* wMaxPacketSize */ + 0x00, /* bInterval: ignore for Bulk transfer */ +/* Terminator */ + 0 /* bLength */ +}; + + + + +/* USB String Descriptor (optional) */ +const uint8_t USB_StringDescriptor[] = { +/* Index 0x00: LANGID Codes */ + 0x04, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL(0x0409), /* US English */ /* wLANGID */ +/* Index 0x01: Manufacturer */ + (13*2 + 2), /* bLength (13 Char + Type + lenght) */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'M',0, + 'I',0, + 'C',0, + 'R',0, + 'O',0, + 'B',0, + 'U',0, + 'I',0, + 'L',0, + 'D',0, + 'E',0, + 'R',0, + ' ',0, +/* Index 0x02: Product */ + (17*2 + 2), /* bLength ( 17 Char + Type + lenght) */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'L',0, + 'P',0, + 'C',0, + '1',0, + '3',0, + '4',0, + '3',0, + ' ',0, + 'C',0, + 'O',0, + 'M',0, + ' ',0, + 'P',0, + 'O',0, + 'R',0, + 'T',0, + ' ',0, +/* Index 0x03: Serial Number */ + (12*2 + 2), /* bLength (12 Char + Type + lenght) */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'D',0, + 'E',0, + 'M',0, + 'O',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, +/* Index 0x04: Interface 0, Alternate Setting 0 */ + ( 4*2 + 2), /* bLength (4 Char + Type + lenght) */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'V',0, + 'C',0, + 'O',0, + 'M',0, +}; diff --git a/core/usbcdc/usbdesc.h b/core/usbcdc/usbdesc.h new file mode 100644 index 0000000..b6eeddc --- /dev/null +++ b/core/usbcdc/usbdesc.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbdesc.h + * Purpose: USB Descriptors Definitions + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __USBDESC_H__ +#define __USBDESC_H__ + + +#define WBVAL(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) + +#define USB_DEVICE_DESC_SIZE (sizeof(USB_DEVICE_DESCRIPTOR)) +#define USB_CONFIGUARTION_DESC_SIZE (sizeof(USB_CONFIGURATION_DESCRIPTOR)) +#define USB_INTERFACE_DESC_SIZE (sizeof(USB_INTERFACE_DESCRIPTOR)) +#define USB_ENDPOINT_DESC_SIZE (sizeof(USB_ENDPOINT_DESCRIPTOR)) + +extern const uint8_t USB_DeviceDescriptor[]; +extern const uint8_t USB_ConfigDescriptor[]; +extern const uint8_t USB_StringDescriptor[]; + + +#endif /* __USBDESC_H__ */ diff --git a/core/usbcdc/usbhw.c b/core/usbcdc/usbhw.c new file mode 100644 index 0000000..fdc88f1 --- /dev/null +++ b/core/usbcdc/usbhw.c @@ -0,0 +1,606 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbhw.c + * Purpose: USB Hardware Layer Module for Philips LPC17xx + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------- + * History: + * V1.20 Added USB_ClearEPBuf + * V1.00 Initial Version + *----------------------------------------------------------------------------*/ +#include "projectconfig.h" /* LPC13xx definitions */ +#include "usb.h" +#include "usbcfg.h" +#include "usbreg.h" +#include "usbhw.h" +#include "usbcore.h" +#include "usbuser.h" + + +/* + * USB and IO Clock configuration only. + * The same as call PeriClkIOInit(IOCON_USB); + * The purpose is to reduce the code space for + * overall USB project and reserve code space for + * USB debugging. + * Parameters: None + * Return Value: None + */ +void USBIOClkConfig( void ) +{ + /* Enable AHB clock to the GPIO domain. */ + SCB_SYSAHBCLKCTRL |= SCB_SYSAHBCLKCTRL_GPIO; + + /* Enable Timer32_1, IOCON, and USB blocks */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B1 | SCB_SYSAHBCLKCTRL_IOCON | SCB_SYSAHBCLKCTRL_USB_REG); + + // Setup USB clock + SCB_PDRUNCFG &= ~(SCB_PDSLEEPCFG_USBPAD_PD); // Power-up USB PHY + SCB_PDRUNCFG &= ~(SCB_PDSLEEPCFG_USBPLL_PD); // Power-up USB PLL + + SCB_USBPLLCLKSEL = SCB_USBPLLCLKSEL_SOURCE_MAINOSC; // Select PLL Input + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_UPDATE; // Update Clock Source + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_DISABLE; // Toggle Update Register + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_UPDATE; + + // Wait until the USB clock is updated + while (!(SCB_USBPLLCLKUEN & SCB_USBPLLCLKUEN_UPDATE)); + + // Set USB clock to 48MHz (12MHz x 4) + SCB_USBPLLCTRL = (SCB_USBPLLCTRL_MULT_4); + while (!(SCB_USBPLLSTAT & SCB_USBPLLSTAT_LOCK)); // Wait Until PLL Locked + SCB_USBCLKSEL = SCB_USBCLKSEL_SOURCE_USBPLLOUT; + + // Set USB pin functions + IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK; + IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CLKOUT; // CLK OUT + IOCON_PIO0_3 &= ~IOCON_PIO0_3_FUNC_MASK; + IOCON_PIO0_3 |= IOCON_PIO0_3_FUNC_USB_VBUS; // VBus + IOCON_PIO0_6 &= ~IOCON_PIO0_6_FUNC_MASK; + IOCON_PIO0_6 |= IOCON_PIO0_6_FUNC_USB_CONNECT; // Soft Connect + + return; +} + +/* + * Delay number of clock cycles + * Parameters: Delay length + * Return Value: None + */ + +void delay (uint32_t length ) +{ + uint32_t i; + + for ( i = 0; i < length; i++ ) + { + __asm("nop"); + } + return; +} + +/* + * Get Endpoint Physical Address + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: Endpoint Physical Address + */ + +uint32_t EPAdr (uint32_t EPNum) +{ + uint32_t val; + + val = (EPNum & 0x0F) << 1; + if (EPNum & 0x80) { + val += 1; + } + return (val); +} + + +/* + * Write Command + * Parameters: cmd: Command + * Return Value: None + */ + +void WrCmd (uint32_t cmd) +{ + USB_DEVINTCLR = CCEMTY_INT; + USB_CMDCODE = cmd; + while ((USB_DEVINTST & (CCEMTY_INT | DEV_STAT_INT)) == 0); +} + + +/* + * Write Command Data + * Parameters: cmd: Command + * val: Data + * Return Value: None + */ + +void WrCmdDat (uint32_t cmd, uint32_t val) +{ + WrCmd(cmd); + WrCmd(val); +} + + +/* + * Write Command to Endpoint + * Parameters: cmd: Command + * val: Data + * Return Value: None + */ + +void WrCmdEP (uint32_t EPNum, uint32_t cmd) +{ + WrCmd(CMD_SEL_EP(EPAdr(EPNum))); + WrCmd(cmd); +} + + +/* + * Read Command Data + * Parameters: cmd: Command + * Return Value: Data Value + */ + +uint32_t RdCmdDat (uint32_t cmd) +{ + USB_DEVINTCLR = CCEMTY_INT | CDFULL_INT; + USB_CMDCODE = cmd; + while ((USB_DEVINTST & (CDFULL_INT | DEV_STAT_INT)) == 0); + return (USB_CMDDATA); +} + + +/* + * USB Initialize Function + * Called by the User to initialize USB + * Return Value: None + */ + +void USB_Init (void) +{ + // Setup USB clock and pins + USBIOClkConfig(); + +#if USB_FIQ_EVENT + /* It's important that only BULK and FRAME(ISO) can be routed + to FIQ. */ + USB_DEVFIQSEL = 0x01; /* SOF Use FIQ */ + + /* Enable the USB Interrupt */ + NVIC_EnableIRQ(USB_FIQn); +#endif + + /* Enable the USB Interrupt */ + NVIC_EnableIRQ(USB_IRQn); + + USB_Reset(); + USB_SetAddress(0); + return; +} + + +/* + * USB Connect Function + * Called by the User to Connect/Disconnect USB + * Parameters: con: Connect/Disconnect + * Return Value: None + */ + +void USB_Connect (uint32_t con) +{ + WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(con ? DEV_CON : 0)); +} + + +/* + * USB Reset Function + * Called automatically on USB Reset + * Return Value: None + */ + +void USB_Reset (void) +{ + USB_DEVINTCLR = 0x000FFFFF; + /* Enable all eight(8) EPs, note: EP won't be ready until it's + configured/enabled when device sending SetEPStatus command + to the command engine. */ + USB_DEVINTEN = DEV_STAT_INT | (0xFF<<1) | + (USB_SOF_EVENT ? FRAME_INT : 0); + return; +} + + +/* + * USB Suspend Function + * Called automatically on USB Suspend + * Return Value: None + */ + +void USB_Suspend (void) +{ + /* Performed by Hardware */ +} + + +/* + * USB Resume Function + * Called automatically on USB Resume + * Return Value: None + */ + +void USB_Resume (void) +{ + /* Performed by Hardware */ +} + + +/* + * USB Remote Wakeup Function + * Called automatically on USB Remote Wakeup + * Return Value: None + */ + +void USB_WakeUp (void) +{ + if (USB_DeviceStatus & USB_GETSTATUS_REMOTE_WAKEUP) + { + WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON)); + } +} + + +/* + * USB Remote Wakeup Configuration Function + * Parameters: cfg: Enable/Disable + * Return Value: None + */ + +void USB_WakeUpCfg (uint32_t cfg) +{ + cfg = cfg; /* Not needed */ +} + + +/* + * USB Set Address Function + * Parameters: adr: USB Address + * Return Value: None + */ + +void USB_SetAddress (uint32_t adr) +{ + WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Don't wait for next */ + WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Setup Status Phase */ +} + + +/* + * USB Configure Function + * Parameters: cfg: Configure/Deconfigure + * Return Value: None + */ + +void USB_Configure (uint32_t cfg) +{ + WrCmdDat(CMD_CFG_DEV, DAT_WR_BYTE(cfg ? CONF_DVICE : 0)); + return; +} + + +/* + * Configure USB Endpoint according to Descriptor + * Parameters: pEPD: Pointer to Endpoint Descriptor + * Return Value: None + */ + +void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD) +{ + return; +} + + +/* + * Set Direction for USB Control Endpoint + * Parameters: dir: Out (dir == 0), In (dir <> 0) + * Return Value: None + */ + +void USB_DirCtrlEP (uint32_t dir) +{ + dir = dir; /* Not needed */ +} + + +/* + * Enable USB Endpoint + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_EnableEP (uint32_t EPNum) +{ + WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0)); +} + + +/* + * Disable USB Endpoint + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_DisableEP (uint32_t EPNum) +{ + WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_DA)); +} + + +/* + * Reset USB Endpoint + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_ResetEP (uint32_t EPNum) +{ + WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0)); +} + + +/* + * Set Stall for USB Endpoint + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_SetStallEP (uint32_t EPNum) +{ + WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(EP_STAT_ST)); +} + + +/* + * Clear Stall for USB Endpoint + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_ClrStallEP (uint32_t EPNum) +{ + WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum)), DAT_WR_BYTE(0)); +} + + +/* + * Clear USB Endpoint Buffer + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * Return Value: None + */ + +void USB_ClearEPBuf (uint32_t EPNum) +{ + WrCmdEP(EPNum, CMD_CLR_BUF); +} + + +/* + * Read USB Endpoint Data + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * pData: Pointer to Data Buffer + * Return Value: Number of bytes read + */ + +uint32_t USB_ReadEP (uint32_t EPNum, uint8_t *pData) +{ + uint32_t cnt, n; + + USB_CTRL = ((EPNum & 0x0F) << 2) | CTRL_RD_EN; + /* 3 clock cycles to fetch the packet length from RAM. */ + delay( 5 ); + + do + { + cnt = USB_RXPLEN; + } while ((cnt & PKT_DV) == 0); + cnt &= PKT_LNGTH_MASK; + + for (n = 0; n < (cnt + 3) / 4; n++) + { + *((uint32_t __attribute__((packed)) *)pData) = USB_RXDATA; + pData += 4; + } + + USB_CTRL = 0; + + if ((EPNum & 0x80) != 0x04) + { /* Non-Isochronous Endpoint */ + WrCmdEP(EPNum, CMD_CLR_BUF); + } + + return (cnt); +} + + +/* + * Write USB Endpoint Data + * Parameters: EPNum: Endpoint Number + * EPNum.0..3: Address + * EPNum.7: Dir + * pData: Pointer to Data Buffer + * cnt: Number of bytes to write + * Return Value: Number of bytes written + */ + +uint32_t USB_WriteEP (uint32_t EPNum, uint8_t *pData, uint32_t cnt) +{ + uint32_t n; + + USB_CTRL = ((EPNum & 0x0F) << 2) | CTRL_WR_EN; + /* 3 clock cycles to fetch the packet length from RAM. */ + delay( 5 ); + USB_TXPLEN = cnt; + + for (n = 0; n < (cnt + 3) / 4; n++) + { + USB_TXDATA = *((uint32_t __attribute__((packed)) *)pData); + pData += 4; + } + + USB_CTRL = 0; + + WrCmdEP(EPNum, CMD_VALID_BUF); + + return (cnt); +} + +/* + * Get USB Last Frame Number + * Parameters: None + * Return Value: Frame Number + */ + +uint32_t USB_GetFrame (void) +{ + uint32_t val; + + WrCmd(CMD_RD_FRAME); + val = RdCmdDat(DAT_RD_FRAME); + val = val | (RdCmdDat(DAT_RD_FRAME) << 8); + + return (val); +} + + +/* + * USB Interrupt Service Routine + */ + +#ifdef CFG_USBCDC +void USB_IRQHandler (void) +{ + uint32_t disr, val, n, m; + + disr = USB_DEVINTST; /* Device Interrupt Status */ + USB_DEVINTCLR = disr; + + /* Device Status Interrupt (Reset, Connect change, Suspend/Resume) */ + if (disr & DEV_STAT_INT) + { + WrCmd(CMD_GET_DEV_STAT); + val = RdCmdDat(DAT_GET_DEV_STAT); /* Device Status */ + if (val & DEV_RST) { /* Reset */ + USB_Reset(); +#if USB_RESET_EVENT + USB_Reset_Event(); +#endif + } + if (val & DEV_CON_CH) { /* Connect change */ +#if USB_POWER_EVENT + USB_Power_Event(val & DEV_CON); +#endif + } + if (val & DEV_SUS_CH) { /* Suspend/Resume */ + if (val & DEV_SUS) { /* Suspend */ + USB_Suspend(); +#if USB_SUSPEND_EVENT + USB_Suspend_Event(); +#endif + } else { /* Resume */ + USB_Resume(); +#if USB_RESUME_EVENT + USB_Resume_Event(); +#endif + } + } + goto isr_end; + } + +#if USB_SOF_EVENT + /* Start of Frame Interrupt */ + if (disr & FRAME_INT) + { + USB_DEVINTCLR = FRAME_INT; + USB_SOF_Event(); + // SOFIRQCount++; + } +#endif + +#if USB_ERROR_EVENT + /* NO error interrupt anymore, below code can be used + as example to get error status from command engine. */ + /* Error Interrupt */ + if (disr & ERR_INT) + { + WrCmd(CMD_RD_ERR_STAT); + val = RdCmdDat(DAT_RD_ERR_STAT); + USB_Error_Event(val); + } +#endif + + /* Endpoint's Interrupt */ + if (disr & (0xFF<<1)) { + /* if any of the EP0 through EP7 is set, or bit 1 through 9 on disr */ + for (n = 0; n < USB_EP_NUM; n++) { /* Check All Endpoints */ + /* skip frame interrupt at bit 0 in disr */ +// if (disr & ((1 << n)<<1)) { + if ((disr>>1) & (1 << n)) { + m = n >> 1; + /* clear EP interrupt by sending cmd to the command engine. */ + WrCmd(CMD_SEL_EP_CLRI(n)); + val = RdCmdDat(DAT_SEL_EP_CLRI(n)); + if ((n & 1) == 0) { /* OUT Endpoint */ + if (n == 0) { /* Control OUT Endpoint */ + if (val & EP_SEL_STP) { /* Setup Packet */ + if (USB_P_EP[0]) { + USB_P_EP[0](USB_EVT_SETUP); + continue; + } + } + } + if (USB_P_EP[m]) { + USB_P_EP[m](USB_EVT_OUT); + } + } else { /* IN Endpoint */ + if (USB_P_EP[m]) { + USB_P_EP[m](USB_EVT_IN); + } + } + } + } + } +isr_end: + return; +} + +#endif diff --git a/core/usbcdc/usbhw.h b/core/usbcdc/usbhw.h new file mode 100644 index 0000000..f31d7d3 --- /dev/null +++ b/core/usbcdc/usbhw.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbhw.h + * Purpose: USB Hardware Layer Definitions + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------- + * History: + * V1.20 Added USB_ClearEPBuf + * V1.00 Initial Version + *----------------------------------------------------------------------------*/ + +#ifndef __USBHW_H__ +#define __USBHW_H__ + + +/* USB Error Codes */ +#define USB_ERR_PID 0x0001 /* PID Error */ +#define USB_ERR_UEPKT 0x0002 /* Unexpected Packet */ +#define USB_ERR_DCRC 0x0004 /* Data CRC Error */ +#define USB_ERR_TIMOUT 0x0008 /* Bus Time-out Error */ +#define USB_ERR_EOP 0x0010 /* End of Packet Error */ +#define USB_ERR_B_OVRN 0x0020 /* Buffer Overrun */ +#define USB_ERR_BTSTF 0x0040 /* Bit Stuff Error */ +#define USB_ERR_TGL 0x0080 /* Toggle Bit Error */ + +/* USB Hardware Functions */ +extern void USBIOClkConfig (void); +extern void USB_Init (void); +extern void USB_Connect (uint32_t con); +extern void USB_Reset (void); +extern void USB_Suspend (void); +extern void USB_Resume (void); +extern void USB_WakeUp (void); +extern void USB_WakeUpCfg (uint32_t cfg); +extern void USB_SetAddress (uint32_t adr); +extern void USB_Configure (uint32_t cfg); +extern void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD); +extern void USB_DirCtrlEP (uint32_t dir); +extern void USB_EnableEP (uint32_t EPNum); +extern void USB_DisableEP (uint32_t EPNum); +extern void USB_ResetEP (uint32_t EPNum); +extern void USB_SetStallEP (uint32_t EPNum); +extern void USB_ClrStallEP (uint32_t EPNum); +extern void USB_ClearEPBuf (uint32_t EPNum); +extern uint32_t USB_ReadEP (uint32_t EPNum, uint8_t *pData); +extern uint32_t USB_WriteEP (uint32_t EPNum, uint8_t *pData, uint32_t cnt); +extern uint32_t USB_GetFrame(void); +extern void USB_IRQHandler (void); + + +#endif /* __USBHW_H__ */ diff --git a/core/usbcdc/usbreg.h b/core/usbcdc/usbreg.h new file mode 100644 index 0000000..d059e6a --- /dev/null +++ b/core/usbcdc/usbreg.h @@ -0,0 +1,134 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBREG.H + * Purpose: USB Hardware Layer Definitions for NXP LPC13xx + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __USBREG_H +#define __USBREG_H + +/* Device Interrupt Bit Definitions */ +#define FRAME_INT (0x1<<0) +#define EP0_INT (0x1<<1) +#define EP1_INT (0x1<<2) +#define EP2_INT (0x1<<3) +#define EP3_INT (0x1<<4) +#define EP4_INT (0x1<<5) +#define EP5_INT (0x1<<6) +#define EP6_INT (0x1<<7) +#define EP7_INT (0x1<<8) +#define DEV_STAT_INT (0x1<<9) +#define CCEMTY_INT (0x1<<10) +#define CDFULL_INT (0x1<<11) +#define RxENDPKT_INT (0x1<<12) +#define TxENDPKT_INT (0x1<<13) + +/* Rx & Tx Packet Length Definitions */ +#define PKT_LNGTH_MASK 0x000003FF +#define PKT_DV 0x00000400 +#define PKT_RDY 0x00000800 + +/* USB Control Definitions */ +#define CTRL_RD_EN 0x00000001 +#define CTRL_WR_EN 0x00000002 + +/* Command Codes */ +#define CMD_SET_ADDR 0x00D00500 +#define CMD_CFG_DEV 0x00D80500 +#define CMD_SET_MODE 0x00F30500 +#define CMD_RD_INT 0x00F40500 +#define DAT_RD_INT 0x00F40200 +#define CMD_RD_FRAME 0x00F50500 +#define DAT_RD_FRAME 0x00F50200 +#define CMD_RD_CHIP_ID 0x00FD0500 +#define DAT_RD_CHIP_ID 0x00FD0200 + +#define CMD_SET_DEV_STAT 0x00FE0500 +#define CMD_GET_DEV_STAT 0x00FE0500 +#define DAT_GET_DEV_STAT 0x00FE0200 +#define CMD_GET_ERR_CODE 0x00FF0500 +#define DAT_GET_ERR_CODE 0x00FF0200 + +#define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) +#define CMD_SEL_EP(x) (0x00000500 | ((x) << 16)) +#define DAT_SEL_EP(x) (0x00000200 | ((x) << 16)) +#define CMD_SEL_EP_CLRI(x) (0x00400500 | ((x) << 16)) +#define DAT_SEL_EP_CLRI(x) (0x00400200 | ((x) << 16)) +#define CMD_SET_EP_STAT(x) (0x00400500 | ((x) << 16)) +#define CMD_CLR_BUF 0x00F20500 +#define CMD_VALID_BUF 0x00FA0500 + +/* Device Address Register Definitions */ +#define DEV_ADDR_MASK 0x7F +#define DEV_EN 0x80 + +/* Device Configure Register Definitions */ +#define CONF_DVICE 0x01 + +/* Device Mode Register Definitions */ +#define AP_CLK 0x01 +#define INAK_CI 0x02 +#define INAK_CO 0x04 +#define INAK_AI 0x08 +#define INAK_AO 0x10 + +/* Device Status Register Definitions */ +#define DEV_CON 0x01 +#define DEV_CON_CH 0x02 +#define DEV_SUS 0x04 +#define DEV_SUS_CH 0x08 +#define DEV_RST 0x10 + +/* Error Code Register Definitions */ +#define ERR_EC_MASK 0x0F +#define ERR_EA 0x10 + +/* Error Status Register Definitions */ +#define ERR_NOERROR 0x00 +#define ERR_PID_ENCODE 0x01 +#define ERR_UNKNOWN_PID 0x02 +#define ERR_UNEXPECT_PKT 0x03 +#define ERR_TCRC 0x04 +#define ERR_DCRC 0x05 +#define ERR_TIMEOUT 0x06 +#define ERR_BABBIE 0x07 +#define ERR_EOF_PKT 0x08 +#define ERR_TX_RX_NAK 0x09 +#define ERR_SENT_STALL 0x0A +#define ERR_BUF_OVERRUN 0x0B +#define ERR_SENT_EPT_PKT 0x0C +#define ERR_BIT_STUFF 0x0D +#define ERR_SYNC 0x0E +#define ERR_TOGGLE_BIT 0x0F + +/* Endpoint Select Register Definitions */ +#define EP_SEL_F 0x01 +#define EP_SEL_ST 0x02 +#define EP_SEL_STP 0x04 +#define EP_SEL_PO 0x08 +#define EP_SEL_EPN 0x10 +#define EP_SEL_B_1_FULL 0x20 +#define EP_SEL_B_2_FULL 0x40 + +/* Endpoint Status Register Definitions */ +#define EP_STAT_ST 0x01 +#define EP_STAT_DA 0x20 +#define EP_STAT_RF_MO 0x40 +#define EP_STAT_CND_ST 0x80 + +/* Clear Buffer Register Definitions */ +#define CLR_BUF_PO 0x01 + +#endif /* __USBREG_H */ diff --git a/core/usbcdc/usbuser.c b/core/usbcdc/usbuser.c new file mode 100644 index 0000000..54f7b9e --- /dev/null +++ b/core/usbcdc/usbuser.c @@ -0,0 +1,208 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usbuser.c + * Purpose: USB Custom User Module + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ +#include "projectconfig.h" + +#include "usb.h" +#include "usbcfg.h" +#include "usbhw.h" +#include "usbcore.h" +#include "usbuser.h" +#include "cdcuser.h" + + +/* + * USB Power Event Callback + * Called automatically on USB Power Event + * Parameter: power: On(TRUE)/Off(FALSE) + */ + +#if USB_POWER_EVENT +void USB_Power_Event (uint32_t power) { +} +#endif + + +/* + * USB Reset Event Callback + * Called automatically on USB Reset Event + */ + +#if USB_RESET_EVENT +void USB_Reset_Event (void) { + USB_ResetCore(); +} +#endif + + +/* + * USB Suspend Event Callback + * Called automatically on USB Suspend Event + */ + +#if USB_SUSPEND_EVENT +void USB_Suspend_Event (void) { +} +#endif + + +/* + * USB Resume Event Callback + * Called automatically on USB Resume Event + */ + +#if USB_RESUME_EVENT +void USB_Resume_Event (void) { +} +#endif + + +/* + * USB Remote Wakeup Event Callback + * Called automatically on USB Remote Wakeup Event + */ + +#if USB_WAKEUP_EVENT +void USB_WakeUp_Event (void) { +} +#endif + + +/* + * USB Start of Frame Event Callback + * Called automatically on USB Start of Frame Event + */ + +#if USB_SOF_EVENT +void USB_SOF_Event (void) { +} +#endif + + +/* + * USB Error Event Callback + * Called automatically on USB Error Event + * Parameter: error: Error Code + */ + +#if USB_ERROR_EVENT +void USB_Error_Event (uint32_t error) { +} +#endif + + +/* + * USB Set Configuration Event Callback + * Called automatically on USB Set Configuration Request + */ + +#if USB_CONFIGURE_EVENT +void USB_Configure_Event (void) { + + if (USB_Configuration) { /* Check if USB is configured */ + /* add your code here */ + } +} +#endif + + +/* + * USB Set Interface Event Callback + * Called automatically on USB Set Interface Request + */ + +#if USB_INTERFACE_EVENT +void USB_Interface_Event (void) { +} +#endif + + +/* + * USB Set/Clear Feature Event Callback + * Called automatically on USB Set/Clear Feature Request + */ + +#if USB_FEATURE_EVENT +void USB_Feature_Event (void) { +} +#endif + + +#define P_EP(n) ((USB_EP_EVENT & (1 << (n))) ? USB_EndPoint##n : NULL) + +/* USB Endpoint Events Callback Pointers */ +void (* const USB_P_EP[USB_LOGIC_EP_NUM]) (uint32_t event) = { + P_EP(0), + P_EP(1), + P_EP(2), + P_EP(3), + P_EP(4), +}; + + +/* + * USB Endpoint 1 Event Callback + * Called automatically on USB Endpoint 1 Event + * Parameter: event + */ + +void USB_EndPoint1 (uint32_t event) { + uint16_t temp; + static uint16_t serialState; + + switch (event) { + case USB_EVT_IN: + temp = CDC_GetSerialState(); + if (serialState != temp) { + serialState = temp; + CDC_NotificationIn(); /* send SERIAL_STATE notification */ + } + break; + } +} + + +/* + * USB Endpoint 2 Event Callback + * Called automatically on USB Endpoint 2 Event + * Parameter: event + */ + +void USB_EndPoint2 (uint32_t event) +{ + event = event; +} + + +/* + * USB Endpoint 3 Event Callback + * Called automatically on USB Endpoint 3 Event + * Parameter: event + */ + +void USB_EndPoint3 (uint32_t event) { + switch (event) { + case USB_EVT_OUT: + CDC_BulkOut (); /* data received from Host */ + break; + case USB_EVT_IN: + CDC_BulkIn (); /* data expected from Host */ + break; + } +} + + diff --git a/core/usbcdc/usbuser.h b/core/usbcdc/usbuser.h new file mode 100644 index 0000000..e4109ec --- /dev/null +++ b/core/usbcdc/usbuser.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBUSER.H + * Purpose: USB Custom User Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2005-2009 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBUSER_H__ +#define __USBUSER_H__ + + +/* USB Device Events Callback Functions */ +extern void USB_Power_Event (uint32_t power); +extern void USB_Reset_Event (void); +extern void USB_Suspend_Event (void); +extern void USB_Resume_Event (void); +extern void USB_WakeUp_Event (void); +extern void USB_SOF_Event (void); +extern void USB_Error_Event (uint32_t error); + +/* USB Endpoint Callback Events */ +#define USB_EVT_SETUP 1 /* Setup Packet */ +#define USB_EVT_OUT 2 /* OUT Packet */ +#define USB_EVT_IN 3 /* IN Packet */ +#define USB_EVT_OUT_NAK 4 /* OUT Packet - Not Acknowledged */ +#define USB_EVT_IN_NAK 5 /* IN Packet - Not Acknowledged */ +#define USB_EVT_OUT_STALL 6 /* OUT Packet - Stalled */ +#define USB_EVT_IN_STALL 7 /* IN Packet - Stalled */ + +/* USB Endpoint Events Callback Pointers */ +extern void (* const USB_P_EP[USB_LOGIC_EP_NUM])(uint32_t event); + +/* USB Endpoint Events Callback Functions */ +extern void USB_EndPoint0 (uint32_t event); +extern void USB_EndPoint1 (uint32_t event); +extern void USB_EndPoint2 (uint32_t event); +extern void USB_EndPoint3 (uint32_t event); +extern void USB_EndPoint4 (uint32_t event); + +/* USB Core Events Callback Functions */ +extern void USB_Configure_Event (void); +extern void USB_Interface_Event (void); +extern void USB_Feature_Event (void); + + +#endif /* __USBUSER_H__ */ diff --git a/core/usbhid-rom/usb.h b/core/usbhid-rom/usb.h new file mode 100644 index 0000000..2e55b8c --- /dev/null +++ b/core/usbhid-rom/usb.h @@ -0,0 +1,240 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: usb.h + * Purpose: USB Definitions + * Version: V1.20 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on NXP Semiconductors LPC microcontroller devices only. Nothing else + * gives you the right to use this software. + * + * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef __USB_H__ +#define __USB_H__ + +#include "sysdefs.h" + +typedef union +{ + uint16_t W; + struct + { + uint8_t L; + uint8_t H; + } __attribute__ ((packed)) WB; +} __attribute__ ((packed)) WORD_BYTE; + + +/* bmRequestType.Dir */ +#define REQUEST_HOST_TO_DEVICE 0 +#define REQUEST_DEVICE_TO_HOST 1 + +/* bmRequestType.Type */ +#define REQUEST_STANDARD 0 +#define REQUEST_CLASS 1 +#define REQUEST_VENDOR 2 +#define REQUEST_RESERVED 3 + +/* bmRequestType.Recipient */ +#define REQUEST_TO_DEVICE 0 +#define REQUEST_TO_INTERFACE 1 +#define REQUEST_TO_ENDPOINT 2 +#define REQUEST_TO_OTHER 3 + +/* bmRequestType Definition */ +typedef union _REQUEST_TYPE +{ + struct _BM + { + uint8_t Recipient : 5; + uint8_t Type : 2; + uint8_t Dir : 1; + } __attribute__ ((packed)) BM; + uint8_t B; +} __attribute__ ((packed)) REQUEST_TYPE; + +/* USB Standard Request Codes */ +#define USB_REQUEST_GET_STATUS 0 +#define USB_REQUEST_CLEAR_FEATURE 1 +#define USB_REQUEST_SET_FEATURE 3 +#define USB_REQUEST_SET_ADDRESS 5 +#define USB_REQUEST_GET_DESCRIPTOR 6 +#define USB_REQUEST_SET_DESCRIPTOR 7 +#define USB_REQUEST_GET_CONFIGURATION 8 +#define USB_REQUEST_SET_CONFIGURATION 9 +#define USB_REQUEST_GET_INTERFACE 10 +#define USB_REQUEST_SET_INTERFACE 11 +#define USB_REQUEST_SYNC_FRAME 12 + +/* USB GET_STATUS Bit Values */ +#define USB_GETSTATUS_SELF_POWERED 0x01 +#define USB_GETSTATUS_REMOTE_WAKEUP 0x02 +#define USB_GETSTATUS_ENDPOINT_STALL 0x01 + +/* USB Standard Feature selectors */ +#define USB_FEATURE_ENDPOINT_STALL 0 +#define USB_FEATURE_REMOTE_WAKEUP 1 + +/* USB Default Control Pipe Setup Packet */ +typedef struct _USB_SETUP_PACKET +{ + REQUEST_TYPE bmRequestType; + uint8_t bRequest; + WORD_BYTE wValue; + WORD_BYTE wIndex; + uint16_t wLength; +} __attribute__ ((packed)) USB_SETUP_PACKET; + + +/* USB Descriptor Types */ +#define USB_DEVICE_DESCRIPTOR_TYPE 1 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 2 +#define USB_STRING_DESCRIPTOR_TYPE 3 +#define USB_INTERFACE_DESCRIPTOR_TYPE 4 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 5 +#define USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE 6 +#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE 7 +#define USB_INTERFACE_POWER_DESCRIPTOR_TYPE 8 +#define USB_OTG_DESCRIPTOR_TYPE 9 +#define USB_DEBUG_DESCRIPTOR_TYPE 10 +#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 11 + +/* USB Device Classes */ +#define USB_DEVICE_CLASS_RESERVED 0x00 +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 +#define USB_DEVICE_CLASS_MONITOR 0x04 +#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05 +#define USB_DEVICE_CLASS_POWER 0x06 +#define USB_DEVICE_CLASS_PRINTER 0x07 +#define USB_DEVICE_CLASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_HUB 0x09 +#define USB_DEVICE_CLASS_MISCELLANEOUS 0xEF +#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF + +/* bmAttributes in Configuration Descriptor */ +#define USB_CONFIG_POWERED_MASK 0x40 +#define USB_CONFIG_BUS_POWERED 0x80 +#define USB_CONFIG_SELF_POWERED 0xC0 +#define USB_CONFIG_REMOTE_WAKEUP 0x20 + +/* bMaxPower in Configuration Descriptor */ +#define USB_CONFIG_POWER_MA(mA) ((mA)/2) + +/* bEndpointAddress in Endpoint Descriptor */ +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00) +#define USB_ENDPOINT_IN(addr) ((addr) | 0x80) + +/* bmAttributes in Endpoint Descriptor */ +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INTERRUPT 0x03 +#define USB_ENDPOINT_SYNC_MASK 0x0C +#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION 0x00 +#define USB_ENDPOINT_SYNC_ASYNCHRONOUS 0x04 +#define USB_ENDPOINT_SYNC_ADAPTIVE 0x08 +#define USB_ENDPOINT_SYNC_SYNCHRONOUS 0x0C +#define USB_ENDPOINT_USAGE_MASK 0x30 +#define USB_ENDPOINT_USAGE_DATA 0x00 +#define USB_ENDPOINT_USAGE_FEEDBACK 0x10 +#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK 0x20 +#define USB_ENDPOINT_USAGE_RESERVED 0x30 + +/* USB Standard Device Descriptor */ +typedef struct _USB_DEVICE_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__ ((packed)) USB_DEVICE_DESCRIPTOR; + +/* USB 2.0 Device Qualifier Descriptor */ +typedef struct _USB_DEVICE_QUALIFIER_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + uint8_t bReserved; +} __attribute__ ((packed)) USB_DEVICE_QUALIFIER_DESCRIPTOR; + +/* USB Standard Configuration Descriptor */ +typedef struct _USB_CONFIGURATION_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} __attribute__ ((packed)) USB_CONFIGURATION_DESCRIPTOR; + +/* USB Standard Interface Descriptor */ +typedef struct _USB_INTERFACE_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __attribute__ ((packed)) USB_INTERFACE_DESCRIPTOR; + +/* USB Standard Endpoint Descriptor */ +typedef struct _USB_ENDPOINT_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} __attribute__ ((packed)) USB_ENDPOINT_DESCRIPTOR; + +/* USB String Descriptor */ +typedef struct _USB_STRING_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString/*[]*/; +} __attribute__ ((packed)) USB_STRING_DESCRIPTOR; + +/* USB Common Descriptor */ +typedef struct _USB_COMMON_DESCRIPTOR +{ + uint8_t bLength; + uint8_t bDescriptorType; +} __attribute__ ((packed)) USB_COMMON_DESCRIPTOR; + +#endif diff --git a/core/usbhid-rom/usbconfig.c b/core/usbhid-rom/usbconfig.c new file mode 100644 index 0000000..6fa0a7f --- /dev/null +++ b/core/usbhid-rom/usbconfig.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/*! + @file usbconfig.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "usb.h" +#include "usbconfig.h" + +#ifndef WBVAL +#define WBVAL(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) +#endif + +/* USB String Descriptor (optional) */ +const uint8_t USB_HIDStringDescriptor[] = +{ + /* Index 0x00: LANGID Codes */ + 0x04, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL(0x0409), /* US English */ /* wLANGID */ + /* Index 0x04: Manufacturer */ + 0x1C, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'm',0, + 'i',0, + 'c',0, + 'r',0, + 'o',0, + 'B',0, + 'u',0, + 'i',0, + 'l',0, + 'd',0, + 'e',0, + 'r',0, + ' ',0, + /* Index 0x20: Product */ + 0x28, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'L',0, + 'P',0, + 'C',0, + '1',0, + '3',0, + '4',0, + '3',0, + ' ',0, + 'R',0, + 'e',0, + 'f',0, + '.',0, + ' ',0, + 'B',0, + 'o',0, + 'a',0, + 'r',0, + 'd',0, + ' ',0, + /* Index 0x48: Serial Number */ + 0x1A, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + '0',0, + /* Index 0x62: Interface 0, Alternate Setting 0 */ + 0x0E, /* bLength */ + USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */ + 'H',0, + 'I',0, + 'D',0, + ' ',0, + ' ',0, + ' ',0, +}; diff --git a/core/usbhid-rom/usbconfig.h b/core/usbhid-rom/usbconfig.h new file mode 100644 index 0000000..9cc9110 --- /dev/null +++ b/core/usbhid-rom/usbconfig.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/*! + @file usbconfig.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _USBCONFIG_H_ +#define _USBCONFIG_H_ + +#include "projectconfig.h" + +#define USB_VENDOR_ID CFG_USB_VID // Vendor ID +#define USB_PROD_ID CFG_USB_PID // Product ID +#define USB_DEVICE 0x0100 // Device ID + +#define WBVAL(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) + +#define USB_DEVICE_DESC_SIZE (sizeof(USB_DEVICE_DESCRIPTOR)) +#define USB_CONFIGUARTION_DESC_SIZE (sizeof(USB_CONFIGURATION_DESCRIPTOR)) +#define USB_INTERFACE_DESC_SIZE (sizeof(USB_INTERFACE_DESCRIPTOR)) +#define USB_ENDPOINT_DESC_SIZE (sizeof(USB_ENDPOINT_DESCRIPTOR)) + +#define HID_DESC_OFFSET 0x0012 +#define HID_DESC_SIZE (sizeof(HID_DESCRIPTOR)) +#define HID_REPORT_DESC_SIZE (sizeof(HID_ReportDescriptor)) + +extern const uint8_t USB_DeviceDescriptor[]; +extern const uint8_t USB_ConfigDescriptor[]; +extern const uint8_t USB_HIDStringDescriptor[]; + +extern const uint8_t HID_ReportDescriptor[]; +extern const uint16_t HID_ReportDescSize; + +#endif \ No newline at end of file diff --git a/core/usbhid-rom/usbhid.c b/core/usbhid-rom/usbhid.c new file mode 100644 index 0000000..ce53742 --- /dev/null +++ b/core/usbhid-rom/usbhid.c @@ -0,0 +1,222 @@ +/**************************************************************************/ +/*! + @file usbhid.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "core/usbhid-rom/usb.h" +#include "core/usbhid-rom/usbconfig.h" +#include "core/rom_drivers.h" +#include "core/gpio/gpio.h" +#include "core/adc/adc.h" +#include "core/systick/systick.h" + +#include "usbhid.h" + +USB_DEV_INFO DeviceInfo; +HID_DEVICE_INFO HidDevInfo; +ROM ** rom = (ROM **)0x1fff1ff8; + +typedef struct usbhid_out_s +{ + uint16_t gpio1Dir; + uint16_t gpio1Data; + uint16_t gpio2Dir; + uint16_t gpio2Data; + uint16_t gpio3Dir; + uint16_t gpio3Data; + uint16_t adc0; + uint16_t adc1; + uint16_t adc2; + uint16_t adc3; + uint32_t systicks; + uint32_t rollovers; +} usbhid_out_t; + +/**************************************************************************/ +/*! + @brief Gets the HID In Report (the report going from the LPC1343 to + the USB host) +*/ +/**************************************************************************/ +void usbHIDGetInReport (uint8_t src[], uint32_t length) +{ + usbhid_out_t out; + + out.gpio1Dir = GPIO_GPIO1DIR; + out.gpio1Data = GPIO_GPIO1DATA; + out.gpio2Dir = GPIO_GPIO2DIR; + out.gpio2Data = GPIO_GPIO2DATA; + out.gpio3Dir = GPIO_GPIO3DIR; + out.gpio3Data = GPIO_GPIO3DATA; + out.adc0 = adcRead(0); + out.adc1 = adcRead(1); + out.adc2 = adcRead(2); + out.adc3 = adcRead(3); + out.systicks = systickGetTicks(); + out.rollovers = systickGetRollovers(); + + size_t i = 0; + memcpy(&src[i], &out.gpio1Dir, sizeof out.gpio1Dir); + i += sizeof out.gpio1Dir; + memcpy(&src[i], &out.gpio1Data, sizeof out.gpio1Data); + i += sizeof out.gpio1Data; + memcpy(&src[i], &out.gpio2Dir, sizeof out.gpio2Dir); + i += sizeof out.gpio2Dir; + memcpy(&src[i], &out.gpio2Data, sizeof out.gpio2Data); + i += sizeof out.gpio2Data; + memcpy(&src[i], &out.gpio3Dir, sizeof out.gpio3Dir); + i += sizeof out.gpio3Dir; + memcpy(&src[i], &out.gpio3Data, sizeof out.gpio3Data); + i += sizeof out.gpio3Data; + memcpy(&src[i], &out.adc0, sizeof out.adc0); + i += sizeof out.adc0; + memcpy(&src[i], &out.adc1, sizeof out.adc1); + i += sizeof out.adc1; + memcpy(&src[i], &out.adc2, sizeof out.adc2); + i += sizeof out.adc2; + memcpy(&src[i], &out.adc3, sizeof out.adc3); + i += sizeof out.adc3; + memcpy(&src[i], &out.systicks, sizeof out.systicks); + i += sizeof out.systicks; + memcpy(&src[i], &out.rollovers, sizeof out.rollovers); + i += sizeof out.rollovers; +} + +/**************************************************************************/ +/*! + @brief Sets the HID Out Report (the report coming in from the USB + host to the LPC1343). +*/ +/**************************************************************************/ +void usbHIDSetOutReport (uint8_t dst[], uint32_t length) +{ + uint8_t PCOutReportData = dst[0]; + // Check bit 0 in the incoming report to determine is LED should + // be enabled or disabled (1 = enabled, 0 = disabled) + if (PCOutReportData & (1<<0)) + { + // Enable LED (set low) + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, 0); + } + else + { + // Disable LED (set high) + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, 1); + } +} + +/**************************************************************************/ +/*! + @brief Initialises the USB port + + The ROM-based USB HID code is capable of configuring the PLL and pins + for USB, but there seems to be a bug in the code that sets the system + clock to 48MHz (normally the USB and System clocks can be configured + seperately). As such, this code does not use the "init_clk_pins()" + function in the rom, and the USB clock and pins are manually + configured. +*/ +/**************************************************************************/ +void usbHIDInit (void) +{ + // Setup USB clock + SCB_PDRUNCFG &= ~(SCB_PDSLEEPCFG_USBPAD_PD); // Power-up USB PHY + SCB_PDRUNCFG &= ~(SCB_PDSLEEPCFG_USBPLL_PD); // Power-up USB PLL + + SCB_USBPLLCLKSEL = SCB_USBPLLCLKSEL_SOURCE_MAINOSC; // Select PLL Input + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_UPDATE; // Update Clock Source + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_DISABLE; // Toggle Update Register + SCB_USBPLLCLKUEN = SCB_USBPLLCLKUEN_UPDATE; + + // Wait until the USB clock is updated + while (!(SCB_USBPLLCLKUEN & SCB_USBPLLCLKUEN_UPDATE)); + + // Set USB clock to 48MHz (12MHz x 4) + SCB_USBPLLCTRL = (SCB_USBPLLCTRL_MULT_4); + while (!(SCB_USBPLLSTAT & SCB_USBPLLSTAT_LOCK)); // Wait Until PLL Locked + SCB_USBCLKSEL = SCB_USBCLKSEL_SOURCE_USBPLLOUT; + + // Set USB pin functions + IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK; + IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CLKOUT; // CLK OUT + IOCON_PIO0_3 &= ~IOCON_PIO0_3_FUNC_MASK; + IOCON_PIO0_3 |= IOCON_PIO0_3_FUNC_USB_VBUS; // VBus + IOCON_PIO0_6 &= ~IOCON_PIO0_6_FUNC_MASK; + IOCON_PIO0_6 |= IOCON_PIO0_6_FUNC_USB_CONNECT; // Soft Connect + + // Disable internal resistor on VBUS (0.3) + gpioSetPullup(&IOCON_PIO0_3, gpioPullupMode_Inactive); + + // HID Device Info + volatile int n; + HidDevInfo.idVendor = USB_VENDOR_ID; + HidDevInfo.idProduct = USB_PROD_ID; + HidDevInfo.bcdDevice = USB_DEVICE; + HidDevInfo.StrDescPtr = (uint32_t)&USB_HIDStringDescriptor[0]; + HidDevInfo.InReportCount = sizeof(usbhid_out_t); + HidDevInfo.OutReportCount = 1; + HidDevInfo.SampleInterval = 0x20; + HidDevInfo.InReport = usbHIDGetInReport; + HidDevInfo.OutReport = usbHIDSetOutReport; + + DeviceInfo.DevType = USB_DEVICE_CLASS_HUMAN_INTERFACE; + DeviceInfo.DevDetailPtr = (uint32_t)&HidDevInfo; + + /* Enable Timer32_1, IOCON, and USB blocks (for USB ROM driver) */ + SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B1 | SCB_SYSAHBCLKCTRL_IOCON | SCB_SYSAHBCLKCTRL_USB_REG); + + /* Use pll and pin init function in rom */ + /* Warning: This will also set the system clock to 48MHz! */ + // (*rom)->pUSBD->init_clk_pins(); + + /* insert a delay between clk init and usb init */ + for (n = 0; n < 75; n++) {__asm("nop");} + + (*rom)->pUSBD->init(&DeviceInfo); /* USB Initialization */ + (*rom)->pUSBD->connect(true); /* USB Connect */ +} + +/**************************************************************************/ +/*! + @brief Passes the USB interrupt to the internal ROM-based handler +*/ +/**************************************************************************/ +#ifdef CFG_USBHID +void USB_IRQHandler() +{ + (*rom)->pUSBD->isr(); +} +#endif + diff --git a/core/usbhid-rom/usbhid.h b/core/usbhid-rom/usbhid.h new file mode 100644 index 0000000..31247dc --- /dev/null +++ b/core/usbhid-rom/usbhid.h @@ -0,0 +1,46 @@ +/**************************************************************************/ +/*! + @file usbhid.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _USBCONFIG_H_ +#define _USBCONFIG_H_ + +#include "projectconfig.h" + +void usbHIDGetInReport (uint8_t src[], uint32_t length); +void usbHIDSetOutReport (uint8_t dst[], uint32_t length); +void usbHIDInit (void); + +#endif diff --git a/core/wdt/wdt.c b/core/wdt/wdt.c new file mode 100644 index 0000000..3cd823b --- /dev/null +++ b/core/wdt/wdt.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/*! + @file wdt.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Sets up the watchdog timer (WDT). The WDT allows you to monitor + whether the device is still executing properly. If the watchdog + isn't 'fed' within a pre-determined delay, it will raise an interrupt + allowing you to decide if you want to reset the device, etc. + + @code + #include "core/cpu/cpu.h" + #include "core/wdt/wdt.h" + ... + cpuInit(); + + // Initialise wdt with no reset on timeout + wdtInit(false); + + // Pat the watchdog (to start the timer) + wdtFeed(); + + while (1) + { + // Keep the watchdog happy by regularly feeding it + wdtFeed(); + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "wdt.h" + +#define WDT_FEED_VALUE (0x003FFFFF) + +volatile uint32_t wdt_counter; + +/**************************************************************************/ +/*! + IRQ Handler when the watchdog times out. Any actions that you wish + to take when a timeout occurs should be called from here. +*/ +/**************************************************************************/ +void WDT_IRQHandler(void) +{ + /* Clear the time-out interrupt flag */ + WDT_WDMOD &= ~WDT_WDMOD_WDTOF; + wdt_counter++; +} + +/**************************************************************************/ +/*! + Setup the clock for the watchdog timer. The default setting is 250kHz. +*/ +/**************************************************************************/ +static void wdtClockSetup (void) +{ + /* Watchdog Configuration */ + /* Freq. = 0.5MHz, div = 2: WDT_OSC = 250kHz */ + SCB_WDTOSCCTRL = SCB_WDTOSCCTRL_FREQSEL_0_5MHZ | + SCB_WDTOSCCTRL_DIVSEL_DIV2; + + /* Set clock source (use WDT oscillator) */ + SCB_WDTCLKSEL = SCB_WDTCLKSEL_SOURCE_WATCHDOGOSC; + SCB_WDTCLKUEN = SCB_WDTCLKUEN_UPDATE; + SCB_WDTCLKUEN = SCB_WDTCLKUEN_DISABLE; + SCB_WDTCLKUEN = SCB_WDTCLKUEN_UPDATE; + + /* Wait until updated */ + while (!(SCB_WDTCLKUEN & SCB_WDTCLKUEN_UPDATE)); + + /* Set divider */ + SCB_WDTCLKDIV = SCB_WDTCLKDIV_DIV1; + + /* Enable WDT clock */ + SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC); +} + +/**************************************************************************/ +/*! + Initialises the watchdog timer and sets up the interrupt. +*/ +/**************************************************************************/ +void wdtInit (bool reset) +{ + /* Setup the WDT clock */ + wdtClockSetup(); + + /* Enable AHB clock to the WDT domain. */ + SCB_SYSAHBCLKCTRL |= SCB_SYSAHBCLKCTRL_WDT; + + wdt_counter = 0; + + /* Enable the WDT interrupt */ + NVIC_EnableIRQ(WDT_IRQn); + + /* Set timeout value (must be at least 0x000000FF) */ + WDT_WDTC = WDT_FEED_VALUE; + + /* Enable the watchdog timer (without system reset) */ + WDT_WDMOD = WDT_WDMOD_WDEN_ENABLED | + reset ? WDT_WDMOD_WDRESET_ENABLED : WDT_WDMOD_WDRESET_DISABLED ; +} + +/**************************************************************************/ +/*! + Feeds the watchdog to keep it from timing out. Interrupts will be + disabled while feeding the watchdog. +*/ +/**************************************************************************/ +void wdtFeed (void) +{ + /* Pet the watchdog */ + __disable_irq(); + WDT_WDFEED = WDT_WDFEED_FEED1; + WDT_WDFEED = WDT_WDFEED_FEED2; + __enable_irq(); +} + diff --git a/core/wdt/wdt.h b/core/wdt/wdt.h new file mode 100644 index 0000000..55f4b48 --- /dev/null +++ b/core/wdt/wdt.h @@ -0,0 +1,47 @@ +/**************************************************************************/ +/*! + @file wdt.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _WDT_H_ +#define _WDT_H_ + +#include "projectconfig.h" + +void wdtInit (bool reset); +void wdtFeed (void); + +#endif \ No newline at end of file diff --git a/drivers/chibi/chb.c b/drivers/chibi/chb.c new file mode 100644 index 0000000..e5a56dd --- /dev/null +++ b/drivers/chibi/chb.c @@ -0,0 +1,229 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#include +#include + +#include "chb.h" +#include "chb_drvr.h" +#include "chb_buf.h" + +static chb_pcb_t pcb; +// these are for the duplicate checking and rejection +static U8 prev_seq = 0xFF; +static U16 prev_src_addr = 0xFFFE; +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_init() +{ + memset(&pcb, 0, sizeof(chb_pcb_t)); + pcb.src_addr = chb_get_short_addr(); + chb_drvr_init(); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +chb_pcb_t *chb_get_pcb() +{ + return &pcb; +} + +/**************************************************************************/ +/*! + Requires the dest addr, location to store data, and len of payload. + Returns the length of the hdr. +*/ +/**************************************************************************/ +static U8 chb_gen_hdr(U8 *hdr, U16 addr, U8 len) +{ + U8 *hdr_ptr = hdr; + + // calc frame size and put in 0 position of array + // frame size = hdr sz + payload len + fcs len + *hdr_ptr++ = CHB_HDR_SZ + len + CHB_FCS_LEN; + + // use default fcf byte 0 val but test for ack request. we won't request + // ack if broadcast. all other cases we will. + *hdr_ptr++ = CHB_FCF_BYTE_0 | ((addr != 0xFFFF) << CHB_ACK_REQ_POS); + *hdr_ptr++ = CHB_FCF_BYTE_1; + + *hdr_ptr++ = pcb.seq++; + + // fill out dest pan ID, dest addr, src addr + *(U16 *)hdr_ptr = CFG_CHIBI_PANID; + hdr_ptr += sizeof(U16); + *(U16 *)hdr_ptr = addr; + hdr_ptr += sizeof(U16); + *(U16 *)hdr_ptr = pcb.src_addr; + hdr_ptr += sizeof(U16); + + // return the len of the header + return hdr_ptr - hdr; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U8 chb_write(U16 addr, U8 *data, U8 len) +{ + U8 status, frm_len, hdr_len, hdr[CHB_HDR_SZ + 1]; + + while (len > 0) + { + // calculate which frame len to use. if greater than max payload, split + // up operation. + frm_len = (len > CHB_MAX_PAYLOAD) ? CHB_MAX_PAYLOAD : len; + + // gen frame header + hdr_len = chb_gen_hdr(hdr, addr, frm_len); + + // send data to chip + status = chb_tx(hdr, data, frm_len); + + if (status != CHB_SUCCESS) + { + switch (status) + { + case RADIO_SUCCESS: + // fall through + case CHB_SUCCESS_DATA_PENDING: + pcb.txd_success++; + break; + + case CHB_NO_ACK: + pcb.txd_noack++; + break; + + case CHB_CHANNEL_ACCESS_FAILURE: + pcb.txd_channel_fail++; + break; + + default: + break; + } + return status; + } + + // adjust len and restart + len = len - frm_len; + } + return CHB_SUCCESS; +} + +/**************************************************************************/ +/*! + Read data from the buffer. Need to pass in a buffer of at leasts max frame + size and two 16-bit containers for the src and dest addresses. + + The read function will automatically populate the addresses and the data with + the frm payload. It will then return the len of the payload. +*/ +/**************************************************************************/ +U8 chb_read(chb_rx_data_t *rx) +{ + U8 i, len, seq, *data_ptr; + + data_ptr = rx->data; + + // first byte is always len. check it to make sure + // we have a valid len byte. + if ((len = chb_buf_read()) > CHB_MAX_FRAME_LENGTH) + { + return 0; + } + *data_ptr++ = len; + + // load the rest of the data into buffer + for (i=0; idata + 3; // location of sequence number + seq = *data_ptr; + + // parse the buffer and extract the dest and src addresses + data_ptr = rx->data + 6; // location of dest addr + rx->dest_addr = *(U16 *)data_ptr; + data_ptr += sizeof(U16); + rx->src_addr = *(U16 *)data_ptr; + data_ptr += sizeof(U16); + + // if the data in the rx buf is 0, then clear the rx_flag. otherwise, keep it raised + if (!chb_buf_get_len()) + { + pcb.data_rcv = false; + } + +#if (CFG_CHIBI_PROMISCUOUS == 1) + // if we're in promiscuous mode, we don't want to do any duplicate rejection and we don't want to move the payload + // to the front of the buffer. We want to capture the full frame so just keep the frame intact and return the length. + return len; +#else + // duplicate frame check (dupe check). we want to remove frames that have been already been received since they + // are just retries. + // note: this dupe check only removes duplicate frames from the previous transfer. if another frame from a different + // node comes in between the dupes, then the dupe will show up as a received frame. + if ((seq == prev_seq) && (rx->src_addr == prev_src_addr)) + { + // this is a duplicate frame from a retry. the remote node thinks we didn't receive + // it properly. discard. + return 0; + } + else + { + prev_seq = seq; + prev_src_addr = rx->src_addr; + } + + // move the payload down to the beginning of the data buffer + memmove(rx->data, data_ptr, len - CHB_HDR_SZ); + // finally, return the len of the payload + return len - CHB_HDR_SZ - CHB_FCS_LEN; +#endif +} + diff --git a/drivers/chibi/chb.h b/drivers/chibi/chb.h new file mode 100644 index 0000000..6fb7827 --- /dev/null +++ b/drivers/chibi/chb.h @@ -0,0 +1,100 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#ifndef CHIBI_H +#define CHIBI_H + +#include "types.h" + +#define CHB_HDR_SZ 9 // FCF + seq + pan_id + dest_addr + src_addr (2 + 1 + 2 + 2 + 2) +#define CHB_FCS_LEN 2 +#define CHB_MAX_PAYLOAD 100 + + +// frame_type = data +// security enabled = false +// frame pending = false +// ack request = false +// pan ID compression = true +#define CHB_FCF_BYTE_0 0x41 + +// dest addr = 16-bit +// frame version = 802.15.4 (not 2003) +// src addr = 16-bit +#define CHB_FCF_BYTE_1 0x98 + +#define CHB_ACK_REQ_POS 5 + +enum +{ + CHB_SUCCESS = 0, + CHB_SUCCESS_DATA_PENDING = 1, + CHB_CHANNEL_ACCESS_FAILURE = 3, + CHB_NO_ACK = 5, + CHB_INVALID = 7 +}; + +// Chibi Protocol control block +typedef struct +{ + U16 src_addr; + U8 seq; + volatile bool data_rcv; + volatile bool tx_end; + + // stats + U16 rcvd_xfers; + U16 txd_success; + U16 txd_noack; + U16 txd_channel_fail; + U16 overflow; + U16 underrun; + U8 battlow; + U8 ed; + U8 crc; +} chb_pcb_t; + +typedef struct +{ + U8 len; + U16 src_addr; + U16 dest_addr; + U8 data[CHB_MAX_PAYLOAD]; +} chb_rx_data_t; + +void chb_init(); +chb_pcb_t *chb_get_pcb(); +U8 chb_write(U16 addr, U8 *data, U8 len); +U8 chb_read(chb_rx_data_t *rx); + +#endif diff --git a/drivers/chibi/chb_buf.c b/drivers/chibi/chb_buf.c new file mode 100644 index 0000000..a08c856 --- /dev/null +++ b/drivers/chibi/chb_buf.c @@ -0,0 +1,89 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#include +#include "chb_buf.h" +#include "projectconfig.h" + +static U8 chb_buf[CFG_CHIBI_BUFFERSIZE]; +static volatile U32 rd_ptr, wr_ptr; +static volatile U32 len; + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_buf_init() +{ + rd_ptr = 0; + wr_ptr = 0; + len = 0; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_buf_write(U8 data) +{ + chb_buf[wr_ptr] = data; + wr_ptr = (wr_ptr + 1) % CFG_CHIBI_BUFFERSIZE; + len++; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U8 chb_buf_read() +{ + U8 data; + + data = chb_buf[rd_ptr]; + rd_ptr = (rd_ptr + 1) % CFG_CHIBI_BUFFERSIZE; + len--; + return data; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U32 chb_buf_get_len() +{ + return len; +} diff --git a/drivers/chibi/chb_buf.h b/drivers/chibi/chb_buf.h new file mode 100644 index 0000000..ec75812 --- /dev/null +++ b/drivers/chibi/chb_buf.h @@ -0,0 +1,44 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#ifndef CHB_BUF_H +#define CHB_BUF_H + +#include "types.h" + +void chb_buf_init(); +void chb_buf_write(U8 data); +U8 chb_buf_read(); +U32 chb_buf_get_len(); + +#endif diff --git a/drivers/chibi/chb_drvr.c b/drivers/chibi/chb_drvr.c new file mode 100644 index 0000000..c56531a --- /dev/null +++ b/drivers/chibi/chb_drvr.c @@ -0,0 +1,917 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#include +#include "chb.h" +#include "chb_drvr.h" +#include "chb_buf.h" +#include "chb_spi.h" +#include "chb_eeprom.h" + +#include "core/systick/systick.h" +#include "core/timer16/timer16.h" + +// store string messages in flash rather than RAM +const char chb_err_overflow[] = "BUFFER FULL. TOSSING INCOMING DATA\r\n"; +const char chb_err_init[] = "RADIO NOT INITIALIZED PROPERLY\r\n"; +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +static U8 chb_get_state() +{ + return chb_reg_read(TRX_STATUS) & 0x1f; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +static U8 chb_get_status() +{ + return chb_reg_read(TRX_STATE) >> CHB_TRAC_STATUS_POS; +} + +/**************************************************************************/ +/*! + Cause a blocking delay for x microseconds +*/ +/**************************************************************************/ +static void chb_delay_us(U16 usec) +{ + // Determine maximum delay using a 16 bit timer + // ToDo: Move this to a macro or fixed value! + uint32_t maxus = 0xFFFF / (CFG_CPU_CCLK / 1000000); + + // Check if delay can be done in one operation + if (usec <= maxus) + { + timer16DelayUS(0, usec); + return; + } + + // Split delay into several steps (to stay within limit of 16-bit timer) + do + { + if (usec >= maxus) + { + timer16DelayUS(0, maxus); + usec -= maxus; + } + else + { + timer16DelayUS(0, usec); + usec = 0; + } + } while (usec > 0); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_reset() +{ + CHB_RST_DISABLE(); + CHB_SLPTR_DISABLE(); + + // wait a bit while transceiver wakes up + chb_delay_us(TIME_P_ON_TO_CLKM_AVAIL); + + // reset the device + CHB_RST_ENABLE(); + chb_delay_us(TIME_RST_PULSE_WIDTH); + CHB_RST_DISABLE(); + + // check that we have the part number that we're expecting + while (1) + { + // if you're stuck in this loop, that means that you're not reading + // the version and part number register correctly. possible that version number + // changes. if so, update version num in header file + if ((chb_reg_read(VERSION_NUM) == CHB_AT86RF212_VER_NUM) && (chb_reg_read(PART_NUM) == CHB_AT86RF212_PART_NUM)) + { + break; + } + } +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U8 chb_reg_read(U8 addr) +{ + U8 val = 0; + + /* Add the register read command to the register address. */ + addr |= 0x80; + + CHB_ENTER_CRIT(); + CHB_SPI_ENABLE(); + + /*Send Register address and read register content.*/ + val = chb_xfer_byte(addr); + val = chb_xfer_byte(val); + + CHB_SPI_DISABLE(); + CHB_LEAVE_CRIT(); + + return val; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U16 chb_reg_read16(U8 addr) +{ + U8 i; + U16 val = 0; + + for (i=0; i<2; i++) + { + addr |= chb_reg_read(addr + i) << (8 * i); + } + return val; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_reg_write(U8 addr, U8 val) +{ + U8 dummy; + + /* Add the Register Write command to the address. */ + addr |= 0xC0; + + CHB_ENTER_CRIT(); + CHB_SPI_ENABLE(); + + /*Send Register address and write register content.*/ + dummy = chb_xfer_byte(addr); + dummy = chb_xfer_byte(val); + + CHB_SPI_DISABLE(); + CHB_LEAVE_CRIT(); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_reg_write16(U8 addr, U16 val) +{ + U8 i; + + for (i=0; i<2; i++) + { + chb_reg_write(addr + i, val >> (8 * i)); + } +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_reg_write64(U8 addr, U8 *val) +{ + U8 i; + + for (i=0; i<8; i++) + { + chb_reg_write(addr + i, *(val + i)); + } +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_reg_read_mod_write(U8 addr, U8 val, U8 mask) +{ + U8 tmp; + + tmp = chb_reg_read(addr); + val &= mask; // mask off stray bits from val + tmp &= ~mask; // mask off bits in reg val + tmp |= val; // copy val into reg val + chb_reg_write(addr, tmp); // write back to reg +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_frame_write(U8 *hdr, U8 hdr_len, U8 *data, U8 data_len) +{ + U8 i, dummy; + + // dont allow transmission longer than max frame size + if ((hdr_len + data_len) > 127) + { + return; + } + + // initiate spi transaction + CHB_ENTER_CRIT(); + CHB_SPI_ENABLE(); + + // send fifo write command + dummy = chb_xfer_byte(CHB_SPI_CMD_FW); + + // write hdr contents to fifo + for (i=0; i= CHB_MIN_FRAME_LENGTH) && (len <= CHB_MAX_FRAME_LENGTH)) + { + // check to see if there is room to write the frame in the buffer. if not, then drop it + if (len < (CFG_CHIBI_BUFFERSIZE - chb_buf_get_len())) + { + chb_buf_write(len); + + for (i=0; ioverflow++; + + // print the error message + printf(chb_err_overflow); + } + } + + CHB_SPI_DISABLE(); + CHB_LEAVE_CRIT(); +} + +/**************************************************************************/ +/*! + Read directly from the SRAM on the radio. This is only used for debugging + purposes. +*/ +/**************************************************************************/ +#ifdef CHB_DEBUG +void chb_sram_read(U8 addr, U8 len, U8 *data) +{ + U8 i, dummy; + + CHB_ENTER_CRIT(); + CHB_SPI_ENABLE(); + + /*Send SRAM read command.*/ + dummy = chb_xfer_byte(CHB_SPI_CMD_SR); + + /*Send address where to start reading.*/ + dummy = chb_xfer_byte(addr); + + for (i=0; i 3) + { + channel = 0; + } + + channel = (channel << 1) + 11; + + chb_reg_read_mod_write(CC_CTRL_1, 0x4, 0x7); // set 769 MHz base frequency for China + chb_reg_write(CC_CTRL_0, channel); // set the center frequency for the channel + +#else + //if (channel == 0) + //{ + // // Channel 0 is for European use only. make sure we are using channel page 2, + // // channel 0 settings for 100 kbps + // if ((chb_reg_read(TRX_CTRL_2) & 0x3f) != 0x08) + // { + // chb_reg_read_mod_write(TRX_CTRL_2, 0x08, 0x3f); + // } + //} + //else if (channel > 10) + //{ + // // if the channel is out of bounds for page 2, then default to channel 1 and + // // assume we're on the US frequency of 915 MHz + // channel = 1; + // if ((chb_reg_read(TRX_CTRL_2) & 0x3f) != 0x0c) + // { + // chb_reg_read_mod_write(TRX_CTRL_2, 0x0c, 0x3f); + // } + //} + //else + //{ + // // Channels 1-10 are for US frequencies of 915 MHz + // if ((chb_reg_read(TRX_CTRL_2) & 0x3f) != 0x0c) + // { + // chb_reg_read_mod_write(TRX_CTRL_2, 0x0c, 0x3f); + // } + //} + + chb_reg_read_mod_write(PHY_CC_CCA, channel, 0x1f); +#endif + + // add a delay to allow the PLL to lock if in active mode. + state = chb_get_state(); + if ((state == RX_ON) || (state == PLL_ON)) + { + chb_delay_us(TIME_PLL_LOCK_TIME); + } + + return ((chb_reg_read(PHY_CC_CCA) & 0x1f) == channel) ? RADIO_SUCCESS : RADIO_TIMED_OUT; +} + +/**************************************************************************/ +/*! + Set the power level +*/ +/**************************************************************************/ +void chb_set_pwr(U8 val) +{ + chb_reg_write(PHY_TX_PWR, val); +} + +/**************************************************************************/ +/*! + Set the TX/RX state machine state. Some manual manipulation is required + for certain operations. Check the datasheet for more details on the state + machine and manipulations. +*/ +/**************************************************************************/ +U8 chb_set_state(U8 state) +{ + U8 curr_state, delay; + + // if we're sleeping then don't allow transition + if (gpioGetValue(CHB_SLPTRPORT, CHB_SLPTRPIN)) + { + return RADIO_WRONG_STATE; + } + + // if we're in a transition state, wait for the state to become stable + curr_state = chb_get_state(); + if ((curr_state == BUSY_TX_ARET) || (curr_state == BUSY_RX_AACK) || (curr_state == BUSY_RX) || (curr_state == BUSY_TX)) + { + while (chb_get_state() == curr_state); + } + + // At this point it is clear that the requested new_state is: + // TRX_OFF, RX_ON, PLL_ON, RX_AACK_ON or TX_ARET_ON. + // we need to handle some special cases before we transition to the new state + switch (state) + { + case TRX_OFF: + /* Go to TRX_OFF from any state. */ + CHB_SLPTR_DISABLE(); + chb_reg_read_mod_write(TRX_STATE, CMD_FORCE_TRX_OFF, 0x1f); + chb_delay_us(TIME_ALL_STATES_TRX_OFF); + break; + + case TX_ARET_ON: + if (curr_state == RX_AACK_ON) + { + /* First do intermediate state transition to PLL_ON, then to TX_ARET_ON. */ + chb_reg_read_mod_write(TRX_STATE, CMD_PLL_ON, 0x1f); + chb_delay_us(TIME_RX_ON_PLL_ON); + } + break; + + case RX_AACK_ON: + if (curr_state == TX_ARET_ON) + { + /* First do intermediate state transition to RX_ON, then to RX_AACK_ON. */ + chb_reg_read_mod_write(TRX_STATE, CMD_PLL_ON, 0x1f); + chb_delay_us(TIME_RX_ON_PLL_ON); + } + break; + } + + /* Now we're okay to transition to any new state. */ + chb_reg_read_mod_write(TRX_STATE, state, 0x1f); + + /* When the PLL is active most states can be reached in 1us. However, from */ + /* TRX_OFF the PLL needs time to activate. */ + delay = (curr_state == TRX_OFF) ? TIME_TRX_OFF_PLL_ON : TIME_RX_ON_PLL_ON; + chb_delay_us(delay); + + if (chb_get_state() == state) + { + return RADIO_SUCCESS; + } + return RADIO_TIMED_OUT; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_set_ieee_addr(U8 *addr) +{ + chb_eeprom_write(CFG_EEPROM_CHIBI_IEEEADDR, addr, 8); + chb_reg_write64(IEEE_ADDR_0, addr); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_get_ieee_addr(U8 *addr) +{ + chb_eeprom_read(CFG_EEPROM_CHIBI_IEEEADDR, addr, 8); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_set_short_addr(U16 addr) +{ + U8 *addr_ptr = (U8 *)&addr; + chb_pcb_t *pcb = chb_get_pcb(); + + chb_eeprom_write(CFG_EEPROM_CHIBI_SHORTADDR, addr_ptr, 2); + chb_reg_write16(SHORT_ADDR_0, addr); + pcb->src_addr = addr; +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +U16 chb_get_short_addr() +{ + int16_t addr; + + chb_eeprom_read(CFG_EEPROM_CHIBI_SHORTADDR, (uint8_t*)&addr, 2); + return addr; +} +/**************************************************************************/ +/*! + Set the high gain mode pin for the CC1190 +*/ +/**************************************************************************/ +#if (CHB_CC1190_PRESENT) +void chb_set_hgm(U8 enb) +{ + if (enb) + { + gpioSetValue(CHB_CC1190_HGM_PORT, CHB_CC1190_HGM_PIN, 1); + } + else + { + gpioSetValue(CHB_CC1190_HGM_PORT, CHB_CC1190_HGM_PIN, 0); + } +} +#endif + +/**************************************************************************/ +/*! + Load the data into the fifo, initiate a transmission attempt, + and return the status of the transmission attempt. +*/ +/**************************************************************************/ +U8 chb_tx(U8 *hdr, U8 *data, U8 len) +{ + U8 state = chb_get_state(); + chb_pcb_t *pcb = chb_get_pcb(); + + if ((state == BUSY_TX) || (state == BUSY_TX_ARET)) + { + return RADIO_WRONG_STATE; + } + + // TODO: check why we need to transition to the off state before we go to tx_aret_on + chb_set_state(TRX_OFF); + chb_set_state(TX_ARET_ON); + + // TODO: try and start the frame transmission by writing TX_START command instead of toggling + // sleep pin...i just feel like it's kind of weird... + + // write frame to buffer. first write header into buffer (add 1 for len byte), then data. + chb_frame_write(hdr, CHB_HDR_SZ + 1, data, len); + + //Do frame transmission + chb_reg_read_mod_write(TRX_STATE, CMD_TX_START, 0x1F); + + // wait for the transmission to end, signalled by the TRX END flag + while (!pcb->tx_end); + pcb->tx_end = false; + + // check the status of the transmission + return chb_get_status(); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +static void chb_radio_init() +{ + U8 ieee_addr[8]; + + // reset chip + chb_reset(); + + // disable intps while we config the radio + chb_reg_write(IRQ_MASK, 0); + + // force transceiver off while we configure the intps + chb_reg_read_mod_write(TRX_STATE, CMD_FORCE_TRX_OFF, 0x1F); + + // make sure the transceiver is in the off state before proceeding + while ((chb_reg_read(TRX_STATUS) & 0x1f) != TRX_OFF); + + // set radio cfg parameters + // **note** uncomment if these will be set to something other than default + //chb_reg_read_mod_write(XAH_CTRL_0, CHB_MAX_FRAME_RETRIES << CHB_MAX_FRAME_RETRIES_POS, 0xF << CHB_MAX_FRAME_RETRIES_POS); + //chb_reg_read_mod_write(XAH_CTRL_0, CHB_MAX_CSMA_RETRIES << CHB_MAX_CSMA_RETIRES_POS, 0x7 << CHB_MAX_CSMA_RETIRES_POS); + //chb_reg_read_mod_write(CSMA_SEED_1, CHB_CSMA_SEED1 << CHB_CSMA_SEED1_POS, 0x7 << CHB_CSMA_SEED1_POS); + //chb_ret_write(CSMA_SEED0, CHB_CSMA_SEED0); + //chb_reg_read_mod_write(PHY_CC_CCA, CHB_CCA_MODE << CHB_CCA_MODE_POS,0x3 << CHB_CCA_MODE_POS); + //chb_reg_write(CCA_THRES, CHB_CCA_ED_THRES); + + // set frame version that we'll accept + chb_reg_read_mod_write(CSMA_SEED_1, CHB_FRM_VER << CHB_FVN_POS, 3 << CHB_FVN_POS); + + // set interrupt mask + // re-enable intps while we config the radio + chb_reg_write(IRQ_MASK, (1<ed = chb_reg_read(PHY_ED_LEVEL); + + // get the crc + pcb->crc = (chb_reg_read(PHY_RSSI) & (1<<7)) ? 1 : 0; + + // if the crc is not valid, then do not read the frame and set the rx flag + if (pcb->crc) + { + // get the data + chb_frame_read(); + pcb->rcvd_xfers++; + pcb->data_rcv = true; + } + } + else + { + pcb->tx_end = true; + } + intp_src &= ~CHB_IRQ_TRX_END_MASK; + while (chb_set_state(RX_STATE) != RADIO_SUCCESS); + } + else if (intp_src & CHB_IRQ_TRX_UR_MASK) + { + intp_src &= ~CHB_IRQ_TRX_UR_MASK; + pcb->underrun++; + } + else if (intp_src & CHB_IRQ_PLL_UNLOCK_MASK) + { + intp_src &= ~CHB_IRQ_PLL_UNLOCK_MASK; + } + else if (intp_src & CHB_IRQ_PLL_LOCK_MASK) + { + intp_src &= ~CHB_IRQ_PLL_LOCK_MASK; + } + else if (intp_src & CHB_IRQ_BAT_LOW_MASK) + { + intp_src &= ~CHB_IRQ_BAT_LOW_MASK; + pcb->battlow++; + } + else + { + } + } + CHB_LEAVE_CRIT(); +} diff --git a/drivers/chibi/chb_drvr.h b/drivers/chibi/chb_drvr.h new file mode 100644 index 0000000..48990bb --- /dev/null +++ b/drivers/chibi/chb_drvr.h @@ -0,0 +1,374 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +#ifndef CHIBI_DRVR_H +#define CHIBI_DRVR_H + +#include "types.h" +#include "projectconfig.h" +#include "core/gpio/gpio.h" + +#define CHB_CC1190_PRESENT 0 /// Set to 1 if CC1190 is being used +#define CHB_CHINA 0 +#define CHB_EEPROM_IEEE_ADDR CFG_CHIBI_EEPROM_IEEEADDR +#define CHB_EEPROM_SHORT_ADDR CFG_CHIBI_EEPROM_SHORTADDR +#define CHB_AT86RF212_VER_NUM 0x01 +#define CHB_AT86RF212_PART_NUM 0x07 +// #define CHB_BPSK 0 // set to 1 if want to use BPSK rather than OQPSK + +#define CHB_SPI_CMD_RW 0xC0 /**< Register Write (short mode). */ +#define CHB_SPI_CMD_RR 0x80 /**< Register Read (short mode). */ +#define CHB_SPI_CMD_FW 0x60 /**< Frame Transmit Mode (long mode). */ +#define CHB_SPI_CMD_FR 0x20 /**< Frame Receive Mode (long mode). */ +#define CHB_SPI_CMD_SW 0x40 /**< SRAM Write. */ +#define CHB_SPI_CMD_SR 0x00 /**< SRAM Read. */ +#define CHB_SPI_CMD_RADDRM 0x7F /**< Register Address Mask. */ + +#define CHB_IRQ_BAT_LOW_MASK 0x80 /**< Mask for the BAT_LOW interrupt. */ +#define CHB_IRQ_TRX_UR_MASK 0x40 /**< Mask for the TRX_UR interrupt. */ +#define CHB_IRQ_TRX_END_MASK 0x08 /**< Mask for the TRX_END interrupt. */ +#define CHB_IRQ_RX_START_MASK 0x04 /**< Mask for the RX_START interrupt. */ +#define CHB_IRQ_PLL_UNLOCK_MASK 0x02 /**< Mask for the PLL_UNLOCK interrupt. */ +#define CHB_IRQ_PLL_LOCK_MASK 0x01 /**< Mask for the PLL_LOCK interrupt. */ + +#define CHB_EINTPORT 1 +#define CHB_EINTPIN 8 +#define CHB_EINTPIN_IOCONREG IOCON_PIO1_8 +#define CHB_RSTPORT 1 +#define CHB_RSTPIN 9 +#define CHB_RSTPIN_IOCONREG IOCON_PIO1_9 +#define CHB_SLPTRPORT 1 +#define CHB_SLPTRPIN 10 +#define CHB_SLPTRPIN_IOCONREG IOCON_PIO1_10 + +// if CC1190 present, set up the ports and pins for high gain mode control +#if (CHB_CC1190_PRESENT) + #define CHB_CC1190_HGM_PORT 1 + #define CHB_CC1190_HGM_PIN 11 + #define CHB_CC1190_HGM_IOCONREG IOCON_PIO1_11 +#endif + +//#define CHB_DDR_SLPTR DDRF +//#define CHB_DDR_RST DDRF +//#define CHB_RADIO_IRQ INT6_vect +//#define CHB_RADIO_IRQ_PIN INT6 + +#define CHB_ENTER_CRIT() __disable_irq() +#define CHB_LEAVE_CRIT() __enable_irq() +#define CHB_RST_ENABLE() do {gpioSetValue(CHB_RSTPORT, CHB_RSTPIN, 0); } while (0) +#define CHB_RST_DISABLE() do {gpioSetValue(CHB_RSTPORT, CHB_RSTPIN, 1); } while (0) +#define CHB_SLPTR_ENABLE() do {gpioSetValue(CHB_SLPTRPORT, CHB_SLPTRPIN, 1); } while (0) +#define CHB_SLPTR_DISABLE() do {gpioSetValue(CHB_SLPTRPORT, CHB_SLPTRPIN, 0); } while (0) + +// CCA constants +enum +{ + CCA_ED = 1, /**< Use energy detection above threshold mode. */ + CCA_CARRIER_SENSE = 2, /**< Use carrier sense mode. */ + CCA_CARRIER_SENSE_WITH_ED = 3 /**< Use a combination of both energy detection and carrier sense. */ +}; + +// configuration parameters +enum +{ + CHB_CHANNEL = 1, // Replaced in projectconfig.h with CFG_CHIBI_CHANNEL + CHB_PAN_ID = 0x1234, // Replaced in projectconfig.h with CFG_CHIBI_PANID + CHB_TX_PWR = 0x0, + CHB_SHORT_ADDR = 0x0, + CHB_IEEE_ADDR = 0x0, + CHB_MAX_FRAME_RETRIES = 3, + CHB_MAX_CSMA_RETRIES = 4, + CHB_CCA_MODE = CCA_ED, + CHB_MIN_BE = 3, + CHB_MAX_BE = 5, + CHB_CCA_ED_THRES = 0x7, + CHB_CSMA_SEED0 = 0, + CHB_CSMA_SEED1 = 0, + CHB_FRM_VER = 1 // accept 802.15.4 ver 0 or 1 frames +}; + +// register addresses +enum +{ + TRX_STATUS = 0x01, + TRX_STATE = 0x02, + TRX_CTRL_0 = 0x03, + TRX_CTRL_1 = 0x04, + PHY_TX_PWR = 0x05, + PHY_RSSI = 0x06, + PHY_ED_LEVEL = 0x07, + PHY_CC_CCA = 0x08, + CCA_THRES = 0x09, + RX_CTRL = 0x0a, + SFD_VALUE = 0x0b, + TRX_CTRL_2 = 0x0c, + ANT_DIV = 0x0d, + IRQ_MASK = 0x0e, + IRQ_STATUS = 0x0f, + VREG_CTRL = 0x10, + BATMON = 0x11, + XOSC_CTRL = 0x12, + CC_CTRL_0 = 0x13, + CC_CTRL_1 = 0x14, + RX_SYN = 0x15, + RF_CTRL_0 = 0x16, + XAH_CTRL_1 = 0x17, + FTN_CTRL = 0x18, + RF_CTRL_1 = 0x19, + PLL_CF = 0x1a, + PLL_DCU = 0x1b, + PART_NUM = 0x1c, + VERSION_NUM = 0x1d, + MAN_ID_0 = 0x1e, + MAN_ID_1 = 0x1f, + SHORT_ADDR_0 = 0x20, + SHORT_ADDR_1 = 0x21, + PAN_ID_0 = 0x22, + PAN_ID_1 = 0x23, + IEEE_ADDR_0 = 0x24, + IEEE_ADDR_1 = 0x25, + IEEE_ADDR_2 = 0x26, + IEEE_ADDR_3 = 0x27, + IEEE_ADDR_4 = 0x28, + IEEE_ADDR_5 = 0x29, + IEEE_ADDR_6 = 0x2a, + IEEE_ADDR_7 = 0x2b, + XAH_CTRL_0 = 0x2c, + CSMA_SEED_0 = 0x2d, + CSMA_SEED_1 = 0x2e, + CSMA_BE = 0x2f +}; + +// random defines +enum +{ + CHB_MAX_FRAME_RETRIES_POS = 4, + CHB_MAX_CSMA_RETIRES_POS = 1, + CHB_CSMA_SEED1_POS = 0, + CHB_CCA_MODE_POS = 5, + CHB_AUTO_CRC_POS = 5, + CHB_TRX_END_POS = 3, + CHB_TRAC_STATUS_POS = 5, + CHB_FVN_POS = 6, + CHB_OQPSK_TX_OFFSET = 2, + CHB_BPSK_TX_OFFSET = 3, + CHB_MIN_FRAME_LENGTH = 3, + CHB_MAX_FRAME_LENGTH = 0x7f, + CHB_PA_EXT_EN_POS = 7 +}; + +// transceiver timing +enum{ + TIME_RST_PULSE_WIDTH = 1, + TIME_P_ON_TO_CLKM_AVAIL = 380, + TIME_SLEEP_TO_TRX_OFF = 240, + TIME_TRX_OFF_TO_SLEEP = 35, + TIME_PLL_ON_TRX_OFF = 1, + TIME_TRX_OFF_RX_ON = 110, + TIME_RX_ON_TRX_OFF = 1, + TIME_PLL_ON_RX_ON = 1, + TIME_RX_ON_PLL_ON = 1, + TIME_PLL_LOCK_TIME = 110, + TIME_BUSY_TX_PLL_ON = 32, + TIME_ALL_STATES_TRX_OFF = 1, + TIME_RESET_TRX_OFF = 26, + TIME_TRX_IRQ_DELAY = 9, + TIME_TRX_OFF_PLL_ON = 110, + TIME_IRQ_PROCESSING_DLY = 32 +}; + +// trac status +enum{ + TRAC_SUCCESS = 0, + TRAC_SUCCESS_DATA_PENDING = 1, + TRAC_WAIT_FOR_ACK = 2, + TRAC_CHANNEL_ACCESS_FAIL = 3, + TRAC_NO_ACK = 5, + TRAC_INVALID = 7 +}; + +// radio statuses +enum{ + RADIO_SUCCESS = 0x40, /**< The requested service was performed successfully. */ + RADIO_UNSUPPORTED_DEVICE, /**< The connected device is not an Atmel AT86RF212. */ + RADIO_INVALID_ARGUMENT, /**< One or more of the supplied function arguments are invalid. */ + RADIO_TIMED_OUT, /**< The requested service timed out. */ + RADIO_WRONG_STATE, /**< The end-user tried to do an invalid state transition. */ + RADIO_BUSY_STATE, /**< The radio transceiver is busy receiving or transmitting. */ + RADIO_STATE_TRANSITION_FAILED, /**< The requested state transition could not be completed. */ + RADIO_CCA_IDLE, /**< Channel is clear, available to transmit a new frame. */ + RADIO_CCA_BUSY, /**< Channel busy. */ + RADIO_TRX_BUSY, /**< Transceiver is busy receiving or transmitting data. */ + RADIO_BAT_LOW, /**< Measured battery voltage is lower than voltage threshold. */ + RADIO_BAT_OK, /**< Measured battery voltage is above the voltage threshold. */ + RADIO_CRC_FAILED, /**< The CRC failed for the actual frame. */ + RADIO_CHANNEL_ACCESS_FAILURE, /**< The channel access failed during the auto mode. */ + RADIO_NO_ACK, /**< No acknowledge frame was received. */ +}; + +// transceiver commands +enum +{ + CMD_NOP = 0, + CMD_TX_START = 2, + CMD_FORCE_TRX_OFF = 3, + CMD_FORCE_PLL_ON = 4, + CMD_RX_ON = 6, + CMD_TRX_OFF = 8, + CMD_PLL_ON = 9, + CMD_RX_AACK_ON = 22, + CMD_TX_ARET_ON = 25 +}; + +// transceiver states +enum +{ + P_ON = 0, + BUSY_RX = 1, + BUSY_TX = 2, + RX_ON = 6, + TRX_OFF = 8, + PLL_ON = 9, + SLEEP = 15, + BUSY_RX_AACK = 17, + BUSY_TX_ARET = 18, + RX_AACK_ON = 22, + TX_ARET_ON = 25, + RX_ON_NOCLK = 28, + RX_AACK_ON_NOCLK = 29, + BUSY_RX_AACK_NOCLK = 30, + TRANS_IN_PROG = 31 +}; + +// transceiver interrupt register +enum +{ + IRQ_PLL_LOCK = 0, + IRQ_PLL_UNLOCK = 1, + IRQ_RX_START = 2, + IRQ_TRX_END = 3, + IRQ_CCA_ED_READY = 4, + IRQ_AMI = 5, + IRQ_TRX_UR = 6, + IRQ_BAT_LOW = 7 +}; + +// transceiver modes +enum +{ + OQPSK_868MHZ = 0, + OQPSK_915MHZ = 1, + OQPSK_780MHZ = 2, + BPSK40_915MHZ = 3, + BPSK20_868MHZ = 4 +}; + +// See Table 7-15 for details +enum +{ + CHB_PWR_EU1_2DBM = 0x63, // EU (868MHz) Linearized PA mode + CHB_PWR_EU1_1DBM = 0x64, // Note: BPSK 20kbit/s only! + CHB_PWR_EU1_0DBM = 0x65, + CHB_PWR_EU2_5DBM = 0xE7, // EU (868MHz) Boost mode (but > supply current) + CHB_PWR_EU2_4DBM = 0xE8, // 4-5dBM BPSK 20 kbit/s only! + CHB_PWR_EU2_3DBM = 0xE9, // 0-3dBM O-QPSK 100/200/400 kbit/s or BPSK + CHB_PWR_EU2_2DBM = 0xEA, + CHB_PWR_EU2_1DBM = 0xCB, + CHB_PWR_EU2_0DBM = 0xAB, + CHB_PWR_NA_10DBM = 0xC0, // North America (915MHz) + CHB_PWR_NA_9DBM = 0xA1, + CHB_PWR_NA_8DBM = 0x81, + CHB_PWR_NA_7DBM = 0x82, + CHB_PWR_NA_6DBM = 0x83, + CHB_PWR_NA_5DBM = 0x60, + CHB_PWR_NA_4DBM = 0x61, + CHB_PWR_NA_3DBM = 0x41, + CHB_PWR_NA_2DBM = 0x42, + CHB_PWR_NA_1DBM = 0x22, + CHB_PWR_NA_0DBM = 0x23, + CHB_PWR_CHINA_5DBM = 0xE7, // China (780MHz) + CHB_PWR_CHINA_4DBM = 0xE8, + CHB_PWR_CHINA_3DBM = 0xE9, + CHB_PWR_CHINA_2DBM = 0xEA, + CHB_PWR_CHINA_1DBM = 0xCA, + CHB_PWR_CHINA_0DBM = 0xAA +}; + +// define receive state based on promiscuous mode setting +#if (CFG_CHIBI_PROMISCUOUS == 1) + #define RX_STATE RX_ON +#else + #define RX_STATE RX_AACK_ON +#endif +// init +void chb_drvr_init(); + +// data access +U8 chb_reg_read(U8 addr); +U16 chb_reg_read16(U8 addr); +void chb_reg_write(U8 addr, U8 val); +void chb_reg_write16(U8 addr, U16 val); +void chb_reg_write64(U8 addr, U8 *val); +void chb_reg_read_mod_write(U8 addr, U8 val, U8 mask); +void chb_frame_write(U8 *hdr, U8 hdr_len, U8 *data, U8 data_len); + +// general configuration +void chb_set_mode(U8 mode); +U8 chb_set_channel(U8 channel); +void chb_set_pwr(U8 val); +void chb_set_ieee_addr(U8 *addr); +void chb_get_ieee_addr(U8 *addr); +void chb_set_short_addr(U16 addr); +U16 chb_get_short_addr(); +U8 chb_set_state(U8 state); + +// Power management +void chb_sleep(U8 enb); + +// data transmit +U8 chb_tx(U8 *hdr, U8 *data, U8 len); + +#if (CHB_CC1190_PRESENT) + void chb_set_hgm(U8 enb); +#endif + +#ifdef CHB_DEBUG +// sram access +void chb_sram_read(U8 addr, U8 len, U8 *data); +void chb_sram_write(U8 addr, U8 len, U8 *data); +#endif + +void chb_ISR_Handler (void); + +#endif + diff --git a/drivers/chibi/chb_eeprom.c b/drivers/chibi/chb_eeprom.c new file mode 100644 index 0000000..d7e5f09 --- /dev/null +++ b/drivers/chibi/chb_eeprom.c @@ -0,0 +1,70 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +/*! + \file + \ingroup + + +*/ +/**************************************************************************/ +#include "chb_eeprom.h" +#include "drivers/eeprom/eeprom.h" + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_eeprom_write(uint16_t addr, uint8_t *buf, uint16_t size) +{ + // Write the address one byte at a time + uint16_t a = 0; + while (a < size) + { + eepromWriteU8(addr + a, buf[a]); + a++; + } +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_eeprom_read(uint16_t addr, uint8_t *buf, uint16_t size) +{ + // Read the contents at address + eepromReadBuffer(addr, buf, size); +} + diff --git a/drivers/chibi/chb_eeprom.h b/drivers/chibi/chb_eeprom.h new file mode 100644 index 0000000..3c3df71 --- /dev/null +++ b/drivers/chibi/chb_eeprom.h @@ -0,0 +1,50 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +/*! + \file + \ingroup + + +*/ +/**************************************************************************/ +#ifndef CHB_EEPROM_H +#define CHB_EEPROM_H + +#include "projectconfig.h" +#include "types.h" + +void chb_eeprom_write(U16 addr, U8 *buf, U16 size); +void chb_eeprom_read(U16 addr, U8 *buf, U16 size); + +#endif diff --git a/drivers/chibi/chb_spi.c b/drivers/chibi/chb_spi.c new file mode 100644 index 0000000..e552184 --- /dev/null +++ b/drivers/chibi/chb_spi.c @@ -0,0 +1,77 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +/*! + \file + \ingroup + + +*/ +/**************************************************************************/ +#include "chb.h" +#include "chb_spi.h" +#include "core/ssp/ssp.h" + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void chb_spi_init() +{ + // initialise spi, high between frames and transition on trailing edge + sspInit(0, sspClockPolarity_High, sspClockPhase_FallingEdge); + + // set the slave select to idle + CHB_SPI_DISABLE(); +} + +/**************************************************************************/ +/*! + This function both reads and writes data. For write operations, include data + to be written as argument. For read ops, use dummy data as arg. Returned + data is read byte val. +*/ +/**************************************************************************/ +U8 chb_xfer_byte(U8 data) +{ + /* Move on only if NOT busy and TX FIFO not full */ + while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_MASK | SSP_SSP0SR_BSY_MASK)) != SSP_SSP0SR_TNF_NOTFULL); + SSP_SSP0DR = data; + + /* Wait until the busy bit is cleared and receive buffer is not empty */ + while ((SSP_SSP0SR & (SSP_SSP0SR_BSY_MASK | SSP_SSP0SR_RNE_MASK)) != SSP_SSP0SR_RNE_NOTEMPTY); + + // Read the queue + return SSP_SSP0DR; +} diff --git a/drivers/chibi/chb_spi.h b/drivers/chibi/chb_spi.h new file mode 100644 index 0000000..220feb7 --- /dev/null +++ b/drivers/chibi/chb_spi.h @@ -0,0 +1,62 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +/*! + \file + \ingroup + + +*/ +/**************************************************************************/ + +#ifndef CHB_SPI_H +#define CHB_SPI_H + +#include "projectconfig.h" +#include "core/gpio/gpio.h" + +#define CHB_SSPORT (0) // P0.2 = SSEL +#define CHB_SSPIN (2) + +#define CHB_SPI_ENABLE() do {gpioSetValue(CHB_SSPORT, CHB_SSPIN, 0);} while (0) // Drive SSEL low +#define CHB_SPI_DISABLE() do {gpioSetValue(CHB_SSPORT, CHB_SSPIN, 1);} while (0) // Drive SSEL high + +#define CHB_SPIPORT 0 +#define CHB_SCK 1 // PB.1 - Output: SPI Serial Clock (SCLK) +#define CHB_MOSI 2 // PB.2 - Output: SPI Master out - slave in (MOSI) +#define CHB_MISO 3 // PB.3 - Input: SPI Master in - slave out (MISO) + +void chb_spi_init(); +U8 chb_xfer_byte(U8 data); + +#endif diff --git a/drivers/chibi/types.h b/drivers/chibi/types.h new file mode 100644 index 0000000..c551bbc --- /dev/null +++ b/drivers/chibi/types.h @@ -0,0 +1,54 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. +*******************************************************************/ +/*! + \file types.h + \ingroup usb +*/ +/*******************************************************************/ +#ifndef TYPES_H +#define TYPES_H + +#include +#include + +// Standard data types +typedef uint8_t U8; /// Generic 8 bit unsigned data type +typedef uint16_t U16; /// Generic 16 bit unsigned data type +typedef uint32_t U32; /// Generic 32 bit unsigned data type +typedef uint64_t U64; /// Generic 64 bit unsigned data type + +typedef int8_t S8; /// Generic 8 bit signed data type +typedef int16_t S16; /// Generic 16 bit signed data type +typedef int32_t S32; /// Generic 32 bit signed data type + +#endif diff --git a/drivers/dac/mcp4725/mcp4725.c b/drivers/dac/mcp4725/mcp4725.c new file mode 100644 index 0000000..7b9c6ac --- /dev/null +++ b/drivers/dac/mcp4725/mcp4725.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/*! + @file mcp4725.c + @author K. Townsend (microBuilder.eu) + + @brief Driver for the I2C-based MCP4725 12-Bit DAC. + + @section Example + + @code + #include "drivers/dac/mcp4725/mcp4725.h" + ... + + mcp4725Init(); + + // Set the voltage to 50% of vref and don't save the value in EEPROM + mcp4725SetVoltage(2048, false); + + // Request the current value from the DAC + uint8_t status = 0; + uint16_t value = 0; + mcp472ReadConfig(&status, &value); + + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "mcp4725.h" +#include "core/i2c/i2c.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +static bool _mcp4725Initialised = false; + +/**************************************************************************/ +/*! + @brief Initialises I2C for the MCP4725. +*/ +/**************************************************************************/ +int mcp4725Init() +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + /* Fatal error */ + return -1; + } + + /* Set initialisation flag */ + _mcp4725Initialised = true; + + return 0; +} + +/**************************************************************************/ +/*! + @brief Sets the output voltage to a fraction of source vref. (Value + can be 0..4095) + + @param[in] output + The 12-bit value representing the relationship between + the DAC's input voltage and its output voltage. + @param[in] writeEEPROM + If this value is true, 'output' will also be written + to the MCP4725's internal non-volatile memory, meaning + that the DAC will retain the current voltage output + after power-down or reset. +*/ +/**************************************************************************/ +void mcp4725SetVoltage( uint16_t output, bool writeEEPROM ) +{ + if (!_mcp4725Initialised) mcp4725Init(); + + // Clear write buffers + uint32_t i; + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 4; + I2CReadLength = 0; + I2CMasterBuffer[0] = MCP4725_ADDRESS; // I2C device address + if (writeEEPROM) // command and config bits (C2.C1.C0.x.x.PD1.PD0.x) + { + I2CMasterBuffer[1] = (MCP4726_CMD_WRITEDACEEPROM); + } + else + { + I2CMasterBuffer[1] = (MCP4726_CMD_WRITEDAC); + } + I2CMasterBuffer[2] = (output / 16); // Upper data bits (D11.D10.D9.D8.D7.D6.D5.D4) + I2CMasterBuffer[3] = (output % 16) << 4; // Lower data bits (D3.D2.D1.D0.x.x.x.x) + i2cEngine(); +} + +/**************************************************************************/ +/*! + @brief Reads the current configuration and output settings for the + DAC. + + @param[out] status + Pointer to hold the contents of the status register + @param[out] value + Pointer to hold the output value of the 12-bit DAC +*/ +/**************************************************************************/ +void mcp472ReadConfig( uint8_t *status, uint16_t *value ) +{ + if (!_mcp4725Initialised) mcp4725Init(); + + // Clear write buffers + uint32_t i; + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 1; + I2CReadLength = 3; + I2CMasterBuffer[0] = MCP4725_ADDRESS | MCP4725_READ; + i2cEngine(); + + // Shift values to create properly formed integers + *status = I2CSlaveBuffer[0]; + *value = ((I2CSlaveBuffer[1] << 4) | (I2CSlaveBuffer[2] >> 4)); +} + diff --git a/drivers/dac/mcp4725/mcp4725.h b/drivers/dac/mcp4725/mcp4725.h new file mode 100644 index 0000000..71100cc --- /dev/null +++ b/drivers/dac/mcp4725/mcp4725.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/*! + @file mcp4725.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _MCP4725_H_ +#define _MCP4725_H_ + +#include "projectconfig.h" + +#define MCP4725_ADDRESS (0xC0) // 1100000x - Assumes A0 is GND and A2,A1 are 0 (MCP4725A0T-E/CH) +#define MCP4725_READ (0x01) +#define MCP4726_CMD_WRITEDAC (0x40) // Writes data to the DAC +#define MCP4726_CMD_WRITEDACEEPROM (0x60) // Writes data to the DAC and the EEPROM (persisting the assigned value after reset) + +int mcp4725Init(); +void mcp4725SetVoltage( uint16_t output, bool writeEEPROM ); +void mcp472ReadConfig( uint8_t *status, uint16_t *value ); + +#endif \ No newline at end of file diff --git a/drivers/eeprom/at25040/at25040.c b/drivers/eeprom/at25040/at25040.c new file mode 100644 index 0000000..d84a733 --- /dev/null +++ b/drivers/eeprom/at25040/at25040.c @@ -0,0 +1,297 @@ +/**************************************************************************/ +/*! + @file at25040.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Driver for Atmel's AT25010a/AT25020a/AT25040a 1K/2K/4K serial EEPROM. + + @note The AT25xxx has an 8-byte buffer, including 1 command byte + and one address offset byte, meaning that a maximum of 6 + bytes can be read or written in one operation. An error + will be returned if a value greater than 6 is passed in + for bufferLength with the eepromRead and eepromWrite + methods. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "drivers/eeprom/at25040/at25040.h" + + int main(void) + { + cpuInit(); + at25Init(); + + // Set read and write buffers + uint8_t wBuffer[1]; + uint8_t rBuffer[1]; + + // Instantiate error message placeholder + at25Error_e error = AT25_ERROR_OK; + + // Write 0xAA to EEPROM at address 0x0000 + wBuffer[0] = 0xAA; + error = at25Write(0x0000, wBuffer, 1); + if (error) + { + // Log the error message or take appropriate actions + switch (error) + { + case (AT25_ERROR_TIMEOUT_WFINISH): + // EEPROM timed out waiting for the write to finish + break; + case (AT25_ERROR_TIMEOUT_WE): + // EEPROM timed out waiting for write-enable + break; + case (AT25_ERROR_ADDRERR): + // Address is out of range + break; + } + } + + // Read the EEPROM at address 0x0000 + at25Read(0x0000, rBuffer, 1); + ... + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "at25040.h" +#include "core/ssp/ssp.h" +#include "core/gpio/gpio.h" + +#define AT25_SELECT() gpioSetValue(0, 2, 0) +#define AT25_DESELECT() gpioSetValue(0, 2, 1) + +uint32_t i, timeout; +uint8_t src_addr[SSP_FIFOSIZE]; +uint8_t dest_addr[SSP_FIFOSIZE]; + +/**************************************************************************/ +/*! + @brief Sends the write enable command (WREN/0x06) +*/ +/**************************************************************************/ +void at25WriteEnable() +{ + AT25_SELECT(); + src_addr[0] = AT25_WREN; + sspSend(0, (uint8_t *)src_addr, 1); + AT25_DESELECT(); + + // Delay for at least 250nS (1nS @ 72MHz = ~0.0072 ticks) + for (i = 0; i < 100; i++); +} + +/**************************************************************************/ +/*! + @brief Gets the value of the Read Status Register (RDSR/0x05) + + @return The 8-bit value returned by the Read Status Register +*/ +/**************************************************************************/ +uint8_t at25GetRSR() +{ + AT25_SELECT(); + src_addr[0] = AT25_RDSR; + sspSend(0, (uint8_t *)src_addr, 1); + sspReceive(0, (uint8_t *)dest_addr, 1); + AT25_DESELECT(); + return dest_addr[0] & (AT25_RDSR_WEN | AT25_RDSR_RDY); +} + +/**************************************************************************/ +/*! + @brief Initialises the SPI block (CLK set low when inactive, trigger + on leading edge). +*/ +/**************************************************************************/ +void at25Init (void) +{ + sspInit(0, sspClockPolarity_Low, sspClockPhase_RisingEdge); +} + +/**************************************************************************/ +/*! + @brief Reads the specified number of bytes from the supplied address. + + This function will read one or more bytes starting at the supplied + address. + + @param[in] address + The 16-bit address where the read will start. The maximum + value for the address depends on the size of the EEPROM + @param[in] *buffer + Pointer to the buffer that will store the read results + @param[in] bufferLength + Length of the buffer +*/ +/**************************************************************************/ +at25Error_e at25Read (uint16_t address, uint8_t *buffer, uint32_t bufferLength) +{ + if (address >= AT25_MAXADDRESS) + { + return AT25_ERROR_ADDRERR; + } + + if (bufferLength > 6) + { + return AT25_ERROR_BUFFEROVERFLOW; + } + + timeout = 0; + while ( timeout < SSP_MAX_TIMEOUT ) + { + // Wait until the device is ready + uint8_t status = at25GetRSR() & AT25_RDSR_RDY; + if (status == 0) + { + break; + } + timeout++; + } + if ( timeout == SSP_MAX_TIMEOUT ) + { + return AT25_ERROR_TIMEOUT_WE; + } + + AT25_SELECT(); + // Read command (0x03), append A8 if > addr 256 bytes + src_addr[0] = address > 0xFF ? AT25_READ | AT25_A8 : AT25_READ; + src_addr[1] = (address); + sspSend(0, (uint8_t *)src_addr, 2); + sspReceive(0, (uint8_t *)&dest_addr[2], bufferLength); + AT25_DESELECT(); + + // Fill response buffer + for (i = 0; i < bufferLength; i++) + { + buffer[i] = dest_addr[i + 2]; + } + + return AT25_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Writes the supplied bytes at a specified address. + + This function will write one or more bytes starting at the supplied + address. + + @param[in] address + The 16-bit address where the write will start. The + maximum value for the address depends on the size of the + EEPROM + @param[in] *buffer + Pointer to the buffer that contains the values to write. + @param[in] bufferLength + Length of the buffer +*/ +/**************************************************************************/ +at25Error_e at25Write (uint16_t address, uint8_t *buffer, uint32_t bufferLength) +{ + if (address >= AT25_MAXADDRESS) + { + return AT25_ERROR_ADDRERR; + } + + if (bufferLength > 6) + { + return AT25_ERROR_BUFFEROVERFLOW; + } + + // Set write enable latch + at25WriteEnable(); + + timeout = 0; + while ( timeout < SSP_MAX_TIMEOUT ) + { + // Wait until the device is write enabled + if (at25GetRSR() == AT25_RDSR_WEN) + { + break; + } + timeout++; + } + if ( timeout == SSP_MAX_TIMEOUT ) + { + return AT25_ERROR_TIMEOUT_WE; + } + + for (i = 0; i < bufferLength; i++) // Init RD and WR buffer + { + src_addr[i+2] = buffer[i]; // leave two bytes for cmd and offset(8 bits) + dest_addr[i] = 0; + } + + AT25_SELECT(); + // Write command (0x02), append A8 if addr > 256 bytes + src_addr[0] = address > 0xFF ? AT25_WRITE | AT25_A8 : AT25_WRITE; + src_addr[1] = (address); + sspSend(0, (uint8_t *)src_addr, bufferLength + 2); + AT25_DESELECT(); + + // Wait at least 3ms + for (i = 0; i < ((CFG_CPU_CCLK / 1000) * 3); i++); + + timeout = 0; + while ( timeout < SSP_MAX_TIMEOUT ) + { + // Check status to see if write cycle is done or not + AT25_SELECT(); + src_addr[0] = AT25_RDSR; + sspSend(0, (uint8_t *)src_addr, 1); + sspReceive(0, (uint8_t *)dest_addr, 1); + AT25_DESELECT(); + // Wait until device is ready + if ((dest_addr[0] & AT25_RDSR_RDY) == 0x00) + { + break; + } + timeout++; + } + if ( timeout == SSP_MAX_TIMEOUT ) + { + return AT25_ERROR_TIMEOUT_WFINISH; + } + + for (i = 0; i < 300; i++); // Wait at least 250ns + + return AT25_ERROR_OK; +} diff --git a/drivers/eeprom/at25040/at25040.h b/drivers/eeprom/at25040/at25040.h new file mode 100644 index 0000000..57a6d6d --- /dev/null +++ b/drivers/eeprom/at25040/at25040.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/*! + @file at25040.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _AT25040_H_ +#define _AT25040_H_ + +#include "projectconfig.h" + +#define AT25_RDSR_RDY 0x01 +#define AT25_RDSR_WEN 0x02 +#define AT25_A8 0x08 // For addresses > 0xFF (AT25040 only) A8 must be added to R/W commands +#define AT25_MAXADDRESS 0x0200 // AT25040 = 0X0200, AT25020 = 0x100, AT25010 = 0x80 + +/**************************************************************************/ +/*! + AT25040 Commands +*/ +/**************************************************************************/ +typedef enum +{ + AT25_WREN = 0x06, + AT25_WRDI = 0x04, + AT25_RDSR = 0x05, + AT25_WRSR = 0x01, + AT25_READ = 0x03, + AT25_WRITE = 0x02 +} at25_Commands_e; + +/**************************************************************************/ +/*! + Error messages +*/ +/**************************************************************************/ +typedef enum +{ + AT25_ERROR_OK = 0, // Everything executed normally + AT25_ERROR_TIMEOUT_WE, // Timed out waiting for write enable status + AT25_ERROR_TIMEOUT_WFINISH, // Timed out waiting for write to finish + AT25_ERROR_ADDRERR, // Address out of range + AT25_ERROR_BUFFEROVERFLOW, // Max 6 bytes can be read/written in one operation + AT2_ERROR_LAST +} +at25Error_e; + +void at25Init (void); +at25Error_e at25Read (uint16_t address, uint8_t *buffer, uint32_t bufferLength); +at25Error_e at25Write (uint16_t address, uint8_t *buffer, uint32_t bufferLength); + +#endif diff --git a/drivers/eeprom/eeprom.c b/drivers/eeprom/eeprom.c new file mode 100644 index 0000000..fda0d9b --- /dev/null +++ b/drivers/eeprom/eeprom.c @@ -0,0 +1,410 @@ +/**************************************************************************/ +/*! + @file eeprom.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "eeprom.h" + +// Currently only the MCP24AA I2C EEPROM is used +#include "drivers/eeprom/mcp24aa/mcp24aa.h" + +static uint8_t buf[32]; + +/**************************************************************************/ +/*! + @brief Checks whether the supplied address is within the valid range + + @param[in] addr + The 16-bit address to check + + @return Zero if the address is valid, otherwise 1 +*/ +/**************************************************************************/ +bool eepromCheckAddress(uint16_t addr) +{ + // Check for invalid values + return addr <= MCP24AA_MAXADDR ? FALSE : TRUE; +} + +/**************************************************************************/ +/*! + @brief Reads 1 byte from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return An unsigned 8-bit value (uint8_t) +*/ +/**************************************************************************/ +uint8_t eepromReadU8(uint16_t addr) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(uint8_t)); + + // ToDo: Handle any errors + if (error) { }; + + return buf[0]; +} + +/**************************************************************************/ +/*! + @brief Reads 1 byte from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A signed 8-bit value (int8_t) +*/ +/**************************************************************************/ +int8_t eepromReadS8(uint16_t addr) +{ + int8_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(int8_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(int8_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 2 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A unsigned 16-bit value (uint16_t) +*/ +/**************************************************************************/ +uint16_t eepromReadU16(uint16_t addr) +{ + uint16_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(uint16_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(uint16_t)); + + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 2 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A signed 16-bit value (int16_t) +*/ +/**************************************************************************/ +int16_t eepromReadS16(uint16_t addr) +{ + int16_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(int16_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(int16_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 4 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A unsigned 32-bit value (uint32_t) +*/ +/**************************************************************************/ +uint32_t eepromReadU32(uint16_t addr) +{ + uint32_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(uint32_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(uint32_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 4 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A signed 32-bit value (int32_t) +*/ +/**************************************************************************/ +int32_t eepromReadS32(uint16_t addr) +{ + int32_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(int32_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(int32_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 8 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A unsigned 64-bit value (uint64_t) +*/ +/**************************************************************************/ +uint64_t eepromReadU64(uint16_t addr) +{ + uint64_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(uint64_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(uint64_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads 8 bytes from EEPROM + + @param[in] addr + The 16-bit address to read from in EEPROM + + @return A signed 64-bit value (int64_t) +*/ +/**************************************************************************/ +int64_t eepromReadS64(uint16_t addr) +{ + int64_t results; + + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaReadBuffer(addr, buf, sizeof(int64_t)); + + // ToDo: Handle any errors + if (error) { }; + + memcpy(&results, buf, sizeof(int64_t)); + return results; +} + +/**************************************************************************/ +/*! + @brief Reads a variabls length buffer from EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM + @param[out] buffer + Pointer to the buffer that will store any retrieved bytes + @param[in] bufferLength + The number of bytes to read +*/ +/**************************************************************************/ +void eepromReadBuffer(uint16_t addr, uint8_t *buffer, uint32_t bufferLength) +{ + // Instantiate error message placeholder + mcp24aaError_e error = MCP24AA_ERROR_OK; + + // Read the contents of address + error = mcp24aaReadBuffer(addr, buffer, bufferLength); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes 1 byte to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteU8(uint16_t addr, uint8_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes 1 signed byte to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteS8(uint16_t addr, int8_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes an unsigned 16-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteU16(uint16_t addr, uint16_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes a signed 16-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteS16(uint16_t addr, int16_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes an unsigned 32-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteU32(uint16_t addr, uint32_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes a signed 32-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteS32(uint16_t addr, int32_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes an unsigned 64-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteU64(uint16_t addr, uint64_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} + +/**************************************************************************/ +/*! + @brief Writes a signed 64-bit integer to EEPROM + + @param[in] addr + The 16-bit address to write to in EEPROM +*/ +/**************************************************************************/ +void eepromWriteS64(uint16_t addr, int64_t value) +{ + mcp24aaError_e error = MCP24AA_ERROR_OK; + error = mcp24aaWriteBuffer(addr, (uint8_t *)&value, sizeof(value)); + + // ToDo: Handle any errors + if (error) { }; +} diff --git a/drivers/eeprom/eeprom.h b/drivers/eeprom/eeprom.h new file mode 100644 index 0000000..71875b7 --- /dev/null +++ b/drivers/eeprom/eeprom.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/*! + @file eeprom.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __EEPROM_H__ +#define __EEPROM_H__ + +#include "projectconfig.h" + +// Method Prototypes +bool eepromCheckAddress ( uint16_t addr ); +uint8_t eepromReadU8 ( uint16_t addr ); +int8_t eepromReadS8 ( uint16_t addr ); +uint16_t eepromReadU16 ( uint16_t addr ); +int16_t eepromReadS16 ( uint16_t addr ); +uint32_t eepromReadU32 ( uint16_t addr ); +int32_t eepromReadS32 ( uint16_t addr ); +uint64_t eepromReadU64 ( uint16_t addr ); +int64_t eepromReadS64 ( uint16_t addr ); +void eepromReadBuffer ( uint16_t addr, uint8_t *buffer, uint32_t bufferLength); +void eepromWriteU8 ( uint16_t addr, uint8_t value ); +void eepromWriteS8 ( uint16_t addr, int8_t value ); +void eepromWriteU16 ( uint16_t addr, uint16_t value ); +void eepromWriteS16 ( uint16_t addr, int16_t value ); +void eepromWriteU32 ( uint16_t addr, uint32_t value ); +void eepromWriteS32 ( uint16_t addr, int32_t value ); +void eepromWriteU64 ( uint16_t addr, uint64_t value ); +void eepromWriteS64 ( uint16_t addr, int64_t value ); + +#endif diff --git a/drivers/eeprom/mcp24aa/mcp24aa.c b/drivers/eeprom/mcp24aa/mcp24aa.c new file mode 100644 index 0000000..f7f981d --- /dev/null +++ b/drivers/eeprom/mcp24aa/mcp24aa.c @@ -0,0 +1,339 @@ +/**************************************************************************/ +/*! + @file mcp24aa.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Driver for Microchip's 24AA32AF serial EEPROM. This driver assumes + that the address is set to 1010 000. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "drivers/eeprom/mcp24aa/mcp24aa.h" + + int main(void) + { + cpuInit(); + + mcp24aaInit(); + + // Instantiate error message placeholder + mcp24aaError_e error = MCP24AA_ERROR_OK; + + // Create read buffer (1 byte) + uint8_t buffer[1] = { 0x00 }; + + // Write 0xCC at address 0x125 + error = mcp24aaWriteByte(0x0125, 0xCC); + if (error) + { + // Handle any errors + switch (error) + { + case (MCP24AA_ERROR_I2CINIT): + // Unable to initialise I2C + break; + case (MCP24AA_ERROR_ADDRERR): + // Address out of range + break; + default: + break; + } + } + + // Read the contents of address 0x0125 + error = MCP24AA_ERROR_OK; + error = mcp24aaReadByte(0x0125, buffer); + if (error) + { + // Handle any errors + switch (error) + { + case (MCP24AA_ERROR_I2CINIT): + // Unable to initialise I2C + break; + case (MCP24AA_ERROR_ADDRERR): + // Address out of range + break; + default: + break; + } + } + + uint8_t results = buffer[0]; + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "mcp24aa.h" +#include "core/systick/systick.h" +#include "core/i2c/i2c.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +uint32_t i, timeout; + +static bool _mcp24aaInitialised = false; + +/**************************************************************************/ +/*! + @brief Initialises the I2C block +*/ +/**************************************************************************/ +mcp24aaError_e mcp24aaInit() +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + return MCP24AA_ERROR_I2CINIT; /* Fatal error */ + } + + // Set initialisation flag + _mcp24aaInitialised = true; + + return MCP24AA_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Reads the specified number of bytes from the supplied address. + + This function will read one or more bytes starting at the supplied + address. A maximum of 8 bytes can be read in one operation. + + @param[in] address + The 16-bit address where the read will start. The maximum + value for the address depends on the size of the EEPROM + @param[in] *buffer + Pointer to the buffer that will store the read results + @param[in] bufferLength + Length of the buffer +*/ +/**************************************************************************/ +mcp24aaError_e mcp24aaReadBuffer (uint16_t address, uint8_t *buffer, uint32_t bufferLength) +{ + if (!_mcp24aaInitialised) mcp24aaInit(); + + if (address >= MCP24AA_MAXADDR) + { + return MCP24AA_ERROR_ADDRERR; + } + + if (bufferLength > 8) + { + return MCP24AA_ERROR_BUFFEROVERFLOW; + } + + // ToDo: Check if I2C is ready + + // Clear buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + I2CSlaveBuffer[i] = 0x00; + } + + // Write address bits to enable random read + I2CWriteLength = 3; + I2CReadLength = bufferLength; + I2CMasterBuffer[0] = MCP24AA_ADDR; // I2C device address + I2CMasterBuffer[1] = (address >> 8); // Address (high byte) + I2CMasterBuffer[2] = (address & 0xFF); // Address (low byte) + // If you wish to read, you need to append the address w/read bit, though this + // needs to be placed one bit higher than the size of I2CWriteLength which + // may be unexpected + I2CMasterBuffer[3] = MCP24AA_ADDR | MCP24AA_READBIT; + + // Transmit command + i2cEngine(); + + // Fill response buffer + for (i = 0; i < bufferLength; i++) + { + buffer[i] = I2CSlaveBuffer[i]; + } + + return MCP24AA_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Writes the supplied bytes at a specified address. + + This function will write one or more bytes starting at the supplied + address. A maximum of 8 bytes can be written in one operation. + + @param[in] address + The 16-bit address where the write will start. The + maximum value for the address depends on the size of the + EEPROM + @param[in] *buffer + Pointer to the buffer that contains the values to write. + @param[in] bufferLength + Length of the buffer +*/ +/**************************************************************************/ +mcp24aaError_e mcp24aaWriteBuffer (uint16_t address, uint8_t *buffer, uint32_t bufferLength) +{ + if (!_mcp24aaInitialised) mcp24aaInit(); + + if (address >= MCP24AA_MAXADDR) + { + return MCP24AA_ERROR_ADDRERR; + } + + if (bufferLength > 8) + { + return MCP24AA_ERROR_BUFFEROVERFLOW; + } + + // ToDo: Check if I2C is ready + + // Clear write buffer + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + // Write address bits and data to the master buffer + I2CWriteLength = 3 + bufferLength; + I2CReadLength = 0; + I2CMasterBuffer[0] = MCP24AA_ADDR; // I2C device address + I2CMasterBuffer[1] = (address >> 8); // Address (high byte) + I2CMasterBuffer[2] = (address & 0xFF); // Address (low byte) + for (i = 0; i < bufferLength; i++) + { + I2CMasterBuffer[i+3] = buffer[i]; + } + + // Transmit command + i2cEngine(); + + // Wait at least 10ms + systickDelay(10); + + return MCP24AA_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Reads one byte from the supplied address. + + This function will read one byte starting at the supplied address. + + @param[in] address + The 16-bit address where the read will start. The maximum + value for the address depends on the size of the EEPROM + @param[in] *buffer + Pointer to the buffer that will store the read results + + @code + #include "core/cpu/cpu/h" + #include "drivers/eeprom/mcp24aa/mcp24aa.h" + ... + cpuInit(); + mcp24aaInit(); + + // Create read buffer (1 byte) + uint8_t buffer[1] = { 0x00 }; + + // Write 0xEE and address 0x0120 + mcp24aaWriteByte(0x0120, 0xEE); + + // Populate buffer with contents of 0x0120 + mcp24aaReadByte(0x0120, buffer); + + // results should equal 0xEE + uint8_t results = buffer[0]; + @endcode +*/ +/**************************************************************************/ +mcp24aaError_e mcp24aaReadByte (uint16_t address, uint8_t *buffer) +{ + if (!_mcp24aaInitialised) mcp24aaInit(); + + return mcp24aaReadBuffer(address, buffer, 1); +} + +/**************************************************************************/ +/*! + @brief Writes one byte to the supplied address. + + This function will write one byte at the supplied address. + + @param[in] address + The 16-bit address where the write will start. The maximum + value for the address depends on the size of the EEPROM + @param[in] value + The data to be written to the EEPROM + + @code + #include "core/cpu/cpu/h" + #include "drivers/eeprom/mcp24aa/mcp24aa.h" + ... + cpuInit(); + mcp24aaInit(); + + // Create read buffer (1 byte) + uint8_t buffer[1] = { 0x00 }; + + // Write 0xEE and address 0x0120 + mcp24aaWriteByte(0x0120, 0xEE); + + // Populate buffer with contents of 0x0120 + mcp24aaReadByte(0x0120, buffer); + + // results should equal 0xEE + uint8_t results = buffer[0]; + @endcode +*/ +/**************************************************************************/ +mcp24aaError_e mcp24aaWriteByte (uint16_t address, uint8_t value) +{ + if (!_mcp24aaInitialised) mcp24aaInit(); + + // Set read buffer + uint8_t wBuffer[1]; + + // Write byte to EEPROM at specified address + wBuffer[0] = value; + return mcp24aaWriteBuffer(address, wBuffer, 1); +} + diff --git a/drivers/eeprom/mcp24aa/mcp24aa.h b/drivers/eeprom/mcp24aa/mcp24aa.h new file mode 100644 index 0000000..3563939 --- /dev/null +++ b/drivers/eeprom/mcp24aa/mcp24aa.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/*! + @file mcp24aa.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _MCP24AA_H_ +#define _MCP24AA_H_ + +#include "projectconfig.h" + +#define MCP24AA_ADDR 0xA0 // 10100000 +#define MCP24AA_RW 0x01 +#define MCP24AA_READBIT 0x01 +#define MCP24AA_MAXADDR 0xFFF // 4K = 4096 + +typedef enum +{ + MCP24AA_ERROR_OK = 0, // Everything executed normally + MCP24AA_ERROR_I2CINIT, // Unable to initialise I2C + MCP24AA_ERROR_I2CBUSY, // I2C already in use + MCP24AA_ERROR_ADDRERR, // Address out of range + MCP24AA_ERROR_BUFFEROVERFLOW, // Max 8 bytes can be read/written in one operation + MCP24AA_ERROR_LAST +} +mcp24aaError_e; + +mcp24aaError_e mcp24aaInit (void); +mcp24aaError_e mcp24aaReadBuffer (uint16_t address, uint8_t *buffer, uint32_t bufferLength); +mcp24aaError_e mcp24aaWriteBuffer (uint16_t address, uint8_t *buffer, uint32_t bufferLength); +mcp24aaError_e mcp24aaReadByte (uint16_t address, uint8_t *buffer); +mcp24aaError_e mcp24aaWriteByte (uint16_t address, uint8_t value); + + +#endif diff --git a/drivers/fatfs/ccsbcs.c b/drivers/fatfs/ccsbcs.c new file mode 100644 index 0000000..f0d211e --- /dev/null +++ b/drivers/fatfs/ccsbcs.c @@ -0,0 +1,540 @@ +/*------------------------------------------------------------------------*/ +/* Unicode - Local code bidirectional converter (C)ChaN, 2009 */ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. (OEM) +/ 720 Arabic (OEM) +/ 1256 Arabic (Windows) +/ 737 Greek (OEM) +/ 1253 Greek (Windows) +/ 1250 Central Europe (Windows) +/ 775 Baltic (OEM) +/ 1257 Baltic (Windows) +/ 850 Multilingual Latin 1 (OEM) +/ 852 Latin 2 (OEM) +/ 1252 Latin 1 (Windows) +/ 855 Cyrillic (OEM) +/ 1251 Cyrillic (Windows) +/ 866 Russian (OEM) +/ 857 Turkish (OEM) +/ 1254 Turkish (Windows) +/ 858 Multilingual Latin 1 + Euro (OEM) +/ 862 Hebrew (OEM) +/ 1255 Hebrew (Windows) +/ 874 Thai (OEM, Windows) +/ 1258 Vietnam (OEM, Windows) +*/ + +#include "ff.h" + + +#if _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 720 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, + 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, + 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 737 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, + 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, + 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 775 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, + 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, + 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, + 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, + 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, + 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, + 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 850 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 852 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, + 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, + 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, + 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, + 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 855 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, + 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, + 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, + 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, + 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, + 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, + 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, + 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 857 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, + 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 858 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 862 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 866 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 874 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, + 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, + 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, + 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, + 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, + 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +#elif _CODE_PAGE == 1250 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 +}; + +#elif _CODE_PAGE == 1251 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */ + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042D, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F +}; + +#elif _CODE_PAGE == 1252 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + +#elif _CODE_PAGE == 1253 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000 +}; + +#elif _CODE_PAGE == 1254 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF +}; + +#elif _CODE_PAGE == 1255 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, + 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000 +}; + +#elif _CODE_PAGE == 1256 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, + 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, + 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2 +} + +#elif _CODE_PAGE == 1257 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, + 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9 +}; + +#elif _CODE_PAGE == 1258 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed in current configuration. Remove from the project. +#endif + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR src, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + + if (src < 0x80) { /* ASCII */ + c = src; + + } else { + if (dir) { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + + } else { /* Unicode to OEMCP */ + for (c = 0; c < 0x80; c++) { + if (src == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + +WCHAR ff_wtoupper ( /* Upper converted character */ + WCHAR chr /* Input character */ +) +{ + static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 }; + static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 }; + int i; + + + for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ; + + return tbl_lower[i] ? tbl_upper[i] : chr; +} diff --git a/drivers/fatfs/diskio.h b/drivers/fatfs/diskio.h new file mode 100644 index 0000000..d4e7e00 --- /dev/null +++ b/drivers/fatfs/diskio.h @@ -0,0 +1,81 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.05 (C)ChaN, 2007 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#endif +DRESULT disk_ioctl (BYTE, BYTE, void*); +void disk_timerproc (void); + + + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + + +/* Command code for disk_ioctrl() */ + +/* Generic command */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ +#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ +#define GET_SECTOR_SIZE 2 +#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 +/* MMC/SDC command */ +#define MMC_GET_TYPE 10 +#define MMC_GET_CSD 11 +#define MMC_GET_CID 12 +#define MMC_GET_OCR 13 +#define MMC_GET_SDSTAT 14 +/* ATA/CF command */ +#define ATA_GET_REV 20 +#define ATA_GET_MODEL 21 +#define ATA_GET_SN 22 + + + +/* Card type flags (CardType) */ +#define CT_MMC 0x01 /* MMC ver 3 */ +#define CT_SD1 0x02 /* SD ver 1 */ +#define CT_SD2 0x04 /* SD ver 2 */ +#define CT_SDC (CT_SD1|CT_SD2) /* SD */ +#define CT_BLOCK 0x08 /* Block addressing */ + + +#define _DISKIO +#endif diff --git a/drivers/fatfs/ff.c b/drivers/fatfs/ff.c new file mode 100644 index 0000000..ad21b27 --- /dev/null +++ b/drivers/fatfs/ff.c @@ -0,0 +1,3154 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.07e (C)ChaN, 2009 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2009, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) patition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close +/ without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same +/ or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. +/ Added long file name support. +/ Added multiple code page support. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size support. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper +/ case on non-LFN cfg. +/---------------------------------------------------------------------------*/ + +#include "projectconfig.h" +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 0x007E +#error Wrong include file (ff.h). +#endif + +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } + +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res + +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + +#ifndef NULL +#define NULL 0 +#endif + +/* Name status flags */ +#define NS 11 /* Offset of name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + + + +/*-------------------------------------------------------------------------- + + Private Work Area + +---------------------------------------------------------------------------*/ + +#if _DRIVES < 1 || _DRIVES > 9 +#error Number of drives must be 1-9. +#endif +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE Drive; /* Current drive */ +#endif + + +#if _USE_LFN == 1 /* LFN with static LFN working buffer */ +static +WCHAR LfnBuf[_MAX_LFN + 1]; +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */ +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#else /* No LFN */ +#define NAMEBUF(sp,lp) BYTE sp[12] +#define INITBUF(dj,sp,lp) dj.fn = sp + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, int cnt) { + char *d = (char*)dst; + const char *s = (const char *)src; + while (cnt--) *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = (char)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)src; + int r = 0; + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT + +static +BOOL lock_fs ( + FATFS *fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make apperance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + if (fs->wflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Refrect the change to all FAT copies */ + wsect += fs->sects_fat; + disk_write(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) + return FR_DISK_ERR; + fs->winsect = sector; + } + } + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS *fs /* File system object */ +) +{ + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + mem_set(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + disk_write(fs->drive, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + DWORD fsect; + + + if (clst < 2 || clst >= fs->max_clust) /* Range check */ + return 1; + + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occured at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + DWORD fsect; + FRESULT res; + + + if (clst < 2 || clst >= fs->max_clust) { /* Range check */ + res = FR_INT_ERR; + + } else { + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fsect + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fsect + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; + + + if (clst < 2 || clst >= fs->max_clust) { /* Check the range of cluster# */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->max_clust) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl, mcl; + + + mcl = fs->max_clust; + if (clst == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < mcl) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */ + return cs; + if (ncl == scl) return 0; /* No free custer */ + } + + if (put_fat(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */ + return 0xFFFFFFFF; + if (clst != 0) { /* Link it to the previous one if needed */ + if (put_fat(fs, clst, ncl)) + return 0xFFFFFFFF; + } + + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Seek directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_seek ( + DIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ +) +{ + DWORD clst; + WORD ic; + + + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->max_clust) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */ + } + else { /* Dynamic table */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->max_clust) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ + DIR *dj, /* Pointer to directory object */ + BOOL streach /* FALSE: Do not streach table, TRUE: Streach table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->max_clust) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!streach) return FR_NO_FILE; /* When do not streach, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Streach cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up streached table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +BOOL cmp_lfn ( /* TRUE:Matched, FALSE:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return FALSE; /* Not matched */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return FALSE; + + return TRUE; /* The part of LFN matched */ +} + + + +static +BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return TRUE; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + int i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to genartated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD num /* Sequense number */ +) +{ + char ns[8]; + int i, j; + + + mem_cpy(dst, src, 11); + + if (num > 5) { /* On many collisions, generate a hash number instead of sequencial number */ + do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + ns[i--] = (num % 10) + '0'; + num /= 10; + } while (num); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + int n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_seek(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME; /* Cannot create dot entry */ + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = NULL; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_seek(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == 0xE5 || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contigulus entry */ + if (++n == ne) break; /* A contiguous entry that requiered count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_seek(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + mem_set(dir, 0, 32); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR *dj, /* Pointer to the directory object */ + const XCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE cvt[] = _EXCVT; +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + int i, ni, si, di; + const XCHAR *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* If it is a DBC 1st byte */ + b = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) /* Reject invalid code for DBC */ + return FR_INVALID_NAME; + w = (w << 8) + b; + } + w = ff_convert(w, 1); /* Convert OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject null string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) break; /* Break on enf of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = cvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;[=]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN; /* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + int ni, si, i; + const char *p; + + /* Create file name in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; + p = *path; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Rerurn pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char */ +#ifdef _EXCVT + c = cvt[c - 0x80]; /* Convert extend char (SBCS) */ +#else + b |= 3; /* Eliminate NT flag if ext char is exist */ +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* DBC 1st byte? */ + d = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (chk_chr(" \"*+,[=]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject null string */ + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Filename has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + int i; + BYTE c, nt, *dir; + char *p; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; + +#if _USE_LFN + if (fno->lfname) { + XCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC */ + tp[i++] = (XCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */ + tp[i++] = (XCHAR)w; + } + } + tp[i] = 0; /* Terminator */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + const XCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, last; + + + while (!_USE_LFN && *path == ' ') path++; /* Skip leading spaces */ +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading saparator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Null path means the start directory itself */ + res = dir_seek(dj, 0); + dj->dir = NULL; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + last = *(dj->fn+NS) & NS_LAST; + if (res != FR_OK) { /* Could not find the object */ + if (res == FR_NO_FILE && !last) + res = FR_NO_PATH; + break; + } + if (last) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 3; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + + +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occured */ + const XCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE fmt, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fsize, tsect, mclst; + const XCHAR *p = *path; + FATFS *fs; + + /* Get logical drive number from the path name */ + vol = p[0] - '0'; /* Is there a drive number? */ + if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = Drive; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _DRIVES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Returen pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drive); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be mounted. Following code attempts to mount the volume */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drive = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if _MAX_SS != 512 /* Get disk sector size if needed */ + if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32); + fs->sects_fat = fsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */ + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Nmuber of root directory entries */ + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + fs->max_clust = mclst = (tsect /* Last cluster# + 1 (Number of clusters + 2) */ + - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32) + ) / fs->csize + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */ + if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */ + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + /* Initialize allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->wflag = 0; + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->winsect = 0; /* Invalidate sector cache */ +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif + fs->id = ++Fsid; /* File system mount ID */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ +) +{ + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE vol, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + FATFS *rfs; + + + if (vol >= _DRIVES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const XCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + fp->fs = NULL; /* Clear file object */ +#if !_FS_READONLY + mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW); + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + if (res != FR_OK) LEAVE_FF(dj.fs, res); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD ps, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ + res = dir_register(&dj); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + mode |= FA_CREATE_ALWAYS; + dir = dj.dir; /* Created entry (SFN entry) */ + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + LEAVE_FF(dj.fs, FR_EXIST); + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + LEAVE_FF(dj.fs, FR_DENIED); + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero on over write mode */ + cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */ + ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */ + ST_WORD(dir+DIR_FstClusLO, 0); + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + dj.fs->wflag = 1; + ps = dj.fs->winsect; /* Remove the cluster chain */ + if (cl) { + res = remove_chain(dj.fs, cl); + if (res) LEAVE_FF(dj.fs, res); + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + } + res = move_window(dj.fs, ps); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */ + dj.fs->wflag = 1; + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + LEAVE_FF(dj.fs, FR_NO_FILE); +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + LEAVE_FF(dj.fs, FR_DENIED); + } + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; fp->csect = 255; /* File pointer */ + fp->dsect = 0; + fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ + + LEAVE_FF(dj.fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE *rbuff = buff; + + + *br = 0; /* Initialize bytes read */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector offset in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + + + *bw = 0; /* Initialize bytes written */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->org_clust; /* Follow from the origin */ + if (clst == 0) /* When there is no cluster chain, */ + fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ + clst = create_chain(fp->fs, fp->curr_clust); /* Follow or streach cluster chain */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ +#if !_FS_TINY /* Write-back dirty buffer */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + ST_WORD(dir+DIR_FstClusLO, fp->org_clust); /* Update start cluster */ + ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if _FS_READONLY + res = validate(fp->fs, fp->id); + if (res == FR_OK) fp->fs = NULL; + LEAVE_FF(fp->fs, res); +#else + res = f_sync(fp); + if (res == FR_OK) fp->fs = NULL; + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Drive/Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + + Drive = drv; + + return FR_OK; +} + + + + +FRESULT f_chdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + dir = dj.dir; /* Pointer to the entry */ + if (!dir) { + dj.fs->cdir = 0; /* No entry (root dir) */ + } else { + if (dir[DIR_Attr] & AM_DIR) /* Reached to the dir */ + dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + else + res = FR_NO_PATH; /* Could not reach the dir (it is a file) */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + +#endif + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clst, bcs, nsect, ifptr; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; fp->csect = 255; + if (ofs > 0) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->curr_clust; + } else { /* When seek to back cluster, */ + clst = fp->org_clust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->org_clust = clst; + } +#endif + fp->curr_clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force streached if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += fp->csect; + fp->csect++; + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + LEAVE_FF(fp->fs, res); +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directroy Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR *dj, /* Pointer to directory object to create */ + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + res = follow_path(dj, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + dir = dj->dir; + if (dir) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_seek(dj, 0); /* Rewind dir */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + if (!fno) { + res = dir_seek(dj, 0); + } else { + res = dir_read(dj); + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, FALSE); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const XCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follwo completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const XCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + if (res != FR_OK) LEAVE_FF(*fatfs, res); + + /* If number of free cluster is valid, return it without cluster scan. */ + if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { + *nclst = (*fatfs)->free_clust; + LEAVE_FF(*fatfs, FR_OK); + } + + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(*fatfs, clst); + if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR); + if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR); + if (stat == 0) n++; + } while (++clst < (*fatfs)->max_clust); + } else { + clst = (*fatfs)->max_clust; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) + LEAVE_FF(*fatfs, res); + p = (*fatfs)->win; + i = SS(*fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + + LEAVE_FF(*fatfs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->org_clust); + fp->org_clust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->curr_clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->max_clust) { + res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } + } + if (res != FR_OK) fp->flag |= FA__ERROR; + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const XCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + NAMEBUF(sfn, lfn); + BYTE *dir; + DWORD dclst; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + + dir = dj.dir; + if (!dir) /* Is it the root directory? */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */ + LEAVE_FF(dj.fs, FR_DENIED); + dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_seek(&sdj, 2); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + res = dir_read(&sdj); + if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */ + if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res); + } + + res = dir_remove(&dj); /* Remove directory entry */ + if (res == FR_OK) { + if (dclst) + res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */ + if (res == FR_OK) res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir, n; + DWORD dsect, dclst, pclst, tim; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_NO_FILE) /* Any error occured */ + LEAVE_FF(dj.fs, res); + + dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */ + res = FR_OK; + if (dclst == 0) res = FR_DENIED; + if (dclst == 1) res = FR_INT_ERR; + if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) + res = move_window(dj.fs, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + dsect = clust2sect(dj.fs, dclst); + + dir = dj.fs->win; /* Initialize the new directory table */ + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_FstClusLO, dclst); + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; + pclst = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase) + pclst = 0; + ST_WORD(dir+32+DIR_FstClusLO, pclst); + ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16); + for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */ + dj.fs->winsect = dsect++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res) LEAVE_FF(dj.fs, res); + mem_set(dir, 0, SS(dj.fs)); + } + + res = dir_register(&dj); + if (res != FR_OK) { + remove_chain(dj.fs, dclst); + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Crated time */ + ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */ + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const XCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const XCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const XCHAR *path_old, /* Pointer to the old name */ + const XCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR dj_old, dj_new; + NAMEBUF(sfn, lfn); + BYTE buf[21], *dir; + DWORD dw; + + + INITBUF(dj_old, sfn, lfn); + res = chk_mounted(&path_old, &dj_old.fs, 1); + if (res == FR_OK) { + dj_new.fs = dj_old.fs; + res = follow_path(&dj_old, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + } + if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */ + + if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ + mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ + + mem_cpy(&dj_new, &dj_old, sizeof(DIR)); + res = follow_path(&dj_new, path_new); /* Check new object */ + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ + res = dir_register(&dj_new); /* Register the new object */ + if (res == FR_OK) { + dir = dj_new.dir; /* Copy object information into new entry */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + dj_old.fs->wflag = 1; + if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */ + dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(dj_new.fs, dw); + dir = dj_new.fs->win+32; + if (res == FR_OK && dir[1] == '.') { + dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust; + ST_WORD(dir+DIR_FstClusLO, dw); + ST_WORD(dir+DIR_FstClusHI, dw >> 16); + dj_new.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj_old); /* Remove old entry */ + if (res == FR_OK) + res = sync(dj_old.fs); + } + } + } + + LEAVE_FF(dj_old.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (Available on only _FS_TINY cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + + + *bf = 0; + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + fp->csect++; /* Next sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect - 1; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */ +#define N_FATS 1 /* 1 or 2 */ +#define MAX_SECTOR 131072000UL /* Maximum partition size */ +#define MIN_SECTOR 2000UL /* Minimum partition size */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ + WORD allocsize /* Allocation unit size [bytes] */ +) +{ + static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 }; + static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 }; + BYTE fmt, m, *tbl; + DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ + DWORD n_clst, d, n; + WORD as; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + if (partition >= 2) return FR_MKFS_ABORTED; + + /* Check mounted drive and clear work area */ + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > _MAX_SS) + return FR_MKFS_ABORTED; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; + b_part = (!partition) ? 63 : 0; /* Boot sector */ + n_part -= b_part; + for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ; /* Check validity of the allocation unit size */ + if (d != allocsize) allocsize = 0; + if (!allocsize) { /* Auto selection of cluster size */ + d = n_part; + for (as = SS(fs); as > 512U; as >>= 1) d >>= 1; + for (n = 0; d < sstbl[n]; n++) ; + allocsize = cstbl[n]; + } + if (allocsize < SS(fs)) allocsize = SS(fs); + + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clst = n_part / allocsize; + fmt = FS_FAT12; + if (n_clst >= 0xFF5) fmt = FS_FAT16; + if (n_clst >= 0xFFF5) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + case FS_FAT16: + n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* b_dir and b_data are no longer used below */ + + /* Determine number of cluster and final check of validity of the FAT type */ + n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clst < 0xFF5) + || (fmt == FS_FAT32 && n_clst < 0xFFF5)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win+MBR_Table; + ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (BYTE)n_disk; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_part); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + partition = 0xF8; + } else { + partition = 0xF0; + } + + /* Create boot record */ + tbl = fs->win; /* Clear buffer */ + mem_set(tbl, 0, SS(fs)); + ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_part); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_part); + } + tbl[BPB_Media] = partition; /* Media descripter */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_part); /* Hidden sectors */ + n = get_fattime(); /* Use current time as a VSN */ + if (fmt != FS_FAT32) { + ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of secters per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ + if (SS(fs) > 512U) { + ST_WORD(tbl+SS(fs)-2, 0xAA55); + } + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_DISK_ERR; + if (fmt == FS_FAT32) + disk_write(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + n |= partition; + ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_DWORD(tbl+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--m); + + /* Create FSInfo record if needed */ + if (fmt == FS_FAT32) { + ST_WORD(tbl+BS_55AA, 0xAA55); + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); + ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF); + disk_write(drv, tbl, b_part+1, 1); + disk_write(drv, tbl, b_part+7, 1); + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* f_gets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + if (*p++ == '\n') break; /* Break when reached end of line */ + } + *p = 0; + return i ? buff : NULL; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const char* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (f_putc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int f_printf ( + FIL* fil, /* Pointer to the file object */ + const char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + cc = f_putc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = f_puts(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = f_putc(va_arg(arp, int), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val & 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + cc = f_puts(&s[i], fil); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/drivers/fatfs/ff.h b/drivers/fatfs/ff.h new file mode 100644 index 0000000..090f405 --- /dev/null +++ b/drivers/fatfs/ff.h @@ -0,0 +1,596 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.07e (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2009, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 0x007E + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONFIG +#error Wrong configuration file (ffconf.h). +#endif + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + + +/* Character code support macros */ + +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) + +#if _DF1S /* DBCS configuration */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ + +typedef struct _PARTITION { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; + +extern +const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition configuration */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Definitions corresponds to multiple sector size */ + +#if _MAX_SS == 512 /* Single sector size */ +#define SS(fs) 512U + +#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 /* Multiple sector size */ +#define SS(fs) ((fs)->s_size) + +#else +#error Sector size must be 512, 1024, 2048 or 4096. + +#endif + + + +/* Type of file name on FatFs API */ + +#if _LFN_UNICODE && _USE_LFN +typedef WCHAR XCHAR; /* Unicode */ +#else +typedef char XCHAR; /* SBCS, DBCS */ +#endif + + + +/* File system object structure */ + +typedef struct _FATFS_ { + BYTE fs_type; /* FAT sub type */ + BYTE drive; /* Physical drive number */ + BYTE csize; /* Number of sectors per cluster */ + BYTE n_fats; /* Number of FAT copies */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if _MAX_SS != 512 + WORD s_size; /* Sector size */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory (0:root)*/ +#endif + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS];/* Disk access window for Directory/FAT */ +} FATFS; + + + +/* Directory object structure */ + +typedef struct _DIR_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Static table) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} DIR; + + + +/* File object structure */ + +typedef struct _FIL_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + DWORD curr_clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS];/* File R/W buffer */ +#endif +} FIL; + + + +/* File status structure */ + +typedef struct _FILINFO_ { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + XCHAR* lfname; /* Pointer to the LFN buffer */ + int lfsize; /* Size of LFN buffer [chrs] */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_DISK_ERR, /* 1 */ + FR_INT_ERR, /* 2 */ + FR_NOT_READY, /* 3 */ + FR_NO_FILE, /* 4 */ + FR_NO_PATH, /* 5 */ + FR_INVALID_NAME, /* 6 */ + FR_DENIED, /* 7 */ + FR_EXIST, /* 8 */ + FR_INVALID_OBJECT, /* 9 */ + FR_WRITE_PROTECTED, /* 10 */ + FR_INVALID_DRIVE, /* 11 */ + FR_NOT_ENABLED, /* 12 */ + FR_NO_FILESYSTEM, /* 13 */ + FR_MKFS_ABORTED, /* 14 */ + FR_TIMEOUT /* 15 */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const XCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const XCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const XCHAR*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const XCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const XCHAR*); /* Create a new directory */ +FRESULT f_chmod (const XCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const XCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const XCHAR*, const XCHAR*); /* Rename/Move a file or directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +FRESULT f_chdir (const XCHAR*); /* Change current directory */ +FRESULT f_chdrive (BYTE); /* Change current drive */ + +#if _USE_STRFUNC +int f_putc (int, FIL*); /* Put a character to the file */ +int f_puts (const char*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* f_gets (char*, int, FIL*); /* Get a string from the file */ +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#ifndef EOF +#define EOF -1 +#endif +#endif + + + +/*--------------------------------------------------------------*/ +/* User defined functions */ + +/* Real time clock */ +#if !_FS_READONLY +DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ +#endif + +/* Unicode - OEM code conversion */ +#if _USE_LFN +WCHAR ff_convert (WCHAR, UINT); +WCHAR ff_wtoupper (WCHAR); +#endif + +/* Sync functions */ +#if _FS_REENTRANT +BOOL ff_cre_syncobj(BYTE, _SYNC_t*); +BOOL ff_del_syncobj(_SYNC_t); +BOOL ff_req_grant(_SYNC_t); +void ff_rel_grant(_SYNC_t); +#endif + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various compilers. */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 +#define LDIR_Ord 0 +#define LDIR_Attr 11 +#define LDIR_Type 12 +#define LDIR_Chksum 13 +#define LDIR_FstClusLO 26 + + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + + +#endif /* _FATFS */ diff --git a/drivers/fatfs/ffconf.h b/drivers/fatfs/ffconf.h new file mode 100644 index 0000000..3453ad1 --- /dev/null +++ b/drivers/fatfs/ffconf.h @@ -0,0 +1,167 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.07e (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONFIG +#define _FFCONFIG 0x007E + +#include "projectconfig.h" + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 1 /* 0 or 1 */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _FS_READONLY CFG_SDCARD_READONLY // 0 /* 0 or 1 */ +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + + +#define _FS_MINIMIZE 0 /* 0, 1, 2 or 3 */ +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + + +#define _USE_STRFUNC 0 /* 0, 1 or 2 */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0 or 1 */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0 or 1 */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 858 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 0 /* 0, 1 or 2 */ +#define _MAX_LFN 64 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the bss. NOT REENTRANT. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ two Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. */ + + +#define _LFN_UNICODE 0 /* 0 or 1 */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. +*/ + + +#define _FS_RPATH 0 /* 0 or 1 */ +/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir, +/ f_chdrive function are available. +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _DRIVES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for floppy disk (512/1024) and optical disk (512/2048). +/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted +/ to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0 or 1 */ +/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical +/ drive number and can mount only first primaly partition. When it is set to 1, +/ each volume is tied to the partitions listed in Drives[]. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* The _WORD_ACCESS option defines which access method is used to the word +/ data on the FAT volume. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned +/ word access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + + +#define _FS_REENTRANT 0 /* 0 or 1 */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ +/* The _FS_REENTRANT option switches the reentrancy of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#endif /* _FFCONFIG */ diff --git a/drivers/fatfs/integer.h b/drivers/fatfs/integer.h new file mode 100644 index 0000000..ee3bfa2 --- /dev/null +++ b/drivers/fatfs/integer.h @@ -0,0 +1,34 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER + +#if 0 +#include +#else + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +#endif + +#define _INTEGER +#endif diff --git a/drivers/fatfs/mmc.c b/drivers/fatfs/mmc.c new file mode 100644 index 0000000..6e01bb1 --- /dev/null +++ b/drivers/fatfs/mmc.c @@ -0,0 +1,684 @@ +/*-----------------------------------------------------------------------*/ +/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + + +#include "projectconfig.h" +#include "diskio.h" +#include "core/gpio/gpio.h" +#include "core/ssp/ssp.h" +#include "core/systick/systick.h" + + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */ +#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */ +#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + + +/* Port Controls (Platform dependent) */ +#define CS_LOW() gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 0) +#define CS_HIGH() gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 1) + +// #define FCLK_SLOW() /* Set slow clock (100k-400k) */ +// #define FCLK_FAST() /* Set fast clock (depends on the CSD) */ + +#define FDELAY(ms) systickDelay(ms) // Assumes delay = 1ms, ugly + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* Card type flags */ + +/**************************************************************************/ +/*! + Set SSP clock to slow (400 KHz) +*/ +/**************************************************************************/ +static void FCLK_SLOW() +{ + /* Divide by 10 (SSPCLKDIV also enables to SSP CLK) */ + SCB_SSP0CLKDIV = SCB_SSP0CLKDIV_DIV10; + + /* (PCLK / (CPSDVSR * [SCR+1])) = (7,200,000 / (2 x [8 + 1])) = 400 KHz */ + uint32_t configReg = ( SSP_SSP0CR0_DSS_8BIT // Data size = 8-bit + | SSP_SSP0CR0_FRF_SPI // Frame format = SPI + | SSP_SSP0CR0_SCR_8); // Serial clock rate = 8 + + // Set clock polarity (low between frames) + // configReg &= ~SSP_SSP0CR0_CPOL_MASK; + // configReg |= SSP_SSP0CR0_CPOL_LOW; + + // Set edge transition (leading edge) + // configReg &= ~SSP_SSP0CR0_CPHA_MASK; + // configReg |= SSP_SSP0CR0_CPHA_FIRST; + + // Assign config values to SSP0CR0 + SSP_SSP0CR0 = configReg; + + /* Clock prescale register must be even and at least 2 in master mode */ + SSP_SSP0CPSR = SSP_SSP0CPSR_CPSDVSR_DIV2; +} + +/**************************************************************************/ +/*! + Set SSP clock to fast (6.0 MHz) +*/ +/**************************************************************************/ +static void FCLK_FAST() +{ + /* Divide by 1 (SSPCLKDIV also enables to SSP CLK) */ + SCB_SSP0CLKDIV = SCB_SSP0CLKDIV_DIV1; + + /* (PCLK / (CPSDVSR * [SCR+1])) = (72,000,000 / (2 * [5 + 1])) = 6.0 MHz */ + uint32_t configReg = ( SSP_SSP0CR0_DSS_8BIT // Data size = 8-bit + | SSP_SSP0CR0_FRF_SPI // Frame format = SPI + | SSP_SSP0CR0_SCR_5); // Serial clock rate = 5 + + // Set clock polarity (low between frames) + // configReg &= ~SSP_SSP0CR0_CPOL_MASK; + // configReg |= SSP_SSP0CR0_CPOL_LOW; + + // Set edge transition (leading edge) + // configReg &= ~SSP_SSP0CR0_CPHA_MASK; + // configReg |= SSP_SSP0CR0_CPHA_FIRST; + + // Assign config values to SSP0CR0 + SSP_SSP0CR0 = configReg; + + /* Clock prescale register must be even and at least 2 in master mode */ + SSP_SSP0CPSR = SSP_SSP0CPSR_CPSDVSR_DIV2; +} + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +//#define xmit_spi(dat) (SSPSend((uint8_t*)&(dat), 1)) +static void xmit_spi(BYTE dat) +{ + sspSend(0, (uint8_t*) &dat, 1); +} + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + BYTE data = 0; + + sspReceive(0, &data, 1); + + return data; +} + +/* Alternative macro to receive data fast */ + +#define rcvr_spi_m(dst) \ + do { \ + sspReceive(0, (uint8_t*)(dst), 1); \ + } while(0) + + + + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Deselect the card and release SPI bus */ +/*-----------------------------------------------------------------------*/ + +static +void deselect (void) +{ + CS_HIGH(); + rcvr_spi(); +} + + + +/*-----------------------------------------------------------------------*/ +/* Select the card and wait ready */ +/*-----------------------------------------------------------------------*/ + +static +BOOL select (void) /* TRUE:Successful, FALSE:Timeout */ +{ + CS_LOW(); + if (wait_ready() != 0xFF) { + deselect(); + return FALSE; + } + return TRUE; +} + + + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ +} + +static +void power_off (void) +{ +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return 1; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be multiple of 4) */ +) +{ + BYTE token; + + + Timer1 = 20; + do { /* Wait for data packet in timeout of 200ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 4); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (cmd & 0x80) { /* ACMD is the command sequense of CMD55-CMD */ + cmd &= 0x7F; + res = send_cmd(CMD55, 0); + if (res > 1) return res; + } + + /* Select the card and wait for ready */ + deselect(); + if (!select()) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Start + Command index */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0x01; /* Dummy CRC + Stop */ + if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + + + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, cmd, ty, ocr[4]; + + // Init SSP (clock low between frames, transition on leading edge) + sspInit(0, sspClockPolarity_Low, sspClockPhase_RisingEdge); + + gpioSetDir( SSP0_CSPORT, SSP0_CSPIN, gpioDirection_Output ); /* CS */ + gpioSetDir( CFG_SDCARD_CDPORT, CFG_SDCARD_CDPIN, gpioDirection_Input ); /* Card Detect */ + gpioSetPullup (&IOCON_PIO3_0, gpioPullupMode_Inactive); + + // Wait 20ms for card detect to stabilise + systickDelay(20); + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + FCLK_SLOW(); + for (n = 100; n; n--) rcvr_spi(); /* 80 dummy clocks */ + + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDHC */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get trailing return value of R7 resp */ + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */ + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */ + } + } + } else { /* SDSC or MMC */ + if (send_cmd(ACMD41, 0) <= 1) { + ty = CT_SD1; cmd = ACMD41; /* SDv1 */ + } else { + ty = CT_MMC; cmd = CMD1; /* MMCv3 */ + } + while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */ + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */ + ty = 0; + } + } + CardType = ty; + deselect(); + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + FCLK_FAST(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ + } + } + deselect(); + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & CT_SDC) send_cmd(ACMD23, count); + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + deselect(); + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY == 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +#if _USE_IOCTL != 0 +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (ctrl) { + case CTRL_SYNC : /* Make sure that no pending write process. Do not remove this or written sector might not left updated. */ + if (select()) { + res = RES_OK; + deselect(); + } + break; + + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* SDC ver 1.XX or MMC*/ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ + if (CardType & CT_SD2) { /* SDC ver 2.00 */ + if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */ + rcvr_spi(); + if (rcvr_datablock(csd, 16)) { /* Read partial block */ + for (n = 64 - 16; n; n--) rcvr_spi(); /* Purge trailing data */ + *(DWORD*)buff = 16UL << (csd[10] >> 4); + res = RES_OK; + } + } + } else { /* SDC ver 1.XX or MMC */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */ + if (CardType & CT_SD1) { /* SDC ver 1.XX */ + *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); + } else { /* MMC */ + *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1); + } + res = RES_OK; + } + } + break; + + case MMC_GET_TYPE : /* Get card type flags (1 byte) */ + *ptr = CardType; + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 4; n; n--) *ptr++ = rcvr_spi(); + res = RES_OK; + } + break; + + case MMC_GET_SDSTAT : /* Receive SD statsu as a data block (64 bytes) */ + if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */ + rcvr_spi(); + if (rcvr_datablock(ptr, 64)) + res = RES_OK; + } + break; + + default: + res = RES_PARERR; + } + + deselect(); + } + + return res; +} +#endif /* _USE_IOCTL != 0 */ + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ +/* Called from SysTick_Handler in systick.c */ + +void disk_timerproc (void) +{ + static BYTE pv; + BYTE n; + BYTE s; + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + + n = pv; + pv = 0; + /* Sample card detect pin */ + pv = gpioGetValue(CFG_SDCARD_CDPORT, CFG_SDCARD_CDPIN); + + /* Have contacts stabled? */ + if (n == pv) + { + s = Stat; + + /* write protect NOT supported */ + + /* check card detect */ + if (!pv) /* (Socket empty) */ + s |= (STA_NODISK | STA_NOINIT); + else /* (Card inserted) */ + s &= ~STA_NODISK; + + Stat = s; + } +} + + diff --git a/drivers/lcd/bitmap/readme.txt b/drivers/lcd/bitmap/readme.txt new file mode 100644 index 0000000..8b144a0 --- /dev/null +++ b/drivers/lcd/bitmap/readme.txt @@ -0,0 +1,12 @@ +Bitmap LCDs +=========== + +This folder contains drivers for 1-bit graphic LCDs (128x64 pixel ST7565, etc.) +or text-only displays. They are placed in a seperate folder because the +drawing routines are handled quite differently than the generic drawing code +for 16-bit TFT LCDs. + +ST7565 Driver for 128x64 pixel I2C-based displays (available from + Adafruit Industries, for example). +SSD1306 Driver for 128x64 pixel OLED displays (also available from + Adafruit Industries). diff --git a/drivers/lcd/bitmap/ssd1306/ssd1306.c b/drivers/lcd/bitmap/ssd1306/ssd1306.c new file mode 100644 index 0000000..ce8872e --- /dev/null +++ b/drivers/lcd/bitmap/ssd1306/ssd1306.c @@ -0,0 +1,404 @@ +/**************************************************************************/ +/*! + @file ssd1306.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for 128x64 OLED display based on the SSD1306 controller. + + This driver is based on the SSD1306 Library from Limor Fried + (Adafruit Industries) at: https://github.com/adafruit/SSD1306 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "ssd1306.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" +#include "drivers/lcd/smallfonts.h" + +void ssd1306SendByte(uint8_t byte); + +#define CMD(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \ + gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 0 ); \ + gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \ + ssd1306SendByte( c ); \ + gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \ + } while (0); +#define DATA(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \ + gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 1 ); \ + gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \ + ssd1306SendByte( c ); \ + gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \ + } while (0); +#define DELAY(mS) do { systickDelay( mS / CFG_SYSTICK_DELAY_IN_MS ); } while(0); + +uint8_t buffer[SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8]; + +/**************************************************************************/ +/* Private Methods */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Simulates an SPI write using GPIO + + @param[in] byte + The byte to send +*/ +/**************************************************************************/ +void ssd1306SendByte(uint8_t byte) +{ + int8_t i; + + // Make sure clock pin starts high + gpioSetValue(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, 1); + + // Write from MSB to LSB + for (i=7; i>=0; i--) + { + // Set clock pin low + gpioSetValue(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, 0); + // Set data pin high or low depending on the value of the current bit + gpioSetValue(SSD1306_SDAT_PORT, SSD1306_SDAT_PIN, byte & (1 << i) ? 1 : 0); + // Set clock pin high + gpioSetValue(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, 1); + } +} + +/**************************************************************************/ +/*! + @brief Draws a single graphic character using the supplied font +*/ +/**************************************************************************/ +static void ssd1306DrawChar(uint16_t x, uint16_t y, uint8_t c, struct FONT_DEF font) +{ + uint8_t col, column[font.u8Width]; + + // Check if the requested character is available + if ((c >= font.u8FirstChar) && (c <= font.u8LastChar)) + { + // Retrieve appropriate columns from font data + for (col = 0; col < font.u8Width; col++) + { + column[col] = font.au8FontTable[((c - 32) * font.u8Width) + col]; // Get first column of appropriate character + } + } + else + { + // Requested character is not available in this font ... send a space instead + for (col = 0; col < font.u8Width; col++) + { + column[col] = 0xFF; // Send solid space + } + } + + // Render each column + uint16_t xoffset, yoffset; + for (xoffset = 0; xoffset < font.u8Width; xoffset++) + { + for (yoffset = 0; yoffset < (font.u8Height + 1); yoffset++) + { + uint8_t bit = 0x00; + bit = (column[xoffset] << (8 - (yoffset + 1))); // Shift current row bit left + bit = (bit >> 7); // Shift current row but right (results in 0x01 for black, and 0x00 for white) + if (bit) + { + ssd1306DrawPixel(x + xoffset, y + yoffset); + } + } + } +} + +/**************************************************************************/ +/* Public Methods */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Initialises the SSD1306 LCD display +*/ +/**************************************************************************/ +void ssd1306Init(uint8_t vccstate) +{ + // Set all pins to output + gpioSetDir(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, gpioDirection_Output); + gpioSetDir(SSD1306_SDAT_PORT, SSD1306_SDAT_PIN, gpioDirection_Output); + gpioSetDir(SSD1306_DC_PORT, SSD1306_DC_PIN, gpioDirection_Output); + gpioSetDir(SSD1306_RST_PORT, SSD1306_RST_PIN, gpioDirection_Output); + gpioSetDir(SSD1306_CS_PORT, SSD1306_CS_PIN, gpioDirection_Output); + + // Reset the LCD + gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 1); + DELAY(1); + gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 0); + DELAY(10); + gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 1); + + // Initialisation sequence + CMD(SSD1306_DISPLAYOFF); // 0xAE + CMD(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + CMD(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + CMD(SSD1306_SETSTARTLINE | 0x0); // line #0 + CMD(SSD1306_SETCONTRAST); // 0x81 + if (vccstate == SSD1306_EXTERNALVCC) + { CMD(0x9F) } + else + { CMD(0xCF) } + CMD(0xa1); // setment remap 95 to 0 (?) + CMD(SSD1306_NORMALDISPLAY); // 0xA6 + CMD(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + CMD(SSD1306_SETMULTIPLEX); // 0xA8 + CMD(0x3F); // 0x3F 1/64 duty + CMD(SSD1306_SETDISPLAYOFFSET); // 0xD3 + CMD(0x0); // no offset + CMD(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + CMD(0x80); // the suggested ratio 0x80 + CMD(SSD1306_SETPRECHARGE); // 0xd9 + if (vccstate == SSD1306_EXTERNALVCC) + { CMD(0x22) } + else + { CMD(0xF1) } + CMD(SSD1306_SETCOMPINS); // 0xDA + CMD(0x12); // disable COM left/right remap + CMD(SSD1306_SETVCOMDETECT); // 0xDB + CMD(0x40); // 0x20 is default? + CMD(SSD1306_MEMORYMODE); // 0x20 + CMD(0x00); // 0x0 act like ks0108 + CMD(SSD1306_SEGREMAP | 0x1); + CMD(SSD1306_COMSCANDEC); + CMD(SSD1306_CHARGEPUMP); //0x8D + if (vccstate == SSD1306_EXTERNALVCC) + { CMD(0x10) } + else + { CMD(0x14) } + + // Enabled the OLED panel + CMD(SSD1306_DISPLAYON); +} + +/**************************************************************************/ +/*! + @brief Draws a single pixel in image buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) +*/ +/**************************************************************************/ +void ssd1306DrawPixel(uint8_t x, uint8_t y) +{ + if ((x >= SSD1306_LCDWIDTH) || (y >= SSD1306_LCDHEIGHT)) + return; + + buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << y%8); +} + +/**************************************************************************/ +/*! + @brief Clears a single pixel in image buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) +*/ +/**************************************************************************/ +void ssd1306ClearPixel(uint8_t x, uint8_t y) +{ + if ((x >= SSD1306_LCDWIDTH) || (y >= SSD1306_LCDHEIGHT)) + return; + + buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << y%8); +} + +/**************************************************************************/ +/*! + @brief Gets the value (1 or 0) of the specified pixel from the buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) + + @return 1 if the pixel is enabled, 0 if disabled +*/ +/**************************************************************************/ +uint8_t ssd1306GetPixel(uint8_t x, uint8_t y) +{ + if ((x >= SSD1306_LCDWIDTH) || (y >=SSD1306_LCDHEIGHT)) return 0; + return buffer[x+ (y/8)*SSD1306_LCDWIDTH] & (1 << y%8) ? 1 : 0; +} + +/**************************************************************************/ +/*! + @brief Clears the screen +*/ +/**************************************************************************/ +void ssd1306ClearScreen() +{ + memset(buffer, 0, 1024); +} + +/**************************************************************************/ +/*! + @brief Renders the contents of the pixel buffer on the LCD +*/ +/**************************************************************************/ +void ssd1306Refresh(void) +{ + CMD(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + CMD(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + CMD(SSD1306_SETSTARTLINE | 0x0); // line #0 + + uint16_t i; + for (i=0; i<1024; i++) + { + DATA(buffer[i]); + } +} + +/**************************************************************************/ +/*! + @brief Draws a string using the supplied font data. + + @param[in] x + Starting x co-ordinate + @param[in] y + Starting y co-ordinate + @param[in] text + The string to render + @param[in] font + Pointer to the FONT_DEF to use when drawing the string + + @section Example + + @code + + #include "drivers/lcd/bitmap/ssd1306/ssd1306.h" + #include "drivers/lcd/smallfonts.h" + + // Configure the pins and initialise the LCD screen + ssd1306Init(); + + // Render some text on the screen + ssd1306DrawString(1, 10, "5x8 System", Font_System5x8); + ssd1306DrawString(1, 20, "7x8 System", Font_System7x8); + + // Refresh the screen to see the results + ssd1306Refresh(); + + @endcode +*/ +/**************************************************************************/ +void ssd1306DrawString(uint16_t x, uint16_t y, char* text, struct FONT_DEF font) +{ + uint8_t l; + for (l = 0; l < strlen(text); l++) + { + ssd1306DrawChar(x + (l * (font.u8Width + 1)), y, text[l], font); + } +} + +/**************************************************************************/ +/*! + @brief Shifts the contents of the frame buffer up the specified + number of pixels + + @param[in] height + The number of pixels to shift the frame buffer up, leaving + a blank space at the bottom of the frame buffer x pixels + high + + @section Example + + @code + + #include "drivers/lcd/bitmap/ssd1306/ssd1306.h" + #include "drivers/lcd/smallfonts.h" + + // Configure the pins and initialise the LCD screen + ssd1306Init(); + + // Enable the backlight + ssd1306BLEnable(); + + // Continually write some text, scrolling upward one line each time + while (1) + { + // Shift the buffer up 8 pixels (adjust for font-height) + ssd1306ShiftFrameBuffer(8); + // Render some text on the screen with different fonts + ssd1306DrawString(1, 56, "INSERT TEXT HERE", Font_System5x8); + // Refresh the screen to see the results + ssd1306Refresh(); + // Wait a bit before writing the next line + systickDelay(1000); + } + + @endcode +*/ +/**************************************************************************/ +void ssd1306ShiftFrameBuffer( uint8_t height ) +{ + if (height == 0) return; + if (height >= SSD1306_LCDHEIGHT) + { + // Clear the entire frame buffer + ssd1306ClearScreen(); + return; + } + + // This is horribly inefficient, but at least easy to understand + // In a production environment, this should be significantly optimised + + uint8_t y, x; + for (y = 0; y < SSD1306_LCDHEIGHT; y++) + { + for (x = 0; x < SSD1306_LCDWIDTH; x++) + { + if ((SSD1306_LCDHEIGHT - 1) - y > height) + { + // Shift height from further ahead in the buffer + ssd1306GetPixel(x, y + height) ? ssd1306DrawPixel(x, y) : ssd1306ClearPixel(x, y); + } + else + { + // Clear the entire line + ssd1306ClearPixel(x, y); + } + } + } +} diff --git a/drivers/lcd/bitmap/ssd1306/ssd1306.h b/drivers/lcd/bitmap/ssd1306/ssd1306.h new file mode 100644 index 0000000..510efa9 --- /dev/null +++ b/drivers/lcd/bitmap/ssd1306/ssd1306.h @@ -0,0 +1,95 @@ +/**************************************************************************/ +/*! + @file ssd1306.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __SSD1306_H__ +#define __SSD1306_H__ + +#include "projectconfig.h" + +#include "drivers/lcd/smallfonts.h" + +// Pin Definitions +#define SSD1306_DC_PORT (2) // Data/Command +#define SSD1306_DC_PIN (1) +#define SSD1306_RST_PORT (2) // Reset +#define SSD1306_RST_PIN (2) +#define SSD1306_CS_PORT (2) // Select +#define SSD1306_CS_PIN (3) +#define SSD1306_SCLK_PORT (2) // Serial Clock +#define SSD1306_SCLK_PIN (5) +#define SSD1306_SDAT_PORT (2) // Serial Data +#define SSD1306_SDAT_PIN (6) + +#define SSD1306_LCDWIDTH 128 +#define SSD1306_LCDHEIGHT 64 + +// Commands +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA +#define SSD1306_SETVCOMDETECT 0xDB +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 +#define SSD1306_SETMULTIPLEX 0xA8 +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 +#define SSD1306_SETSTARTLINE 0x40 +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 +#define SSD1306_SEGREMAP 0xA0 +#define SSD1306_CHARGEPUMP 0x8D +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 + +// Initialisation/Config Prototypes +void ssd1306Init ( uint8_t vccstate ); +void ssd1306DrawPixel ( uint8_t x, uint8_t y ); +void ssd1306ClearPixel ( uint8_t x, uint8_t y ); +uint8_t ssd1306GetPixel ( uint8_t x, uint8_t y ); +void ssd1306ClearScreen ( void ); +void ssd1306Refresh ( void ); +void ssd1306DrawString( uint16_t x, uint16_t y, char* text, struct FONT_DEF font ); +void ssd1306ShiftFrameBuffer( uint8_t height ); + +#endif diff --git a/drivers/lcd/bitmap/st7565/st7565.c b/drivers/lcd/bitmap/st7565/st7565.c new file mode 100644 index 0000000..b0995ac --- /dev/null +++ b/drivers/lcd/bitmap/st7565/st7565.c @@ -0,0 +1,445 @@ +/**************************************************************************/ +/*! + @file ST7565.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for 128x64 pixel display based on the ST7565 LCD controller. + + This driver is based on the ST7565 Library from Limor Fried + (Adafruit Industries) at: http://github.com/adafruit/ST7565-LCD/ + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "st7565.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" +#include "drivers/lcd/smallfonts.h" + +void sendByte(uint8_t byte); + +#define CMD(c) do { gpioSetValue( ST7565_A0_PORT, ST7565_A0_PIN, 0 ); sendByte( c ); } while (0); +#define DATA(d) do { gpioSetValue( ST7565_A0_PORT, ST7565_A0_PIN, 1 ); sendByte( d ); } while (0); +#define DELAY(mS) do { systickDelay( mS / CFG_SYSTICK_DELAY_IN_MS ); } while(0); + +uint8_t buffer[128*64/8]; + +/**************************************************************************/ +/* Private Methods */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Renders the buffer contents + + @param[in] buffer + Pointer to the buffer containing the raw pixel data +*/ +/**************************************************************************/ +void writeBuffer(uint8_t *buffer) +{ + uint8_t c, p; + int pagemap[] = { 3, 2, 1, 0, 7, 6, 5, 4 }; + + for(p = 0; p < 8; p++) + { + CMD(ST7565_CMD_SET_PAGE | pagemap[p]); + CMD(ST7565_CMD_SET_COLUMN_LOWER | (0x0 & 0xf)); + CMD(ST7565_CMD_SET_COLUMN_UPPER | ((0x0 >> 4) & 0xf)); + CMD(ST7565_CMD_RMW); + DATA(0xff); + + for(c = 0; c < 128; c++) + { + DATA(buffer[(128*p)+c]); + } + } +} + +/**************************************************************************/ +/*! + @brief Simulates an SPI write using GPIO + + @param[in] byte + The byte to send +*/ +/**************************************************************************/ +void sendByte(uint8_t byte) +{ + int8_t i; + + // Note: This code can be optimised to avoid the branches by setting + // GPIO registers directly, but we'll leave it as is for the moment + // for simplicity sake + + // Make sure clock pin starts high + gpioSetValue(ST7565_SCLK_PORT, ST7565_SCLK_PIN, 1); + + // Write from MSB to LSB + for (i=7; i>=0; i--) + { + // Set clock pin low + gpioSetValue(ST7565_SCLK_PORT, ST7565_SCLK_PIN, 0); + // Set data pin high or low depending on the value of the current bit + gpioSetValue(ST7565_SDAT_PORT, ST7565_SDAT_PIN, byte & (1 << i) ? 1 : 0); + // Set clock pin high + gpioSetValue(ST7565_SCLK_PORT, ST7565_SCLK_PIN, 1); + } +} + +/**************************************************************************/ +/*! + @brief Draws a single graphic character using the supplied font +*/ +/**************************************************************************/ +void drawChar(uint16_t x, uint16_t y, uint8_t c, struct FONT_DEF font) +{ + uint8_t col, column[font.u8Width]; + + // Check if the requested character is available + if ((c >= font.u8FirstChar) && (c <= font.u8LastChar)) + { + // Retrieve appropriate columns from font data + for (col = 0; col < font.u8Width; col++) + { + column[col] = font.au8FontTable[((c - 32) * font.u8Width) + col]; // Get first column of appropriate character + } + } + else + { + // Requested character is not available in this font ... send a space instead + for (col = 0; col < font.u8Width; col++) + { + column[col] = 0xFF; // Send solid space + } + } + + // Render each column + uint16_t xoffset, yoffset; + for (xoffset = 0; xoffset < font.u8Width; xoffset++) + { + for (yoffset = 0; yoffset < (font.u8Height + 1); yoffset++) + { + uint8_t bit = 0x00; + bit = (column[xoffset] << (8 - (yoffset + 1))); // Shift current row bit left + bit = (bit >> 7); // Shift current row but right (results in 0x01 for black, and 0x00 for white) + if (bit) + { + st7565DrawPixel(x + xoffset, y + yoffset); + } + } + } +} + +/**************************************************************************/ +/* Public Methods */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Initialises the ST7565 LCD display +*/ +/**************************************************************************/ +void st7565Init(void) +{ + // Note: This can be optimised to set all pins to output and high + // in two commands by manipulating the registers directly (assuming + // that the pins are located in the same GPIO bank). The code is left + // as is for clarity sake in case the pins are not all located in the + // same bank. + + // Set clock pin to output and high + gpioSetDir(ST7565_SCLK_PORT, ST7565_SCLK_PIN, 1); + gpioSetValue(ST7565_SCLK_PORT, ST7565_SCLK_PIN, 1); + + // Set data pin to output and high + gpioSetDir(ST7565_SDAT_PORT, ST7565_SDAT_PIN, 1); + gpioSetValue(ST7565_SDAT_PORT, ST7565_SDAT_PIN, 1); + + // Configure backlight pin to output and set high (off) + gpioSetDir(ST7565_BL_PORT, ST7565_BL_PIN, 1); + gpioSetValue(ST7565_BL_PORT, ST7565_BL_PIN, 1); + + // Configure A0 pin to output and set high + gpioSetDir(ST7565_A0_PORT, ST7565_A0_PIN, 1); + gpioSetValue(ST7565_A0_PORT, ST7565_A0_PIN, 1); + + // Configure Reset pin and set high + gpioSetDir(ST7565_RST_PORT, ST7565_RST_PIN, 1); + gpioSetValue(ST7565_RST_PORT, ST7565_RST_PIN, 1); + + // Configure select pin and set high + gpioSetDir(ST7565_CS_PORT, ST7565_CS_PIN, 1); + gpioSetValue(ST7565_CS_PORT, ST7565_CS_PIN, 1); + + // Reset + gpioSetValue(ST7565_CS_PORT, ST7565_CS_PIN, 0); // Set CS low + gpioSetValue(ST7565_RST_PORT, ST7565_RST_PIN, 0); // Set reset low + DELAY(500 / CFG_SYSTICK_DELAY_IN_MS); // Wait 500mS + gpioSetValue(ST7565_RST_PORT, ST7565_RST_PIN, 1); // Set reset high + + // Configure Display + CMD(ST7565_CMD_SET_BIAS_7); // LCD Bias Select + CMD(ST7565_CMD_SET_ADC_NORMAL); // ADC Select + CMD(ST7565_CMD_SET_COM_NORMAL); // SHL Select + CMD(ST7565_CMD_SET_DISP_START_LINE); // Initial Display Line + CMD(ST7565_CMD_SET_POWER_CONTROL | 0x04); // Turn on voltage converter (VC=1, VR=0, VF=0) + DELAY(50 / CFG_SYSTICK_DELAY_IN_MS); // Wait 50mS + CMD(ST7565_CMD_SET_POWER_CONTROL | 0x06); // Turn on voltage regulator (VC=1, VR=1, VF=0) + DELAY(50 / CFG_SYSTICK_DELAY_IN_MS); // Wait 50mS + CMD(ST7565_CMD_SET_POWER_CONTROL | 0x07); // Turn on voltage follower + DELAY(10 / CFG_SYSTICK_DELAY_IN_MS); // Wait 10mS + CMD(ST7565_CMD_SET_RESISTOR_RATIO | 0x6); // Set LCD operating voltage + + // Turn display on + CMD(ST7565_CMD_DISPLAY_ON); + CMD(ST7565_CMD_SET_ALLPTS_NORMAL); + st7565SetBrightness(0x18); +} + +/**************************************************************************/ +/*! + @brief Enables or disables the backlight +*/ +/**************************************************************************/ +void st7565Backlight(bool state) +{ + gpioSetValue( ST7565_BL_PORT, ST7565_BL_PIN, state ? 0 : 1 ); +} + +/**************************************************************************/ +/*! + @brief Sets the display brightness +*/ +/**************************************************************************/ +void st7565SetBrightness(uint8_t val) +{ + CMD(ST7565_CMD_SET_VOLUME_FIRST); + CMD(ST7565_CMD_SET_VOLUME_SECOND | (val & 0x3f)); +} + +/**************************************************************************/ +/*! + @brief Clears the screen +*/ +/**************************************************************************/ +void st7565ClearScreen(void) +{ + memset(&buffer, 0x00, 128*64/8); +} + +/**************************************************************************/ +/*! + @brief Renders the contents of the pixel buffer on the LCD +*/ +/**************************************************************************/ +void st7565Refresh(void) +{ + writeBuffer(buffer); +} + +/**************************************************************************/ +/*! + @brief Draws a single pixel in image buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) +*/ +/**************************************************************************/ +void st7565DrawPixel(uint8_t x, uint8_t y) +{ + if ((x >= 128) || (y >= 64)) + return; + + // x is which column + buffer[x+ (y/8)*128] |= (1 << (7-(y%8))); +} + +/**************************************************************************/ +/*! + @brief Clears a single pixel in image buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) +*/ +/**************************************************************************/ +void st7565ClearPixel(uint8_t x, uint8_t y) +{ + if ((x >= 128) || (y >= 64)) + return; + + // x is which column + buffer[x+ (y/8)*128] &= ~(1 << (7-(y%8))); +} + +/**************************************************************************/ +/*! + @brief Gets the value (1 or 0) of the specified pixel from the buffer + + @param[in] x + The x position (0..127) + @param[in] y + The y position (0..63) + + @return 1 if the pixel is enabled, 0 if disabled +*/ +/**************************************************************************/ +uint8_t st7565GetPixel(uint8_t x, uint8_t y) +{ + if ((x >= 128) || (y >= 64)) return 0; + return buffer[x+ (y/8)*128] & (1 << (7-(y%8))); +} + +/**************************************************************************/ +/*! + @brief Draws a string using the supplied font data. + + @param[in] x + Starting x co-ordinate + @param[in] y + Starting y co-ordinate + @param[in] text + The string to render + @param[in] font + Pointer to the FONT_DEF to use when drawing the string + + @section Example + + @code + + #include "drivers/lcd/bitmap/st7565/st7565.h" + #include "drivers/lcd/smallfonts.h" + + // Configure the pins and initialise the LCD screen + st7565Init(); + + // Enable the backlight + st7565BLEnable(); + + // Render some text on the screen with different fonts + st7565DrawString(1, 1, "3X6 SYSTEM", Font_System3x6); // 3x6 is UPPER CASE only + st7565DrawString(1, 10, "5x8 System", Font_System5x8); + st7565DrawString(1, 20, "7x8 System", Font_System7x8); + + // Refresh the screen to see the results + st7565Refresh(); + + @endcode +*/ +/**************************************************************************/ +void st7565DrawString(uint16_t x, uint16_t y, char* text, struct FONT_DEF font) +{ + uint8_t l; + for (l = 0; l < strlen(text); l++) + { + drawChar(x + (l * (font.u8Width + 1)), y, text[l], font); + } +} + +/**************************************************************************/ +/*! + @brief Shifts the contents of the frame buffer up the specified + number of pixels + + @param[in] height + The number of pixels to shift the frame buffer up, leaving + a blank space at the bottom of the frame buffer x pixels + high + + @section Example + + @code + + #include "drivers/lcd/bitmap/st7565/st7565.h" + #include "drivers/lcd/smallfonts.h" + + // Configure the pins and initialise the LCD screen + st7565Init(); + + // Enable the backlight + st7565BLEnable(); + + // Continually write some text, scrolling upward one line each time + while (1) + { + // Shift the buffer up 8 pixels (adjust for font-height) + st7565ShiftFrameBuffer(8); + // Render some text on the screen with different fonts + st7565DrawString(1, 56, "INSERT TEXT HERE", Font_System3x6); // 3x6 is UPPER CASE only + // Refresh the screen to see the results + st7565Refresh(); + // Wait a bit before writing the next line + systickDelay(1000); + } + + @endcode +*/ +/**************************************************************************/ +void st7565ShiftFrameBuffer( uint8_t height ) +{ + if (height == 0) return; + if (height >= 64) + { + // Clear the entire frame buffer + st7565ClearScreen(); + return; + } + + // This is horribly inefficient, but at least easy to understand + // In a production environment, this should be significantly optimised + + uint8_t y, x; + for (y = 0; y < 64; y++) + { + for (x = 0; x < 128; x++) + { + if (63 - y > height) + { + // Shift height from further ahead in the buffer + st7565GetPixel(x, y + height) ? st7565DrawPixel(x, y) : st7565ClearPixel(x, y); + } + else + { + // Clear the entire line + st7565ClearPixel(x, y); + } + } + } +} + diff --git a/drivers/lcd/bitmap/st7565/st7565.h b/drivers/lcd/bitmap/st7565/st7565.h new file mode 100644 index 0000000..c27a06f --- /dev/null +++ b/drivers/lcd/bitmap/st7565/st7565.h @@ -0,0 +1,109 @@ +/**************************************************************************/ +/*! + @file ST7565.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __ST7565_H__ +#define __ST7565_H__ + +#include "projectconfig.h" + +#include "drivers/lcd/smallfonts.h" + +// Pin Definitions +#define ST7565_A0_PORT (2) // Register Select Pin (A0) +#define ST7565_A0_PIN (1) +#define ST7565_RST_PORT (2) // Reset +#define ST7565_RST_PIN (2) +#define ST7565_CS_PORT (2) // Select +#define ST7565_CS_PIN (3) +#define ST7565_BL_PORT (2) // Backlight +#define ST7565_BL_PIN (4) +#define ST7565_SCLK_PORT (2) // Serial Clock +#define ST7565_SCLK_PIN (5) +#define ST7565_SDAT_PORT (2) // Serial Data +#define ST7565_SDAT_PIN (6) + +// Commands +#define ST7565_CMD_DISPLAY_OFF 0xAE +#define ST7565_CMD_DISPLAY_ON 0xAF +#define ST7565_CMD_SET_DISP_START_LINE 0x40 +#define ST7565_CMD_SET_PAGE 0xB0 +#define ST7565_CMD_SET_COLUMN_UPPER 0x10 +#define ST7565_CMD_SET_COLUMN_LOWER 0x00 +#define ST7565_CMD_SET_ADC_NORMAL 0xA0 +#define ST7565_CMD_SET_ADC_REVERSE 0xA1 +#define ST7565_CMD_SET_DISP_NORMAL 0xA6 +#define ST7565_CMD_SET_DISP_REVERSE 0xA7 +#define ST7565_CMD_SET_ALLPTS_NORMAL 0xA4 +#define ST7565_CMD_SET_ALLPTS_ON 0xA5 +#define ST7565_CMD_SET_BIAS_9 0xA2 +#define ST7565_CMD_SET_BIAS_7 0xA3 +#define ST7565_CMD_RMW 0xE0 +#define ST7565_CMD_RMW_CLEAR 0xEE +#define ST7565_CMD_INTERNAL_RESET 0xE2 +#define ST7565_CMD_SET_COM_NORMAL 0xC0 +#define ST7565_CMD_SET_COM_REVERSE 0xC8 +#define ST7565_CMD_SET_POWER_CONTROL 0x28 +#define ST7565_CMD_SET_RESISTOR_RATIO 0x20 +#define ST7565_CMD_SET_VOLUME_FIRST 0x81 +#define ST7565_CMD_SET_VOLUME_SECOND 0 +#define ST7565_CMD_SET_STATIC_OFF 0xAC +#define ST7565_CMD_SET_STATIC_ON 0xAD +#define ST7565_CMD_SET_STATIC_REG 0x0 +#define ST7565_CMD_SET_BOOSTER_FIRST 0xF8 +#define ST7565_CMD_SET_BOOSTER_234 0 +#define ST7565_CMD_SET_BOOSTER_5 1 +#define ST7565_CMD_SET_BOOSTER_6 3 +#define ST7565_CMD_NOP 0xE3 +#define ST7565_CMD_TEST 0xF0 + +// Initialisation/Config Prototypes +void st7565Init( void ); +void st7565Command( uint8_t c ); +void st7565Data( uint8_t d ); +void st7565SetBrightness( uint8_t val ); + +// Backlight Prototypes +void st7565Backlight(bool state); + +// Drawing Prototypes +void st7565ClearScreen( void ); +void st7565Refresh( void ); +void st7565DrawPixel( uint8_t x, uint8_t y ); +void st7565ClearPixel( uint8_t x, uint8_t y ); +uint8_t st7565GetPixel( uint8_t x, uint8_t y ); +void st7565DrawString( uint16_t x, uint16_t y, char* text, struct FONT_DEF font ); +void st7565ShiftFrameBuffer( uint8_t pixels ); + +#endif diff --git a/drivers/lcd/icons16.h b/drivers/lcd/icons16.h new file mode 100644 index 0000000..e37f074 --- /dev/null +++ b/drivers/lcd/icons16.h @@ -0,0 +1,62 @@ +/**************************************************************************/ +/*! + @file icons16.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +/* + All icons have been converted from the GentleFace Toolbar Icon Set, + licensed under the Creative Commons Attribution-NonCommercial License. + for more information see: http://www.gentleface.com/free_icon_set.html +*/ + +// Each icon used will consume 32 bytes of flash memory + +#ifndef __ICONS16_H__ +#define __ICONS16_H__ + +uint16_t icons16_alert[16] = { 0x0000, 0x0000, 0x0180, 0x03C0, 0x03C0, 0x0660, 0x0660, 0x0E70, 0x0E70, 0x1E78, 0x3E7C, 0x3FFC, 0x7E7E, 0x7E7E, 0xFFFF, 0x0000 }; +uint16_t icons16_alert_interior[16] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0000, 0x0180, 0x0180, 0x0000, 0x0000 }; +uint16_t icons16_info[16] = { 0x0000, 0x07F0, 0x0FF8, 0x1FFC, 0x3F3E, 0x7F3F, 0x7FFF, 0x7F3F, 0x7F3F, 0x7F3F, 0x7F3F, 0x7F3F, 0x3F3E, 0x1FFC, 0x0FF8, 0x07F0 }; +uint16_t icons16_info_interior[16] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x00C0, 0x00C0, 0x0000, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x0000, 0x0000, 0x0000 }; +uint16_t icons16_failed[16] = { 0x0000, 0x07F0, 0x0FF8, 0x1FFC, 0x3FFE, 0x79CF, 0x788F, 0x7C1F, 0x7E3F, 0x7C1F, 0x788F, 0x79CF, 0x3FFE, 0x1FFC, 0x0FF8, 0x07F0 }; +uint16_t icons16_failed_interior[16] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0630, 0x0770, 0x03E0, 0x01C0, 0x03E0, 0x0770, 0x0630, 0x0000, 0x0000, 0x0000, 0x0000 }; +uint16_t icons16_passed[16] = { 0x0000, 0x07F0, 0x0FF8, 0x1FFC, 0x3FFE, 0x7FEF, 0x7FC7, 0x7F8F, 0x731F, 0x783F, 0x7C7F, 0x7EFF, 0x3FFE, 0x1FFC, 0x0FF8, 0x07F0 }; +uint16_t icons16_passed_interior[16] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0038, 0x0070, 0x0CE0, 0x07C0, 0x0380, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000 }; +uint16_t icons16_pointer[16] = { 0x07C0, 0x1FF0, 0x3FF8, 0x3FF8, 0x7FFC, 0x7FFC, 0x7FFC, 0x7FFC, 0x7FFC, 0x7FFC, 0x3FF8, 0x1FF0, 0x0FE0, 0x07C0, 0x0380, 0x0100 }; +uint16_t icons16_pointer_dot[16] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0380, 0x07C0, 0x07C0, 0x07C0, 0x0380, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; +uint16_t icons16_tag[16] = { 0xFF00, 0xFF80, 0xCFC0, 0xCFE0, 0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0x7FFE, 0x3FFE, 0x1FFC, 0x0FF8, 0x07F0, 0x03E0, 0x01C0, 0x0000 }; +uint16_t icons16_tag_dot[16] = { 0x0000, 0x0000, 0x3000, 0x3000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; +uint16_t icons16_tools[16] = { 0x118C, 0x118C, 0x11FC, 0x11FC, 0x10F8, 0x1070, 0x1070, 0x3870, 0x1070, 0x3870, 0x3870, 0x3870, 0x3870, 0x3870, 0x3870, 0x3870 }; + +#endif diff --git a/drivers/lcd/smallfonts.c b/drivers/lcd/smallfonts.c new file mode 100644 index 0000000..5a185f8 --- /dev/null +++ b/drivers/lcd/smallfonts.c @@ -0,0 +1,556 @@ +/* Partially based on original code for the KS0108 by Stephane Rey */ + +/**************************************************************************/ +/*! + @file smallfonts.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "sysdefs.h" +#include "smallfonts.h" + +/* Global variables */ +const struct FONT_DEF Font_System3x6 = {3, 6, 32, 96, au8FontSystem3x6}; +const struct FONT_DEF Font_System5x8 = {5, 8, 32, 128, au8FontSystem5x8}; +const struct FONT_DEF Font_System7x8 = {7, 8, 32, 128, au8FontSystem7x8}; +const struct FONT_DEF Font_8x8 = {8, 8, 32, 128, au8Font8x8}; +const struct FONT_DEF Font_8x8Thin = {8, 8, 32, 128, au8Font8x8Thin}; + +/* System 3x6 - UPPER CASE ONLY */ +const uint8_t au8FontSystem3x6[]= { + 0x00,0x00,0x00, /* Space */ + 0x00,0x5C,0x00, /* ! */ + 0x0C,0x00,0x0C, /* " */ + 0x7C,0x28,0x7C, /* # */ + 0x7C,0x44,0x7C, /* 0x */ + 0x24,0x10,0x48, /* % */ + 0x28,0x54,0x08, /* & */ + 0x00,0x0C,0x00, /* ' */ + 0x38,0x44,0x00, /* ( */ + 0x44,0x38,0x00, /* ) */ + 0x20,0x10,0x08, /* // */ + 0x10,0x38,0x10, /* + */ + 0x80,0x40,0x00, /* , */ + 0x10,0x10,0x10, /* - */ + 0x00,0x40,0x00, /* . */ + 0x20,0x10,0x08, /* / */ + 0x38,0x44,0x38, /* 0 */ + 0x00,0x7C,0x00, /* 1 */ + 0x64,0x54,0x48, /* 2 */ + 0x44,0x54,0x28, /* 3 */ + 0x1C,0x10,0x7C, /* 4 */ + 0x4C,0x54,0x24, /* 5 */ + 0x38,0x54,0x20, /* 6 */ + 0x04,0x74,0x0C, /* 7 */ + 0x28,0x54,0x28, /* 8 */ + 0x08,0x54,0x38, /* 9 */ + 0x00,0x50,0x00, /* : */ + 0x80,0x50,0x00, /* ; */ + 0x10,0x28,0x44, /* < */ + 0x28,0x28,0x28, /* = */ + 0x44,0x28,0x10, /* > */ + 0x04,0x54,0x08, /* ? */ + 0x38,0x4C,0x5C, /* @ */ + 0x78,0x14,0x78, /* A */ + 0x7C,0x54,0x28, /* B */ + 0x38,0x44,0x44, /* C */ + 0x7C,0x44,0x38, /* D */ + 0x7C,0x54,0x44, /* E */ + 0x7C,0x14,0x04, /* F */ + 0x38,0x44,0x34, /* G */ + 0x7C,0x10,0x7C, /* H */ + 0x00,0x7C,0x00, /* I */ + 0x20,0x40,0x3C, /* J */ + 0x7C,0x10,0x6C, /* K */ + 0x7C,0x40,0x40, /* L */ + 0x7C,0x08,0x7C, /* M */ + 0x7C,0x04,0x7C, /* N */ + 0x7C,0x44,0x7C, /* O */ + 0x7C,0x14,0x08, /* P */ + 0x38,0x44,0x78, /* Q */ + 0x7C,0x14,0x68, /* R */ + 0x48,0x54,0x24, /* S */ + 0x04,0x7C,0x04, /* T */ + 0x7C,0x40,0x7C, /* U */ + 0x3C,0x40,0x3C, /* V */ + 0x7C,0x20,0x7C, /* W */ + 0x6C,0x10,0x6C, /* X */ + 0x1C,0x60,0x1C, /* Y */ + 0x64,0x54,0x4C, /* Z */ + 0x7C,0x44,0x00, /* [ */ + 0x08,0x10,0x20, /* \ */ + 0x44,0x7C,0x00, /* ] */ + 0x08,0x04,0x08, /* ^ */ + 0x80,0x80,0x80, /* _ */ + 0x04,0x08,0x00 /* ` */ +}; + +/* System 5x8 */ +const uint8_t au8FontSystem5x8[]= +{ + 0x00,0x00,0x00,0x00,0x00, /* Space */ + 0x00,0x00,0x4f,0x00,0x00, /* ! */ + 0x00,0x07,0x00,0x07,0x00, /* " */ + 0x14,0x7f,0x14,0x7f,0x14, /* # */ + 0x24,0x2a,0x7f,0x2a,0x12, /* 0x */ + 0x23,0x13,0x08,0x64,0x62, /* % */ + 0x36,0x49,0x55,0x22,0x20, /* & */ + 0x00,0x05,0x03,0x00,0x00, /* ' */ + 0x00,0x1c,0x22,0x41,0x00, /* ( */ + 0x00,0x41,0x22,0x1c,0x00, /* ) */ + 0x14,0x08,0x3e,0x08,0x14, /* // */ + 0x08,0x08,0x3e,0x08,0x08, /* + */ + 0x50,0x30,0x00,0x00,0x00, /* , */ + 0x08,0x08,0x08,0x08,0x08, /* - */ + 0x00,0x60,0x60,0x00,0x00, /* . */ + 0x20,0x10,0x08,0x04,0x02, /* / */ + 0x3e,0x51,0x49,0x45,0x3e, /* 0 */ + 0x00,0x42,0x7f,0x40,0x00, /* 1 */ + 0x42,0x61,0x51,0x49,0x46, /* 2 */ + 0x21,0x41,0x45,0x4b,0x31, /* 3 */ + 0x18,0x14,0x12,0x7f,0x10, /* 4 */ + 0x27,0x45,0x45,0x45,0x39, /* 5 */ + 0x3c,0x4a,0x49,0x49,0x30, /* 6 */ + 0x01,0x71,0x09,0x05,0x03, /* 7 */ + 0x36,0x49,0x49,0x49,0x36, /* 8 */ + 0x06,0x49,0x49,0x29,0x1e, /* 9 */ + 0x00,0x36,0x36,0x00,0x00, /* : */ + 0x00,0x56,0x36,0x00,0x00, /* ; */ + 0x08,0x14,0x22,0x41,0x00, /* < */ + 0x14,0x14,0x14,0x14,0x14, /* = */ + 0x00,0x41,0x22,0x14,0x08, /* > */ + 0x02,0x01,0x51,0x09,0x06, /* ? */ + 0x3e,0x41,0x5d,0x55,0x1e, /* @ */ + 0x7e,0x11,0x11,0x11,0x7e, /* A */ + 0x7f,0x49,0x49,0x49,0x36, /* B */ + 0x3e,0x41,0x41,0x41,0x22, /* C */ + 0x7f,0x41,0x41,0x22,0x1c, /* D */ + 0x7f,0x49,0x49,0x49,0x41, /* E */ + 0x7f,0x09,0x09,0x09,0x01, /* F */ + 0x3e,0x41,0x49,0x49,0x7a, /* G */ + 0x7f,0x08,0x08,0x08,0x7f, /* H */ + 0x00,0x41,0x7f,0x41,0x00, /* I */ + 0x20,0x40,0x41,0x3f,0x01, /* J */ + 0x7f,0x08,0x14,0x22,0x41, /* K */ + 0x7f,0x40,0x40,0x40,0x40, /* L */ + 0x7f,0x02,0x0c,0x02,0x7f, /* M */ + 0x7f,0x04,0x08,0x10,0x7f, /* N */ + 0x3e,0x41,0x41,0x41,0x3e, /* O */ + 0x7f,0x09,0x09,0x09,0x06, /* P */ + 0x3e,0x41,0x51,0x21,0x5e, /* Q */ + 0x7f,0x09,0x19,0x29,0x46, /* R */ + 0x26,0x49,0x49,0x49,0x32, /* S */ + 0x01,0x01,0x7f,0x01,0x01, /* T */ + 0x3f,0x40,0x40,0x40,0x3f, /* U */ + 0x1f,0x20,0x40,0x20,0x1f, /* V */ + 0x3f,0x40,0x38,0x40,0x3f, /* W */ + 0x63,0x14,0x08,0x14,0x63, /* X */ + 0x07,0x08,0x70,0x08,0x07, /* Y */ + 0x61,0x51,0x49,0x45,0x43, /* Z */ + 0x00,0x7f,0x41,0x41,0x00, /* [ */ + 0x02,0x04,0x08,0x10,0x20, /* \ */ + 0x00,0x41,0x41,0x7f,0x00, /* ] */ + 0x04,0x02,0x01,0x02,0x04, /* ^ */ + 0x40,0x40,0x40,0x40,0x40, /* _ */ + 0x00,0x00,0x03,0x05,0x00, /* ` */ + 0x20,0x54,0x54,0x54,0x78, /* a */ + 0x7F,0x44,0x44,0x44,0x38, /* b */ + 0x38,0x44,0x44,0x44,0x44, /* c */ + 0x38,0x44,0x44,0x44,0x7f, /* d */ + 0x38,0x54,0x54,0x54,0x18, /* e */ + 0x04,0x04,0x7e,0x05,0x05, /* f */ + 0x08,0x54,0x54,0x54,0x3c, /* g */ + 0x7f,0x08,0x04,0x04,0x78, /* h */ + 0x00,0x44,0x7d,0x40,0x00, /* i */ + 0x20,0x40,0x44,0x3d,0x00, /* j */ + 0x7f,0x10,0x28,0x44,0x00, /* k */ + 0x00,0x41,0x7f,0x40,0x00, /* l */ + 0x7c,0x04,0x7c,0x04,0x78, /* m */ + 0x7c,0x08,0x04,0x04,0x78, /* n */ + 0x38,0x44,0x44,0x44,0x38, /* o */ + 0x7c,0x14,0x14,0x14,0x08, /* p */ + 0x08,0x14,0x14,0x14,0x7c, /* q */ + 0x7c,0x08,0x04,0x04,0x00, /* r */ + 0x48,0x54,0x54,0x54,0x24, /* s */ + 0x04,0x04,0x3f,0x44,0x44, /* t */ + 0x3c,0x40,0x40,0x20,0x7c, /* u */ + 0x1c,0x20,0x40,0x20,0x1c, /* v */ + 0x3c,0x40,0x30,0x40,0x3c, /* w */ + 0x44,0x28,0x10,0x28,0x44, /* x */ + 0x0c,0x50,0x50,0x50,0x3c, /* y */ + 0x44,0x64,0x54,0x4c,0x44, /* z */ + 0x08,0x36,0x41,0x41,0x00, /* { */ + 0x00,0x00,0x77,0x00,0x00, /* | */ + 0x00,0x41,0x41,0x36,0x08, /* } */ + 0x08,0x08,0x2a,0x1c,0x08, /* <- */ + 0x08,0x1c,0x2a,0x08,0x08, /* -> */ + 0xff,0xff,0xff,0xff,0xff, /*  */ +}; + +/* System 7x8 */ +const uint8_t au8FontSystem7x8[]= +{ + 0, 0, 0, 0, 0, 0, 0, //' ' + 0, 6, 95, 95, 6, 0, 0, //'!' + 0, 7, 7, 0, 7, 7, 0, //'"' + 20, 127, 127, 20, 127, 127, 20, //'#' + 36, 46, 107, 107, 58, 18, 0, //'$' + 70, 102, 48, 24, 12, 102, 98, //'%' + 48, 122, 79, 93, 55, 122, 72, //'&' + 4, 7, 3, 0, 0, 0, 0, //''' + 0, 28, 62, 99, 65, 0, 0, //'(' + 0, 65, 99, 62, 28, 0, 0, //')' + 8, 42, 62, 28, 28, 62, 42, //'*' + 8, 8, 62, 62, 8, 8, 0, //'+' + 0, 128, 224, 96, 0, 0, 0, //',' + 8, 8, 8, 8, 8, 8, 0, //'-' + 0, 0, 96, 96, 0, 0, 0, //'.' + 96, 48, 24, 12, 6, 3, 1, //'/' + 62, 127, 113, 89, 77, 127, 62, //'0' + 64, 66, 127, 127, 64, 64, 0, //'1' + 98, 115, 89, 73, 111, 102, 0, //'2' + 34, 99, 73, 73, 127, 54, 0, //'3' + 24, 28, 22, 83, 127, 127, 80, //'4' + 39, 103, 69, 69, 125, 57, 0, //'5' + 60, 126, 75, 73, 121, 48, 0, //'6' + 3, 3, 113, 121, 15, 7, 0, //'7' + 54, 127, 73, 73, 127, 54, 0, //'8' + 6, 79, 73, 105, 63, 30, 0, //'9' + 0, 0, 102, 102, 0, 0, 0, //':' + 0, 128, 230, 102, 0, 0, 0, //';' + 8, 28, 54, 99, 65, 0, 0, //'<' + 36, 36, 36, 36, 36, 36, 0, //'=' + 0, 65, 99, 54, 28, 8, 0, //'>' + 2, 3, 81, 89, 15, 6, 0, //'?' + 62, 127, 65, 93, 93, 31, 30, //'@' + 124,126, 19, 19, 126, 124, 0, //'A' + 65, 127, 127, 73, 73, 127, 54, //'B' + 28, 62, 99, 65, 65, 99, 34, //'C' + 65, 127, 127, 65, 99, 62, 28, //'D' + 65, 127, 127, 73, 93, 65, 99, //'E' + 65, 127, 127, 73, 29, 1, 3, //'F' + 28, 62, 99, 65, 81, 115, 114, //'G' + 127,127, 8, 8, 127, 127, 0, //'H' + 0, 65, 127, 127, 65, 0, 0, //'I' + 48, 112, 64, 65, 127, 63, 1, //'J' + 65, 127, 127, 8, 28, 119, 99, //'K' + 65, 127, 127, 65, 64, 96, 112, //'L' + 127,127, 14, 28, 14, 127, 127, //'M' + 127,127, 6, 12, 24, 127, 127, //'N' + 28, 62, 99, 65, 99, 62, 28, //'O' + 65, 127, 127, 73, 9, 15, 6, //'P' + 30, 63, 33, 113, 127, 94, 0, //'Q' + 65, 127, 127, 9, 25, 127, 102, //'R' + 38, 111, 77, 89, 115, 50, 0, //'S' + 3, 65, 127, 127, 65, 3, 0, //'T' + 127,127, 64, 64, 127, 127, 0, //'U' + 31, 63, 96, 96, 63, 31, 0, //'V' + 127,127, 48, 24, 48, 127, 127, //'W' + 67, 103, 60, 24, 60, 103, 67, //'X' + 7, 79, 120, 120, 79, 7, 0, //'Y' + 71, 99, 113, 89, 77, 103, 115, //'Z' + 0, 127, 127, 65, 65, 0, 0, //'[' + 1, 3, 6, 12, 24, 48, 96, //'\' + 0, 65, 65, 127, 127, 0, 0, //']' + 8, 12, 6, 3, 6, 12, 8, //'^' + 128,128, 128, 128, 128, 128, 128, //'_' + 0, 0, 3, 7, 4, 0, 0, //'`' + 32, 116, 84, 84, 60, 120, 64, //'a' + 65, 127, 63, 72, 72, 120, 48, //'b' + 56, 124, 68, 68, 108, 40, 0, //'c' + 48, 120, 72, 73, 63, 127, 64, //'d' + 56, 124, 84, 84, 92, 24, 0, //'e' + 72, 126, 127, 73, 3, 2, 0, //'f' + 56, 188, 164, 164, 252, 120, 0, //'g' + 65, 127, 127, 8, 4, 124, 120, //'h' + 0, 68, 125, 125, 64, 0, 0, //'i' + 96, 224, 128, 128, 253, 125, 0, //'j' + 65, 127, 127, 16, 56, 108, 68, //'k' + 0, 65, 127, 127, 64, 0, 0, //'l' + 120,124, 28, 56, 28, 124, 120, //'m' + 124,124, 4, 4, 124, 120, 0, //'n' + 56, 124, 68, 68, 124, 56, 0, //'o' + 0, 252, 252, 164, 36, 60, 24, //'p' + 24, 60, 36, 164, 248, 252, 132, //'q' + 68, 124, 120, 76, 4, 28, 24, //'r' + 72, 92, 84, 84, 116, 36, 0, //'s' + 0, 4, 62, 127, 68, 36, 0, //'t' + 60, 124, 64, 64, 60, 124, 64, //'u' + 28, 60, 96, 96, 60, 28, 0, //'v' + 60, 124, 112, 56, 112, 124, 60, //'w' + 68, 108, 56, 16, 56, 108, 68, //'x' + 60, 188, 160, 160, 252, 124, 0, //'y' + 76, 100, 116, 92, 76, 100, 0, //'z' + 8, 8, 62, 119, 65, 65, 0, //'{' + 0, 0, 0, 119, 119, 0, 0, //'|' + 65, 65, 119, 62, 8, 8, 0, //'}' + 2, 3, 1, 3, 2, 3, 1, //'~' + 255,129, 129, 129, 129, 129, 255, //'' + 14, 159, 145, 177, 251, 74, 0 //'Á' +}; + +/* 8x8 Normal */ +const uint8_t au8Font8x8[]= { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ASCII - 32 + 0x00,0x00,0x00,0x5F,0x5F,0x00,0x00,0x00, // ASCII - 33 + 0x00,0x00,0x03,0x07,0x00,0x07,0x03,0x00, // ASCII - 34 + 0x00,0x10,0x74,0x1C,0x77,0x1C,0x17,0x04, // ASCII - 35 + 0x00,0x24,0x2E,0x2A,0x7F,0x2A,0x3A,0x10, // ASCII - 36 + 0x00,0x4C,0x6A,0x76,0x1A,0x6A,0x56,0x33, // ASCII - 37 + 0x00,0x30,0x7A,0x4F,0x5D,0x37,0x7A,0x48, // ASCII - 38 + 0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00, // ASCII - 39 + 0x00,0x00,0x00,0x1C,0x3E,0x63,0x41,0x00, // ASCII - 40 + 0x00,0x00,0x41,0x63,0x3E,0x1C,0x00,0x00, // ASCII - 41 + 0x00,0x08,0x2A,0x3E,0x1C,0x3E,0x2A,0x08, // ASCII - 42 + 0x00,0x08,0x08,0x3E,0x3E,0x08,0x08,0x00, // ASCII - 43 + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00, // ASCII - 44 + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, // ASCII - 45 + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00, // ASCII - 46 + 0x00,0x60,0x30,0x18,0x0C,0x06,0x03,0x01, // ASCII - 47 + 0x00,0x1C,0x3E,0x61,0x43,0x3E,0x1C,0x00, // ASCII - 48 + 0x00,0x00,0x44,0x7F,0x7F,0x40,0x00,0x00, // ASCII - 49 + 0x00,0x46,0x67,0x71,0x59,0x4F,0x66,0x00, // ASCII - 50 + 0x00,0x22,0x63,0x49,0x4D,0x7F,0x32,0x00, // ASCII - 51 + 0x00,0x18,0x1C,0x52,0x7F,0x7F,0x50,0x00, // ASCII - 52 + 0x00,0x2F,0x6F,0x45,0x45,0x7D,0x39,0x00, // ASCII - 53 + 0x00,0x3C,0x7E,0x4B,0x49,0x79,0x30,0x00, // ASCII - 54 + 0x00,0x07,0x43,0x71,0x7D,0x0F,0x03,0x00, // ASCII - 55 + 0x00,0x36,0x7F,0x4D,0x59,0x7F,0x36,0x00, // ASCII - 56 + 0x00,0x06,0x4F,0x49,0x69,0x3F,0x1E,0x00, // ASCII - 57 + 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00, // ASCII - 58 + 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00, // ASCII - 59 + 0x00,0x00,0x08,0x1C,0x36,0x63,0x41,0x00, // ASCII - 60 + 0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00, // ASCII - 61 + 0x00,0x00,0x41,0x63,0x36,0x1C,0x08,0x00, // ASCII - 62 + 0x00,0x02,0x07,0x51,0x59,0x0F,0x06,0x00, // ASCII - 63 + 0x00,0x3E,0x41,0x5D,0x55,0x5D,0x51,0x1E, // ASCII - 64 + 0x00,0x40,0x70,0x1D,0x17,0x1F,0x78,0x60, // ASCII - 65 + 0x00,0x41,0x7F,0x7F,0x49,0x4F,0x7E,0x30, // ASCII - 66 + 0x00,0x1C,0x3E,0x63,0x41,0x41,0x42,0x27, // ASCII - 67 + 0x00,0x41,0x7F,0x7F,0x41,0x63,0x3E,0x1C, // ASCII - 68 + 0x00,0x41,0x7F,0x7F,0x49,0x5D,0x41,0x63, // ASCII - 69 + 0x00,0x41,0x7F,0x7F,0x49,0x1D,0x01,0x03, // ASCII - 70 + 0x00,0x1C,0x3E,0x63,0x41,0x51,0x72,0x77, // ASCII - 71 + 0x00,0x7F,0x7F,0x08,0x08,0x7F,0x7F,0x00, // ASCII - 72 + 0x00,0x00,0x41,0x7F,0x7F,0x41,0x00,0x00, // ASCII - 73 + 0x00,0x30,0x70,0x41,0x41,0x7F,0x3F,0x01, // ASCII - 74 + 0x00,0x7F,0x7F,0x08,0x1C,0x77,0x63,0x41, // ASCII - 75 + 0x00,0x41,0x7F,0x7F,0x41,0x40,0x60,0x70, // ASCII - 76 + 0x00,0x7F,0x7E,0x0C,0x18,0x0C,0x7E,0x7F, // ASCII - 77 + 0x00,0x7F,0x7E,0x0C,0x18,0x30,0x7F,0x7F, // ASCII - 78 + 0x00,0x1C,0x3E,0x63,0x41,0x63,0x3E,0x1C, // ASCII - 79 + 0x00,0x41,0x7F,0x7F,0x49,0x09,0x0F,0x06, // ASCII - 80 + 0x00,0x1C,0x3E,0x63,0x51,0x63,0x3E,0x1C, // ASCII - 81 + 0x00,0x7F,0x7F,0x09,0x19,0x7F,0x66,0x40, // ASCII - 82 + 0x00,0x66,0x6F,0x4D,0x59,0x7B,0x33,0x00, // ASCII - 83 + 0x00,0x03,0x41,0x7F,0x7F,0x41,0x03,0x00, // ASCII - 84 + 0x00,0x3F,0x7F,0x40,0x40,0x40,0x7F,0x3F, // ASCII - 85 + 0x00,0x03,0x0F,0x3D,0x70,0x1D,0x07,0x01, // ASCII - 86 + 0x00,0x0F,0x7F,0x30,0x1C,0x30,0x7F,0x0F, // ASCII - 87 + 0x00,0x63,0x77,0x1C,0x1C,0x77,0x63,0x00, // ASCII - 88 + 0x01,0x03,0x47,0x7C,0x78,0x47,0x03,0x01, // ASCII - 89 + 0x00,0x67,0x73,0x59,0x4D,0x67,0x73,0x00, // ASCII - 90 + 0x00,0x00,0x00,0x7F,0x7F,0x41,0x41,0x00, // ASCII - 91 + 0x00,0x01,0x03,0x06,0x0C,0x18,0x30,0x60, // ASCII - 92 + 0x00,0x00,0x41,0x41,0x7F,0x7F,0x00,0x00, // ASCII - 93 + 0x00,0x00,0x04,0x06,0x03,0x06,0x04,0x00, // ASCII - 94 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ASCII - 95 + 0x00,0x00,0x01,0x03,0x06,0x04,0x00,0x00, // ASCII - 96 + 0x00,0x68,0x6C,0x54,0x54,0x3C,0x78,0x40, // ASCII - 97 + 0x00,0x41,0x7F,0x3F,0x6C,0x44,0x7C,0x38, // ASCII - 98 + 0x00,0x38,0x7C,0x44,0x44,0x6C,0x2C,0x00, // ASCII - 99 + 0x00,0x38,0x7C,0x44,0x49,0x3F,0x7F,0x40, // ASCII - 100 + 0x00,0x38,0x7C,0x54,0x54,0x5C,0x58,0x00, // ASCII - 101 + 0x00,0x00,0x48,0x7E,0x7F,0x49,0x0B,0x02, // ASCII - 102 + 0x00,0x48,0x7C,0x34,0x34,0x2C,0x68,0x44, // ASCII - 103 + 0x00,0x41,0x7F,0x7F,0x08,0x04,0x7C,0x78, // ASCII - 104 + 0x00,0x00,0x44,0x7D,0x7D,0x40,0x00,0x00, // ASCII - 105 + 0x00,0x60,0x60,0x04,0x7D,0x7D,0x00,0x00, // ASCII - 106 + 0x00,0x41,0x7F,0x7F,0x10,0x78,0x6C,0x44, // ASCII - 107 + 0x00,0x00,0x41,0x7F,0x7F,0x40,0x00,0x00, // ASCII - 108 + 0x00,0x7C,0x7C,0x0C,0x78,0x0C,0x7C,0x78, // ASCII - 109 + 0x00,0x44,0x7C,0x7C,0x08,0x04,0x7C,0x78, // ASCII - 110 + 0x00,0x38,0x7C,0x44,0x44,0x7C,0x38,0x00, // ASCII - 111 + 0x00,0x04,0x7C,0x78,0x24,0x24,0x3C,0x18, // ASCII - 112 + 0x00,0x18,0x3C,0x24,0x24,0x78,0x7C,0x00, // ASCII - 113 + 0x00,0x44,0x7C,0x78,0x4C,0x04,0x1C,0x18, // ASCII - 114 + 0x00,0x48,0x5C,0x5C,0x74,0x74,0x24,0x00, // ASCII - 115 + 0x00,0x00,0x04,0x3E,0x7F,0x44,0x24,0x00, // ASCII - 116 + 0x00,0x3C,0x7C,0x40,0x40,0x3C,0x7C,0x40, // ASCII - 117 + 0x00,0x04,0x1C,0x3C,0x60,0x30,0x1C,0x04, // ASCII - 118 + 0x00,0x1C,0x7C,0x30,0x1C,0x30,0x7C,0x1C, // ASCII - 119 + 0x00,0x44,0x6C,0x3C,0x10,0x78,0x6C,0x44, // ASCII - 120 + 0x00,0x44,0x4C,0x1C,0x70,0x64,0x1C,0x0C, // ASCII - 121 + 0x00,0x4C,0x64,0x74,0x5C,0x4C,0x64,0x00, // ASCII - 122 + 0x00,0x08,0x08,0x3E,0x77,0x41,0x41,0x00, // ASCII - 123 + 0x00,0x00,0x00,0x7F,0x7F,0x00,0x00,0x00, // ASCII - 124 + 0x00,0x41,0x41,0x77,0x3E,0x08,0x08,0x00, // ASCII - 125 + 0x00,0x02,0x01,0x01,0x03,0x02,0x02,0x01, // ASCII - 126 + 0x00,0x60,0x78,0x4E,0x47,0x5E,0x78,0x60, // ASCII - 127 + 0x00,0x1C,0x3E,0x23,0x41,0x41,0x42,0x27, // ASCII - 128 + 0x00,0x3D,0x7D,0x40,0x41,0x3D,0x7C,0x40, // ASCII - 129 +}; + +/* 8x8 Thin */ +const uint8_t au8Font8x8Thin[]= { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x00, + 0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00, + 0x00,0x14,0x7F,0x14,0x14,0x7F,0x14,0x00, + 0x00,0x24,0x2A,0x6B,0x6B,0x2A,0x12,0x00, + 0x00,0x46,0x26,0x10,0x08,0x64,0x62,0x00, + 0x30,0x4A,0x45,0x4D,0x32,0x48,0x48,0x00, + 0x00,0x00,0x04,0x03,0x00,0x00,0x00,0x00, + 0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00, + 0x00,0x00,0x41,0x22,0x1C,0x00,0x00,0x00, + 0x08,0x2A,0x1C,0x1C,0x1C,0x2A,0x08,0x00, + 0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00, + 0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00, + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, + 0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00, + 0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x00, + 0x00,0x3E,0x61,0x51,0x49,0x45,0x3E,0x00, + 0x00,0x44,0x42,0x7F,0x40,0x40,0x00,0x00, + 0x00,0x62,0x51,0x51,0x49,0x49,0x66,0x00, + 0x00,0x22,0x41,0x49,0x49,0x49,0x36,0x00, + 0x10,0x18,0x14,0x52,0x7F,0x50,0x10,0x00, + 0x00,0x27,0x45,0x45,0x45,0x45,0x39,0x00, + 0x00,0x3C,0x4A,0x49,0x49,0x49,0x30,0x00, + 0x00,0x03,0x01,0x71,0x09,0x05,0x03,0x00, + 0x00,0x36,0x49,0x49,0x49,0x49,0x36,0x00, + 0x00,0x06,0x49,0x49,0x49,0x29,0x1E,0x00, + 0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x00, + 0x00,0x00,0x80,0x66,0x00,0x00,0x00,0x00, + 0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00, + 0x00,0x24,0x24,0x24,0x24,0x24,0x24,0x00, + 0x00,0x00,0x00,0x41,0x22,0x14,0x08,0x00, + 0x00,0x02,0x01,0x01,0x51,0x09,0x06,0x00, + 0x00,0x3E,0x41,0x5D,0x55,0x55,0x1E,0x00, + 0x00,0x7C,0x12,0x11,0x11,0x12,0x7C,0x00, + 0x00,0x41,0x7F,0x49,0x49,0x49,0x36,0x00, + 0x00,0x1C,0x22,0x41,0x41,0x41,0x22,0x00, + 0x00,0x41,0x7F,0x41,0x41,0x22,0x1C,0x00, + 0x00,0x41,0x7F,0x49,0x5D,0x41,0x63,0x00, + 0x00,0x41,0x7F,0x49,0x1D,0x01,0x03,0x00, + 0x00,0x1C,0x22,0x41,0x51,0x51,0x72,0x00, + 0x00,0x7F,0x08,0x08,0x08,0x08,0x7F,0x00, + 0x00,0x00,0x41,0x7F,0x41,0x00,0x00,0x00, + 0x00,0x30,0x40,0x40,0x41,0x3F,0x01,0x00, + 0x00,0x41,0x7F,0x08,0x14,0x22,0x41,0x40, + 0x00,0x41,0x7F,0x41,0x40,0x40,0x60,0x00, + 0x00,0x7F,0x01,0x02,0x04,0x02,0x01,0x7F, + 0x00,0x7F,0x01,0x02,0x04,0x08,0x7F,0x00, + 0x00,0x3E,0x41,0x41,0x41,0x41,0x3E,0x00, + 0x00,0x41,0x7F,0x49,0x09,0x09,0x06,0x00, + 0x00,0x1E,0x21,0x21,0x31,0x21,0x5E,0x40, + 0x00,0x41,0x7F,0x49,0x19,0x29,0x46,0x00, + 0x00,0x26,0x49,0x49,0x49,0x49,0x32,0x00, + 0x00,0x03,0x01,0x41,0x7F,0x41,0x01,0x03, + 0x00,0x3F,0x40,0x40,0x40,0x40,0x3F,0x00, + 0x00,0x0F,0x10,0x20,0x40,0x20,0x10,0x0F, + 0x00,0x3F,0x40,0x40,0x38,0x40,0x40,0x3F, + 0x00,0x41,0x22,0x14,0x08,0x14,0x22,0x41, + 0x00,0x01,0x02,0x44,0x78,0x44,0x02,0x01, + 0x00,0x43,0x61,0x51,0x49,0x45,0x43,0x61, + 0x00,0x7F,0x41,0x41,0x41,0x00,0x00,0x00, + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x00, + 0x00,0x41,0x41,0x41,0x7F,0x00,0x00,0x00, + 0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x00,0x00,0x00,0x03,0x04,0x00,0x00,0x00, + 0x00,0x20,0x54,0x54,0x54,0x54,0x78,0x40, + 0x00,0x01,0x7F,0x30,0x48,0x48,0x48,0x30, + 0x00,0x38,0x44,0x44,0x44,0x44,0x28,0x00, + 0x00,0x30,0x48,0x48,0x48,0x31,0x7F,0x40, + 0x00,0x38,0x54,0x54,0x54,0x54,0x18,0x00, + 0x00,0x00,0x48,0x7E,0x49,0x01,0x02,0x00, + 0x00,0x98,0xA4,0xA4,0xA4,0xA4,0x78,0x04, + 0x00,0x41,0x7F,0x08,0x04,0x04,0x78,0x00, + 0x00,0x00,0x44,0x7D,0x40,0x00,0x00,0x00, + 0x00,0x60,0x80,0x80,0x80,0x84,0x7D,0x00, + 0x00,0x01,0x7F,0x10,0x28,0x44,0x40,0x00, + 0x00,0x00,0x41,0x7F,0x40,0x00,0x00,0x00, + 0x00,0x7C,0x04,0x04,0x78,0x04,0x04,0x78, + 0x00,0x7C,0x08,0x04,0x04,0x04,0x78,0x00, + 0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00, + 0x00,0x84,0xFC,0x98,0x24,0x24,0x18,0x00, + 0x00,0x18,0x24,0x24,0x98,0xFC,0x84,0x00, + 0x00,0x44,0x7C,0x48,0x04,0x04,0x18,0x00, + 0x00,0x48,0x54,0x54,0x54,0x54,0x24,0x00, + 0x00,0x04,0x04,0x3F,0x44,0x44,0x20,0x00, + 0x00,0x3C,0x40,0x40,0x40,0x20,0x7C,0x00, + 0x00,0x0C,0x10,0x20,0x40,0x20,0x10,0x0C, + 0x00,0x3C,0x40,0x40,0x38,0x40,0x40,0x3C, + 0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00, + 0x00,0x9C,0xA0,0xA0,0xA0,0xA0,0x7C,0x00, + 0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00, + 0x00,0x08,0x08,0x36,0x41,0x41,0x00,0x00, + 0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x00, + 0x00,0x00,0x41,0x41,0x36,0x08,0x08,0x00, + 0x00,0x02,0x01,0x01,0x02,0x02,0x01,0x00, + 0x00,0x70,0x48,0x44,0x42,0x44,0x48,0x70, + 0x00,0x0E,0x91,0x91,0xB1,0xB1,0x4A,0x00, + 0x00,0x3A,0x40,0x40,0x40,0x7A,0x40,0x00, + 0x00,0x38,0x54,0x54,0x55,0x55,0x18,0x00, + 0x00,0x22,0x55,0x55,0x55,0x79,0x42,0x00, + 0x00,0x21,0x54,0x54,0x54,0x78,0x41,0x00, + 0x00,0x20,0x55,0x55,0x54,0x78,0x40,0x00, + 0x00,0x20,0x54,0x55,0x54,0x78,0x40,0x00, + 0x00,0x18,0x24,0xA4,0xA4,0xE4,0x40,0x00, + 0x00,0x3A,0x55,0x55,0x55,0x55,0x1A,0x00, + 0x00,0x39,0x54,0x54,0x54,0x54,0x19,0x00, + 0x00,0x38,0x55,0x55,0x54,0x54,0x18,0x00, + 0x00,0x00,0x01,0x44,0x7C,0x41,0x00,0x00, + 0x02,0x01,0x45,0x7D,0x41,0x01,0x02,0x00, + 0x00,0x00,0x01,0x45,0x7C,0x40,0x00,0x00, + 0x00,0x79,0x14,0x12,0x12,0x14,0x79,0x00, + 0x00,0x70,0x28,0x2B,0x2B,0x28,0x70,0x00, + 0x00,0x44,0x7C,0x54,0x55,0x45,0x00,0x00, + 0x00,0x20,0x54,0x54,0x58,0x38,0x54,0x54, + 0x00,0x7C,0x0A,0x09,0x09,0x7F,0x49,0x49, + 0x00,0x30,0x4A,0x49,0x49,0x4A,0x30,0x00, + 0x00,0x32,0x48,0x48,0x48,0x48,0x32,0x00, + 0x00,0x30,0x49,0x4A,0x48,0x48,0x30,0x00, + 0x00,0x38,0x42,0x41,0x41,0x42,0x38,0x00, + 0x00,0x38,0x41,0x42,0x40,0x40,0x38,0x00, + 0x00,0x1A,0xA0,0xA0,0xA0,0xA0,0x7A,0x00, + 0x00,0x19,0x24,0x42,0x42,0x24,0x19,0x00, + 0x00,0x3D,0x40,0x40,0x40,0x40,0x3D,0x00, + 0x00,0x18,0x24,0x24,0xE7,0x24,0x24,0x00, + 0x00,0x68,0x5E,0x49,0x41,0x42,0x20,0x00, + 0x00,0x15,0x16,0x7C,0x16,0x15,0x00,0x00, + 0x81,0xFF,0x85,0x05,0x17,0xFA,0x90,0x50, + 0x40,0x88,0x88,0x7F,0x09,0x09,0x02,0x00, + 0x00,0x20,0x54,0x54,0x55,0x79,0x40,0x00, +}; diff --git a/drivers/lcd/smallfonts.h b/drivers/lcd/smallfonts.h new file mode 100644 index 0000000..e0c2a9f --- /dev/null +++ b/drivers/lcd/smallfonts.h @@ -0,0 +1,68 @@ +/**************************************************************************/ +/*! + @file smallfonts.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __SMALLFONTS_H_ +#define __SMALLFONTS_H_ + +/* Partially based on original code for the KS0108 by Stephane Rey */ +/* Current version by Kevin Townsend */ +/* Last Updated: 12 May 2009 */ + +#include "projectconfig.h" + +struct FONT_DEF +{ + uint8_t u8Width; /* Character width for storage */ + uint8_t u8Height; /* Character height for storage */ + uint8_t u8FirstChar; /* The first character available */ + uint8_t u8LastChar; /* The last character available */ + const uint8_t *au8FontTable; /* Font table start address in memory */ +}; + +extern const struct FONT_DEF Font_System3x6; +extern const struct FONT_DEF Font_System5x8; +extern const struct FONT_DEF Font_System7x8; +extern const struct FONT_DEF Font_8x8; +extern const struct FONT_DEF Font_8x8Thin; + +extern const uint8_t au8FontSystem3x6[]; +extern const uint8_t au8FontSystem5x8[]; +extern const uint8_t au8FontSystem7x8[]; +extern const uint8_t au8Font8x8[]; +extern const uint8_t au8Font8x8Thin[]; + +#endif diff --git a/drivers/lcd/tft/bmp.c b/drivers/lcd/tft/bmp.c new file mode 100644 index 0000000..60d1ebf --- /dev/null +++ b/drivers/lcd/tft/bmp.c @@ -0,0 +1,351 @@ +/**************************************************************************/ +/*! + @file bmp.c + @author K. Townsend (microBuilder.eu) + + @brief Loads uncomprssed 24-bit windows bitmaps images + + Based on the information available at: + http://local.wasp.uwa.edu.au/~pbourke/dataformats/bmp/ + and some sample code written by Michael Sweet + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "bmp.h" + +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/lcd.h" + +// Only include read support if CFG_SDCARD is defined +#ifdef CFG_SDCARD + #include "drivers/fatfs/diskio.h" + #include "drivers/fatfs/ff.h" + static FATFS Fatfs[1]; + #if defined CFG_SDCARD_READONLY && CFG_SDCARD_READONLY == 0 + static FILINFO Finfo; + static FIL bmpSDFile; + #endif + +/**************************************************************************/ +/* */ +/* ----------------------- Private Methods ------------------------------ */ +/* */ +/**************************************************************************/ +bmp_error_t bmpParseBitmap(uint16_t x, uint16_t y, FIL file) +{ + UINT bytesRead; + bmp_header_t header; + bmp_infoheader_t infoHeader; + + // Read the file header + // ToDo: Optimise this to read buffer once and parse it + f_read(&file, &header.type, sizeof(header.type), &bytesRead); + f_read(&file, &header.size, sizeof(header.size), &bytesRead); + f_read(&file, &header.reserved1, sizeof(header.reserved1), &bytesRead); + f_read(&file, &header.reserved2, sizeof(header.reserved2), &bytesRead); + f_read(&file, &header.offset, sizeof(header.offset), &bytesRead); + + // Make sure this is a bitmap (first two bytes = 'BM' or 0x4D42 on little-endian systems) + if (header.type != 0x4D42) return BMP_ERROR_NOTABITMAP; + + // Read the info header + // ToDo: Optimise this to read buffer once and parse it + f_read(&file, &infoHeader.size, sizeof(infoHeader.size), &bytesRead); + f_read(&file, &infoHeader.width, sizeof(infoHeader.width), &bytesRead); + f_read(&file, &infoHeader.height, sizeof(infoHeader.height), &bytesRead); + f_read(&file, &infoHeader.planes, sizeof(infoHeader.planes), &bytesRead); + f_read(&file, &infoHeader.bits, sizeof(infoHeader.bits), &bytesRead); + f_read(&file, &infoHeader.compression, sizeof(infoHeader.compression), &bytesRead); + f_read(&file, &infoHeader.imagesize, sizeof(infoHeader.imagesize), &bytesRead); + f_read(&file, &infoHeader.xresolution, sizeof(infoHeader.xresolution), &bytesRead); + f_read(&file, &infoHeader.yresolution, sizeof(infoHeader.yresolution), &bytesRead); + f_read(&file, &infoHeader.ncolours, sizeof(infoHeader.ncolours), &bytesRead); + f_read(&file, &infoHeader.importantcolours, sizeof(infoHeader.importantcolours), &bytesRead); + + // Make sure that this is a 24-bit image + if (infoHeader.bits != 24) + return BMP_ERROR_INVALIDBITDEPTH; + + // Check image dimensions + if ((infoHeader.width > lcdGetWidth()) | (infoHeader.height > lcdGetHeight())) + return BMP_ERROR_INVALIDDIMENSIONS; + + // Make sure image is not compressed + if (infoHeader.compression != BMP_COMPRESSION_NONE) + return BMP_ERROR_COMPRESSEDDATA; + + // Read 24-bit image data + uint32_t px, py; + FRESULT res; + uint8_t buffer[infoHeader.width * 3]; + for (py = infoHeader.height; py > 0; py--) + { + // Read one row at a time + res = f_read(&file, &buffer, infoHeader.width * 3, &bytesRead); + if (res || bytesRead == 0) + { + // Error or EOF + return BMP_ERROR_PREMATUREEOF; + } + for (px = 0; px < infoHeader.width; px++) + { + // Render pixel + // ToDo: This is a brutally slow way of rendering bitmaps ... + // update to pass one row of data at a time + drawPixel(x + px, y + py - 1, drawRGB24toRGB565(buffer[(px * 3) + 2], buffer[(px * 3) + 1], buffer[(px * 3)])); + } + } + + return BMP_ERROR_NONE; +} + +/**************************************************************************/ +/* */ +/* ----------------------- Public Methods ------------------------------- */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Loads a 24-bit Windows bitmap image from an SD card and + renders it + + @section Example + + @code + + #include "drivers/lcd/tft/bmp.h" + + bmp_error_t error; + + // Draw image.bmp (from the root folder) starting at pixel 0,0 + error = bmpDrawBitmap(0, 0, "/image.bmp"); + + // Check 'error' for problems such as BMP_ERROR_FILENOTFOUND + + @endcode +*/ +/**************************************************************************/ +bmp_error_t bmpDrawBitmap(uint16_t x, uint16_t y, const char* filename) +{ + bmp_error_t error = BMP_ERROR_NONE; + DSTATUS stat; + BYTE res; + + stat = disk_initialize(0); + + if ((stat & STA_NOINIT) || (stat & STA_NODISK)) + { + // Card not initialised or no disk present + return BMP_ERROR_SDINITFAIL; + } + + if (stat == 0) + { + // Try to mount drive + res = f_mount(0, &Fatfs[0]); + if (res != FR_OK) + { + // Failed to mount 0: + return BMP_ERROR_SDINITFAIL; + } + if (res == FR_OK) + { + // Try to open the requested file + FIL imgfile; + if(f_open(&imgfile, filename, FA_READ | FA_OPEN_EXISTING) != FR_OK) + { + // Unable to open the requested file + return BMP_ERROR_FILENOTFOUND; + } + // Try to render the specified image + error = bmpParseBitmap(x, y, imgfile); + // Close file + f_close(&imgfile); + // Unmount drive + f_mount(0,0); + // Return results + return error; + } + } + + // Return OK signal + return BMP_ERROR_NONE; +} + +#if defined CFG_SDCARD_READONLY && CFG_SDCARD_READONLY == 0 +/**************************************************************************/ +/*! + @brief Writes the contents of the LCD screen to a 24-bit bitmap + images. CFG_SDCARD_READONLY must be set to '0' to be able + to use this function. + + @section Example + + @code + + #include "drivers/lcd/tft/bmp.h" + + bmp_error_t error; + + // Note: The LED stays on while the image is being written since + // it can take quite a while to read the entire screen + // pixel by pixel and write the data to the SD card + + // Turn the LED on to signal busy state + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + // Write the screen contents to a bitmap image + error = bmpSaveScreenshot("capture.bmp"); + // Turn the LED off to indicate that the capture is complete + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + + // Check 'error' for problems + + @endcode +*/ +/**************************************************************************/ +bmp_error_t bmpSaveScreenshot(const char* filename) +{ + DSTATUS stat; + bmp_error_t error = BMP_ERROR_NONE; + bmp_header_t header; + bmp_infoheader_t infoHeader; + uint32_t lcdWidth, lcdHeight, x, y, bgra32; + UINT bytesWritten; + uint16_t rgb565, eof; + uint8_t r, g, b; + + // Create a new file (Crossworks only) + stat = disk_initialize(0); + if (stat & STA_NOINIT) + { + return BMP_ERROR_SDINITFAIL; + } + if (stat & STA_NODISK) + { + return BMP_ERROR_SDINITFAIL; + } + if (stat == 0) + { + // SD card sucessfully initialised + DSTATUS stat; + DWORD p2; + WORD w1; + BYTE res, b1; + DIR dir; + // Try to mount drive + res = f_mount(0, &Fatfs[0]); + if (res != FR_OK) + { + return BMP_ERROR_SDINITFAIL; + } + if (res == FR_OK) + { + // Create a file (overwriting any existing file!) + if(f_open(&bmpSDFile, filename, FA_READ | FA_WRITE | FA_CREATE_ALWAYS)!=FR_OK) + { + return BMP_ERROR_UNABLETOCREATEFILE; + } + } + } + + lcdWidth = lcdGetWidth(); + lcdHeight = lcdGetHeight(); + + // Create header + header.type = 0x4d42; // 'BM' + header.size = (lcdWidth * lcdHeight * 3) + sizeof(header) + sizeof(infoHeader); // File size in bytes + header.reserved1 = 0; + header.reserved2 = 0; + header.offset = 0x36; // Offset in bytes to the image data + + // Create infoheader + infoHeader.size = sizeof(infoHeader); + infoHeader.width = lcdWidth; + infoHeader.height = lcdHeight; + infoHeader.planes = 1; + infoHeader.bits = 24; + infoHeader.compression = BMP_COMPRESSION_NONE; + infoHeader.imagesize = (lcdWidth * lcdHeight * 3) + 2; // 3 bytes per pixel + 2 bytes for EOF + infoHeader.xresolution = 0x0B12; + infoHeader.yresolution = 0x0B12; + infoHeader.ncolours = 0; + infoHeader.importantcolours = 0; + + // Write header to disk + f_write(&bmpSDFile, &header.type, sizeof(header.type), &bytesWritten); + f_write(&bmpSDFile, &header.size, sizeof(header.size), &bytesWritten); + f_write(&bmpSDFile, &header.reserved1, sizeof(header.reserved1), &bytesWritten); + f_write(&bmpSDFile, &header.reserved2, sizeof(header.reserved2), &bytesWritten); + f_write(&bmpSDFile, &header.offset, sizeof(header.offset), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.size, sizeof(infoHeader.size), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.width, sizeof(infoHeader.width), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.height, sizeof(infoHeader.height), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.planes, sizeof(infoHeader.planes), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.bits, sizeof(infoHeader.bits), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.compression, sizeof(infoHeader.compression), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.imagesize, sizeof(infoHeader.imagesize), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.xresolution, sizeof(infoHeader.xresolution), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.yresolution, sizeof(infoHeader.yresolution), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.ncolours, sizeof(infoHeader.ncolours), &bytesWritten); + f_write(&bmpSDFile, &infoHeader.importantcolours, sizeof(infoHeader.importantcolours), &bytesWritten); + + // Write image data to disk (starting from bottom row) + for (y = lcdHeight; y != 0; y--) + { + for (x = 0; x < lcdWidth; x++) + { + rgb565 = lcdGetPixel(x, y - 1); // Get RGB565 pixel + bgra32 = drawRGB565toBGRA32(rgb565); // Convert RGB565 to 24-bit color + r = (bgra32 & 0x00FF0000) >> 16; + g = (bgra32 & 0x0000FF00) >> 8; + b = (bgra32 & 0x000000FF); + f_write(&bmpSDFile, &b, 1, &bytesWritten); // Write RGB data + f_write(&bmpSDFile, &g, 1, &bytesWritten); + f_write(&bmpSDFile, &r, 1, &bytesWritten); + } + } + + // Write EOF (2 bytes) + eof = 0x0000; + f_write(&bmpSDFile, &eof, 2, &bytesWritten); + + // Close the file + f_close(&bmpSDFile); + + // Return OK signal + return BMP_ERROR_NONE; +} +#endif // End of read-only check to write bitmaps + +#endif // End of CFG_SDCARD check diff --git a/drivers/lcd/tft/bmp.h b/drivers/lcd/tft/bmp.h new file mode 100644 index 0000000..f734047 --- /dev/null +++ b/drivers/lcd/tft/bmp.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/*! + @file bmp.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __BMP_H__ +#define __BMP_H__ + +#include "projectconfig.h" + +/************************************************************************** + Windows Bitmap File Format + ----------------------------------------------------------------------- + Windows bitmap images are relatively easy to work with because the + format is basic and requires limited overhead to work with (in + practice, image data is very rarely compressed). Bitmap files have + the following structure: + + -------------------------- + | Header | 14 bytes + |------------------------- + | Info Header | 40 bytes + ----------------------- + | Palette (optional) | + |------------------------- + | Image Data | + -------------------------- + + For more information on the bitmap file format, see: + http://local.wasp.uwa.edu.au/~pbourke/dataformats/bmp/ + + **************************************************************************/ + + +/**************************************************************************/ +/*! + @brief 14-byte Windows bitmap header +*/ +/**************************************************************************/ +typedef struct +{ + uint16_t type; /* Magic identifier */ + uint32_t size; /* File size in bytes */ + uint16_t reserved1; + uint16_t reserved2; + uint32_t offset; /* Offset to image data, bytes */ +} bmp_header_t; + +/**************************************************************************/ +/*! + @brief 40-byte Windows bitmap info header +*/ +/**************************************************************************/ +typedef struct +{ + uint32_t size; /* Header size in bytes */ + int32_t width; /* Width of the image */ + int32_t height; /* Height of image */ + uint16_t planes; /* Number of colour planes */ + uint16_t bits; /* Bits per pixel */ + uint32_t compression; /* Compression type */ + uint32_t imagesize; /* Image size in bytes */ + int32_t xresolution; /* Pixels per meter */ + int32_t yresolution; /* Pixels per meter */ + uint32_t ncolours; /* Number of colours */ + uint32_t importantcolours; /* Important colours */ +} bmp_infoheader_t; + +/**************************************************************************/ +/*! + @brief Describes the different compression methods available in + Windows bitmap images, though compression is not currently + supported by this code. +*/ +/**************************************************************************/ +typedef enum +{ + BMP_COMPRESSION_NONE = 0, + BMP_COMPRESSION_RLE8 = 1, + BMP_COMPRESSION_RLE4 = 2, + BMP_COMPRESSION_RGBMASK = 3 +} bmp_compression_t; + +/**************************************************************************/ +/*! + @brief 24-bit pixel data +*/ +/**************************************************************************/ +typedef struct +{ + uint8_t rgbBlue; /* Blue value */ + uint8_t rgbGreen; /* Green value */ + uint8_t rgbRed; /* Red value */ +} bmp_rgbdata_t; + +/**************************************************************************/ +/*! + @brief Error return codes when processing bitmap images +*/ +/**************************************************************************/ +typedef enum +{ + BMP_ERROR_NONE = 0, + BMP_ERROR_SDINITFAIL = 1, + BMP_ERROR_FILENOTFOUND = 2, + BMP_ERROR_UNABLETOCREATEFILE = 3, + BMP_ERROR_NOTABITMAP = 10, /* First two bytes of the image not 'BM' */ + BMP_ERROR_INVALIDBITDEPTH = 11, /* Image is not 24-bits */ + BMP_ERROR_COMPRESSEDDATA = 12, /* Image contains compressed data (not supported) */ + BMP_ERROR_INVALIDDIMENSIONS = 13, /* Image is > CFG_TFTLCD_WIDTH pixels wide or > CFG_TFTLCD_HEIGHT pixels high */ + BMP_ERROR_PREMATUREEOF = 14 /* EOF reached unexpectedly in pixel data */ +} bmp_error_t; + +bmp_error_t bmpDrawBitmap(uint16_t x, uint16_t y, const char* filename); + +#if defined CFG_SDCARD_READONLY && CFG_SDCARD_READONLY == 0 +bmp_error_t bmpSaveScreenshot(const char* filename); +#endif + +#endif diff --git a/drivers/lcd/tft/colors.h b/drivers/lcd/tft/colors.h new file mode 100644 index 0000000..eb39265 --- /dev/null +++ b/drivers/lcd/tft/colors.h @@ -0,0 +1,94 @@ +/**************************************************************************/ +/*! + @file colors.h + @author K. Townsend (microBuilder.eu) + + @brief Common 16-bit RGB565 color definitions + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __COLORS_H__ +#define __COLORS_H__ + +#include "projectconfig.h" + +// Basic Color definitions +#define COLOR_BLACK (uint16_t)(0x0000) +#define COLOR_BLUE (uint16_t)(0x001F) +#define COLOR_RED (uint16_t)(0xF800) +#define COLOR_GREEN (uint16_t)(0x07E0) +#define COLOR_CYAN (uint16_t)(0x07FF) +#define COLOR_MAGENTA (uint16_t)(0xF81F) +#define COLOR_YELLOW (uint16_t)(0xFFE0) +#define COLOR_WHITE (uint16_t)(0xFFFF) + +// Grayscale Values +#define COLOR_GRAY_15 (uint16_t)(0x0861) // 15 15 15 +#define COLOR_GRAY_30 (uint16_t)(0x18E3) // 30 30 30 +#define COLOR_GRAY_50 (uint16_t)(0x3186) // 50 50 50 +#define COLOR_GRAY_80 (uint16_t)(0x528A) // 80 80 80 +#define COLOR_GRAY_128 (uint16_t)(0x8410) // 128 128 128 +#define COLOR_GRAY_200 (uint16_t)(0xCE59) // 200 200 200 +#define COLOR_GRAY_225 (uint16_t)(0xE71C) // 225 225 225 + +// Color Palettes +#define COLOR_THEME_LIMEGREEN_BASE (uint16_t)(0xD7F0) // 211 255 130 +#define COLOR_THEME_LIMEGREEN_DARKER (uint16_t)(0x8DE8) // 137 188 69 +#define COLOR_THEME_LIMEGREEN_LIGHTER (uint16_t)(0xEFF9) // 238 255 207 +#define COLOR_THEME_LIMEGREEN_SHADOW (uint16_t)(0x73EC) // 119 127 103 +#define COLOR_THEME_LIMEGREEN_ACCENT (uint16_t)(0xAE6D) // 169 204 104 + +#define COLOR_THEME_VIOLET_BASE (uint16_t)(0x8AEF) // 143 94 124 +#define COLOR_THEME_VIOLET_DARKER (uint16_t)(0x4187) // 66 49 59 +#define COLOR_THEME_VIOLET_LIGHTER (uint16_t)(0xC475) // 194 142 174 +#define COLOR_THEME_VIOLET_SHADOW (uint16_t)(0x40E6) // 66 29 52 +#define COLOR_THEME_VIOLET_ACCENT (uint16_t)(0xC992) // 204 50 144 + +#define COLOR_THEME_EARTHY_BASE (uint16_t)(0x6269) // 97 79 73 +#define COLOR_THEME_EARTHY_DARKER (uint16_t)(0x3103) // 48 35 31 +#define COLOR_THEME_EARTHY_LIGHTER (uint16_t)(0x8C30) // 140 135 129 +#define COLOR_THEME_EARTHY_SHADOW (uint16_t)(0xAB29) // 173 102 79 +#define COLOR_THEME_EARTHY_ACCENT (uint16_t)(0xFE77) // 250 204 188 + +#define COLOR_THEME_SKYBLUE_BASE (uint16_t)(0x95BF) // 150 180 255 +#define COLOR_THEME_SKYBLUE_DARKER (uint16_t)(0x73B0) // 113 118 131 +#define COLOR_THEME_SKYBLUE_LIGHTER (uint16_t)(0xE75F) // 227 235 255 +#define COLOR_THEME_SKYBLUE_SHADOW (uint16_t)(0x4ACF) // 75 90 127 +#define COLOR_THEME_SKYBLUE_ACCENT (uint16_t)(0xB5F9) // 182 188 204 + +// Using these values allows you to update the entire UI color scheme in one location +#define COLOR_THEME_DEFAULT_BASE COLOR_THEME_LIMEGREEN_BASE +#define COLOR_THEME_DEFAULT_DARKER COLOR_THEME_LIMEGREEN_DARKER +#define COLOR_THEME_DEFAULT_LIGHTER COLOR_THEME_LIMEGREEN_LIGHTER +#define COLOR_THEME_DEFAULT_SHADOW COLOR_THEME_LIMEGREEN_SHADOW +#define COLOR_THEME_DEFAULT_ACCENT COLOR_THEME_LIMEGREEN_ACCENT + +#endif diff --git a/drivers/lcd/tft/dialogues/alphanumeric.c b/drivers/lcd/tft/dialogues/alphanumeric.c new file mode 100644 index 0000000..db22c32 --- /dev/null +++ b/drivers/lcd/tft/dialogues/alphanumeric.c @@ -0,0 +1,364 @@ +/**************************************************************************/ +/*! + @file alphanumeric.c + @author K. Townsend (microBuilder.eu) + + @brief Shows an alpha-numeric input dialogue + + @section Example + + @code + #include "drivers/lcd/tft/dialogues/alphanumeric.h" + + // Print results from an alpha-numeric dialogue + char* results = alphaShowDialogue(); + printf("%s\r\n", results); + + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "alphanumeric.h" + +#include "core/systick/systick.h" +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/touchscreen.h" +#include "drivers/lcd/tft/fonts/dejavusans9.h" + +// Background and text input region colors +#define ALPHA_COLOR_BACKGROUND COLOR_GRAY_15 +#define ALPHA_COLOR_INPUTFILL COLOR_GRAY_30 +#define ALPHA_COLOR_INPUTTEXT COLOR_WHITE +// Button colors +#define ALPHA_COLOR_BTN_BORDER COLOR_GRAY_30 +#define ALPHA_COLOR_BTN_FILL COLOR_GRAY_30 +#define ALPHA_COLOR_BTN_FONT COLOR_WHITE +// Active button colors +#define ALPHA_COLOR_BTNEN_BORDER COLOR_THEME_DEFAULT_DARKER +#define ALPHA_COLOR_BTNEN_FILL COLOR_THEME_DEFAULT_BASE +#define ALPHA_COLOR_BTNEN_FONT COLOR_BLACK + +/* This kind of messes with your head, but defining the pixel locations + this way allows the calculator example to be rendered on another + TFT LCD with a different screen resolution or orientation without + having to rewrite all the individual pixel-level code */ + +#define ALPHA_BTN_SPACING (5) +#define ALPHA_BTN_WIDTH ((lcdGetWidth() - (ALPHA_BTN_SPACING * 6)) / 5) +#define ALPHA_KEYPAD_TOP ((lcdGetHeight() / 7) + (ALPHA_BTN_SPACING * 2)) +#define ALPHA_BTN_HEIGHT (((lcdGetHeight() - ALPHA_KEYPAD_TOP) - (ALPHA_BTN_SPACING * 7)) / 6) +// #define ALPHA_BTN_WIDTH ((240 - (ALPHA_BTN_SPACING * 6)) / 5) +// #define ALPHA_KEYPAD_TOP ((320 / 6) + (ALPHA_BTN_SPACING * 2)) +// #define ALPHA_BTN_HEIGHT (((320 - ALPHA_KEYPAD_TOP) - (ALPHA_BTN_SPACING * 7)) / 6) + +/* X/Y positions for buttons */ +#define ALPHA_ROW1_TOP (ALPHA_KEYPAD_TOP + ALPHA_BTN_SPACING) +#define ALPHA_ROW2_TOP (ALPHA_KEYPAD_TOP + ALPHA_BTN_HEIGHT + (ALPHA_BTN_SPACING * 2)) +#define ALPHA_ROW3_TOP (ALPHA_KEYPAD_TOP + ((ALPHA_BTN_HEIGHT * 2) + ((ALPHA_BTN_SPACING) * 3))) +#define ALPHA_ROW4_TOP (ALPHA_KEYPAD_TOP + ((ALPHA_BTN_HEIGHT * 3) + ((ALPHA_BTN_SPACING) * 4))) +#define ALPHA_ROW5_TOP (ALPHA_KEYPAD_TOP + ((ALPHA_BTN_HEIGHT * 4) + ((ALPHA_BTN_SPACING) * 5))) +#define ALPHA_ROW6_TOP (ALPHA_KEYPAD_TOP + ((ALPHA_BTN_HEIGHT * 5) + ((ALPHA_BTN_SPACING) * 6))) +#define ALPHA_COL1_LEFT (ALPHA_BTN_SPACING) +#define ALPHA_COL2_LEFT ((ALPHA_BTN_SPACING * 2) + ALPHA_BTN_WIDTH) +#define ALPHA_COL3_LEFT ((ALPHA_BTN_SPACING * 3) + (ALPHA_BTN_WIDTH * 2)) +#define ALPHA_COL4_LEFT ((ALPHA_BTN_SPACING * 4) + (ALPHA_BTN_WIDTH * 3)) +#define ALPHA_COL5_LEFT ((ALPHA_BTN_SPACING * 5) + (ALPHA_BTN_WIDTH * 4)) + +/* Control which 'page' is currently shown on the keypad */ +static uint8_t alphaPage = 0; + +/* Keeps track of the string contents */ +static uint8_t alphaString[80]; +static uint8_t *alphaString_ptr; + +/* For quick retrieval of button X/Y locqtions */ +uint32_t alphaBtnX[5], alphaBtnY[6]; + +/* Array showing which characters should be displayed on each alphaPage */ +/* You can rearrange the keypad by modifying the array contents below */ +/* -------------------- -------------------- -------------------- -------------------- + A B C D BACK a b c d BACK . , : ; BACK 7 8 9 BACK + E F G H I e f g h i ' " ( ) 4 5 6 + J K L M N j k l m n ? ! { } 7 8 9 + O P Q R S o p q r s # & @ ~ . 0 SPC + T U V W SHFT t u v w SHFT % = / \ SHFT SHFT + X Y Z SPC OK x y z SPC OK + - _ SPC OK OK + -------------------- -------------------- -------------------- -------------------- */ +char alphaKeys[4][6][5] = { { { 'A', 'B', 'C', 'D', '<' }, + { 'E', 'F', 'G', 'H', 'I' }, + { 'J', 'K', 'L', 'M', 'N' }, + { 'O', 'P', 'Q', 'R', 'S' }, + { 'T', 'U', 'V', 'W', '*' }, + { 'X', 'Y', 'Z', ' ', '>' } }, + + { { 'a', 'b', 'c', 'd', '<' }, + { 'e', 'f', 'g', 'h', 'i' }, + { 'j', 'k', 'l', 'm', 'n' }, + { 'o', 'p', 'q', 'r', 's' }, + { 't', 'u', 'v', 'w', '*' }, + { 'x', 'y', 'z', ' ', '>' } }, + + { { '.', ',', ':', ';', '<' }, + { '\'', '\"', '(', ')', ' ' }, + { '?', '!', '{', '}', ' ' }, + { '#', '&', '@', '~', ' ' }, + { '%', '=', '/', '\\', '*' }, + { '+', '-', '_', ' ', '>' } }, + + { { '7', '8', '9', ' ', '<' }, + { '4', '5', '6', ' ', ' ' }, + { '1', '2', '3', ' ', ' ' }, + { '.', '0', ' ', ' ', ' ' }, + { ' ', ' ', ' ', ' ', '*'}, + { ' ', ' ', ' ', ' ', '>' } } }; + +/**************************************************************************/ +/*! + @brief Renders the UI +*/ +/**************************************************************************/ +void alphaRenderButton(uint8_t alphaPage, uint8_t col, uint8_t row, bool selected) +{ + // Set colors depending on button state + uint16_t border, fill, font; + if (selected) + { + border = ALPHA_COLOR_BTNEN_BORDER; + fill = ALPHA_COLOR_BTNEN_FILL; + font = ALPHA_COLOR_BTNEN_FONT; + } + else + { + border = ALPHA_COLOR_BTN_BORDER; + fill = ALPHA_COLOR_BTN_FILL; + font = ALPHA_COLOR_BTN_FONT; + } + + char c = alphaKeys[alphaPage][row][col]; + char key[2] = { alphaKeys[alphaPage][row][col], '\0' }; + // Handle special characters + switch (c) + { + case '<': + // Backspace + drawButton (alphaBtnX[col], alphaBtnY[row], ALPHA_BTN_WIDTH, ALPHA_BTN_HEIGHT, &dejaVuSans9ptFontInfo, 7, border, fill, font, NULL); + drawArrow (alphaBtnX[col] + ALPHA_BTN_WIDTH / 2 - 3, alphaBtnY[row] + ALPHA_BTN_HEIGHT / 2, 7, DRAW_DIRECTION_LEFT, font); + break; + case '*': + // Page Shift + drawButton (alphaBtnX[col], alphaBtnY[row], ALPHA_BTN_WIDTH, ALPHA_BTN_HEIGHT, &dejaVuSans9ptFontInfo, 7, border, fill, font, NULL); + drawArrow (alphaBtnX[col] + ALPHA_BTN_WIDTH / 2, (alphaBtnY[row] + ALPHA_BTN_HEIGHT / 2) - 3, 7, DRAW_DIRECTION_UP, font); + break; + case '>': + // OK + drawButton (alphaBtnX[col], alphaBtnY[row], ALPHA_BTN_WIDTH, ALPHA_BTN_HEIGHT, &dejaVuSans9ptFontInfo, 7, border, fill, font, "OK"); + break; + default: + // Standard character + drawButton (alphaBtnX[col], alphaBtnY[row], ALPHA_BTN_WIDTH, ALPHA_BTN_HEIGHT, &dejaVuSans9ptFontInfo, 7, border, fill, font, key); + break; + } +} + +/**************************************************************************/ +/*! + @brief Renders the UI +*/ +/**************************************************************************/ +void alphaRefreshScreen(void) +{ + uint8_t x, y; + + /* Draw keypad */ + for (y = 0; y < 6; y++) + { + for (x = 0; x < 5; x++) + { + alphaRenderButton(alphaPage, x, y, false); + } + } + + /* Render Text */ + drawRectangleRounded(ALPHA_BTN_SPACING, ALPHA_BTN_SPACING, lcdGetWidth() - 1 - ALPHA_BTN_SPACING, ALPHA_KEYPAD_TOP - ALPHA_BTN_SPACING, ALPHA_COLOR_INPUTFILL, 10, DRAW_ROUNDEDCORNERS_ALL); + drawString(ALPHA_BTN_SPACING * 3, ALPHA_BTN_SPACING * 3, ALPHA_COLOR_INPUTTEXT, &dejaVuSans9ptFontInfo, (char *)&alphaString); +} + +/**************************************************************************/ +/*! + @brief Processes the supplied touch data +*/ +/**************************************************************************/ +char alphaHandleTouchEvent(void) +{ + tsTouchData_t data; + char result = '\0'; + uint8_t row, col; + int32_t tsError = -1; + + // Blocking delay until a valid touch event occurs + while (tsError) + { + tsError = tsWaitForEvent(&data, 0); + } + + // Attempt to convert touch data to char + if ((data.ylcd < ALPHA_ROW1_TOP) || (data.ylcd > ALPHA_ROW6_TOP + ALPHA_BTN_HEIGHT)) + { + return result; + } + + // Get column + if ((data.xlcd > alphaBtnX[0]) && (data.xlcd < alphaBtnX[0] + ALPHA_BTN_WIDTH)) + col = 0; + else if ((data.xlcd > alphaBtnX[1]) && (data.xlcd < alphaBtnX[1] + ALPHA_BTN_WIDTH)) + col = 1; + else if ((data.xlcd > alphaBtnX[2]) && (data.xlcd < alphaBtnX[2] + ALPHA_BTN_WIDTH)) + col = 2; + else if ((data.xlcd > alphaBtnX[3]) && (data.xlcd < alphaBtnX[3] + ALPHA_BTN_WIDTH)) + col = 3; + else if ((data.xlcd > ALPHA_COL5_LEFT) && (data.xlcd < ALPHA_COL5_LEFT + ALPHA_BTN_WIDTH)) + col = 4; + else + return result; + + // Get row + if ((data.ylcd > ALPHA_ROW1_TOP) && (data.ylcd < ALPHA_ROW1_TOP + ALPHA_BTN_HEIGHT)) + row = 0; + else if ((data.ylcd > ALPHA_ROW2_TOP) && (data.ylcd < ALPHA_ROW2_TOP + ALPHA_BTN_HEIGHT)) + row = 1; + else if ((data.ylcd > ALPHA_ROW3_TOP) && (data.ylcd < ALPHA_ROW3_TOP + ALPHA_BTN_HEIGHT)) + row = 2; + else if ((data.ylcd > ALPHA_ROW4_TOP) && (data.ylcd < ALPHA_ROW4_TOP + ALPHA_BTN_HEIGHT)) + row = 3; + else if ((data.ylcd > ALPHA_ROW5_TOP) && (data.ylcd < ALPHA_ROW5_TOP + ALPHA_BTN_HEIGHT)) + row = 4; + else if ((data.ylcd > ALPHA_ROW6_TOP) && (data.ylcd < ALPHA_ROW6_TOP + ALPHA_BTN_HEIGHT)) + row = 5; + else + return result; + + // Match found ... update button and process the results + alphaRenderButton(alphaPage, col, row, true); + result = alphaKeys[alphaPage][row][col]; + + if (result == '<') + { + // Trim character if backspace was pressed + if (alphaString_ptr > alphaString) + { + alphaString_ptr--; + *alphaString_ptr = '\0'; + } + } + else if (result == '*') + { + // Switch page if the shift button was pressed + alphaPage++; + if (alphaPage > 3) + { + alphaPage = 0; + } + // Update the UI + alphaRefreshScreen(); + } + else if (result == '>') + { + // OK button + systickDelay(CFG_TFTLCD_TS_KEYPADDELAY); + return '>'; + } + else + { + // Add text to string buffer + *alphaString_ptr++ = result; + } + + // Brief delay + systickDelay(CFG_TFTLCD_TS_KEYPADDELAY); + + // Return button to deselected state + alphaRefreshScreen(); + return result; +} + +/**************************************************************************/ +/*! + @brief Displays the dialogue box and waits for valid user input + + @section Example + + @code + #include "drivers/lcd/tft/dialogues/alphanumeric.h" + + // Print results from an alpha-numeric dialogue + char* results = alphaShowDialogue(); + printf("%s\r\n", results); + + @endcode +*/ +/**************************************************************************/ +char* alphaShowDialogue(void) +{ + char result; + + /* These need to be instantiated here since the width and height of + the lcd is retrieved dynamically depending on screen orientation */ + alphaBtnX[0] = ALPHA_COL1_LEFT; + alphaBtnX[1] = ALPHA_COL2_LEFT; + alphaBtnX[2] = ALPHA_COL3_LEFT; + alphaBtnX[3] = ALPHA_COL4_LEFT; + alphaBtnX[4] = ALPHA_COL5_LEFT; + alphaBtnY[0] = ALPHA_ROW1_TOP; + alphaBtnY[1] = ALPHA_ROW2_TOP; + alphaBtnY[2] = ALPHA_ROW3_TOP; + alphaBtnY[3] = ALPHA_ROW4_TOP; + alphaBtnY[4] = ALPHA_ROW5_TOP; + alphaBtnY[5] = ALPHA_ROW6_TOP; + + /* Initialise the string buffer */ + memset(&alphaString[0], 0, sizeof(alphaString)); + alphaString_ptr = alphaString; + alphaPage = 0; + + /* Draw the background and render the buttons */ + drawFill(ALPHA_COLOR_BACKGROUND); + alphaRefreshScreen(); + + /* Capture results until the 'OK' button is pressed */ + while(1) + { + result = alphaHandleTouchEvent(); + if (result == '>') return (char *)&alphaString; + } +} diff --git a/drivers/lcd/tft/dialogues/alphanumeric.h b/drivers/lcd/tft/dialogues/alphanumeric.h new file mode 100644 index 0000000..13353f8 --- /dev/null +++ b/drivers/lcd/tft/dialogues/alphanumeric.h @@ -0,0 +1,43 @@ +/**************************************************************************/ +/*! + @file alphanumeric.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __DIALOGUE_ALPHANUMERIC_H__ +#define __DIALOGUE_ALPHANUMERIC_H__ + +#include "projectconfig.h" + +char * alphaShowDialogue(void); + +#endif diff --git a/drivers/lcd/tft/drawing.c b/drivers/lcd/tft/drawing.c new file mode 100644 index 0000000..cd612e6 --- /dev/null +++ b/drivers/lcd/tft/drawing.c @@ -0,0 +1,1205 @@ +/**************************************************************************/ +/*! + @file drawing.c + @author K. Townsend (microBuilder.eu) + + drawLine and drawCircle adapted from a tutorial by Leonard McMillan: + http://www.cs.unc.edu/~mcmillan/ + + drawString based on an example from Eran Duchan: + http://www.pavius.net/downloads/tools/53-the-dot-factory + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "drawing.h" + +#ifdef CFG_SDCARD + #include "bmp.h" +#endif + +/**************************************************************************/ +/* */ +/* ----------------------- Private Methods ------------------------------ */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Draws a single bitmap character +*/ +/**************************************************************************/ +void drawCharBitmap(const uint16_t xPixel, const uint16_t yPixel, uint16_t color, const uint8_t *glyph, uint8_t glyphHeightPages, uint8_t glyphWidthBits) +{ + uint16_t verticalPage, horizBit, currentY, currentX; + uint16_t indexIntoGlyph; + + // set initial current y + currentY = yPixel; + currentX = xPixel; + + // for each page of the glyph + for (verticalPage = glyphHeightPages; verticalPage > 0; --verticalPage) + { + // for each horizontol bit + for (horizBit = 0; horizBit < glyphWidthBits; ++horizBit) + { + // next byte + indexIntoGlyph = (glyphHeightPages * horizBit) + verticalPage - 1; + + currentX = xPixel + (horizBit); + // send the data byte + if (glyph[indexIntoGlyph] & (0X80)) drawPixel(currentX, currentY, color); + if (glyph[indexIntoGlyph] & (0X40)) drawPixel(currentX, currentY - 1, color); + if (glyph[indexIntoGlyph] & (0X20)) drawPixel(currentX, currentY - 2, color); + if (glyph[indexIntoGlyph] & (0X10)) drawPixel(currentX, currentY - 3, color); + if (glyph[indexIntoGlyph] & (0X08)) drawPixel(currentX, currentY - 4, color); + if (glyph[indexIntoGlyph] & (0X04)) drawPixel(currentX, currentY - 5, color); + if (glyph[indexIntoGlyph] & (0X02)) drawPixel(currentX, currentY - 6, color); + if (glyph[indexIntoGlyph] & (0X01)) drawPixel(currentX, currentY - 7, color); + } + // next line of pages + currentY += 8; + } +} + +#if defined CFG_TFTLCD_INCLUDESMALLFONTS & CFG_TFTLCD_INCLUDESMALLFONTS == 1 +/**************************************************************************/ +/*! + @brief Draws a single smallfont character +*/ +/**************************************************************************/ +void drawCharSmall(uint16_t x, uint16_t y, uint16_t color, uint8_t c, struct FONT_DEF font) +{ + uint8_t col, column[font.u8Width]; + + // Check if the requested character is available + if ((c >= font.u8FirstChar) && (c <= font.u8LastChar)) + { + // Retrieve appropriate columns from font data + for (col = 0; col < font.u8Width; col++) + { + column[col] = font.au8FontTable[((c - 32) * font.u8Width) + col]; // Get first column of appropriate character + } + } + else + { + // Requested character is not available in this font ... send a space instead + for (col = 0; col < font.u8Width; col++) + { + column[col] = 0xFF; // Send solid space + } + } + + // Render each column + uint16_t xoffset, yoffset; + for (xoffset = 0; xoffset < font.u8Width; xoffset++) + { + for (yoffset = 0; yoffset < (font.u8Height + 1); yoffset++) + { + uint8_t bit = 0x00; + bit = (column[xoffset] << (8 - (yoffset + 1))); // Shift current row bit left + bit = (bit >> 7); // Shift current row but right (results in 0x01 for black, and 0x00 for white) + if (bit) + { + drawPixel(x + xoffset, y + yoffset, color); + } + } + } +} +#endif + +/**************************************************************************/ +/*! + @brief Helper method to accurately draw individual circle points +*/ +/**************************************************************************/ +void drawCirclePoints(int cx, int cy, int x, int y, uint16_t color) +{ + if (x == 0) + { + drawPixel(cx, cy + y, color); + drawPixel(cx, cy - y, color); + drawPixel(cx + y, cy, color); + drawPixel(cx - y, cy, color); + } + else if (x == y) + { + drawPixel(cx + x, cy + y, color); + drawPixel(cx - x, cy + y, color); + drawPixel(cx + x, cy - y, color); + drawPixel(cx - x, cy - y, color); + } + else if (x < y) + { + drawPixel(cx + x, cy + y, color); + drawPixel(cx - x, cy + y, color); + drawPixel(cx + x, cy - y, color); + drawPixel(cx - x, cy - y, color); + drawPixel(cx + y, cy + x, color); + drawPixel(cx - y, cy + x, color); + drawPixel(cx + y, cy - x, color); + drawPixel(cx - y, cy - x, color); + } +} + +/**************************************************************************/ +/* */ +/* ----------------------- Public Methods ------------------------------- */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Draws a single pixel at the specified location + + @param[in] x + Horizontal position + @param[in] y + Vertical position + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawPixel(uint16_t x, uint16_t y, uint16_t color) +{ + if ((x >= lcdGetWidth()) || (y >= lcdGetHeight())) + { + // Pixel out of range + return; + } + + // Redirect to LCD + lcdDrawPixel(x, y, color); +} + +/**************************************************************************/ +/*! + @brief Fills the screen with the specified color + + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawFill(uint16_t color) +{ + lcdFillRGB(color); +} + +/**************************************************************************/ +/*! + @brief Draws a simple color test pattern +*/ +/**************************************************************************/ +void drawTestPattern(void) +{ + lcdTest(); +} + +#if defined CFG_TFTLCD_INCLUDESMALLFONTS & CFG_TFTLCD_INCLUDESMALLFONTS == 1 +/**************************************************************************/ +/*! + @brief Draws a string using a small font (6 of 8 pixels high). + + @param[in] x + Starting x co-ordinate + @param[in] y + Starting y co-ordinate + @param[in] color + Color to use when rendering the font + @param[in] text + The string to render + @param[in] font + Pointer to the FONT_DEF to use when drawing the string + + @section Example + + @code + + #include "drivers/lcd/fonts/smallfonts.h" + + drawStringSmall(1, 210, COLOR_WHITE, "5x8 System (Max 40 Characters)", Font_System5x8); + drawStringSmall(1, 220, COLOR_WHITE, "7x8 System (Max 30 Characters)", Font_System7x8); + + @endcode +*/ +/**************************************************************************/ +void drawStringSmall(uint16_t x, uint16_t y, uint16_t color, char* text, struct FONT_DEF font) +{ + uint8_t l; + for (l = 0; l < strlen(text); l++) + { + drawCharSmall(x + (l * (font.u8Width + 1)), y, color, text[l], font); + } +} +#endif + +/**************************************************************************/ +/*! + @brief Draws a string using the supplied font + + @param[in] x + Starting x co-ordinate + @param[in] y + Starting y co-ordinate + @param[in] color + Color to use when rendering the font + @param[in] fontInfo + Pointer to the FONT_INFO to use when drawing the string + @param[in] str + The string to render + + @section Example + + @code + + #include "drivers/lcd/tft/fonts/veramono9.h" + + drawString(0, 90, COLOR_BLACK, &bitstreamVeraSansMono9ptFontInfo, "Vera Mono 9 (30 chars wide)"); + drawString(0, 105, COLOR_BLACK, &bitstreamVeraSansMono9ptFontInfo, "123456789012345678901234567890"); + + @endcode +*/ +/**************************************************************************/ +void drawString(uint16_t x, uint16_t y, uint16_t color, const FONT_INFO *fontInfo, char *str) +{ + uint16_t currentX, charWidth, characterToOutput; + const FONT_CHAR_INFO *charInfo; + uint16_t charOffset; + + // set current x, y to that of requested + currentX = x; + + // while not NULL + while (*str != '\0') + { + // get character to output + characterToOutput = *str; + + // get char info + charInfo = fontInfo->charInfo; + + // some fonts have character descriptors, some don't + if (charInfo != NULL) + { + // get correct char offset + charInfo += (characterToOutput - fontInfo->startChar); + + // get width from char info + charWidth = charInfo->widthBits; + + // get offset from char info + charOffset = charInfo->offset; + } + else + { + // if no char info, char width is always 5 + charWidth = 5; + + // char offset - assume 5 * letter offset + charOffset = (characterToOutput - fontInfo->startChar) * 5; + } + + // Send individual characters + drawCharBitmap(currentX, y, color, &fontInfo->data[charOffset], fontInfo->heightPages, charWidth); + + // next char X + currentX += charWidth + 1; + + // next char + str++; + } +} + +/**************************************************************************/ +/*! + @brief Returns the width in pixels of a string when it is rendered + + This method can be used to determine whether a string will fit + inside a specific area, or if it needs to be broken up into multiple + lines to be properly rendered on the screen. + + This function only applied to bitmap fonts (which can have variable + widths). All smallfonts (if available) are fixed width and can + easily have their width calculated without costly functions like + this one. + + @param[in] fontInfo + Pointer to the FONT_INFO for the font that will be used + @param[in] str + The string that will be rendered +*/ +/**************************************************************************/ +uint16_t drawGetStringWidth(const FONT_INFO *fontInfo, char *str) +{ + uint16_t width = 0; + uint32_t currChar; + uint32_t startChar = fontInfo->startChar; + + // until termination + for (currChar = *str; currChar; currChar = *(++str)) + { + // if char info exists for the font, use width from there + if (fontInfo->charInfo != NULL) + { + width += fontInfo->charInfo[currChar - startChar].widthBits + 1; + } + else + { + width += 5 + 1; + } + } + + /* return the wdith */ + return width; +} + +/**************************************************************************/ +/*! + @brief Draws a bresenham line + + @param[in] x0 + Starting x co-ordinate + @param[in] y0 + Starting y co-ordinate + @param[in] x1 + Ending x co-ordinate + @param[in] y1 + Ending y co-ordinate + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawLine ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color ) +{ + drawLineDotted(x0, y0, x1, y1, 0, 1, color); +} + +/**************************************************************************/ +/*! + @brief Draws a bresenham line with a fixed pattern of empty + and solid pixels + + Based on: http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html + + @param[in] x0 + Starting x co-ordinate + @param[in] y0 + Starting y co-ordinate + @param[in] x1 + Ending x co-ordinate + @param[in] y1 + Ending y co-ordinate + @param[in] empty + The number of 'empty' pixels to render + @param[in] solid + The number of 'solid' pixels to render + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawLineDotted ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t empty, uint16_t solid, uint16_t color ) +{ + if (solid == 0) + { + return; + } + + // If a negative y int was passed in it will overflow to 65K something + // Ugly, but drawCircleFilled() can pass in negative values so we need + // to check the values here + y0 = y0 > 65000 ? 0 : y0; + y1 = y1 > 65000 ? 0 : y1; + + // Check if we can use the optimised horizontal line method + if ((y0 == y1) && (empty == 0)) + { + lcdDrawHLine(x0, x1, y0, color); + return; + } + + // Check if we can use the optimised vertical line method. + // This can make a huge difference in performance, but may + // not work properly on every LCD controller: + // ex.: drawCircleFilled(120, 160, 50, COLOR_RED); + // = 678834 cycles using lcdDrawVLine w/ILI9328 = 9.43mS @ 72MHz + // = 7546261 w/o lcdDrawVLine, setting each pixel = 104.8mS @ 72MHz + if ((x0 == x1) && (empty == 0)) + { + // Warning: This may actually be slower than drawing individual pixels on + // short lines ... Set a minimum line size to use the 'optimised' method + // (which changes the screen orientation) ? + lcdDrawVLine(x0, y0, y1, color); + return; + } + + // Draw non-horizontal or dotted line + int dy = y1 - y0; + int dx = x1 - x0; + int stepx, stepy; + int emptycount, solidcount; + + if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } + if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } + dy <<= 1; // dy is now 2*dy + dx <<= 1; // dx is now 2*dx + + emptycount = empty; + solidcount = solid; + + drawPixel(x0, y0, color); // always start with solid pixels + solidcount--; + if (dx > dy) + { + int fraction = dy - (dx >> 1); // same as 2*dy - dx + while (x0 != x1) + { + if (fraction >= 0) + { + y0 += stepy; + fraction -= dx; // same as fraction -= 2*dx + } + x0 += stepx; + fraction += dy; // same as fraction -= 2*dy + if (empty == 0) + { + // always draw a pixel ... no dotted line requested + drawPixel(x0, y0, color); + } + else if (solidcount) + { + // Draw solid pxiel and decrement counter + drawPixel(x0, y0, color); + solidcount--; + } + else if(emptycount) + { + // Empty pixel ... don't draw anything an decrement counter + emptycount--; + } + else + { + // Reset counters and draw solid pixel + emptycount = empty; + solidcount = solid; + drawPixel(x0, y0, color); + solidcount--; + } + } + } + else + { + int fraction = dx - (dy >> 1); + while (y0 != y1) + { + if (fraction >= 0) + { + x0 += stepx; + fraction -= dy; + } + y0 += stepy; + fraction += dx; + if (empty == 0) + { + // always draw a pixel ... no dotted line requested + drawPixel(x0, y0, color); + } + if (solidcount) + { + // Draw solid pxiel and decrement counter + drawPixel(x0, y0, color); + solidcount--; + } + else if(emptycount) + { + // Empty pixel ... don't draw anything an decrement counter + emptycount--; + } + else + { + // Reset counters and draw solid pixel + emptycount = empty; + solidcount = solid; + drawPixel(x0, y0, color); + solidcount--; + } + } + } +} + +/**************************************************************************/ +/*! + @brief Draws a circle + + Based on: http://www.cs.unc.edu/~mcmillan/comp136/Lecture7/circle.html + + @param[in] xCenter + The horizontal center of the circle + @param[in] yCenter + The vertical center of the circle + @param[in] radius + The circle's radius in pixels + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawCircle (uint16_t xCenter, uint16_t yCenter, uint16_t radius, uint16_t color) +{ + int x = 0; + int y = radius; + int p = (5 - radius*4)/4; + + drawCirclePoints(xCenter, yCenter, x, y, color); + while (x < y) + { + x++; + if (p < 0) + { + p += 2*x+1; + } + else + { + y--; + p += 2*(x-y)+1; + } + drawCirclePoints(xCenter, yCenter, x, y, color); + } +} + +/**************************************************************************/ +/*! + @brief Draws a filled circle + + @param[in] xCenter + The horizontal center of the circle + @param[in] yCenter + The vertical center of the circle + @param[in] radius + The circle's radius in pixels + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawCircleFilled (uint16_t xCenter, uint16_t yCenter, uint16_t radius, uint16_t color) +{ + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * radius; + int16_t x = 0; + int16_t y = radius; + int16_t xc_px, yc_my, xc_mx, xc_py, yc_mx, xc_my; + int16_t lcdWidth = lcdGetWidth(); + + if (xCenter < lcdWidth) drawLine(xCenter, yCenter-radius < 0 ? 0 : yCenter-radius, xCenter, (yCenter-radius) + (2*radius), color); + + while (x= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + xc_px = xCenter+x; + xc_mx = xCenter-x; + xc_py = xCenter+y; + xc_my = xCenter-y; + yc_mx = yCenter-x; + yc_my = yCenter-y; + + // Make sure X positions are not negative or too large or the pixels will + // overflow. Y overflow is handled in drawLine(). + if ((xc_px < lcdWidth) && (xc_px >= 0)) drawLine(xc_px, yc_my, xc_px, yc_my + 2*y, color); + if ((xc_mx < lcdWidth) && (xc_mx >= 0)) drawLine(xc_mx, yc_my, xc_mx, yc_my + 2*y, color); + if ((xc_py < lcdWidth) && (xc_py >= 0)) drawLine(xc_py, yc_mx, xc_py, yc_mx + 2*x, color); + if ((xc_my < lcdWidth) && (xc_my >= 0)) drawLine(xc_my, yc_mx, xc_my, yc_mx + 2*x, color); + } +} + +/**************************************************************************/ +/*! + @brief Draws a simple arrow of the specified width + + @param[in] x + X co-ordinate of the smallest point of the arrow + @param[in] y + Y co-ordinate of the smallest point of the arrow + @param[in] size + Total width/height of the arrow in pixels + @param[in] direction + The direction that the arrow is pointing + @param[in] color + Color used when drawing +*/ +/**************************************************************************/ +void drawArrow(uint16_t x, uint16_t y, uint16_t size, drawDirection_t direction, uint16_t color) +{ + drawPixel(x, y, color); + + if (size == 1) + { + return; + } + + uint32_t i; + switch (direction) + { + case DRAW_DIRECTION_LEFT: + for (i = 1; i height - 1; ++height) + { + drawLine(x0, height, x1, height, color); + } +} + +/**************************************************************************/ +/*! + @brief Draws a filled rectangle with rounded corners + + @param[in] x0 + Starting x co-ordinate + @param[in] y0 + Starting y co-ordinate + @param[in] x1 + Ending x co-ordinate + @param[in] y1 + Ending y co-ordinate + @param[in] color + Color used when drawing + @param[in] radius + Corner radius in pixels + @param[in] corners + Which corners to round +*/ +/**************************************************************************/ +void drawRectangleRounded ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color, uint16_t radius, drawRoundedCorners_t corners ) +{ + int height; + uint16_t y; + + if (corners == DRAW_ROUNDEDCORNERS_NONE) + { + drawRectangleFilled(x0, y0, x1, y1, color); + return; + } + + // Calculate height + if (y1 < y0) + { + y = y1; + y1 = y0; + y0 = y; + } + height = y1 - y0; + + // Check radius + if (radius > height / 2) + { + radius = height / 2; + } + radius -= 1; + + // Draw body + drawRectangleFilled(x0 + radius, y0, x1 - radius, y1, color); + + switch (corners) + { + case DRAW_ROUNDEDCORNERS_ALL: + drawCircleFilled(x0 + radius, y0 + radius, radius, color); + drawCircleFilled(x1 - radius, y0 + radius, radius, color); + drawCircleFilled(x0 + radius, y1 - radius, radius, color); + drawCircleFilled(x1 - radius, y1 - radius, radius, color); + if (radius*2+1 < height) + { + drawRectangleFilled(x0, y0 + radius, x0 + radius, y1 - radius, color); + drawRectangleFilled(x1 - radius, y0 + radius, x1, y1 - radius, color); + } + break; + case DRAW_ROUNDEDCORNERS_TOP: + drawCircleFilled(x0 + radius, y0 + radius, radius, color); + drawCircleFilled(x1 - radius, y0 + radius, radius, color); + drawRectangleFilled(x0, y0 + radius, x0 + radius, y1, color); + drawRectangleFilled(x1 - radius, y0 + radius, x1, y1, color); + break; + case DRAW_ROUNDEDCORNERS_BOTTOM: + drawCircleFilled(x0 + radius, y1 - radius, radius, color); + drawCircleFilled(x1 - radius, y1 - radius, radius, color); + drawRectangleFilled(x0, y0, x0 + radius, y1 - radius, color); + drawRectangleFilled(x1 - radius, y0, x1, y1 - radius, color); + break; + case DRAW_ROUNDEDCORNERS_LEFT: + drawCircleFilled(x0 + radius, y0 + radius, radius, color); + drawCircleFilled(x0 + radius, y1 - radius, radius, color); + if (radius*2+1 < height) + { + drawRectangleFilled(x0, y0 + radius, x0 + radius, y1 - radius, color); + } + drawRectangleFilled(x1 - radius, y0, x1, y1, color); + break; + case DRAW_ROUNDEDCORNERS_RIGHT: + drawCircleFilled(x1 - radius, y0 + radius, radius, color); + drawCircleFilled(x1 - radius, y1 - radius, radius, color); + if (radius*2+1 < height) + { + drawRectangleFilled(x1 - radius, y0 + radius, x1, y1 - radius, color); + } + drawRectangleFilled(x0, y0, x0 + radius, y1, color); + break; + default: + break; + } +} + +/**************************************************************************/ +/*! + @brief Converts a 24-bit RGB color to an equivalent 16-bit RGB565 value + + @param[in] r + 8-bit red + @param[in] g + 8-bit green + @param[in] b + 8-bit blue + + @section Example + + @code + + // Get 16-bit equivalent of 24-bit color + uint16_t gray = drawRGB24toRGB565(0x33, 0x33, 0x33); + + @endcode +*/ +/**************************************************************************/ +uint16_t drawRGB24toRGB565(uint8_t r, uint8_t g, uint8_t b) +{ + return ((r / 8) << 11) | ((g / 4) << 5) | (b / 8); +} + +/**************************************************************************/ +/*! + @brief Converts a 16-bit RGB565 color to a standard 32-bit BGRA32 + color (with alpha set to 0xFF) + + @param[in] color + 16-bit rgb565 color + + @section Example + + @code + + // First convert 24-bit color to RGB565 + uint16_t rgb565 = drawRGB24toRGB565(0xFF, 0x00, 0x00); + + // Convert RGB565 color back to BGRA32 + uint32_t bgra32 = drawRGB565toBGRA32(rgb565); + + // Display results + printf("BGRA32: 0x%08X R: %u G: %u B: %u A: %u \r\n", + bgra32, + (bgra32 & 0x000000FF), // Blue + (bgra32 & 0x0000FF00) >> 8, // Green + (bgra32 & 0x00FF0000) >> 16, // Red + (bgra32 & 0xFF000000) >> 24); // Alpha + + @endcode +*/ +/**************************************************************************/ +uint32_t drawRGB565toBGRA32(uint16_t color) +{ + uint32_t bits = (uint32_t)color; + uint32_t blue = bits & 0x001F; // 5 bits blue + uint32_t green = bits & 0x07E0; // 6 bits green + uint32_t red = bits & 0xF800; // 5 bits red + + // Return shifted bits with alpha set to 0xFF + return (red << 8) | (green << 5) | (blue << 3) | 0xFF000000; +} + +/**************************************************************************/ +/*! + @brief Reverses a 16-bit color from BGR to RGB +*/ +/**************************************************************************/ +uint16_t drawBGR2RGB(uint16_t color) +{ + uint16_t r, g, b; + + b = (color>>0) & 0x1f; + g = (color>>5) & 0x3f; + r = (color>>11) & 0x1f; + + return( (b<<11) + (g<<5) + (r<<0) ); +} + +/**************************************************************************/ +/*! + @brief Draws a progress bar with rounded corners + + @param[in] x + Starting x location + @param[in] y + Starting y location + @param[in] width + Total width of the progress bar in pixels + @param[in] height + Total height of the progress bar in pixels + @param[in] borderCorners + The type of rounded corners to render with the progress bar border + @param[in] progressCorners + The type of rounded corners to render with the inner progress bar + @param[in] borderColor + 16-bit color for the outer border + @param[in] borderFillColor + 16-bit color for the interior of the outer border + @param[in] progressBorderColor + 16-bit color for the progress bar's border + @param[in] progressFillColor + 16-bit color for the inner bar's fill + @param[in] progress + Progress percentage (between 0 and 100) + + @section Example + + @code + #include "drivers/lcd/tft/drawing.h" + + // Draw a the progress bar (150x15 pixels large, starting at X:10, Y:195 + // with rounded corners on the top and showing 72% progress) + drawProgressBar(10, 195, 150, 15, DRAW_ROUNDEDCORNERS_TOP, DRAW_ROUNDEDCORNERS_TOP, COLOR_DARKERGRAY, COLOR_DARKGRAY, COLOR_LIMEGREENDIM, COLOR_LIMEGREEN, 72 ); + + @endcode +*/ +/**************************************************************************/ +void drawProgressBar ( uint16_t x, uint16_t y, uint16_t width, uint16_t height, drawRoundedCorners_t borderCorners, drawRoundedCorners_t progressCorners, uint16_t borderColor, uint16_t borderFillColor, uint16_t progressBorderColor, uint16_t progressFillColor, uint8_t progress ) +{ + // Draw border with rounded corners + drawRectangleRounded(x, y, x + width, y + height, borderColor, 5, borderCorners); + drawRectangleRounded(x+1, y+1, x + width - 1, y + height - 1, borderFillColor, 5, borderCorners); + + // Progress bar + if (progress > 0 && progress <= 100) + { + // Calculate bar size + uint16_t bw; + bw = (width - 6); // bar at 100% + if (progress != 100) + { + bw = (bw * progress) / 100; + } + drawRectangleRounded(x + 3, y + 3, bw + x + 3, y + height - 3, progressBorderColor, 5, progressCorners); + drawRectangleRounded(x + 4, y + 4, bw + x + 3 - 1, y + height - 4, progressFillColor, 5, progressCorners); + } +} + +/**************************************************************************/ +/*! + @brief Draws a simple button + + @param[in] x + Starting x location + @param[in] y + Starting y location + @param[in] width + Total width of the button in pixels + @param[in] height + Total height of the button in pixels + @param[in] fontInfo + Pointer to the FONT_INFO used to render the button text + @param[in] fontHeight + The height in pixels of the font (used for centering) + @param[in] borderclr + The rgb565 border color + @param[in] fillclr + The rgb565 background color + @param[in] fontclr + The rgb565 font color + @param[in] text + The text to render on the button + + @section Example + + @code + + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/tft/fonts/dejavusansbold9.h" + + // Draw two buttons using Vera Sans Bold 9 + drawButton(20, 195, 200, 35, &dejaVuSansBold9ptFontInfo, 7, COLOR_DARKERGRAY, COLOR_DARKERGRAY, COLOR_WHITE, "System Settings"); + drawButton(20, 235, 200, 35, &dejaVuSansBold9ptFontInfo, 7, COLOR_LIMEGREENDIM, COLOR_LIMEGREEN, COLOR_BLACK, "System Settings"); + + @endcode +*/ +/**************************************************************************/ +void drawButton(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const FONT_INFO *fontInfo, uint16_t fontHeight, uint16_t borderclr, uint16_t fillclr, uint16_t fontclr, char* text) +{ + uint16_t border, fill, font, activeborder, activefill, activefont; + + // Set colors + border = COLOR_GRAY_30; + fill = COLOR_GRAY_30; + font = COLOR_WHITE; + activeborder = COLOR_THEME_DEFAULT_DARKER; + activefill = COLOR_THEME_DEFAULT_BASE; + activefont = COLOR_BLACK; + + // Border + drawRectangleRounded(x, y, x + width, y + height, borderclr, 5, DRAW_ROUNDEDCORNERS_ALL); + // Fill + drawRectangleRounded(x+2, y+2, x+width-2, y+height-2, fillclr, 5, DRAW_ROUNDEDCORNERS_ALL); + + // Render text + if (text != NULL) + { + uint16_t textWidth = drawGetStringWidth(&*fontInfo, text); + uint16_t xStart = x + (width / 2) - (textWidth / 2); + uint16_t yStart = y + (height / 2) - (fontHeight / 2) + 1; + drawString(xStart, yStart, fontclr, &*fontInfo, text); + } +} + +/**************************************************************************/ +/*! + @brief Renders a 16x16 monochrome icon using the supplied uint16_t + array. + + @param[in] x + The horizontal location to start rendering from + @param[in] x + The vertical location to start rendering from + @param[in] color + The RGB565 color to use when rendering the icon + @param[in] icon + The uint16_t array containing the 16x16 image data + + @section Example + + @code + + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/icons16.h" + + // Renders the info icon, which has two seperate parts ... the exterior + // and a seperate interior mask if you want to fill the contents with a + // different color + drawIcon16(132, 202, COLOR_BLUE, icons16_info); + drawIcon16(132, 202, COLOR_WHITE, icons16_info_interior); + + @endcode +*/ +/**************************************************************************/ +void drawIcon16(uint16_t x, uint16_t y, uint16_t color, uint16_t icon[]) +{ + int i; + uint16_t row; + for (i = 0; i<16; i++) + { + if (icon[i] & (0X8000)) drawPixel(x, y+i, color); + if (icon[i] & (0X4000)) drawPixel(x+1, y+i, color); + if (icon[i] & (0X2000)) drawPixel(x+2, y+i, color); + if (icon[i] & (0X1000)) drawPixel(x+3, y+i, color); + if (icon[i] & (0X0800)) drawPixel(x+4, y+i, color); + if (icon[i] & (0X0400)) drawPixel(x+5, y+i, color); + if (icon[i] & (0X0200)) drawPixel(x+6, y+i, color); + if (icon[i] & (0X0100)) drawPixel(x+7, y+i, color); + if (icon[i] & (0X0080)) drawPixel(x+8, y+i, color); + if (icon[i] & (0x0040)) drawPixel(x+9, y+i, color); + if (icon[i] & (0X0020)) drawPixel(x+10, y+i, color); + if (icon[i] & (0X0010)) drawPixel(x+11, y+i, color); + if (icon[i] & (0X0008)) drawPixel(x+12, y+i, color); + if (icon[i] & (0X0004)) drawPixel(x+13, y+i, color); + if (icon[i] & (0X0002)) drawPixel(x+14, y+i, color); + if (icon[i] & (0X0001)) drawPixel(x+15, y+i, color); + } +} + +#ifdef CFG_SDCARD +/**************************************************************************/ +/*! + @brief Loads a 24-bit Windows bitmap image from an SD card and + renders it + + @section Example + + @code + + #include "drivers/lcd/tft/drawing.h" + + // Draw image.bmp (from the root folder) starting at pixel 0,0 + bmp_error_t error = drawBitmapImage(0, 0, "/image.bmp"); + + if (error) + { + switch (error) + { + case BMP_ERROR_SDINITFAIL: + break; + case BMP_ERROR_FILENOTFOUND: + break; + case BMP_ERROR_NOTABITMAP: + // First two bytes of image not 'BM' + break; + case BMP_ERROR_INVALIDBITDEPTH: + // Image is not 24-bits + break; + case BMP_ERROR_COMPRESSEDDATA: + // Image contains compressed data + break; + case BMP_ERROR_INVALIDDIMENSIONS: + // Width or Height is > LCD size + break; + case BMP_ERROR_PREMATUREEOF: + // EOF unexpectedly reached in pixel data + break; + } + } + + @endcode +*/ +/**************************************************************************/ +bmp_error_t drawBitmapImage(uint16_t x, uint16_t y, char *filename) +{ + return bmpDrawBitmap(x, y, filename); +} + +#endif diff --git a/drivers/lcd/tft/drawing.h b/drivers/lcd/tft/drawing.h new file mode 100644 index 0000000..d9d1b1d --- /dev/null +++ b/drivers/lcd/tft/drawing.h @@ -0,0 +1,107 @@ +/**************************************************************************/ +/*! + @file drawing.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __DRAWING_H__ +#define __DRAWING_H__ + +#include "projectconfig.h" +#include "lcd.h" +#include "colors.h" +#include "drivers/lcd/tft/fonts/bitmapfonts.h" + +#if defined CFG_TFTLCD_INCLUDESMALLFONTS & CFG_TFTLCD_INCLUDESMALLFONTS == 1 + #include "drivers/lcd/smallfonts.h" +#endif + +#ifdef CFG_SDCARD + #include "bmp.h" +#endif + +typedef struct +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} drawColorRGB24_t; + +typedef enum +{ + DRAW_ROUNDEDCORNERS_NONE, + DRAW_ROUNDEDCORNERS_ALL, + DRAW_ROUNDEDCORNERS_TOP, + DRAW_ROUNDEDCORNERS_BOTTOM, + DRAW_ROUNDEDCORNERS_LEFT, + DRAW_ROUNDEDCORNERS_RIGHT +} drawRoundedCorners_t; + +typedef enum +{ + DRAW_DIRECTION_LEFT, + DRAW_DIRECTION_RIGHT, + DRAW_DIRECTION_UP, + DRAW_DIRECTION_DOWN +} drawDirection_t; + +void drawTestPattern ( void ); +void drawPixel ( uint16_t x, uint16_t y, uint16_t color ); +void drawFill ( uint16_t color ); +void drawLine ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color ); +void drawLineDotted ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t space, uint16_t solid, uint16_t color ); +void drawCircle ( uint16_t xCenter, uint16_t yCenter, uint16_t radius, uint16_t color ); +void drawCircleFilled ( uint16_t xCenter, uint16_t yCenter, uint16_t radius, uint16_t color ); +void drawArrow ( uint16_t x, uint16_t y, uint16_t size, drawDirection_t, uint16_t color ); +void drawRectangle ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color ); +void drawRectangleFilled ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color ); +void drawRectangleRounded ( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color, uint16_t radius, drawRoundedCorners_t corners ); +void drawString ( uint16_t x, uint16_t y, uint16_t color, const FONT_INFO *fontInfo, char *str ); +uint16_t drawGetStringWidth ( const FONT_INFO *fontInfo, char *str ); +void drawProgressBar ( uint16_t x, uint16_t y, uint16_t width, uint16_t height, drawRoundedCorners_t borderCorners, drawRoundedCorners_t progressCorners, uint16_t borderColor, uint16_t borderFillColor, uint16_t progressBorderColor, uint16_t progressFillColor, uint8_t progress ); +void drawButton ( uint16_t x, uint16_t y, uint16_t width, uint16_t height, const FONT_INFO *fontInfo, uint16_t fontHeight, uint16_t borderclr, uint16_t fillclr, uint16_t fontclr, char* text ); +void drawIcon16 ( uint16_t x, uint16_t y, uint16_t color, uint16_t icon[] ); +uint16_t drawRGB24toRGB565 ( uint8_t r, uint8_t g, uint8_t b ); +uint32_t drawRGB565toBGRA32 ( uint16_t color ); +uint16_t drawBGR2RGB ( uint16_t color ); + +#if defined CFG_TFTLCD_INCLUDESMALLFONTS & CFG_TFTLCD_INCLUDESMALLFONTS == 1 +void drawStringSmall ( uint16_t x, uint16_t y, uint16_t color, char* text, struct FONT_DEF font ); +#endif + +#if defined CFG_SDCARD +bmp_error_t drawBitmapImage ( uint16_t x, uint16_t y, char *filename ); +#endif + +#endif diff --git a/drivers/lcd/tft/fonts/bitmapfonts.h b/drivers/lcd/tft/fonts/bitmapfonts.h new file mode 100644 index 0000000..23fac6d --- /dev/null +++ b/drivers/lcd/tft/fonts/bitmapfonts.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/*! + @file bitmapfonts.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __BITMAPFONTS_H__ +#define __BITMAPFONTS_H__ + +#include "projectconfig.h" + +/**************************************************************************/ +/*! + @brief Describes a single character's display information +*/ +/**************************************************************************/ +typedef struct +{ + const uint8_t widthBits; // width, in bits (or pixels), of the character + const uint16_t offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array +} FONT_CHAR_INFO; + +/**************************************************************************/ +/*! + @brief Describes a single font +*/ +/**************************************************************************/ +typedef struct +{ + const uint8_t heightPages; // height, in pages (8 pixels), of the font's characters + const uint8_t startChar; // the first character in the font (e.g. in charInfo and data) + const FONT_CHAR_INFO* charInfo; // pointer to array of char information + const uint8_t* data; // pointer to generated array of character visual representation +} FONT_INFO; + +#endif diff --git a/drivers/lcd/tft/fonts/dejavusans9.c b/drivers/lcd/tft/fonts/dejavusans9.c new file mode 100644 index 0000000..1b4da96 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusans9.c @@ -0,0 +1,826 @@ +#include "dejavusans9.h" + +/* +** Font data for DejaVu Sans 9pt +*/ + +/* Character bitmaps for DejaVu Sans 9pt */ +const uint8_t dejaVuSans9ptCharBitmaps[] = +{ + /* @0 ' ' (5 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @10 '!' (1 pixels wide) */ + 0x1B, 0xF0, /* ## ###### */ + + /* @12 '"' (3 pixels wide) */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + + /* @18 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x14, 0x80, /* # # # */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x04, 0xA0, /* # # # */ + 0x00, 0x80, /* # */ + + /* @34 '$' (5 pixels wide) */ + 0x09, 0xC0, /* # ### */ + 0x11, 0x20, /* # # # */ + 0x7F, 0xF0, /* ########### */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + + /* @44 '%' (10 pixels wide) */ + 0x00, 0xE0, /* ### */ + 0x01, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x0C, 0xE0, /* ## ### */ + 0x03, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x0E, 0x60, /* ### ## */ + 0x11, 0x10, /* # # # */ + 0x11, 0x00, /* # # */ + 0x0E, 0x00, /* ### */ + + /* @64 '&' (8 pixels wide) */ + 0x0E, 0x00, /* ### */ + 0x19, 0xE0, /* ## #### */ + 0x10, 0x90, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x12, 0x20, /* # # # */ + 0x0C, 0x00, /* ## */ + 0x14, 0x00, /* # # */ + 0x13, 0x00, /* # ## */ + + /* @80 ''' (1 pixels wide) */ + 0x00, 0x70, /* ### */ + + /* @82 '(' (3 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x38, 0x38, /* ### ### */ + 0x20, 0x08, /* # # */ + + /* @88 ')' (3 pixels wide) */ + 0x20, 0x08, /* # # */ + 0x38, 0x38, /* ### ### */ + 0x07, 0xC0, /* ##### */ + + /* @94 '*' (5 pixels wide) */ + 0x01, 0x20, /* # # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + + /* @104 '+' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @118 ',' (1 pixels wide) */ + 0x38, 0x00, /* ### */ + + /* @120 '-' (3 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @126 '.' (1 pixels wide) */ + 0x18, 0x00, /* ## */ + + /* @128 '/' (4 pixels wide) */ + 0x30, 0x00, /* ## */ + 0x0E, 0x00, /* ### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x30, /* ## */ + + /* @136 '0' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x07, 0xC0, /* ##### */ + + /* @148 '1' (5 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @158 '2' (6 pixels wide) */ + 0x10, 0x20, /* # # */ + 0x18, 0x10, /* ## # */ + 0x14, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x11, 0x30, /* # # ## */ + 0x10, 0xE0, /* # ### */ + + /* @170 '3' (6 pixels wide) */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @182 '4' (6 pixels wide) */ + 0x06, 0x00, /* ## */ + 0x05, 0x80, /* # ## */ + 0x04, 0x40, /* # # */ + 0x04, 0x30, /* # ## */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + + /* @194 '5' (6 pixels wide) */ + 0x08, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x00, /* #### */ + + /* @206 '6' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x19, 0x20, /* ## # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x20, /* #### # */ + + /* @218 '7' (6 pixels wide) */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x0C, 0x10, /* ## # */ + 0x03, 0x10, /* ## # */ + 0x00, 0xD0, /* ## # */ + 0x00, 0x30, /* ## */ + + /* @230 '8' (6 pixels wide) */ + 0x0E, 0xE0, /* ### ### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @242 '9' (6 pixels wide) */ + 0x09, 0xE0, /* # #### */ + 0x13, 0x30, /* # ## ## */ + 0x12, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x09, 0x30, /* # # ## */ + 0x07, 0xC0, /* ##### */ + + /* @254 ':' (1 pixels wide) */ + 0x19, 0x80, /* ## ## */ + + /* @256 ';' (1 pixels wide) */ + 0x39, 0x80, /* ### ## */ + + /* @258 '<' (8 pixels wide) */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x08, 0x40, /* # # */ + + /* @274 '=' (8 pixels wide) */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + + /* @290 '>' (8 pixels wide) */ + 0x08, 0x40, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + + /* @306 '?' (5 pixels wide) */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x1B, 0x10, /* ## ## # */ + 0x00, 0x90, /* # # */ + 0x00, 0x60, /* ## */ + + /* @316 '@' (11 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x20, 0x20, /* # # */ + 0x47, 0x10, /* # ### # */ + 0x48, 0x90, /* # # # # */ + 0x48, 0x90, /* # # # # */ + 0x48, 0x90, /* # # # # */ + 0x4F, 0x90, /* # ##### # */ + 0x28, 0x20, /* # # # */ + 0x04, 0x60, /* # ## */ + 0x03, 0x80, /* ### */ + + /* @338 'A' (8 pixels wide) */ + 0x10, 0x00, /* # */ + 0x0E, 0x00, /* ### */ + 0x05, 0xC0, /* # ### */ + 0x04, 0x30, /* # ## */ + 0x04, 0x30, /* # ## */ + 0x05, 0xC0, /* # ### */ + 0x0E, 0x00, /* ### */ + 0x10, 0x00, /* # */ + + /* @354 'B' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @366 'C' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + + /* @378 'D' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x07, 0xC0, /* ##### */ + + /* @392 'E' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + + /* @404 'F' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + + /* @414 'G' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0F, 0x20, /* #### # */ + + /* @428 'H' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + + /* @442 'I' (1 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + + /* @444 'J' (3 pixels wide) */ + 0x40, 0x00, /* # */ + 0x40, 0x00, /* # */ + 0x3F, 0xF0, /* ########## */ + + /* @450 'K' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x02, 0x80, /* # # */ + 0x04, 0x40, /* # # */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + + /* @462 'L' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @472 'M' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @488 'N' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x30, /* ## */ + 0x00, 0xC0, /* ## */ + 0x01, 0x00, /* # */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @502 'O' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x07, 0xC0, /* ##### */ + + /* @516 'P' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0xE0, /* ### */ + + /* @528 'Q' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x30, 0x10, /* ## # */ + 0x48, 0x20, /* # # # */ + 0x07, 0xC0, /* ##### */ + + /* @542 'R' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x03, 0x10, /* ## # */ + 0x0C, 0xE0, /* ## ### */ + 0x10, 0x00, /* # */ + + /* @556 'S' (6 pixels wide) */ + 0x08, 0xE0, /* # ### */ + 0x11, 0x90, /* # ## # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0x20, /* ### # */ + + /* @568 'T' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + + /* @582 'U' (7 pixels wide) */ + 0x0F, 0xF0, /* ######## */ + 0x18, 0x00, /* ## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x0F, 0xF0, /* ######## */ + + /* @596 'V' (8 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x01, 0xC0, /* ### */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0xC0, /* ### */ + 0x00, 0x30, /* ## */ + + /* @612 'W' (11 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0xE0, /* ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x80, /* #### */ + 0x00, 0x70, /* ### */ + 0x07, 0x80, /* #### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x10, /* # */ + + /* @634 'X' (7 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x08, 0x30, /* # ## */ + 0x06, 0xC0, /* ## ## */ + 0x01, 0x00, /* # */ + 0x06, 0xC0, /* ## ## */ + 0x08, 0x30, /* # ## */ + 0x10, 0x10, /* # # */ + + /* @648 'Y' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x1E, 0x00, /* #### */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x10, /* # */ + + /* @662 'Z' (7 pixels wide) */ + 0x18, 0x10, /* ## # */ + 0x14, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x50, /* # # # */ + 0x10, 0x30, /* # ## */ + + /* @676 '[' (2 pixels wide) */ + 0x7F, 0xF0, /* ########### */ + 0x40, 0x10, /* # # */ + + /* @680 '\' (4 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x01, 0xC0, /* ### */ + 0x0E, 0x00, /* ### */ + 0x30, 0x00, /* ## */ + + /* @688 ']' (2 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x7F, 0xF0, /* ########### */ + + /* @692 '^' (6 pixels wide) */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + + /* @704 '_' (6 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + + /* @716 '`' (2 pixels wide) */ + 0x00, 0x08, /* # */ + 0x00, 0x10, /* # */ + + /* @720 'a' (6 pixels wide) */ + 0x0C, 0x80, /* ## # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0A, 0x40, /* # # # */ + 0x1F, 0x80, /* ###### */ + + /* @732 'b' (6 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @744 'c' (5 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x08, 0x80, /* # # */ + + /* @754 'd' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x1F, 0xF8, /* ########## */ + + /* @766 'e' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x0A, 0xC0, /* # # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0xC0, /* # # ## */ + 0x0B, 0x80, /* # ### */ + + /* @778 'f' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x48, /* # # */ + 0x00, 0x48, /* # # */ + + /* @786 'g' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x58, 0xC0, /* # ## ## */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0xD8, 0xC0, /* ## ## ## */ + 0x7F, 0xC0, /* ######### */ + + /* @798 'h' (6 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @810 'i' (1 pixels wide) */ + 0x1F, 0xD0, /* ####### # */ + + /* @812 'j' (2 pixels wide) */ + 0x80, 0x00, /* # */ + 0xFF, 0xD0, /* ########## # */ + + /* @816 'k' (5 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x08, 0x80, /* # # */ + 0x10, 0x40, /* # # */ + + /* @826 'l' (1 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + + /* @828 'm' (9 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @846 'n' (6 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @858 'o' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @870 'p' (6 pixels wide) */ + 0xFF, 0xC0, /* ########## */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @882 'q' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0xFF, 0xC0, /* ########## */ + + /* @894 'r' (4 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + + /* @902 's' (5 pixels wide) */ + 0x09, 0x80, /* # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0C, 0x80, /* ## # */ + + /* @912 't' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + + /* @920 'u' (6 pixels wide) */ + 0x0F, 0xC0, /* ###### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + + /* @932 'v' (6 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @944 'w' (9 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @962 'x' (6 pixels wide) */ + 0x10, 0x40, /* # # */ + 0x0D, 0x80, /* ## ## */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x0D, 0x80, /* ## ## */ + 0x10, 0x40, /* # # */ + + /* @974 'y' (6 pixels wide) */ + 0x80, 0xC0, /* # ## */ + 0x83, 0x00, /* # ## */ + 0x4C, 0x00, /* # ## */ + 0x38, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @986 'z' (5 pixels wide) */ + 0x18, 0x40, /* ## # */ + 0x14, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x11, 0x40, /* # # # */ + 0x10, 0xC0, /* # ## */ + + /* @996 '{' (5 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x7D, 0xF0, /* ##### ##### */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + + /* @1006 '|' (1 pixels wide) */ + 0xFF, 0xF0, /* ############ */ + + /* @1008 '}' (5 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + 0x7D, 0xF0, /* ##### ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @1018 '~' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ +}; + +/* Character descriptors for DejaVu Sans 9pt */ +/* { [Char width in bits], [Offset into dejaVuSans9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO dejaVuSans9ptCharDescriptors[] = +{ + {5, 0}, /* */ + {1, 10}, /* ! */ + {3, 12}, /* " */ + {8, 18}, /* # */ + {5, 34}, /* $ */ + {10, 44}, /* % */ + {8, 64}, /* & */ + {1, 80}, /* ' */ + {3, 82}, /* ( */ + {3, 88}, /* ) */ + {5, 94}, /* * */ + {7, 104}, /* + */ + {1, 118}, /* , */ + {3, 120}, /* - */ + {1, 126}, /* . */ + {4, 128}, /* / */ + {6, 136}, /* 0 */ + {5, 148}, /* 1 */ + {6, 158}, /* 2 */ + {6, 170}, /* 3 */ + {6, 182}, /* 4 */ + {6, 194}, /* 5 */ + {6, 206}, /* 6 */ + {6, 218}, /* 7 */ + {6, 230}, /* 8 */ + {6, 242}, /* 9 */ + {1, 254}, /* : */ + {1, 256}, /* ; */ + {8, 258}, /* < */ + {8, 274}, /* = */ + {8, 290}, /* > */ + {5, 306}, /* ? */ + {11, 316}, /* @ */ + {8, 338}, /* A */ + {6, 354}, /* B */ + {6, 366}, /* C */ + {7, 378}, /* D */ + {6, 392}, /* E */ + {5, 404}, /* F */ + {7, 414}, /* G */ + {7, 428}, /* H */ + {1, 442}, /* I */ + {3, 444}, /* J */ + {6, 450}, /* K */ + {5, 462}, /* L */ + {8, 472}, /* M */ + {7, 488}, /* N */ + {7, 502}, /* O */ + {6, 516}, /* P */ + {7, 528}, /* Q */ + {7, 542}, /* R */ + {6, 556}, /* S */ + {7, 568}, /* T */ + {7, 582}, /* U */ + {8, 596}, /* V */ + {11, 612}, /* W */ + {7, 634}, /* X */ + {7, 648}, /* Y */ + {7, 662}, /* Z */ + {2, 676}, /* [ */ + {4, 680}, /* \ */ + {2, 688}, /* ] */ + {6, 692}, /* ^ */ + {6, 704}, /* _ */ + {2, 716}, /* ` */ + {6, 720}, /* a */ + {6, 732}, /* b */ + {5, 744}, /* c */ + {6, 754}, /* d */ + {6, 766}, /* e */ + {4, 778}, /* f */ + {6, 786}, /* g */ + {6, 798}, /* h */ + {1, 810}, /* i */ + {2, 812}, /* j */ + {5, 816}, /* k */ + {1, 826}, /* l */ + {9, 828}, /* m */ + {6, 846}, /* n */ + {6, 858}, /* o */ + {6, 870}, /* p */ + {6, 882}, /* q */ + {4, 894}, /* r */ + {5, 902}, /* s */ + {4, 912}, /* t */ + {6, 920}, /* u */ + {6, 932}, /* v */ + {9, 944}, /* w */ + {6, 962}, /* x */ + {6, 974}, /* y */ + {5, 986}, /* z */ + {5, 996}, /* { */ + {1, 1006}, /* | */ + {5, 1008}, /* } */ + {8, 1018}, /* ~ */ +}; + +/* Font information for DejaVu Sans 9pt */ +const FONT_INFO dejaVuSans9ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + dejaVuSans9ptCharDescriptors, /* Character decriptor array */ + dejaVuSans9ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/dejavusans9.h b/drivers/lcd/tft/fonts/dejavusans9.h new file mode 100644 index 0000000..c5f4af0 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusans9.h @@ -0,0 +1,10 @@ +#ifndef __DEJA_VU_SANS_9__ +#define __DEJA_VU_SANS_9__ + +#include "bitmapfonts.h" + +extern const uint8_t dejaVuSans9ptCharBitmaps[]; +extern const FONT_CHAR_INFO dejaVuSans9ptCharDescriptors[]; +extern const FONT_INFO dejaVuSans9ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/dejavusansbold9.c b/drivers/lcd/tft/fonts/dejavusansbold9.c new file mode 100644 index 0000000..2164903 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansbold9.c @@ -0,0 +1,913 @@ +#include "dejavusansbold9.h" + +/* +** Font data for DejaVu Sans Bold 9pt +*/ + +/* Character bitmaps for DejaVu Sans Bold 9pt */ +const uint8_t dejaVuSansBold9ptCharBitmaps[] = +{ + /* @0 ' ' (5 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @10 '!' (2 pixels wide) */ + 0x1B, 0xF0, /* ## ###### */ + 0x1B, 0xF0, /* ## ###### */ + + /* @14 '"' (3 pixels wide) */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + + /* @20 '#' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x1E, 0x40, /* #### # */ + 0x0F, 0xC0, /* ###### */ + 0x02, 0x70, /* # ### */ + 0x1E, 0x40, /* #### # */ + 0x03, 0xE0, /* ##### */ + 0x02, 0x70, /* # ### */ + 0x00, 0x40, /* # */ + + /* @36 '$' (7 pixels wide) */ + 0x11, 0xC0, /* # ### */ + 0x13, 0xE0, /* # ##### */ + 0x13, 0xA0, /* # ### # */ + 0x7F, 0xF0, /* ########### */ + 0x17, 0x20, /* # ### # */ + 0x1F, 0x20, /* ##### # */ + 0x0E, 0x00, /* ### */ + + /* @50 '%' (11 pixels wide) */ + 0x00, 0xE0, /* ### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x19, 0x10, /* ## # # */ + 0x06, 0xE0, /* ## ### */ + 0x03, 0x80, /* ### */ + 0x0E, 0xC0, /* ### ## */ + 0x11, 0x30, /* # # ## */ + 0x11, 0x00, /* # # */ + 0x11, 0x00, /* # # */ + 0x0E, 0x00, /* ### */ + + /* @72 '&' (9 pixels wide) */ + 0x0E, 0x00, /* ### */ + 0x1F, 0x60, /* ##### ## */ + 0x11, 0xF0, /* # ##### */ + 0x13, 0x90, /* # ### # */ + 0x17, 0x10, /* # ### # */ + 0x1E, 0x20, /* #### # */ + 0x0C, 0x00, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x13, 0x00, /* # ## */ + + /* @90 ''' (1 pixels wide) */ + 0x00, 0x70, /* ### */ + + /* @92 '(' (4 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x3F, 0xF8, /* ########### */ + 0x38, 0x38, /* ### ### */ + 0x20, 0x08, /* # # */ + + /* @100 ')' (4 pixels wide) */ + 0x20, 0x08, /* # # */ + 0x38, 0x38, /* ### ### */ + 0x3F, 0xF8, /* ########### */ + 0x07, 0xC0, /* ##### */ + + /* @108 '*' (5 pixels wide) */ + 0x01, 0x20, /* # # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + + /* @118 '+' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @132 ',' (3 pixels wide) */ + 0x40, 0x00, /* # */ + 0x38, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + + /* @138 '-' (4 pixels wide) */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + + /* @146 '.' (2 pixels wide) */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + + /* @150 '/' (4 pixels wide) */ + 0x30, 0x00, /* ## */ + 0x1E, 0x00, /* #### */ + 0x01, 0xE0, /* #### */ + 0x00, 0x30, /* ## */ + + /* @158 '0' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xC0, /* ##### */ + + /* @172 '1' (6 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @184 '2' (7 pixels wide) */ + 0x10, 0x20, /* # # */ + 0x18, 0x10, /* ## # */ + 0x1C, 0x10, /* ### # */ + 0x16, 0x10, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x11, 0xF0, /* # ##### */ + 0x10, 0xE0, /* # ### */ + + /* @198 '3' (7 pixels wide) */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1F, 0xF0, /* ######### */ + 0x0E, 0xE0, /* ### ### */ + + /* @212 '4' (7 pixels wide) */ + 0x07, 0x00, /* ### */ + 0x05, 0x80, /* # ## */ + 0x04, 0x60, /* # ## */ + 0x04, 0x30, /* # ## */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + + /* @226 '5' (7 pixels wide) */ + 0x10, 0xF0, /* # #### */ + 0x10, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x1F, 0x90, /* ###### # */ + 0x0F, 0x00, /* #### */ + + /* @240 '6' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x10, 0xB0, /* # # ## */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x1F, 0x90, /* ###### # */ + 0x0F, 0x00, /* #### */ + + /* @254 '7' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x1C, 0x10, /* ### # */ + 0x0F, 0x10, /* #### # */ + 0x03, 0xD0, /* #### # */ + 0x00, 0xF0, /* #### */ + 0x00, 0x30, /* ## */ + + /* @268 '8' (7 pixels wide) */ + 0x0E, 0xE0, /* ### ### */ + 0x1E, 0xF0, /* #### #### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1E, 0xF0, /* #### #### */ + 0x0E, 0xE0, /* ### ### */ + + /* @282 '9' (7 pixels wide) */ + 0x01, 0xE0, /* #### */ + 0x13, 0xF0, /* # ###### */ + 0x12, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x1A, 0x10, /* ## # # */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xC0, /* ##### */ + + /* @296 ':' (2 pixels wide) */ + 0x18, 0xC0, /* ## ## */ + 0x18, 0xC0, /* ## ## */ + + /* @300 ';' (3 pixels wide) */ + 0x40, 0x00, /* # */ + 0x38, 0xC0, /* ### ## */ + 0x18, 0xC0, /* ## ## */ + + /* @306 '<' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x07, 0x00, /* ### */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x0D, 0x80, /* ## ## */ + 0x08, 0x80, /* # # */ + 0x08, 0x80, /* # # */ + 0x18, 0xC0, /* ## ## */ + + /* @322 '=' (8 pixels wide) */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + + /* @338 '>' (8 pixels wide) */ + 0x18, 0xC0, /* ## ## */ + 0x08, 0x80, /* # # */ + 0x08, 0x80, /* # # */ + 0x0D, 0x80, /* ## ## */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x07, 0x00, /* ### */ + 0x02, 0x00, /* # */ + + /* @354 '?' (5 pixels wide) */ + 0x00, 0x10, /* # */ + 0x1B, 0x10, /* ## ## # */ + 0x1B, 0x90, /* ## ### # */ + 0x00, 0xF0, /* #### */ + 0x00, 0x60, /* ## */ + + /* @364 '@' (10 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x20, 0x20, /* # # */ + 0x47, 0x10, /* # ### # */ + 0x48, 0x90, /* # # # # */ + 0x48, 0x90, /* # # # # */ + 0x4F, 0x90, /* # ##### # */ + 0x68, 0x20, /* ## # # */ + 0x0C, 0x60, /* ## ## */ + 0x07, 0x80, /* #### */ + + /* @384 'A' (9 pixels wide) */ + 0x10, 0x00, /* # */ + 0x1E, 0x00, /* #### */ + 0x0F, 0xC0, /* ###### */ + 0x05, 0xF0, /* # ##### */ + 0x04, 0x30, /* # ## */ + 0x05, 0xF0, /* # ##### */ + 0x0F, 0xC0, /* ###### */ + 0x1E, 0x00, /* #### */ + 0x10, 0x00, /* # */ + + /* @402 'B' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1F, 0xF0, /* ######### */ + 0x0E, 0xE0, /* ### ### */ + + /* @416 'C' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + + /* @430 'D' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xC0, /* ##### */ + + /* @446 'E' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + + /* @458 'F' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + + /* @470 'G' (8 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x1F, 0x10, /* ##### # */ + 0x1F, 0x30, /* ##### ## */ + + /* @486 'H' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + + /* @502 'I' (2 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + + /* @506 'J' (4 pixels wide) */ + 0x40, 0x00, /* # */ + 0x40, 0x00, /* # */ + 0x7F, 0xF0, /* ########### */ + 0x3F, 0xF0, /* ########## */ + + /* @514 'K' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x03, 0x80, /* ### */ + 0x06, 0xC0, /* ## ## */ + 0x0C, 0x60, /* ## ## */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + + /* @530 'L' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @542 'M' (10 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x70, /* ### */ + 0x01, 0xC0, /* ### */ + 0x07, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x70, /* ### */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + + /* @562 'N' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x70, /* ### */ + 0x01, 0xC0, /* ### */ + 0x07, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + + /* @578 'O' (9 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xC0, /* ##### */ + + /* @596 'P' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x02, 0x10, /* # # */ + 0x02, 0x10, /* # # */ + 0x02, 0x10, /* # # */ + 0x03, 0xF0, /* ###### */ + 0x01, 0xE0, /* #### */ + + /* @610 'Q' (9 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x30, 0x10, /* ## # */ + 0x78, 0x30, /* #### ## */ + 0x4F, 0xE0, /* # ####### */ + 0x07, 0xC0, /* ##### */ + + /* @628 'R' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x03, 0x10, /* ## # */ + 0x0F, 0xF0, /* ######## */ + 0x1C, 0xE0, /* ### ### */ + 0x10, 0x00, /* # */ + + /* @644 'S' (7 pixels wide) */ + 0x18, 0xE0, /* ## ### */ + 0x11, 0xF0, /* # ##### */ + 0x11, 0x90, /* # ## # */ + 0x11, 0x90, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x1F, 0x30, /* ##### ## */ + 0x0E, 0x00, /* ### */ + + /* @658 'T' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + + /* @674 'U' (8 pixels wide) */ + 0x0F, 0xF0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x0F, 0xF0, /* ######## */ + + /* @690 'V' (9 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0xF0, /* #### */ + 0x07, 0xE0, /* ###### */ + 0x1F, 0x00, /* ##### */ + 0x18, 0x00, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x07, 0xE0, /* ###### */ + 0x00, 0xF0, /* #### */ + 0x00, 0x10, /* # */ + + /* @708 'W' (12 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x1F, 0xC0, /* ####### */ + 0x1E, 0x00, /* #### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0xF0, /* #### */ + 0x00, 0xF0, /* #### */ + 0x07, 0xC0, /* ##### */ + 0x1E, 0x00, /* #### */ + 0x1F, 0xC0, /* ####### */ + 0x03, 0xF0, /* ###### */ + 0x00, 0x30, /* ## */ + + /* @732 'X' (9 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x0E, 0xE0, /* ### ### */ + 0x07, 0xC0, /* ##### */ + 0x01, 0x00, /* # */ + 0x07, 0xC0, /* ##### */ + 0x0E, 0xE0, /* ### ### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + + /* @750 'Y' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x70, /* ### */ + 0x00, 0xE0, /* ### */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x70, /* ### */ + 0x00, 0x10, /* # */ + + /* @766 'Z' (8 pixels wide) */ + 0x18, 0x10, /* ## # */ + 0x1C, 0x10, /* ### # */ + 0x16, 0x10, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x11, 0x90, /* # ## # */ + 0x10, 0xD0, /* # ## # */ + 0x10, 0x70, /* # ### */ + 0x10, 0x30, /* # ## */ + + /* @782 '[' (4 pixels wide) */ + 0x3F, 0xF8, /* ########### */ + 0x3F, 0xF8, /* ########### */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + + /* @790 '\' (4 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x01, 0xE0, /* #### */ + 0x1E, 0x00, /* #### */ + 0x30, 0x00, /* ## */ + + /* @798 ']' (4 pixels wide) */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x3F, 0xF8, /* ########### */ + 0x3F, 0xF8, /* ########### */ + + /* @806 '^' (6 pixels wide) */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x30, /* ## */ + 0x00, 0x30, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + + /* @818 '_' (6 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + + /* @830 '`' (3 pixels wide) */ + 0x00, 0x08, /* # */ + 0x00, 0x18, /* ## */ + 0x00, 0x10, /* # */ + + /* @836 'a' (7 pixels wide) */ + 0x0C, 0x00, /* ## */ + 0x1E, 0x80, /* #### # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x1A, 0x40, /* ## # # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + + /* @850 'b' (7 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + + /* @864 'c' (6 pixels wide) */ + 0x07, 0x00, /* ### */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x08, 0x80, /* # # */ + + /* @876 'd' (7 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + + /* @890 'e' (7 pixels wide) */ + 0x07, 0x00, /* ### */ + 0x0F, 0x80, /* ##### */ + 0x1A, 0xC0, /* ## # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x13, 0xC0, /* # #### */ + 0x0B, 0x00, /* # ## */ + + /* @904 'f' (5 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x48, /* # # */ + 0x00, 0x48, /* # # */ + + /* @914 'g' (7 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x5F, 0xC0, /* # ####### */ + 0x90, 0xC0, /* # # ## */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0xFF, 0xC0, /* ########## */ + 0x7F, 0xC0, /* ######### */ + + /* @928 'h' (7 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0xC0, /* ## */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + + /* @942 'i' (2 pixels wide) */ + 0x1F, 0xD8, /* ####### ## */ + 0x1F, 0xD8, /* ####### ## */ + + /* @946 'j' (3 pixels wide) */ + 0x80, 0x00, /* # */ + 0xFF, 0xD8, /* ########## ## */ + 0x7F, 0xD8, /* ######### ## */ + + /* @952 'k' (7 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x03, 0x00, /* ## */ + 0x07, 0x80, /* #### */ + 0x0C, 0xC0, /* ## ## */ + 0x18, 0x40, /* ## # */ + 0x10, 0x00, /* # */ + + /* @966 'l' (2 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + + /* @970 'm' (10 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + + /* @990 'n' (7 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + + /* @1004 'o' (7 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + + /* @1018 'p' (7 pixels wide) */ + 0xFF, 0xC0, /* ########## */ + 0xFF, 0xC0, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + + /* @1032 'q' (7 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0xFF, 0xC0, /* ########## */ + 0xFF, 0xC0, /* ########## */ + + /* @1046 'r' (5 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + + /* @1056 's' (6 pixels wide) */ + 0x09, 0x80, /* # ## */ + 0x13, 0xC0, /* # #### */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x1E, 0x40, /* #### # */ + 0x0C, 0x80, /* ## # */ + + /* @1068 't' (5 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + + /* @1078 'u' (7 pixels wide) */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + + /* @1092 'v' (7 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0xC0, /* ##### */ + 0x1F, 0x00, /* ##### */ + 0x18, 0x00, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0xC0, /* ## */ + + /* @1106 'w' (10 pixels wide) */ + 0x01, 0xC0, /* ### */ + 0x0F, 0xC0, /* ###### */ + 0x1C, 0x00, /* ### */ + 0x1F, 0x00, /* ##### */ + 0x03, 0xC0, /* #### */ + 0x03, 0xC0, /* #### */ + 0x1F, 0x00, /* ##### */ + 0x1C, 0x00, /* ### */ + 0x0F, 0xC0, /* ###### */ + 0x01, 0xC0, /* ### */ + + /* @1126 'x' (7 pixels wide) */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + 0x07, 0x00, /* ### */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + + /* @1140 'y' (7 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x83, 0xC0, /* # #### */ + 0xCF, 0x00, /* ## #### */ + 0x7C, 0x00, /* ##### */ + 0x3F, 0x00, /* ###### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0xC0, /* ## */ + + /* @1154 'z' (6 pixels wide) */ + 0x18, 0x40, /* ## # */ + 0x1C, 0x40, /* ### # */ + 0x16, 0x40, /* # ## # */ + 0x13, 0x40, /* # ## # */ + 0x11, 0xC0, /* # ### */ + 0x10, 0xC0, /* # ## */ + + /* @1166 '{' (6 pixels wide) */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x3E, 0xF8, /* ##### ##### */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + + /* @1178 '|' (1 pixels wide) */ + 0xFF, 0xF0, /* ############ */ + + /* @1180 '}' (6 pixels wide) */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x3E, 0xF8, /* ##### ##### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + + /* @1192 '~' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ +}; + +/* Character descriptors for DejaVu Sans 9pt */ +/* { [Char width in bits], [Offset into dejaVuSansBold9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO dejaVuSansBold9ptCharDescriptors[] = +{ + {5, 0}, /* */ + {2, 10}, /* ! */ + {3, 14}, /* " */ + {8, 20}, /* # */ + {7, 36}, /* $ */ + {11, 50}, /* % */ + {9, 72}, /* & */ + {1, 90}, /* ' */ + {4, 92}, /* ( */ + {4, 100}, /* ) */ + {5, 108}, /* * */ + {7, 118}, /* + */ + {3, 132}, /* , */ + {4, 138}, /* - */ + {2, 146}, /* . */ + {4, 150}, /* / */ + {7, 158}, /* 0 */ + {6, 172}, /* 1 */ + {7, 184}, /* 2 */ + {7, 198}, /* 3 */ + {7, 212}, /* 4 */ + {7, 226}, /* 5 */ + {7, 240}, /* 6 */ + {7, 254}, /* 7 */ + {7, 268}, /* 8 */ + {7, 282}, /* 9 */ + {2, 296}, /* : */ + {3, 300}, /* ; */ + {8, 306}, /* < */ + {8, 322}, /* = */ + {8, 338}, /* > */ + {5, 354}, /* ? */ + {10, 364}, /* @ */ + {9, 384}, /* A */ + {7, 402}, /* B */ + {7, 416}, /* C */ + {8, 430}, /* D */ + {6, 446}, /* E */ + {6, 458}, /* F */ + {8, 470}, /* G */ + {8, 486}, /* H */ + {2, 502}, /* I */ + {4, 506}, /* J */ + {8, 514}, /* K */ + {6, 530}, /* L */ + {10, 542}, /* M */ + {8, 562}, /* N */ + {9, 578}, /* O */ + {7, 596}, /* P */ + {9, 610}, /* Q */ + {8, 628}, /* R */ + {7, 644}, /* S */ + {8, 658}, /* T */ + {8, 674}, /* U */ + {9, 690}, /* V */ + {12, 708}, /* W */ + {9, 732}, /* X */ + {8, 750}, /* Y */ + {8, 766}, /* Z */ + {4, 782}, /* [ */ + {4, 790}, /* \ */ + {4, 798}, /* ] */ + {6, 806}, /* ^ */ + {6, 818}, /* _ */ + {3, 830}, /* ` */ + {7, 836}, /* a */ + {7, 850}, /* b */ + {6, 864}, /* c */ + {7, 876}, /* d */ + {7, 890}, /* e */ + {5, 904}, /* f */ + {7, 914}, /* g */ + {7, 928}, /* h */ + {2, 942}, /* i */ + {3, 946}, /* j */ + {7, 952}, /* k */ + {2, 966}, /* l */ + {10, 970}, /* m */ + {7, 990}, /* n */ + {7, 1004}, /* o */ + {7, 1018}, /* p */ + {7, 1032}, /* q */ + {5, 1046}, /* r */ + {6, 1056}, /* s */ + {5, 1068}, /* t */ + {7, 1078}, /* u */ + {7, 1092}, /* v */ + {10, 1106}, /* w */ + {7, 1126}, /* x */ + {7, 1140}, /* y */ + {6, 1154}, /* z */ + {6, 1166}, /* { */ + {1, 1178}, /* | */ + {6, 1180}, /* } */ + {8, 1192}, /* ~ */ +}; + +/* Font information for DejaVu Sans 9pt */ +const FONT_INFO dejaVuSansBold9ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + dejaVuSansBold9ptCharDescriptors, /* Character decriptor array */ + dejaVuSansBold9ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/dejavusansbold9.h b/drivers/lcd/tft/fonts/dejavusansbold9.h new file mode 100644 index 0000000..d4b6490 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansbold9.h @@ -0,0 +1,10 @@ +#ifndef __DEJA_VU_SANS_BOLD_9__ +#define __DEJA_VU_SANS_BOLD_9__ + +#include "bitmapfonts.h" + +extern const uint8_t dejaVuSansBold9ptCharBitmaps[]; +extern const FONT_CHAR_INFO dejaVuSansBold9ptCharDescriptors[]; +extern const FONT_INFO dejaVuSansBold9ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/dejavusanscondensed9.c b/drivers/lcd/tft/fonts/dejavusanscondensed9.c new file mode 100644 index 0000000..204ca28 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusanscondensed9.c @@ -0,0 +1,774 @@ +#include "dejavusanscondensed9.h" + +/* +** Font data for DejaVu Sans Condensed 9pt +*/ + +/* Character bitmaps for DejaVu Sans Condensed 9pt */ +const uint8_t dejaVuSansCondensed9ptCharBitmaps[] = +{ + /* @0 ' ' (5 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @10 '!' (1 pixels wide) */ + 0x13, 0xF0, /* # ###### */ + + /* @12 '"' (3 pixels wide) */ + 0x00, 0xF0, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0xF0, /* #### */ + + /* @18 '#' (7 pixels wide) */ + 0x04, 0x80, /* # # */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xE0, /* ###### */ + 0x14, 0x90, /* # # # # */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xF0, /* # #### */ + 0x04, 0x80, /* # # */ + + /* @32 '$' (5 pixels wide) */ + 0x11, 0xC0, /* # ### */ + 0x11, 0x20, /* # # # */ + 0x7F, 0xF0, /* ########### */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + + /* @42 '%' (9 pixels wide) */ + 0x01, 0xF0, /* ##### */ + 0x11, 0x10, /* # # # */ + 0x08, 0xF0, /* # #### */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x0E, 0x60, /* ### ## */ + 0x11, 0x10, /* # # # */ + 0x12, 0x00, /* # # */ + 0x0C, 0x00, /* ## */ + + /* @60 '&' (7 pixels wide) */ + 0x0F, 0x00, /* #### */ + 0x10, 0xF0, /* # #### */ + 0x11, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x1C, 0x00, /* ### */ + 0x1E, 0x00, /* #### */ + 0x11, 0x00, /* # # */ + + /* @74 ''' (1 pixels wide) */ + 0x00, 0xF0, /* #### */ + + /* @76 '(' (2 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x60, 0x30, /* ## ## */ + + /* @80 ')' (2 pixels wide) */ + 0x60, 0x10, /* ## # */ + 0x1F, 0xE0, /* ######## */ + + /* @84 '*' (5 pixels wide) */ + 0x00, 0x20, /* # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + + /* @94 '+' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @108 ',' (1 pixels wide) */ + 0x30, 0x00, /* ## */ + + /* @110 '-' (2 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @114 '.' (1 pixels wide) */ + 0x10, 0x00, /* # */ + + /* @116 '/' (4 pixels wide) */ + 0x30, 0x00, /* ## */ + 0x0F, 0x00, /* #### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x10, /* # */ + + /* @124 '0' (5 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x30, /* # ## */ + 0x0F, 0xE0, /* ####### */ + + /* @134 '1' (5 pixels wide) */ + 0x10, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @144 '2' (5 pixels wide) */ + 0x18, 0x30, /* ## ## */ + 0x1C, 0x10, /* ### # */ + 0x12, 0x10, /* # # # */ + 0x11, 0xB0, /* # ## ## */ + 0x10, 0xE0, /* # ### */ + + /* @154 '3' (5 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0xB0, /* # ## ## */ + 0x0E, 0xE0, /* ### ### */ + + /* @164 '4' (5 pixels wide) */ + 0x07, 0x00, /* ### */ + 0x04, 0xC0, /* # ## */ + 0x04, 0x20, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + + /* @174 '5' (5 pixels wide) */ + 0x10, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x10, /* ## # # */ + 0x0F, 0x00, /* #### */ + + /* @184 '6' (5 pixels wide) */ + 0x0F, 0xC0, /* ###### */ + 0x11, 0x20, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x0F, 0x10, /* #### # */ + + /* @194 '7' (5 pixels wide) */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x0E, 0x10, /* ### # */ + 0x01, 0xD0, /* ### # */ + 0x00, 0x30, /* ## */ + + /* @204 '8' (5 pixels wide) */ + 0x1E, 0xE0, /* #### ### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @214 '9' (5 pixels wide) */ + 0x11, 0xE0, /* # #### */ + 0x12, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x1B, 0x30, /* ## ## ## */ + 0x0F, 0xE0, /* ####### */ + + /* @224 ':' (1 pixels wide) */ + 0x10, 0x80, /* # # */ + + /* @226 ';' (1 pixels wide) */ + 0x30, 0x80, /* ## # */ + + /* @228 '<' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x03, 0x00, /* ## */ + 0x05, 0x00, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x08, 0x80, /* # # */ + 0x08, 0x40, /* # # */ + + /* @242 '=' (7 pixels wide) */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + + /* @256 '>' (7 pixels wide) */ + 0x08, 0x40, /* # # */ + 0x08, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x05, 0x00, /* # # */ + 0x03, 0x00, /* ## */ + 0x02, 0x00, /* # */ + + /* @270 '?' (4 pixels wide) */ + 0x00, 0x10, /* # */ + 0x17, 0x10, /* # ### # */ + 0x01, 0x90, /* ## # */ + 0x00, 0x60, /* ## */ + + /* @278 '@' (9 pixels wide) */ + 0x1F, 0x80, /* ###### */ + 0x20, 0x40, /* # # */ + 0x47, 0x20, /* # ### # */ + 0x48, 0xA0, /* # # # # */ + 0x50, 0xA0, /* # # # # */ + 0x49, 0x20, /* # # # # */ + 0x4F, 0xA0, /* # ##### # */ + 0x08, 0x40, /* # # */ + 0x07, 0x80, /* #### */ + + /* @296 'A' (7 pixels wide) */ + 0x10, 0x00, /* # */ + 0x0E, 0x00, /* ### */ + 0x05, 0xC0, /* # ### */ + 0x04, 0x30, /* # ## */ + 0x04, 0xE0, /* # ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + + /* @310 'B' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1B, 0xE0, /* ## ##### */ + 0x04, 0x00, /* # */ + + /* @322 'C' (6 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x20, /* ## # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x20, /* # # */ + + /* @334 'D' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x20, /* ## # */ + 0x0C, 0xE0, /* ## ### */ + 0x03, 0x80, /* ### */ + + /* @348 'E' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + + /* @358 'F' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0x10, /* # */ + + /* @368 'G' (6 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x20, /* ## # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x1F, 0x30, /* ##### ## */ + + /* @380 'H' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + + /* @392 'I' (1 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + + /* @394 'J' (3 pixels wide) */ + 0x40, 0x00, /* # */ + 0x40, 0x00, /* # */ + 0x7F, 0xF0, /* ########### */ + + /* @400 'K' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x02, 0x80, /* # # */ + 0x04, 0x60, /* # ## */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + + /* @412 'L' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @422 'M' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x70, /* ### */ + 0x03, 0x80, /* ### */ + 0x04, 0x00, /* # */ + 0x03, 0x80, /* ### */ + 0x00, 0x60, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @436 'N' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x70, /* ### */ + 0x01, 0xC0, /* ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @448 'O' (7 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x20, /* ## # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x0C, 0x60, /* ## ## */ + 0x07, 0xC0, /* ##### */ + + /* @462 'P' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x02, 0x10, /* # # */ + 0x02, 0x10, /* # # */ + 0x01, 0x30, /* # ## */ + 0x01, 0xE0, /* #### */ + + /* @472 'Q' (7 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x20, /* ## # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x30, 0x10, /* ## # */ + 0x4C, 0x60, /* # ## ## */ + 0x07, 0xC0, /* ##### */ + + /* @486 'R' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x03, 0x30, /* ## ## */ + 0x0E, 0xE0, /* ### ### */ + 0x18, 0x00, /* ## */ + + /* @498 'S' (5 pixels wide) */ + 0x10, 0xE0, /* # ### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1E, 0x30, /* #### ## */ + + /* @508 'T' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + + /* @522 'U' (6 pixels wide) */ + 0x0F, 0xF0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x0F, 0xF0, /* ######## */ + + /* @534 'V' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0xE0, /* ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x0E, 0x00, /* ### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x70, /* ### */ + + /* @548 'W' (10 pixels wide) */ + 0x00, 0x10, /* # */ + 0x01, 0xE0, /* #### */ + 0x1E, 0x00, /* #### */ + 0x1E, 0x00, /* #### */ + 0x01, 0xE0, /* #### */ + 0x00, 0x70, /* ### */ + 0x07, 0x80, /* #### */ + 0x18, 0x00, /* ## */ + 0x0F, 0x00, /* #### */ + 0x00, 0xF0, /* #### */ + + /* @568 'X' (6 pixels wide) */ + 0x18, 0x30, /* ## ## */ + 0x06, 0x60, /* ## ## */ + 0x03, 0x80, /* ### */ + 0x06, 0xC0, /* ## ## */ + 0x0C, 0x60, /* ## ## */ + 0x10, 0x10, /* # # */ + + /* @580 'Y' (6 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x60, /* ## */ + 0x00, 0x80, /* # */ + 0x1F, 0x00, /* ##### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x30, /* ## */ + + /* @592 'Z' (7 pixels wide) */ + 0x10, 0x00, /* # */ + 0x18, 0x10, /* ## # */ + 0x16, 0x10, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x70, /* # ### */ + 0x10, 0x30, /* # ## */ + + /* @606 '[' (2 pixels wide) */ + 0x7F, 0xF0, /* ########### */ + 0x40, 0x10, /* # # */ + + /* @610 '\' (3 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x03, 0xC0, /* #### */ + 0x3C, 0x00, /* #### */ + + /* @616 ']' (2 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x7F, 0xF0, /* ########### */ + + /* @620 '^' (7 pixels wide) */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x80, /* # */ + + /* @634 '_' (6 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + + /* @646 '`' (2 pixels wide) */ + 0x00, 0x08, /* # */ + 0x00, 0x10, /* # */ + + /* @650 'a' (5 pixels wide) */ + 0x1E, 0x80, /* #### # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0A, 0x80, /* # # # */ + 0x1F, 0x00, /* ##### */ + + /* @660 'b' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x80, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x0F, 0x80, /* ##### */ + + /* @670 'c' (4 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0xC0, /* # ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + + /* @678 'd' (5 pixels wide) */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0xF0, /* ######### */ + + /* @688 'e' (5 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0xC0, /* # # ## */ + 0x13, 0x80, /* # ### */ + + /* @698 'f' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x50, /* # # */ + 0x00, 0x50, /* # # */ + + /* @706 'g' (5 pixels wide) */ + 0x4F, 0x80, /* # ##### */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0x48, 0x80, /* # # # */ + 0x3F, 0xC0, /* ######## */ + + /* @716 'h' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x80, /* ###### */ + + /* @726 'i' (1 pixels wide) */ + 0x1F, 0xD0, /* ####### # */ + + /* @728 'j' (2 pixels wide) */ + 0x80, 0x00, /* # */ + 0x7F, 0xD0, /* ######### # */ + + /* @732 'k' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x18, 0x80, /* ## # */ + 0x10, 0x40, /* # # */ + + /* @742 'l' (1 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + + /* @744 'm' (9 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x00, /* ##### */ + + /* @762 'n' (5 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x80, /* ###### */ + + /* @772 'o' (5 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x80, /* # # */ + 0x0F, 0x80, /* ##### */ + + /* @782 'p' (5 pixels wide) */ + 0xFF, 0xC0, /* ########## */ + 0x10, 0x80, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x0F, 0x80, /* ##### */ + + /* @792 'q' (5 pixels wide) */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x80, /* # # */ + 0xFF, 0xC0, /* ########## */ + + /* @802 'r' (3 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + + /* @808 's' (4 pixels wide) */ + 0x13, 0x80, /* # ### */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x1C, 0x80, /* ### # */ + + /* @816 't' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + + /* @824 'u' (5 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + + /* @834 'v' (6 pixels wide) */ + 0x00, 0x40, /* # */ + 0x03, 0x80, /* ### */ + 0x1C, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @846 'w' (8 pixels wide) */ + 0x00, 0x40, /* # */ + 0x07, 0x80, /* #### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x01, 0xC0, /* ### */ + 0x0E, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x03, 0xC0, /* #### */ + + /* @862 'x' (5 pixels wide) */ + 0x18, 0xC0, /* ## ## */ + 0x05, 0x00, /* # # */ + 0x07, 0x00, /* ### */ + 0x09, 0x80, /* # ## */ + 0x10, 0x40, /* # # */ + + /* @872 'y' (6 pixels wide) */ + 0x00, 0x40, /* # */ + 0x83, 0x80, /* # ### */ + 0x6C, 0x00, /* ## ## */ + 0x38, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @884 'z' (5 pixels wide) */ + 0x10, 0x00, /* # */ + 0x18, 0x40, /* ## # */ + 0x16, 0x40, /* # ## # */ + 0x13, 0x40, /* # ## # */ + 0x10, 0xC0, /* # ## */ + + /* @894 '{' (5 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x7D, 0xF0, /* ##### ##### */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + + /* @904 '|' (1 pixels wide) */ + 0xFF, 0xF0, /* ############ */ + + /* @906 '}' (5 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + 0x3D, 0xF0, /* #### ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @916 '~' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ +}; + +/* Character descriptors for DejaVu Sans Condensed 9pt */ +/* { [Char width in bits], [Offset into dejaVuSansCondensed9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO dejaVuSansCondensed9ptCharDescriptors[] = +{ + {5, 0}, /* */ + {1, 10}, /* ! */ + {3, 12}, /* " */ + {7, 18}, /* # */ + {5, 32}, /* $ */ + {9, 42}, /* % */ + {7, 60}, /* & */ + {1, 74}, /* ' */ + {2, 76}, /* ( */ + {2, 80}, /* ) */ + {5, 84}, /* * */ + {7, 94}, /* + */ + {1, 108}, /* , */ + {2, 110}, /* - */ + {1, 114}, /* . */ + {4, 116}, /* / */ + {5, 124}, /* 0 */ + {5, 134}, /* 1 */ + {5, 144}, /* 2 */ + {5, 154}, /* 3 */ + {5, 164}, /* 4 */ + {5, 174}, /* 5 */ + {5, 184}, /* 6 */ + {5, 194}, /* 7 */ + {5, 204}, /* 8 */ + {5, 214}, /* 9 */ + {1, 224}, /* : */ + {1, 226}, /* ; */ + {7, 228}, /* < */ + {7, 242}, /* = */ + {7, 256}, /* > */ + {4, 270}, /* ? */ + {9, 278}, /* @ */ + {7, 296}, /* A */ + {6, 310}, /* B */ + {6, 322}, /* C */ + {7, 334}, /* D */ + {5, 348}, /* E */ + {5, 358}, /* F */ + {6, 368}, /* G */ + {6, 380}, /* H */ + {1, 392}, /* I */ + {3, 394}, /* J */ + {6, 400}, /* K */ + {5, 412}, /* L */ + {7, 422}, /* M */ + {6, 436}, /* N */ + {7, 448}, /* O */ + {5, 462}, /* P */ + {7, 472}, /* Q */ + {6, 486}, /* R */ + {5, 498}, /* S */ + {7, 508}, /* T */ + {6, 522}, /* U */ + {7, 534}, /* V */ + {10, 548}, /* W */ + {6, 568}, /* X */ + {6, 580}, /* Y */ + {7, 592}, /* Z */ + {2, 606}, /* [ */ + {3, 610}, /* \ */ + {2, 616}, /* ] */ + {7, 620}, /* ^ */ + {6, 634}, /* _ */ + {2, 646}, /* ` */ + {5, 650}, /* a */ + {5, 660}, /* b */ + {4, 670}, /* c */ + {5, 678}, /* d */ + {5, 688}, /* e */ + {4, 698}, /* f */ + {5, 706}, /* g */ + {5, 716}, /* h */ + {1, 726}, /* i */ + {2, 728}, /* j */ + {5, 732}, /* k */ + {1, 742}, /* l */ + {9, 744}, /* m */ + {5, 762}, /* n */ + {5, 772}, /* o */ + {5, 782}, /* p */ + {5, 792}, /* q */ + {3, 802}, /* r */ + {4, 808}, /* s */ + {4, 816}, /* t */ + {5, 824}, /* u */ + {6, 834}, /* v */ + {8, 846}, /* w */ + {5, 862}, /* x */ + {6, 872}, /* y */ + {5, 884}, /* z */ + {5, 894}, /* { */ + {1, 904}, /* | */ + {5, 906}, /* } */ + {7, 916}, /* ~ */ +}; + +/* Font information for DejaVu Sans Condensed 9pt */ +const FONT_INFO dejaVuSansCondensed9ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + dejaVuSansCondensed9ptCharDescriptors, /* Character decriptor array */ + dejaVuSansCondensed9ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/dejavusanscondensed9.h b/drivers/lcd/tft/fonts/dejavusanscondensed9.h new file mode 100644 index 0000000..a07c51f --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusanscondensed9.h @@ -0,0 +1,10 @@ +#ifndef __DEJA_VU_SANS_CONDENSED_9__ +#define __DEJA_VU_SANS_CONDENSED_9__ + +#include "bitmapfonts.h" + +extern const uint8_t dejaVuSansCondensed9ptCharBitmaps[]; +extern const FONT_CHAR_INFO dejaVuSansCondensed9ptCharDescriptors[]; +extern const FONT_INFO dejaVuSansCondensed9ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/dejavusansmono8.c b/drivers/lcd/tft/fonts/dejavusansmono8.c new file mode 100644 index 0000000..cf758ee --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansmono8.c @@ -0,0 +1,1069 @@ +#include "dejavusansmono8.h" + +/* +** Font data for DejaVu Sans Mono 8pt +*/ + +/* Character bitmaps for DejaVu Sans Mono 8pt */ +const uint8_t dejaVuSansMono8ptCharBitmaps[] = +{ + /* @0 ' ' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @16 '!' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x17, 0xE0, /* # ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @32 '"' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @48 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x14, 0xA0, /* # # # # */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @64 '$' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x11, 0x80, /* # ## */ + 0x12, 0x40, /* # # # */ + 0x7F, 0xE0, /* ########## */ + 0x12, 0x40, /* # # # */ + 0x0C, 0x40, /* ## # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @80 '%' (8 pixels wide) */ + 0x04, 0xE0, /* # ### */ + 0x04, 0xA0, /* # # # */ + 0x02, 0xE0, /* # ### */ + 0x1D, 0x00, /* ### # */ + 0x15, 0x00, /* # # # */ + 0x1C, 0x80, /* ### # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @96 '&' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0x00, /* ### */ + 0x11, 0xE0, /* # #### */ + 0x13, 0x20, /* # ## # */ + 0x16, 0x20, /* # ## # */ + 0x18, 0x00, /* ## */ + 0x16, 0x00, /* # ## */ + 0x00, 0x00, /* */ + + /* @112 ''' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @128 '(' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x20, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 ')' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x30, 0x30, /* ## ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @160 '*' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x40, /* # # */ + 0x01, 0x80, /* ## */ + 0x07, 0xE0, /* ###### */ + 0x01, 0x80, /* ## */ + 0x02, 0x40, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @176 '+' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x0F, 0x80, /* ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @192 ',' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @208 '-' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @224 '.' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @240 '/' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @256 '0' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x18, 0x60, /* ## ## */ + 0x10, 0x20, /* # # */ + 0x11, 0x20, /* # # # */ + 0x18, 0x60, /* ## ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @272 '1' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @288 '2' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x40, /* # # */ + 0x18, 0x20, /* ## # */ + 0x14, 0x20, /* # # # */ + 0x16, 0x20, /* # ## # */ + 0x13, 0x20, /* # ## # */ + 0x11, 0xC0, /* # ### */ + 0x00, 0x00, /* */ + + /* @304 '3' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x40, /* # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x13, 0x20, /* # ## # */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @320 '4' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x06, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x04, 0x80, /* # # */ + 0x04, 0x60, /* # ## */ + 0x1F, 0xE0, /* ######## */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @336 '5' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x11, 0xE0, /* # #### */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x13, 0x20, /* # ## # */ + 0x0E, 0x00, /* ### */ + 0x00, 0x00, /* */ + + /* @352 '6' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x12, 0x40, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x0E, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @368 '7' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x10, 0x20, /* # # */ + 0x0C, 0x20, /* ## # */ + 0x03, 0x20, /* ## # */ + 0x00, 0xE0, /* ### */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @384 '8' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0xC0, /* ### ## */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @400 '9' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x11, 0xC0, /* # ### */ + 0x12, 0x20, /* # # # */ + 0x12, 0x20, /* # # # */ + 0x12, 0x20, /* # # # */ + 0x0A, 0x20, /* # # # */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @416 ':' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @432 ';' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @448 '<' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x09, 0x00, /* # # */ + 0x08, 0x80, /* # # */ + 0x00, 0x00, /* */ + + /* @464 '=' (8 pixels wide) */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @480 '>' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x80, /* # # */ + 0x09, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @496 '?' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x17, 0x20, /* # ### # */ + 0x01, 0xA0, /* ## # */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @512 '@' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x30, 0x40, /* ## # */ + 0x46, 0x20, /* # ## # */ + 0x49, 0x20, /* # # # # */ + 0x49, 0x60, /* # # # ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @528 'A' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x04, 0xE0, /* # ### */ + 0x04, 0xE0, /* # ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @544 'B' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @560 'C' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x08, 0x40, /* # # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x08, 0x40, /* # # */ + 0x00, 0x00, /* */ + + /* @576 'D' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x08, 0x40, /* # # */ + 0x07, 0x80, /* #### */ + 0x00, 0x00, /* */ + + /* @592 'E' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x00, 0x00, /* */ + + /* @608 'F' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @624 'G' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x08, 0x40, /* # # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + 0x00, 0x00, /* */ + + /* @640 'H' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @656 'I' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @672 'J' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @688 'K' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0x00, /* # */ + 0x02, 0x80, /* # # */ + 0x04, 0x40, /* # # */ + 0x08, 0x20, /* # # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @704 'L' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @720 'M' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0xC0, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0xC0, /* ## */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @736 'N' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x40, /* # */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x08, 0x00, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @752 'O' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x18, 0x60, /* ## ## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x18, 0x60, /* ## ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @768 'P' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x01, 0xC0, /* ### */ + 0x00, 0x00, /* */ + + /* @784 'Q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x18, 0x60, /* ## ## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x38, 0x60, /* ### ## */ + 0x2F, 0xC0, /* # ###### */ + 0x00, 0x00, /* */ + + /* @800 'R' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x06, 0x20, /* ## # */ + 0x09, 0xC0, /* # ### */ + 0x10, 0x00, /* # */ + + /* @816 'S' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0xC0, /* # ### */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + 0x00, 0x00, /* */ + + /* @832 'T' (8 pixels wide) */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @848 'U' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xE0, /* ####### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @864 'V' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x60, /* ## */ + 0x03, 0x80, /* ### */ + 0x1C, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x03, 0x80, /* ### */ + 0x00, 0x60, /* ## */ + 0x00, 0x00, /* */ + + /* @880 'W' (8 pixels wide) */ + 0x01, 0xE0, /* #### */ + 0x1E, 0x00, /* #### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x1E, 0x00, /* #### */ + 0x01, 0xE0, /* #### */ + 0x00, 0x00, /* */ + + /* @896 'X' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x0C, 0xC0, /* ## ## */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @912 'Y' (8 pixels wide) */ + 0x00, 0x20, /* # */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @928 'Z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x1C, 0x20, /* ### # */ + 0x16, 0x20, /* # ## # */ + 0x11, 0x20, /* # # # */ + 0x10, 0xE0, /* # ### */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @944 '[' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xF0, /* ########## */ + 0x20, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @960 '\' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x20, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @976 ']' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x10, /* # # */ + 0x3F, 0xF0, /* ########## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @992 '^' (8 pixels wide) */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1008 '_' (8 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1024 '`' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1040 'a' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x80, /* ## # */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1056 'b' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1072 'c' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x19, 0x80, /* ## ## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1088 'd' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1104 'e' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x13, 0x00, /* # ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1120 'f' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x90, /* # # */ + 0x00, 0x90, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1136 'g' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x50, 0x80, /* # # # */ + 0x50, 0x80, /* # # # */ + 0x50, 0x80, /* # # # */ + 0x3F, 0x80, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1152 'h' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1168 'i' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0x90, /* ###### # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1184 'j' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x80, /* # # */ + 0x40, 0x80, /* # # */ + 0x3F, 0x90, /* ####### # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1200 'k' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x08, 0x80, /* # # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1216 'l' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1232 'm' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1248 'n' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x01, 0x00, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1264 'o' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1280 'p' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x7F, 0x80, /* ######## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1296 'q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x7F, 0x80, /* ######## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1312 'r' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1328 's' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x13, 0x00, /* # ## */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x14, 0x80, /* # # # */ + 0x0C, 0x80, /* ## # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1344 't' (8 pixels wide) */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1360 'u' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1376 'v' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x80, /* ## */ + 0x0E, 0x00, /* ### */ + 0x10, 0x00, /* # */ + 0x0E, 0x00, /* ### */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1392 'w' (8 pixels wide) */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + + /* @1408 'x' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x80, /* # # */ + 0x19, 0x80, /* ## ## */ + 0x06, 0x00, /* ## */ + 0x19, 0x80, /* ## ## */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1424 'y' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x41, 0x80, /* # ## */ + 0x6E, 0x00, /* ## ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1440 'z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x80, /* # # */ + 0x1C, 0x80, /* ### # */ + 0x16, 0x80, /* # ## # */ + 0x13, 0x80, /* # ### */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1456 '{' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x3E, 0xF0, /* ##### #### */ + 0x20, 0x10, /* # # */ + 0x20, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1472 '|' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x7F, 0xF0, /* ########### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1488 '}' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x10, /* # # */ + 0x20, 0x10, /* # # */ + 0x3E, 0xF0, /* ##### #### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1504 '~' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ +}; + +/* Character descriptors for DejaVu Sans Mono 8pt */ +/* { [Char width in bits], [Offset into dejaVuSansMono8ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO dejaVuSansMono8ptCharDescriptors[] = +{ + {8, 0}, /* */ + {8, 16}, /* ! */ + {8, 32}, /* " */ + {8, 48}, /* # */ + {8, 64}, /* $ */ + {8, 80}, /* % */ + {8, 96}, /* & */ + {8, 112}, /* ' */ + {8, 128}, /* ( */ + {8, 144}, /* ) */ + {8, 160}, /* * */ + {8, 176}, /* + */ + {8, 192}, /* , */ + {8, 208}, /* - */ + {8, 224}, /* . */ + {8, 240}, /* / */ + {8, 256}, /* 0 */ + {8, 272}, /* 1 */ + {8, 288}, /* 2 */ + {8, 304}, /* 3 */ + {8, 320}, /* 4 */ + {8, 336}, /* 5 */ + {8, 352}, /* 6 */ + {8, 368}, /* 7 */ + {8, 384}, /* 8 */ + {8, 400}, /* 9 */ + {8, 416}, /* : */ + {8, 432}, /* ; */ + {8, 448}, /* < */ + {8, 464}, /* = */ + {8, 480}, /* > */ + {8, 496}, /* ? */ + {8, 512}, /* @ */ + {8, 528}, /* A */ + {8, 544}, /* B */ + {8, 560}, /* C */ + {8, 576}, /* D */ + {8, 592}, /* E */ + {8, 608}, /* F */ + {8, 624}, /* G */ + {8, 640}, /* H */ + {8, 656}, /* I */ + {8, 672}, /* J */ + {8, 688}, /* K */ + {8, 704}, /* L */ + {8, 720}, /* M */ + {8, 736}, /* N */ + {8, 752}, /* O */ + {8, 768}, /* P */ + {8, 784}, /* Q */ + {8, 800}, /* R */ + {8, 816}, /* S */ + {8, 832}, /* T */ + {8, 848}, /* U */ + {8, 864}, /* V */ + {8, 880}, /* W */ + {8, 896}, /* X */ + {8, 912}, /* Y */ + {8, 928}, /* Z */ + {8, 944}, /* [ */ + {8, 960}, /* \ */ + {8, 976}, /* ] */ + {8, 992}, /* ^ */ + {8, 1008}, /* _ */ + {8, 1024}, /* ` */ + {8, 1040}, /* a */ + {8, 1056}, /* b */ + {8, 1072}, /* c */ + {8, 1088}, /* d */ + {8, 1104}, /* e */ + {8, 1120}, /* f */ + {8, 1136}, /* g */ + {8, 1152}, /* h */ + {8, 1168}, /* i */ + {8, 1184}, /* j */ + {8, 1200}, /* k */ + {8, 1216}, /* l */ + {8, 1232}, /* m */ + {8, 1248}, /* n */ + {8, 1264}, /* o */ + {8, 1280}, /* p */ + {8, 1296}, /* q */ + {8, 1312}, /* r */ + {8, 1328}, /* s */ + {8, 1344}, /* t */ + {8, 1360}, /* u */ + {8, 1376}, /* v */ + {8, 1392}, /* w */ + {8, 1408}, /* x */ + {8, 1424}, /* y */ + {8, 1440}, /* z */ + {8, 1456}, /* { */ + {8, 1472}, /* | */ + {8, 1488}, /* } */ + {8, 1504}, /* ~ */ +}; + +/* Font information for DejaVu Sans Mono 8pt */ +const FONT_INFO dejaVuSansMono8ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + dejaVuSansMono8ptCharDescriptors, /* Character decriptor array */ + dejaVuSansMono8ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/dejavusansmono8.h b/drivers/lcd/tft/fonts/dejavusansmono8.h new file mode 100644 index 0000000..3310409 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansmono8.h @@ -0,0 +1,10 @@ +#ifndef __DEJA_VU_SANS_MONO_8__ +#define __DEJA_VU_SANS_MONO_8__ + +#include "bitmapfonts.h" + +extern const uint8_t dejaVuSansMono8ptCharBitmaps[]; +extern const FONT_CHAR_INFO dejaVuSansMono8ptCharDescriptors[]; +extern const FONT_INFO dejaVuSansMono8ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/dejavusansmonobold8.c b/drivers/lcd/tft/fonts/dejavusansmonobold8.c new file mode 100644 index 0000000..0a057e6 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansmonobold8.c @@ -0,0 +1,1069 @@ +#include "dejavusansmonobold8.h" + +/* +** Font data for DejaVu Sans Mono Bold 8pt +*/ + +/* Character bitmaps for DejaVu Sans Mono Bold 8pt */ +const uint8_t dejaVuSansMonoBold8ptCharBitmaps[] = +{ + /* @0 ' ' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @16 '!' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1B, 0xE0, /* ## ##### */ + 0x1B, 0xE0, /* ## ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @32 '"' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @48 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x1C, 0xE0, /* ### ### */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @64 '$' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0x80, /* # ## */ + 0x13, 0x40, /* # ## # */ + 0x7F, 0xE0, /* ########## */ + 0x13, 0x40, /* # ## # */ + 0x0E, 0x00, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @80 '%' (8 pixels wide) */ + 0x04, 0xE0, /* # ### */ + 0x04, 0xA0, /* # # # */ + 0x02, 0xE0, /* # ### */ + 0x1D, 0x00, /* ### # */ + 0x15, 0x00, /* # # # */ + 0x1C, 0x80, /* ### # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @96 '&' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0x00, /* ### */ + 0x1E, 0xE0, /* #### ### */ + 0x13, 0xE0, /* # ##### */ + 0x1E, 0x20, /* #### # */ + 0x18, 0x00, /* ## */ + 0x1E, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @112 ''' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @128 '(' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x1F, 0xE0, /* ######## */ + 0x30, 0x30, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 ')' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x30, 0x30, /* ## ## */ + 0x1F, 0xE0, /* ######## */ + 0x07, 0x80, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @160 '*' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x40, /* # # */ + 0x01, 0x80, /* ## */ + 0x07, 0xE0, /* ###### */ + 0x01, 0x80, /* ## */ + 0x02, 0x40, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @176 '+' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x0F, 0x80, /* ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @192 ',' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x38, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @208 '-' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @224 '.' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @240 '/' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @256 '0' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x11, 0x20, /* # # # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @272 '1' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @288 '2' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x18, 0x20, /* ## # */ + 0x14, 0x20, /* # # # */ + 0x13, 0x20, /* # ## # */ + 0x11, 0xE0, /* # #### */ + 0x10, 0xC0, /* # ## */ + 0x00, 0x00, /* */ + + /* @304 '3' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x1E, 0xE0, /* #### ### */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @320 '4' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x06, 0x00, /* ## */ + 0x05, 0x80, /* # ## */ + 0x04, 0xC0, /* # ## */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @336 '5' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x11, 0xE0, /* # #### */ + 0x11, 0xE0, /* # #### */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x1F, 0x20, /* ##### # */ + 0x0E, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @352 '6' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xE0, /* ######## */ + 0x11, 0x60, /* # # ## */ + 0x11, 0x20, /* # # # */ + 0x1F, 0x20, /* ##### # */ + 0x0E, 0x00, /* ### */ + 0x00, 0x00, /* */ + + /* @368 '7' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x10, 0x20, /* # # */ + 0x1E, 0x20, /* #### # */ + 0x0F, 0xA0, /* ##### # */ + 0x03, 0xE0, /* ##### */ + 0x00, 0x60, /* ## */ + 0x00, 0x00, /* */ + + /* @384 '8' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0xC0, /* ### ## */ + 0x1E, 0xE0, /* #### ### */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x1E, 0xE0, /* #### ### */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @400 '9' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xC0, /* ### */ + 0x13, 0xE0, /* # ##### */ + 0x12, 0x20, /* # # # */ + 0x1A, 0x20, /* ## # # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @416 ':' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x19, 0x80, /* ## ## */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @432 ';' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x39, 0x80, /* ### ## */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @448 '<' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x0F, 0x00, /* #### */ + 0x09, 0x00, /* # # */ + 0x09, 0x00, /* # # */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + + /* @464 '=' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x00, 0x00, /* */ + + /* @480 '>' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x19, 0x80, /* ## ## */ + 0x09, 0x00, /* # # */ + 0x09, 0x00, /* # # */ + 0x0F, 0x00, /* #### */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @496 '?' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x40, /* # */ + 0x1B, 0x20, /* ## ## # */ + 0x1A, 0xE0, /* ## # ### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @512 '@' (8 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x27, 0x20, /* # ### # */ + 0x28, 0xA0, /* # # # # */ + 0x28, 0xA0, /* # # # # */ + 0x3F, 0xC0, /* ######## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @528 'A' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x1F, 0x80, /* ###### */ + 0x05, 0xE0, /* # #### */ + 0x05, 0xE0, /* # #### */ + 0x1F, 0x80, /* ###### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @544 'B' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x1E, 0xE0, /* #### ### */ + 0x0E, 0xC0, /* ### ## */ + 0x00, 0x00, /* */ + + /* @560 'C' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x0F, 0xC0, /* ###### */ + 0x18, 0x60, /* ## ## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x08, 0x40, /* # # */ + 0x00, 0x00, /* */ + + /* @576 'D' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @592 'E' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x11, 0x20, /* # # # */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @608 'F' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @624 'G' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x0F, 0xC0, /* ###### */ + 0x18, 0x60, /* ## ## */ + 0x12, 0x20, /* # # # */ + 0x1E, 0x20, /* #### # */ + 0x1E, 0x40, /* #### # */ + 0x00, 0x00, /* */ + + /* @640 'H' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @656 'I' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @672 'J' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @688 'K' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x03, 0x00, /* ## */ + 0x07, 0xC0, /* ##### */ + 0x1C, 0x60, /* ### ## */ + 0x18, 0x20, /* ## # */ + 0x00, 0x00, /* */ + + /* @704 'L' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @720 'M' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xC0, /* ####### */ + 0x03, 0x80, /* ### */ + 0x03, 0x80, /* ### */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @736 'N' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0xC0, /* ### */ + 0x0E, 0x00, /* ### */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x00, /* */ + + /* @752 'O' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @768 'P' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x03, 0xE0, /* ##### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x00, /* */ + + /* @784 'Q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x20, /* # # */ + 0x10, 0x20, /* # # */ + 0x3F, 0xE0, /* ######### */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @800 'R' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x0D, 0xE0, /* ## #### */ + 0x19, 0xC0, /* ## ### */ + 0x10, 0x00, /* # */ + + /* @816 'S' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0xC0, /* # ### */ + 0x11, 0xE0, /* # #### */ + 0x13, 0x20, /* # ## # */ + 0x13, 0x20, /* # ## # */ + 0x1E, 0x20, /* #### # */ + 0x0E, 0x40, /* ### # */ + 0x00, 0x00, /* */ + + /* @832 'T' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x00, 0x20, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @848 'U' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xE0, /* ####### */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @864 'V' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x60, /* ## */ + 0x07, 0xE0, /* ###### */ + 0x1E, 0x00, /* #### */ + 0x1E, 0x00, /* #### */ + 0x07, 0xE0, /* ###### */ + 0x00, 0x60, /* ## */ + 0x00, 0x00, /* */ + + /* @880 'W' (8 pixels wide) */ + 0x01, 0xE0, /* #### */ + 0x1F, 0xE0, /* ######## */ + 0x1E, 0x00, /* #### */ + 0x01, 0x80, /* ## */ + 0x1E, 0x00, /* #### */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0xE0, /* #### */ + 0x00, 0x00, /* */ + + /* @896 'X' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x1C, 0xE0, /* ### ### */ + 0x07, 0x80, /* #### */ + 0x07, 0x80, /* #### */ + 0x1C, 0xE0, /* ### ### */ + 0x10, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @912 'Y' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x01, 0xE0, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x01, 0xE0, /* #### */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + + /* @928 'Z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x20, /* ## # */ + 0x1C, 0x20, /* ### # */ + 0x17, 0x20, /* # ### # */ + 0x11, 0xA0, /* # ## # */ + 0x10, 0xE0, /* # ### */ + 0x10, 0x60, /* # ## */ + 0x00, 0x00, /* */ + + /* @944 '[' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xF0, /* ########## */ + 0x3F, 0xF0, /* ########## */ + 0x20, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @960 '\' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x20, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @976 ']' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x10, /* # # */ + 0x3F, 0xF0, /* ########## */ + 0x3F, 0xF0, /* ########## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @992 '^' (8 pixels wide) */ + 0x00, 0x80, /* # */ + 0x00, 0xC0, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1008 '_' (8 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1024 '`' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x30, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1040 'a' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x00, /* ## */ + 0x1E, 0x80, /* #### # */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + + /* @1056 'b' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0x80, /* ###### */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @1072 'c' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x19, 0x80, /* ## ## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + + /* @1088 'd' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @1104 'e' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x12, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x13, 0x80, /* # ### */ + 0x13, 0x00, /* # ## */ + 0x00, 0x00, /* */ + + /* @1120 'f' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x90, /* # # */ + 0x00, 0x90, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1136 'g' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x5F, 0x80, /* # ###### */ + 0x50, 0x80, /* # # # */ + 0x50, 0x80, /* # # # */ + 0x7F, 0x80, /* ######## */ + 0x3F, 0x80, /* ####### */ + 0x00, 0x00, /* */ + + /* @1152 'h' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + + /* @1168 'i' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0xB0, /* ###### ## */ + 0x1F, 0xB0, /* ###### ## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1184 'j' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x80, /* # # */ + 0x40, 0x80, /* # # */ + 0x7F, 0xB0, /* ######## ## */ + 0x3F, 0xB0, /* ####### ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1200 'k' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x02, 0x00, /* # */ + 0x0F, 0x80, /* ##### */ + 0x18, 0x80, /* ## # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1216 'l' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1232 'm' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + + /* @1248 'n' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x00, /* ##### */ + 0x00, 0x00, /* */ + + /* @1264 'o' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0x80, /* ###### */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @1280 'p' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x7F, 0x80, /* ######## */ + 0x7F, 0x80, /* ######## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x1F, 0x80, /* ###### */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @1296 'q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x00, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x7F, 0x80, /* ######## */ + 0x7F, 0x80, /* ######## */ + 0x00, 0x00, /* */ + + /* @1312 'r' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @1328 's' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0B, 0x00, /* # ## */ + 0x13, 0x80, /* # ### */ + 0x16, 0x80, /* # ## # */ + 0x16, 0x80, /* # ## # */ + 0x1C, 0x80, /* ### # */ + 0x0D, 0x00, /* ## # */ + 0x00, 0x00, /* */ + + /* @1344 't' (8 pixels wide) */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xE0, /* ######## */ + 0x10, 0x80, /* # # */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1360 'u' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0x80, /* ###### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + + /* @1376 'v' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x80, /* ## */ + 0x0F, 0x80, /* ##### */ + 0x1C, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x0F, 0x80, /* ##### */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + + /* @1392 'w' (8 pixels wide) */ + 0x01, 0x80, /* ## */ + 0x1F, 0x80, /* ###### */ + 0x1C, 0x00, /* ### */ + 0x02, 0x00, /* # */ + 0x1C, 0x00, /* ### */ + 0x1F, 0x80, /* ###### */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + + /* @1408 'x' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x80, /* # # */ + 0x19, 0x80, /* ## ## */ + 0x0F, 0x00, /* #### */ + 0x0F, 0x00, /* #### */ + 0x19, 0x80, /* ## ## */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + + /* @1424 'y' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x80, /* # # */ + 0x47, 0x80, /* # #### */ + 0x7C, 0x00, /* ##### */ + 0x1E, 0x00, /* #### */ + 0x07, 0x80, /* #### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @1440 'z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x80, /* ## # */ + 0x1C, 0x80, /* ### # */ + 0x14, 0x80, /* # # # */ + 0x12, 0x80, /* # # # */ + 0x13, 0x80, /* # ### */ + 0x11, 0x80, /* # ## */ + 0x00, 0x00, /* */ + + /* @1456 '{' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x3D, 0xF0, /* #### ##### */ + 0x3D, 0xF0, /* #### ##### */ + 0x20, 0x10, /* # # */ + 0x20, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @1472 '|' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x7F, 0xF0, /* ########### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1488 '}' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x10, /* # # */ + 0x20, 0x10, /* # # */ + 0x3D, 0xF0, /* #### ##### */ + 0x3D, 0xF0, /* #### ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1504 '~' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ +}; + +/* Character descriptors for DejaVu Sans Mono 8pt */ +/* { [Char width in bits], [Offset into dejaVuSansMonoBold8ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO dejaVuSansMonoBold8ptCharDescriptors[] = +{ + {8, 0}, /* */ + {8, 16}, /* ! */ + {8, 32}, /* " */ + {8, 48}, /* # */ + {8, 64}, /* $ */ + {8, 80}, /* % */ + {8, 96}, /* & */ + {8, 112}, /* ' */ + {8, 128}, /* ( */ + {8, 144}, /* ) */ + {8, 160}, /* * */ + {8, 176}, /* + */ + {8, 192}, /* , */ + {8, 208}, /* - */ + {8, 224}, /* . */ + {8, 240}, /* / */ + {8, 256}, /* 0 */ + {8, 272}, /* 1 */ + {8, 288}, /* 2 */ + {8, 304}, /* 3 */ + {8, 320}, /* 4 */ + {8, 336}, /* 5 */ + {8, 352}, /* 6 */ + {8, 368}, /* 7 */ + {8, 384}, /* 8 */ + {8, 400}, /* 9 */ + {8, 416}, /* : */ + {8, 432}, /* ; */ + {8, 448}, /* < */ + {8, 464}, /* = */ + {8, 480}, /* > */ + {8, 496}, /* ? */ + {8, 512}, /* @ */ + {8, 528}, /* A */ + {8, 544}, /* B */ + {8, 560}, /* C */ + {8, 576}, /* D */ + {8, 592}, /* E */ + {8, 608}, /* F */ + {8, 624}, /* G */ + {8, 640}, /* H */ + {8, 656}, /* I */ + {8, 672}, /* J */ + {8, 688}, /* K */ + {8, 704}, /* L */ + {8, 720}, /* M */ + {8, 736}, /* N */ + {8, 752}, /* O */ + {8, 768}, /* P */ + {8, 784}, /* Q */ + {8, 800}, /* R */ + {8, 816}, /* S */ + {8, 832}, /* T */ + {8, 848}, /* U */ + {8, 864}, /* V */ + {8, 880}, /* W */ + {8, 896}, /* X */ + {8, 912}, /* Y */ + {8, 928}, /* Z */ + {8, 944}, /* [ */ + {8, 960}, /* \ */ + {8, 976}, /* ] */ + {8, 992}, /* ^ */ + {8, 1008}, /* _ */ + {8, 1024}, /* ` */ + {8, 1040}, /* a */ + {8, 1056}, /* b */ + {8, 1072}, /* c */ + {8, 1088}, /* d */ + {8, 1104}, /* e */ + {8, 1120}, /* f */ + {8, 1136}, /* g */ + {8, 1152}, /* h */ + {8, 1168}, /* i */ + {8, 1184}, /* j */ + {8, 1200}, /* k */ + {8, 1216}, /* l */ + {8, 1232}, /* m */ + {8, 1248}, /* n */ + {8, 1264}, /* o */ + {8, 1280}, /* p */ + {8, 1296}, /* q */ + {8, 1312}, /* r */ + {8, 1328}, /* s */ + {8, 1344}, /* t */ + {8, 1360}, /* u */ + {8, 1376}, /* v */ + {8, 1392}, /* w */ + {8, 1408}, /* x */ + {8, 1424}, /* y */ + {8, 1440}, /* z */ + {8, 1456}, /* { */ + {8, 1472}, /* | */ + {8, 1488}, /* } */ + {8, 1504}, /* ~ */ +}; + +/* Font information for DejaVu Sans Mono 8pt */ +const FONT_INFO dejaVuSansMonoBold8ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + dejaVuSansMonoBold8ptCharDescriptors, /* Character decriptor array */ + dejaVuSansMonoBold8ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/dejavusansmonobold8.h b/drivers/lcd/tft/fonts/dejavusansmonobold8.h new file mode 100644 index 0000000..4a38f88 --- /dev/null +++ b/drivers/lcd/tft/fonts/dejavusansmonobold8.h @@ -0,0 +1,10 @@ +#ifndef __DEJA_VU_SANS_MONO_BOLD_8__ +#define __DEJA_VU_SANS_MONO_BOLD_8__ + +#include "bitmapfonts.h" + +extern const uint8_t dejaVuSansMonoBold8ptCharBitmaps[]; +extern const FONT_CHAR_INFO dejaVuSansMonoBold8ptCharDescriptors[]; +extern const FONT_INFO dejaVuSansMonoBold8ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/veramono11.c b/drivers/lcd/tft/fonts/veramono11.c new file mode 100644 index 0000000..082b7c7 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramono11.c @@ -0,0 +1,1164 @@ +#include "veramono11.h" + +/* +** Font data for Bitstream Vera Sans Mono 11pt +*/ + +/* Character bitmaps for Bitstream Vera Sans Mono 11pt */ +const uint8_t bitstreamVeraSansMono11ptCharBitmaps[] = +{ + /* @0 ' ' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @18 '!' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0C, 0xFE, /* ## ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @36 '"' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @54 '#' (9 pixels wide) */ + 0x01, 0x00, /* # */ + 0x09, 0x10, /* # # # */ + 0x07, 0xD0, /* ##### # */ + 0x01, 0x7C, /* # ##### */ + 0x0D, 0x16, /* ## # # ## */ + 0x07, 0xD0, /* ##### # */ + 0x01, 0x7C, /* # ##### */ + 0x01, 0x16, /* # # ## */ + 0x00, 0x10, /* # */ + + /* @72 '$' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x38, /* # ### */ + 0x08, 0x4C, /* # # ## */ + 0x08, 0x44, /* # # # */ + 0x3F, 0xFF, /* ############## */ + 0x08, 0x84, /* # # # */ + 0x08, 0x84, /* # # # */ + 0x07, 0x08, /* ### # */ + 0x00, 0x00, /* */ + + /* @90 '%' (9 pixels wide) */ + 0x00, 0x1C, /* ### */ + 0x00, 0xA2, /* # # # */ + 0x00, 0xA2, /* # # # */ + 0x00, 0x62, /* ## # */ + 0x07, 0x5C, /* ### # ### */ + 0x08, 0xC0, /* # ## */ + 0x08, 0xA0, /* # # # */ + 0x08, 0xA0, /* # # # */ + 0x07, 0x00, /* ### */ + + /* @108 '&' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x04, 0x7C, /* # ##### */ + 0x08, 0x32, /* # ## # */ + 0x08, 0x62, /* # ## # */ + 0x09, 0x82, /* # ## # */ + 0x07, 0x02, /* ### # */ + 0x09, 0xC0, /* # ### */ + 0x00, 0x00, /* */ + + /* @126 ''' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 '(' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x07, 0xF0, /* ####### */ + 0x18, 0x0C, /* ## ## */ + 0x20, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @162 ')' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x02, /* # # */ + 0x18, 0x0C, /* ## ## */ + 0x07, 0xF0, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @180 '*' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x24, /* # # */ + 0x00, 0x28, /* # # */ + 0x00, 0x18, /* ## */ + 0x00, 0x7E, /* ###### */ + 0x00, 0x18, /* ## */ + 0x00, 0x28, /* # # */ + 0x00, 0x24, /* # # */ + 0x00, 0x00, /* */ + + /* @198 '+' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x07, 0xF0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @216 ',' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x1C, 0x00, /* ### */ + 0x0C, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @234 '-' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @252 '.' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0C, 0x00, /* ## */ + 0x0C, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @270 '/' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x00, /* # */ + 0x0C, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0x30, /* ## */ + 0x00, 0x0C, /* ## */ + 0x00, 0x02, /* # */ + 0x00, 0x00, /* */ + + /* @288 '0' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x04, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x62, /* # ## # */ + 0x08, 0x62, /* # ## # */ + 0x04, 0x04, /* # # */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @306 '1' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x08, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @324 '2' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x0C, /* # ## */ + 0x0C, 0x06, /* ## ## */ + 0x0A, 0x02, /* # # # */ + 0x09, 0x02, /* # # # */ + 0x08, 0x82, /* # # # */ + 0x08, 0x44, /* # # # */ + 0x08, 0x3C, /* # #### */ + 0x00, 0x00, /* */ + + /* @342 '3' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x0C, 0xA4, /* ## # # # */ + 0x07, 0xBC, /* #### #### */ + 0x00, 0x00, /* */ + + /* @360 '4' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x80, /* ## */ + 0x01, 0x60, /* # ## */ + 0x01, 0x30, /* # ## */ + 0x01, 0x0C, /* # ## */ + 0x01, 0x06, /* # ## */ + 0x0F, 0xFE, /* ########### */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @378 '5' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x7E, /* # ###### */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x04, 0x42, /* # # # */ + 0x03, 0x80, /* ### */ + 0x00, 0x00, /* */ + + /* @396 '6' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF0, /* ###### */ + 0x04, 0x4C, /* # # ## */ + 0x08, 0x26, /* # # ## */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x0C, 0x62, /* ## ## # */ + 0x07, 0xC4, /* ##### # */ + 0x00, 0x00, /* */ + + /* @414 '7' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x08, 0x02, /* # # */ + 0x06, 0x02, /* ## # */ + 0x01, 0x82, /* ## # */ + 0x00, 0x62, /* ## # */ + 0x00, 0x1E, /* #### */ + 0x00, 0x06, /* ## */ + 0x00, 0x00, /* */ + + /* @432 '8' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xBC, /* #### #### */ + 0x0C, 0xA6, /* ## # # ## */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x0C, 0xA6, /* ## # # ## */ + 0x07, 0xBC, /* #### #### */ + 0x00, 0x00, /* */ + + /* @450 '9' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x7C, /* # ##### */ + 0x08, 0xC6, /* # ## ## */ + 0x08, 0x82, /* # # # */ + 0x08, 0x82, /* # # # */ + 0x0C, 0x82, /* ## # # */ + 0x06, 0x44, /* ## # # */ + 0x01, 0xF8, /* ###### */ + 0x00, 0x00, /* */ + + /* @468 ':' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0C, 0x30, /* ## ## */ + 0x0C, 0x30, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @486 ';' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x1C, 0x30, /* ### ## */ + 0x0C, 0x30, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @504 '<' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x01, 0xC0, /* ### */ + 0x01, 0x40, /* # # */ + 0x01, 0x40, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x04, 0x10, /* # # */ + + /* @522 '=' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + 0x01, 0x20, /* # # */ + + /* @540 '>' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x10, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x01, 0x40, /* # # */ + 0x01, 0x40, /* # # */ + 0x01, 0xC0, /* ### */ + 0x00, 0x80, /* # */ + + /* @558 '?' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x04, /* # */ + 0x00, 0x02, /* # */ + 0x0D, 0xC2, /* ## ### # */ + 0x00, 0x42, /* # # */ + 0x00, 0x22, /* # # */ + 0x00, 0x1C, /* ### */ + 0x00, 0x00, /* */ + + /* @576 '@' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xE0, /* ###### */ + 0x18, 0x18, /* ## ## */ + 0x10, 0x0C, /* # ## */ + 0x23, 0xC4, /* # #### # */ + 0x24, 0x24, /* # # # # */ + 0x24, 0x2C, /* # # # ## */ + 0x07, 0xF8, /* ######## */ + 0x00, 0x00, /* */ + + /* @594 'A' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x00, /* ## */ + 0x03, 0xC0, /* #### */ + 0x01, 0x3C, /* # #### */ + 0x01, 0x02, /* # # */ + 0x01, 0x3C, /* # #### */ + 0x03, 0xC0, /* #### */ + 0x0C, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @612 'B' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x0C, 0xE6, /* ## ### ## */ + 0x07, 0xBC, /* #### #### */ + 0x00, 0x00, /* */ + + /* @630 'C' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xF0, /* ##### */ + 0x06, 0x0C, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x04, 0x04, /* # # */ + 0x00, 0x00, /* */ + + /* @648 'D' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0C, 0x06, /* ## ## */ + 0x06, 0x0C, /* ## ## */ + 0x01, 0xF0, /* ##### */ + 0x00, 0x00, /* */ + + /* @666 'E' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x00, 0x00, /* */ + + /* @684 'F' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x00, /* */ + + /* @702 'G' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xF0, /* ##### */ + 0x06, 0x0C, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x07, 0xC4, /* ##### # */ + 0x00, 0x00, /* */ + + /* @720 'H' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @738 'I' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @756 'J' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0C, 0x02, /* ## # */ + 0x07, 0xFE, /* ########## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @774 'K' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0xD0, /* ## # */ + 0x01, 0x08, /* # # */ + 0x06, 0x04, /* ## # */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + + /* @792 'L' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @810 'M' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x0E, /* ### */ + 0x00, 0x70, /* ### */ + 0x00, 0x80, /* # */ + 0x00, 0x70, /* ### */ + 0x00, 0x0E, /* ### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @828 'N' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x06, /* ## */ + 0x00, 0x18, /* ## */ + 0x00, 0xE0, /* ### */ + 0x03, 0x00, /* ## */ + 0x0C, 0x00, /* ## */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @846 'O' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x04, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x04, 0x04, /* # # */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @864 'P' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x82, /* # # */ + 0x00, 0x82, /* # # */ + 0x00, 0x82, /* # # */ + 0x00, 0x82, /* # # */ + 0x00, 0xC6, /* ## ## */ + 0x00, 0x7C, /* ##### */ + 0x00, 0x00, /* */ + + /* @882 'Q' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x04, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x18, 0x02, /* ## # */ + 0x3C, 0x04, /* #### # */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @900 'R' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0xA6, /* # # ## */ + 0x07, 0x3C, /* ### #### */ + 0x08, 0x00, /* # */ + + /* @918 'S' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x3C, /* # #### */ + 0x0C, 0x24, /* ## # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x0C, 0x86, /* ## # ## */ + 0x07, 0x84, /* #### # */ + 0x00, 0x00, /* */ + + /* @936 'T' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x00, /* */ + + /* @954 'U' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xFE, /* ########## */ + 0x0C, 0x00, /* ## */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x0C, 0x00, /* ## */ + 0x07, 0xFE, /* ########## */ + 0x00, 0x00, /* */ + + /* @972 'V' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x06, /* ## */ + 0x00, 0x78, /* #### */ + 0x07, 0x80, /* #### */ + 0x08, 0x00, /* # */ + 0x07, 0x80, /* #### */ + 0x00, 0x78, /* #### */ + 0x00, 0x06, /* ## */ + 0x00, 0x00, /* */ + + /* @990 'W' (9 pixels wide) */ + 0x00, 0x0E, /* ### */ + 0x03, 0xF0, /* ###### */ + 0x0C, 0x00, /* ## */ + 0x03, 0xE0, /* ##### */ + 0x00, 0x10, /* # */ + 0x03, 0xE0, /* ##### */ + 0x0C, 0x00, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0x0E, /* ### */ + + /* @1008 'X' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x02, /* # # */ + 0x06, 0x0C, /* ## ## */ + 0x01, 0xB0, /* ## ## */ + 0x00, 0x40, /* # */ + 0x01, 0xB0, /* ## ## */ + 0x06, 0x0C, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + + /* @1026 'Y' (9 pixels wide) */ + 0x00, 0x02, /* # */ + 0x00, 0x06, /* ## */ + 0x00, 0x18, /* ## */ + 0x00, 0x30, /* ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x20, /* # */ + 0x00, 0x18, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x02, /* # */ + + /* @1044 'Z' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x02, /* ## # */ + 0x0A, 0x02, /* # # # */ + 0x09, 0x82, /* # ## # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x32, /* # ## # */ + 0x08, 0x0A, /* # # # */ + 0x08, 0x06, /* # ## */ + 0x00, 0x00, /* */ + + /* @1062 '[' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xFE, /* ############# */ + 0x20, 0x02, /* # # */ + 0x20, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1080 '\' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x00, 0x0C, /* ## */ + 0x00, 0x30, /* ## */ + 0x00, 0xC0, /* ## */ + 0x03, 0x00, /* ## */ + 0x0C, 0x00, /* ## */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1098 ']' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x02, /* # # */ + 0x20, 0x02, /* # # */ + 0x3F, 0xFE, /* ############# */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1116 '^' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x18, /* ## */ + 0x00, 0x0C, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x0C, /* ## */ + 0x00, 0x18, /* ## */ + 0x00, 0x10, /* # */ + + /* @1134 '_' (9 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1152 '`' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x01, /* # */ + 0x00, 0x03, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x04, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1170 'a' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x00, /* ### */ + 0x09, 0xA0, /* # ## # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x04, 0x90, /* # # # */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1188 'b' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x03, 0xC0, /* #### */ + 0x00, 0x00, /* */ + + /* @1206 'c' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1224 'd' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @1242 'e' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x05, 0x20, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x09, 0x20, /* # # # */ + 0x05, 0xC0, /* # ### */ + 0x00, 0x00, /* */ + + /* @1260 'f' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xFC, /* ########## */ + 0x00, 0x12, /* # # */ + 0x00, 0x12, /* # # */ + 0x00, 0x12, /* # # */ + 0x00, 0x00, /* */ + + /* @1278 'g' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x24, 0x20, /* # # # */ + 0x48, 0x10, /* # # # */ + 0x48, 0x10, /* # # # */ + 0x48, 0x10, /* # # # */ + 0x64, 0x20, /* ## # # */ + 0x3F, 0xF0, /* ########## */ + 0x00, 0x00, /* */ + + /* @1296 'h' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x30, /* ## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1314 'i' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x0F, 0xF6, /* ######## ## */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1332 'j' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + 0x3F, 0xF6, /* ########## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1350 'k' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x80, /* # */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + 0x02, 0x20, /* # # */ + 0x04, 0x10, /* # # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1368 'l' (9 pixels wide) */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x07, 0xFE, /* ########## */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1386 'm' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1404 'n' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x30, /* ## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1422 'o' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x03, 0xC0, /* #### */ + 0x00, 0x00, /* */ + + /* @1440 'p' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x7F, 0xF0, /* ########### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x03, 0xC0, /* #### */ + 0x00, 0x00, /* */ + + /* @1458 'q' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x04, 0x20, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x7F, 0xF0, /* ########### */ + 0x00, 0x00, /* */ + + /* @1476 'r' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + + /* @1494 's' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0xE0, /* # ### */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x07, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @1512 't' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x07, 0xFC, /* ######### */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1530 'u' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xF0, /* ####### */ + 0x0C, 0x00, /* ## */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + + /* @1548 'v' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0xE0, /* ### */ + 0x07, 0x00, /* ### */ + 0x08, 0x00, /* # */ + 0x07, 0x00, /* ### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @1566 'w' (9 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x03, 0xC0, /* #### */ + 0x0C, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0xC0, /* ## */ + 0x03, 0x00, /* ## */ + 0x0C, 0x00, /* ## */ + 0x03, 0xC0, /* #### */ + 0x00, 0x30, /* ## */ + + /* @1584 'x' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x02, 0x40, /* # # */ + 0x01, 0x80, /* ## */ + 0x02, 0x40, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @1602 'y' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x40, 0xE0, /* # ### */ + 0x43, 0x00, /* # ## */ + 0x3C, 0x00, /* #### */ + 0x07, 0x00, /* ### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @1620 'z' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x10, /* ## # */ + 0x0A, 0x10, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x09, 0x10, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x50, /* # # # */ + 0x08, 0x30, /* # ## */ + 0x00, 0x00, /* */ + + /* @1638 '{' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x3F, 0x7C, /* ###### ##### */ + 0x40, 0x02, /* # # */ + 0x40, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1656 '|' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0xFF, 0xFE, /* ############### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1674 '}' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x40, 0x02, /* # # */ + 0x40, 0x02, /* # # */ + 0x3F, 0x7C, /* ###### ##### */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1692 '~' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ +}; + +/* Character descriptors for Bitstream Vera Sans Mono 11pt */ +/* { [Char width in bits], [Offset into bitstreamVeraSansMono11ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO bitstreamVeraSansMono11ptCharDescriptors[] = +{ + {9, 0}, /* */ + {9, 18}, /* ! */ + {9, 36}, /* " */ + {9, 54}, /* # */ + {9, 72}, /* $ */ + {9, 90}, /* % */ + {9, 108}, /* & */ + {9, 126}, /* ' */ + {9, 144}, /* ( */ + {9, 162}, /* ) */ + {9, 180}, /* * */ + {9, 198}, /* + */ + {9, 216}, /* , */ + {9, 234}, /* - */ + {9, 252}, /* . */ + {9, 270}, /* / */ + {9, 288}, /* 0 */ + {9, 306}, /* 1 */ + {9, 324}, /* 2 */ + {9, 342}, /* 3 */ + {9, 360}, /* 4 */ + {9, 378}, /* 5 */ + {9, 396}, /* 6 */ + {9, 414}, /* 7 */ + {9, 432}, /* 8 */ + {9, 450}, /* 9 */ + {9, 468}, /* : */ + {9, 486}, /* ; */ + {9, 504}, /* < */ + {9, 522}, /* = */ + {9, 540}, /* > */ + {9, 558}, /* ? */ + {9, 576}, /* @ */ + {9, 594}, /* A */ + {9, 612}, /* B */ + {9, 630}, /* C */ + {9, 648}, /* D */ + {9, 666}, /* E */ + {9, 684}, /* F */ + {9, 702}, /* G */ + {9, 720}, /* H */ + {9, 738}, /* I */ + {9, 756}, /* J */ + {9, 774}, /* K */ + {9, 792}, /* L */ + {9, 810}, /* M */ + {9, 828}, /* N */ + {9, 846}, /* O */ + {9, 864}, /* P */ + {9, 882}, /* Q */ + {9, 900}, /* R */ + {9, 918}, /* S */ + {9, 936}, /* T */ + {9, 954}, /* U */ + {9, 972}, /* V */ + {9, 990}, /* W */ + {9, 1008}, /* X */ + {9, 1026}, /* Y */ + {9, 1044}, /* Z */ + {9, 1062}, /* [ */ + {9, 1080}, /* \ */ + {9, 1098}, /* ] */ + {9, 1116}, /* ^ */ + {9, 1134}, /* _ */ + {9, 1152}, /* ` */ + {9, 1170}, /* a */ + {9, 1188}, /* b */ + {9, 1206}, /* c */ + {9, 1224}, /* d */ + {9, 1242}, /* e */ + {9, 1260}, /* f */ + {9, 1278}, /* g */ + {9, 1296}, /* h */ + {9, 1314}, /* i */ + {9, 1332}, /* j */ + {9, 1350}, /* k */ + {9, 1368}, /* l */ + {9, 1386}, /* m */ + {9, 1404}, /* n */ + {9, 1422}, /* o */ + {9, 1440}, /* p */ + {9, 1458}, /* q */ + {9, 1476}, /* r */ + {9, 1494}, /* s */ + {9, 1512}, /* t */ + {9, 1530}, /* u */ + {9, 1548}, /* v */ + {9, 1566}, /* w */ + {9, 1584}, /* x */ + {9, 1602}, /* y */ + {9, 1620}, /* z */ + {9, 1638}, /* { */ + {9, 1656}, /* | */ + {9, 1674}, /* } */ + {9, 1692}, /* ~ */ +}; + +/* Font information for Bitstream Vera Sans Mono 11pt */ +const FONT_INFO bitstreamVeraSansMono11ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + bitstreamVeraSansMono11ptCharDescriptors, /* Character decriptor array */ + bitstreamVeraSansMono11ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/veramono11.h b/drivers/lcd/tft/fonts/veramono11.h new file mode 100644 index 0000000..a887041 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramono11.h @@ -0,0 +1,11 @@ +#ifndef __VERA_MONO_11__ +#define __VERA_MONO_11__ + +#include "bitmapfonts.h" + +/* Font data for Bitstream Vera Sans Mono 11pt */ +extern const uint8_t bitstreamVeraSansMono11ptCharBitmaps[]; +extern const FONT_CHAR_INFO bitstreamVeraSansMono11ptCharDescriptors[]; +extern const FONT_INFO bitstreamVeraSansMono11ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/veramono9.c b/drivers/lcd/tft/fonts/veramono9.c new file mode 100644 index 0000000..569f39a --- /dev/null +++ b/drivers/lcd/tft/fonts/veramono9.c @@ -0,0 +1,1069 @@ +#include "veramono9.h" + +/* +** Font data for Bitstream Vera Sans Mono 9pt +*/ + +/* Character bitmaps for Bitstream Vera Sans Mono 9pt */ +const uint8_t bitstreamVeraSansMono9ptCharBitmaps[] = +{ + /* @0 ' ' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @16 '!' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1B, 0xF0, /* ## ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @32 '"' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @48 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x14, 0xA0, /* # # # # */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @64 '$' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0xC0, /* # ### */ + 0x11, 0x20, /* # # # */ + 0x7F, 0xF0, /* ########### */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @80 '%' (8 pixels wide) */ + 0x00, 0x60, /* ## */ + 0x02, 0x90, /* # # # */ + 0x02, 0x90, /* # # # */ + 0x0D, 0x60, /* ## # ## */ + 0x13, 0x00, /* # ## */ + 0x12, 0x80, /* # # # */ + 0x0C, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @96 '&' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0x00, /* ### */ + 0x19, 0xE0, /* ## #### */ + 0x11, 0x90, /* # ## # */ + 0x16, 0x10, /* # ## # */ + 0x0C, 0x10, /* ## # */ + 0x16, 0x00, /* # ## */ + 0x00, 0x00, /* */ + + /* @112 ''' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @128 '(' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x38, 0x38, /* ### ### */ + 0x20, 0x08, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 ')' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x08, /* # # */ + 0x38, 0x38, /* ### ### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @160 '*' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x20, /* # # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @176 '+' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @192 ',' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @208 '-' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @224 '.' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @240 '/' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @256 '0' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x18, 0x30, /* ## ## */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @272 '1' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @288 '2' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x20, /* # # */ + 0x18, 0x10, /* ## # */ + 0x14, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x10, 0xE0, /* # ### */ + 0x00, 0x00, /* */ + + /* @304 '3' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @320 '4' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x06, 0x00, /* ## */ + 0x05, 0x80, /* # ## */ + 0x04, 0xC0, /* # ## */ + 0x04, 0x30, /* # ## */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @336 '5' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @352 '6' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x19, 0x20, /* ## # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x20, /* #### # */ + 0x00, 0x00, /* */ + + /* @368 '7' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x0C, 0x10, /* ## # */ + 0x03, 0x10, /* ## # */ + 0x00, 0xF0, /* #### */ + 0x00, 0x30, /* ## */ + 0x00, 0x00, /* */ + + /* @384 '8' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0xE0, /* ### ### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @400 '9' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0xE0, /* # #### */ + 0x13, 0x30, /* # ## ## */ + 0x12, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x09, 0x30, /* # # ## */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @416 ':' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @432 ';' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @448 '<' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x08, 0x40, /* # # */ + 0x00, 0x00, /* */ + + /* @464 '=' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x00, 0x00, /* */ + + /* @480 '>' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x40, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @496 '?' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x1B, 0x10, /* ## ## # */ + 0x01, 0x90, /* ## # */ + 0x00, 0x90, /* # # */ + 0x00, 0x60, /* ## */ + 0x00, 0x00, /* */ + + /* @512 '@' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0x80, /* ###### */ + 0x30, 0x40, /* ## # */ + 0x46, 0x20, /* # ## # */ + 0x49, 0x20, /* # # # # */ + 0x49, 0x60, /* # # # ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @528 'A' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x07, 0x80, /* #### */ + 0x04, 0x70, /* # ### */ + 0x04, 0x70, /* # ### */ + 0x07, 0x80, /* #### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @544 'B' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @560 'C' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @576 'D' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @592 'E' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x00, 0x00, /* */ + + /* @608 'F' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @624 'G' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x0F, 0x20, /* #### # */ + 0x00, 0x00, /* */ + + /* @640 'H' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @656 'I' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @672 'J' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @688 'K' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x80, /* ## */ + 0x06, 0x40, /* ## # */ + 0x0C, 0x20, /* ## # */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @704 'L' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @720 'M' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x60, /* ## */ + 0x03, 0x80, /* ### */ + 0x03, 0x80, /* ### */ + 0x00, 0x60, /* ## */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @736 'N' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x30, /* ## */ + 0x01, 0xC0, /* ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @752 'O' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @768 'P' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + + /* @784 'Q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x78, 0x30, /* #### ## */ + 0x0F, 0xC0, /* ###### */ + 0x00, 0x00, /* */ + + /* @800 'R' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x03, 0x10, /* ## # */ + 0x0C, 0xE0, /* ## ### */ + 0x10, 0x00, /* # */ + + /* @816 'S' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0xE0, /* # ### */ + 0x11, 0x90, /* # ## # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @832 'T' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @848 'U' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + + /* @864 'V' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x30, /* ## */ + 0x03, 0xC0, /* #### */ + 0x1C, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x03, 0xC0, /* #### */ + 0x00, 0x30, /* ## */ + 0x00, 0x00, /* */ + + /* @880 'W' (8 pixels wide) */ + 0x03, 0xF0, /* ###### */ + 0x1C, 0x00, /* ### */ + 0x07, 0x80, /* #### */ + 0x00, 0x60, /* ## */ + 0x07, 0x80, /* #### */ + 0x1C, 0x00, /* ### */ + 0x03, 0xF0, /* ###### */ + 0x00, 0x00, /* */ + + /* @896 'X' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x0C, 0x60, /* ## ## */ + 0x03, 0x80, /* ### */ + 0x03, 0x80, /* ### */ + 0x0C, 0x60, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @912 'Y' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @928 'Z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x10, /* ## # */ + 0x1C, 0x10, /* ### # */ + 0x13, 0x10, /* # ## # */ + 0x11, 0x90, /* # ## # */ + 0x10, 0x70, /* # ### */ + 0x10, 0x30, /* # ## */ + 0x00, 0x00, /* */ + + /* @944 '[' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xF8, /* ########### */ + 0x20, 0x08, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @960 '\' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x20, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @976 ']' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x08, /* # # */ + 0x3F, 0xF8, /* ########### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @992 '^' (8 pixels wide) */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1008 '_' (8 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1024 '`' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x08, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1040 'a' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x80, /* ## # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1056 'b' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1072 'c' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x80, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1088 'd' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1104 'e' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x12, 0xC0, /* # # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0B, 0x80, /* # ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1120 'f' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x48, /* # # */ + 0x00, 0x48, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1136 'g' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x58, 0x40, /* # ## # */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0x7F, 0xC0, /* ######### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1152 'h' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1168 'i' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC8, /* ####### # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1184 'j' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x80, 0x40, /* # # */ + 0x80, 0x40, /* # # */ + 0x7F, 0xC8, /* ######### # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1200 'k' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x08, 0x80, /* # # */ + 0x10, 0x40, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1216 'l' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x08, /* # */ + 0x00, 0x08, /* # */ + 0x0F, 0xF8, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1232 'm' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1248 'n' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1264 'o' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1280 'p' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0xFF, 0xC0, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1296 'q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0xFF, 0xC0, /* ########## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1312 'r' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @1328 's' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0x80, /* # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0C, 0x80, /* ## # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1344 't' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1360 'u' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1376 'v' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1392 'w' (8 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x1C, 0x00, /* ### */ + 0x03, 0x00, /* ## */ + 0x1C, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x00, /* */ + + /* @1408 'x' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x40, /* # # */ + 0x0D, 0x80, /* ## ## */ + 0x02, 0x00, /* # */ + 0x0D, 0x80, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1424 'y' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x80, 0xC0, /* # ## */ + 0xCF, 0x00, /* ## #### */ + 0x38, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1440 'z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x40, /* ## # */ + 0x14, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x11, 0x40, /* # # # */ + 0x10, 0xC0, /* # ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1456 '{' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x3E, 0xF8, /* ##### ##### */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1472 '|' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x7F, 0xF8, /* ############ */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1488 '}' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x3E, 0xF8, /* ##### ##### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1504 '~' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ +}; + +/* Character descriptors for Bitstream Vera Sans Mono 9pt */ +/* { [Char width in bits], [Offset into bitstreamVeraSansMono9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO bitstreamVeraSansMono9ptCharDescriptors[] = +{ + {8, 0}, /* */ + {8, 16}, /* ! */ + {8, 32}, /* " */ + {8, 48}, /* # */ + {8, 64}, /* $ */ + {8, 80}, /* % */ + {8, 96}, /* & */ + {8, 112}, /* ' */ + {8, 128}, /* ( */ + {8, 144}, /* ) */ + {8, 160}, /* * */ + {8, 176}, /* + */ + {8, 192}, /* , */ + {8, 208}, /* - */ + {8, 224}, /* . */ + {8, 240}, /* / */ + {8, 256}, /* 0 */ + {8, 272}, /* 1 */ + {8, 288}, /* 2 */ + {8, 304}, /* 3 */ + {8, 320}, /* 4 */ + {8, 336}, /* 5 */ + {8, 352}, /* 6 */ + {8, 368}, /* 7 */ + {8, 384}, /* 8 */ + {8, 400}, /* 9 */ + {8, 416}, /* : */ + {8, 432}, /* ; */ + {8, 448}, /* < */ + {8, 464}, /* = */ + {8, 480}, /* > */ + {8, 496}, /* ? */ + {8, 512}, /* @ */ + {8, 528}, /* A */ + {8, 544}, /* B */ + {8, 560}, /* C */ + {8, 576}, /* D */ + {8, 592}, /* E */ + {8, 608}, /* F */ + {8, 624}, /* G */ + {8, 640}, /* H */ + {8, 656}, /* I */ + {8, 672}, /* J */ + {8, 688}, /* K */ + {8, 704}, /* L */ + {8, 720}, /* M */ + {8, 736}, /* N */ + {8, 752}, /* O */ + {8, 768}, /* P */ + {8, 784}, /* Q */ + {8, 800}, /* R */ + {8, 816}, /* S */ + {8, 832}, /* T */ + {8, 848}, /* U */ + {8, 864}, /* V */ + {8, 880}, /* W */ + {8, 896}, /* X */ + {8, 912}, /* Y */ + {8, 928}, /* Z */ + {8, 944}, /* [ */ + {8, 960}, /* \ */ + {8, 976}, /* ] */ + {8, 992}, /* ^ */ + {8, 1008}, /* _ */ + {8, 1024}, /* ` */ + {8, 1040}, /* a */ + {8, 1056}, /* b */ + {8, 1072}, /* c */ + {8, 1088}, /* d */ + {8, 1104}, /* e */ + {8, 1120}, /* f */ + {8, 1136}, /* g */ + {8, 1152}, /* h */ + {8, 1168}, /* i */ + {8, 1184}, /* j */ + {8, 1200}, /* k */ + {8, 1216}, /* l */ + {8, 1232}, /* m */ + {8, 1248}, /* n */ + {8, 1264}, /* o */ + {8, 1280}, /* p */ + {8, 1296}, /* q */ + {8, 1312}, /* r */ + {8, 1328}, /* s */ + {8, 1344}, /* t */ + {8, 1360}, /* u */ + {8, 1376}, /* v */ + {8, 1392}, /* w */ + {8, 1408}, /* x */ + {8, 1424}, /* y */ + {8, 1440}, /* z */ + {8, 1456}, /* { */ + {8, 1472}, /* | */ + {8, 1488}, /* } */ + {8, 1504}, /* ~ */ +}; + +/* Font information for Bitstream Vera Sans Mono 9pt */ +const FONT_INFO bitstreamVeraSansMono9ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + bitstreamVeraSansMono9ptCharDescriptors, /* Character decriptor array */ + bitstreamVeraSansMono9ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/veramono9.h b/drivers/lcd/tft/fonts/veramono9.h new file mode 100644 index 0000000..96c82c6 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramono9.h @@ -0,0 +1,11 @@ +#ifndef __VERA_MONO_9__ +#define __VERA_MONO_9__ + +#include "bitmapfonts.h" + +/* Font data for Bitstream Vera Sans Mono 9pt */ +extern const uint8_t bitstreamVeraSansMono9ptCharBitmaps[]; +extern const FONT_CHAR_INFO bitstreamVeraSansMono9ptCharDescriptors[]; +extern const FONT_INFO bitstreamVeraSansMono9ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/veramonobold11.c b/drivers/lcd/tft/fonts/veramonobold11.c new file mode 100644 index 0000000..d1abeb8 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramonobold11.c @@ -0,0 +1,1164 @@ +#include "veramonobold11.h" + +/* +** Font data for Bitstream Vera Sans Mono Bold 11pt +*/ + +/* Character bitmaps for Bitstream Vera Sans Mono Bold 11pt */ +const uint8_t bitstreamVeraSansMonoBold11ptCharBitmaps[] = +{ + /* @0 ' ' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @18 '!' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0D, 0xFE, /* ## ######## */ + 0x0D, 0xFE, /* ## ######## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @36 '"' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + + /* @54 '#' (9 pixels wide) */ + 0x01, 0x00, /* # */ + 0x0F, 0x10, /* #### # */ + 0x0F, 0xF0, /* ######## */ + 0x01, 0xFE, /* ######## */ + 0x0D, 0x1E, /* ## # #### */ + 0x0F, 0xF0, /* ######## */ + 0x01, 0xFC, /* ####### */ + 0x01, 0x1E, /* # #### */ + 0x00, 0x10, /* # */ + + /* @72 '$' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x38, /* # ### */ + 0x0C, 0x7C, /* ## ##### */ + 0x08, 0x64, /* # ## # */ + 0x3F, 0xFF, /* ############## */ + 0x08, 0xC4, /* # ## # */ + 0x0F, 0xCC, /* ###### ## */ + 0x07, 0x80, /* #### */ + 0x00, 0x00, /* */ + + /* @90 '%' (9 pixels wide) */ + 0x00, 0x9C, /* # ### */ + 0x00, 0xA2, /* # # # */ + 0x00, 0xA2, /* # # # */ + 0x00, 0x62, /* ## # */ + 0x07, 0x5C, /* ### # ### */ + 0x08, 0xC0, /* # ## */ + 0x08, 0xC0, /* # ## */ + 0x08, 0xA0, /* # # # */ + 0x07, 0x20, /* ### # */ + + /* @108 '&' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x80, /* #### */ + 0x0F, 0xEC, /* ####### ## */ + 0x0C, 0x7E, /* ## ###### */ + 0x0B, 0xE2, /* # ##### # */ + 0x0F, 0x82, /* ##### # */ + 0x0F, 0xC0, /* ###### */ + 0x09, 0xC0, /* # ### */ + 0x00, 0x00, /* */ + + /* @126 ''' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x1E, /* #### */ + 0x00, 0x1E, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 '(' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x07, 0xF0, /* ####### */ + 0x1F, 0xFC, /* ########### */ + 0x38, 0x0E, /* ### ### */ + 0x20, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @162 ')' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x02, /* # # */ + 0x38, 0x0E, /* ### ### */ + 0x1F, 0xFC, /* ########### */ + 0x07, 0xF0, /* ####### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @180 '*' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x24, /* # # */ + 0x00, 0x3C, /* #### */ + 0x00, 0x18, /* ## */ + 0x00, 0x7E, /* ###### */ + 0x00, 0x18, /* ## */ + 0x00, 0x3C, /* #### */ + 0x00, 0x24, /* # # */ + 0x00, 0x00, /* */ + + /* @198 '+' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x07, 0xF8, /* ######## */ + 0x07, 0xF8, /* ######## */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + + /* @216 ',' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x3E, 0x00, /* ##### */ + 0x1E, 0x00, /* #### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @234 '-' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @252 '.' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0E, 0x00, /* ### */ + 0x0E, 0x00, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @270 '/' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x00, /* # */ + 0x1C, 0x00, /* ### */ + 0x0F, 0x00, /* #### */ + 0x03, 0xC0, /* #### */ + 0x00, 0xF0, /* #### */ + 0x00, 0x3C, /* #### */ + 0x00, 0x0E, /* ### */ + 0x00, 0x02, /* # */ + + /* @288 '0' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x07, 0xFC, /* ######### */ + 0x0C, 0x06, /* ## ## */ + 0x08, 0x62, /* # ## # */ + 0x0C, 0x06, /* ## ## */ + 0x07, 0xFC, /* ######### */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @306 '1' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x08, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @324 '2' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x04, /* ## # */ + 0x0E, 0x02, /* ### # */ + 0x0F, 0x02, /* #### # */ + 0x0B, 0x82, /* # ### # */ + 0x09, 0xE2, /* # #### # */ + 0x08, 0xFC, /* # ###### */ + 0x08, 0x7C, /* # ##### */ + 0x00, 0x00, /* */ + + /* @342 '3' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x04, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x0C, 0x62, /* ## ## # */ + 0x07, 0xDE, /* ##### #### */ + 0x07, 0x9C, /* #### ### */ + 0x00, 0x00, /* */ + + /* @360 '4' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xC0, /* ### */ + 0x01, 0x60, /* # ## */ + 0x01, 0x38, /* # ### */ + 0x01, 0x0C, /* # ## */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @378 '5' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x7E, /* # ###### */ + 0x08, 0x3E, /* # ##### */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x0C, 0x62, /* ## ## # */ + 0x07, 0xC2, /* ##### # */ + 0x03, 0x80, /* ### */ + 0x00, 0x00, /* */ + + /* @396 '6' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x07, 0xFC, /* ######### */ + 0x08, 0x26, /* # # ## */ + 0x08, 0x22, /* # # # */ + 0x08, 0x22, /* # # # */ + 0x0F, 0xE4, /* ####### # */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @414 '7' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x08, 0x02, /* # # */ + 0x0F, 0x02, /* #### # */ + 0x07, 0xC2, /* ##### # */ + 0x01, 0xFA, /* ###### # */ + 0x00, 0x3E, /* ##### */ + 0x00, 0x0E, /* ### */ + 0x00, 0x00, /* */ + + /* @432 '8' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x9C, /* #### ### */ + 0x0F, 0xDE, /* ###### #### */ + 0x0C, 0x62, /* ## ## # */ + 0x08, 0x22, /* # # # */ + 0x0C, 0x62, /* ## ## # */ + 0x0F, 0xDE, /* ###### #### */ + 0x07, 0x9C, /* #### ### */ + 0x00, 0x00, /* */ + + /* @450 '9' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x7C, /* ##### */ + 0x04, 0xFE, /* # ####### */ + 0x08, 0x82, /* # # # */ + 0x08, 0x82, /* # # # */ + 0x0C, 0x82, /* ## # # */ + 0x07, 0xFC, /* ######### */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @468 ':' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0E, 0x70, /* ### ### */ + 0x0E, 0x70, /* ### ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @486 ';' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x3E, 0x70, /* ##### ### */ + 0x1E, 0x70, /* #### ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @504 '<' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x01, 0xE0, /* #### */ + 0x01, 0x20, /* # # */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x02, 0x10, /* # # */ + 0x06, 0x18, /* ## ## */ + + /* @522 '=' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + + /* @540 '>' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x06, 0x18, /* ## ## */ + 0x02, 0x10, /* # # */ + 0x03, 0x30, /* ## ## */ + 0x03, 0x30, /* ## ## */ + 0x01, 0x20, /* # # */ + 0x01, 0xE0, /* #### */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + + /* @558 '?' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x04, /* # */ + 0x00, 0x02, /* # */ + 0x0D, 0xE2, /* ## #### # */ + 0x0D, 0xF2, /* ## ##### # */ + 0x00, 0x3E, /* ##### */ + 0x00, 0x1C, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @576 '@' (9 pixels wide) */ + 0x0F, 0xE0, /* ####### */ + 0x1F, 0xF0, /* ######### */ + 0x30, 0x18, /* ## ## */ + 0x67, 0xC4, /* ## ##### # */ + 0x4F, 0xE4, /* # ####### # */ + 0x48, 0x24, /* # # # # */ + 0x6F, 0xFC, /* ## ########## */ + 0x4F, 0xF8, /* # ######### */ + 0x00, 0x00, /* */ + + /* @594 'A' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x00, /* ## */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xFE, /* ########## */ + 0x01, 0x1E, /* # #### */ + 0x07, 0xFE, /* ########## */ + 0x0F, 0xE0, /* ####### */ + 0x0C, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @612 'B' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x0F, 0xBE, /* ##### ##### */ + 0x07, 0xBC, /* #### #### */ + 0x00, 0x00, /* */ + + /* @630 'C' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xF0, /* ##### */ + 0x07, 0xFC, /* ######### */ + 0x0C, 0x06, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x04, 0x04, /* # # */ + 0x00, 0x00, /* */ + + /* @648 'D' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0C, 0x06, /* ## ## */ + 0x07, 0xFC, /* ######### */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @666 'E' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x42, /* # # # */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + + /* @684 'F' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x42, /* # # */ + 0x00, 0x02, /* # */ + 0x00, 0x00, /* */ + + /* @702 'G' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF0, /* ###### */ + 0x07, 0xFC, /* ######### */ + 0x0C, 0x06, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x08, 0x82, /* # # # */ + 0x0F, 0x82, /* ##### # */ + 0x0F, 0x84, /* ##### # */ + 0x00, 0x00, /* */ + + /* @720 'H' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @738 'I' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @756 'J' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x08, 0x02, /* # # */ + 0x0F, 0xFE, /* ########### */ + 0x07, 0xFE, /* ########## */ + 0x00, 0x00, /* */ + + /* @774 'K' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0xE0, /* ### */ + 0x00, 0xF8, /* ##### */ + 0x03, 0xDC, /* #### ### */ + 0x0F, 0x8E, /* ##### ### */ + 0x0E, 0x06, /* ### ## */ + 0x08, 0x00, /* # */ + + /* @792 'L' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @810 'M' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x7E, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x7E, /* ###### */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @828 'N' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x3E, /* ##### */ + 0x01, 0xF0, /* ##### */ + 0x0F, 0x80, /* ##### */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @846 'O' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x07, 0xFC, /* ######### */ + 0x0C, 0x06, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x0C, 0x06, /* ## ## */ + 0x07, 0xFC, /* ######### */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @864 'P' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x82, /* # # */ + 0x00, 0x82, /* # # */ + 0x00, 0xC6, /* ## ## */ + 0x00, 0xFE, /* ####### */ + 0x00, 0x7C, /* ##### */ + 0x00, 0x00, /* */ + + /* @882 'Q' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xF8, /* ####### */ + 0x07, 0xFC, /* ######### */ + 0x0C, 0x06, /* ## ## */ + 0x08, 0x02, /* # # */ + 0x1C, 0x06, /* ### ## */ + 0x3F, 0xFC, /* ############ */ + 0x03, 0xF8, /* ####### */ + 0x00, 0x00, /* */ + + /* @900 'R' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x82, /* # # */ + 0x00, 0x82, /* # # */ + 0x03, 0x82, /* ### # */ + 0x0F, 0x7E, /* #### ###### */ + 0x0E, 0x7C, /* ### ##### */ + 0x08, 0x00, /* # */ + + /* @918 'S' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0x3C, /* # #### */ + 0x08, 0x7C, /* # ##### */ + 0x08, 0x62, /* # ## # */ + 0x08, 0xE2, /* # ### # */ + 0x08, 0xC2, /* # ## # */ + 0x0F, 0xC2, /* ###### # */ + 0x07, 0x84, /* #### # */ + 0x00, 0x00, /* */ + + /* @936 'T' (9 pixels wide) */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x00, 0x00, /* */ + + /* @954 'U' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xFE, /* ########## */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x0F, 0xFE, /* ########### */ + 0x07, 0xFE, /* ########## */ + 0x00, 0x00, /* */ + + /* @972 'V' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x06, /* ## */ + 0x00, 0xFE, /* ####### */ + 0x0F, 0xF8, /* ######### */ + 0x0E, 0x00, /* ### */ + 0x0F, 0xF8, /* ######### */ + 0x00, 0xFE, /* ####### */ + 0x00, 0x06, /* ## */ + 0x00, 0x00, /* */ + + /* @990 'W' (9 pixels wide) */ + 0x00, 0x3E, /* ##### */ + 0x0F, 0xFE, /* ########### */ + 0x0E, 0x00, /* ### */ + 0x03, 0xE0, /* ##### */ + 0x00, 0x70, /* ### */ + 0x03, 0xE0, /* ##### */ + 0x0E, 0x00, /* ### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x3E, /* ##### */ + + /* @1008 'X' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x02, /* # # */ + 0x0E, 0x0E, /* ### ### */ + 0x07, 0xBC, /* #### #### */ + 0x01, 0xF0, /* ##### */ + 0x07, 0xBC, /* #### #### */ + 0x0E, 0x0E, /* ### ### */ + 0x08, 0x02, /* # # */ + 0x00, 0x00, /* */ + + /* @1026 'Y' (9 pixels wide) */ + 0x00, 0x02, /* # */ + 0x00, 0x0E, /* ### */ + 0x00, 0x7E, /* ###### */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x7E, /* ###### */ + 0x00, 0x0E, /* ### */ + 0x00, 0x02, /* # */ + 0x00, 0x00, /* */ + + /* @1044 'Z' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0x02, /* ### # */ + 0x0F, 0x02, /* #### # */ + 0x0B, 0xC2, /* # #### # */ + 0x08, 0xF2, /* # #### # */ + 0x08, 0x7A, /* # #### # */ + 0x08, 0x1E, /* # #### */ + 0x08, 0x0E, /* # ### */ + 0x00, 0x00, /* */ + + /* @1062 '[' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xFE, /* ############# */ + 0x3F, 0xFE, /* ############# */ + 0x20, 0x02, /* # # */ + 0x20, 0x02, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1080 '\' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x00, 0x0E, /* ### */ + 0x00, 0x78, /* #### */ + 0x01, 0xE0, /* #### */ + 0x07, 0x80, /* #### */ + 0x1C, 0x00, /* ### */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1098 ']' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x02, /* # # */ + 0x20, 0x02, /* # # */ + 0x3F, 0xFE, /* ############# */ + 0x3F, 0xFE, /* ############# */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1116 '^' (9 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x18, /* ## */ + 0x00, 0x0C, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x0C, /* ## */ + 0x00, 0x18, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @1134 '_' (9 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + + /* @1152 '`' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x01, /* # */ + 0x00, 0x03, /* ## */ + 0x00, 0x06, /* ## */ + 0x00, 0x04, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1170 'a' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x00, /* ### */ + 0x0F, 0xA0, /* ##### # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x0C, 0x90, /* ## # # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1188 'b' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x0F, 0xF0, /* ######## */ + 0x07, 0xE0, /* ###### */ + 0x00, 0x00, /* */ + + /* @1206 'c' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x07, 0xE0, /* ###### */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x04, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @1224 'd' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xE0, /* ###### */ + 0x0F, 0xF0, /* ######## */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x00, /* */ + + /* @1242 'e' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x07, 0xE0, /* ###### */ + 0x0C, 0x90, /* ## # # */ + 0x08, 0x90, /* # # # */ + 0x08, 0x90, /* # # # */ + 0x08, 0xF0, /* # #### */ + 0x04, 0xE0, /* # ### */ + 0x00, 0x00, /* */ + + /* @1260 'f' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xFC, /* ########## */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x12, /* # # */ + 0x00, 0x12, /* # # */ + 0x00, 0x12, /* # # */ + 0x00, 0x00, /* */ + + /* @1278 'g' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x2F, 0xF0, /* # ######## */ + 0x4C, 0x30, /* # ## ## */ + 0x48, 0x10, /* # # # */ + 0x4C, 0x30, /* # ## ## */ + 0x7F, 0xF0, /* ########### */ + 0x3F, 0xF0, /* ########## */ + 0x00, 0x00, /* */ + + /* @1296 'h' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1314 'i' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x0F, 0xF7, /* ######## ### */ + 0x0F, 0xF7, /* ######## ### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + + /* @1332 'j' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + 0x7F, 0xF7, /* ########### ### */ + 0x3F, 0xF7, /* ########## ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1350 'k' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xFE, /* ########### */ + 0x0F, 0xFE, /* ########### */ + 0x00, 0xC0, /* ## */ + 0x03, 0xE0, /* ##### */ + 0x0F, 0x30, /* #### ## */ + 0x0C, 0x10, /* ## # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1368 'l' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x02, /* # */ + 0x00, 0x02, /* # */ + 0x07, 0xFE, /* ########## */ + 0x0F, 0xFE, /* ########### */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1386 'm' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + + /* @1404 'n' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xE0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1422 'o' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0xC0, /* #### */ + 0x07, 0xE0, /* ###### */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x07, 0xE0, /* ###### */ + 0x03, 0xC0, /* #### */ + 0x00, 0x00, /* */ + + /* @1440 'p' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x7F, 0xF0, /* ########### */ + 0x7F, 0xF0, /* ########### */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x0F, 0xF0, /* ######## */ + 0x07, 0xE0, /* ###### */ + 0x00, 0x00, /* */ + + /* @1458 'q' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xE0, /* ###### */ + 0x0F, 0xF0, /* ######## */ + 0x0C, 0x30, /* ## ## */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x7F, 0xF0, /* ########### */ + 0x7F, 0xF0, /* ########### */ + 0x00, 0x00, /* */ + + /* @1476 'r' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x30, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @1494 's' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x04, 0xE0, /* # ### */ + 0x08, 0xF0, /* # #### */ + 0x09, 0x90, /* # ## # */ + 0x09, 0x90, /* # ## # */ + 0x09, 0x90, /* # ## # */ + 0x0F, 0x10, /* #### # */ + 0x07, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @1512 't' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x07, 0xFC, /* ######### */ + 0x0F, 0xFC, /* ########## */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x08, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @1530 'u' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xF0, /* ####### */ + 0x0F, 0xF0, /* ######## */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + + /* @1548 'v' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x30, /* ## */ + 0x01, 0xF0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x0C, 0x00, /* ## */ + 0x0F, 0xE0, /* ####### */ + 0x01, 0xF0, /* ##### */ + 0x00, 0x30, /* ## */ + 0x00, 0x00, /* */ + + /* @1566 'w' (9 pixels wide) */ + 0x00, 0x70, /* ### */ + 0x0F, 0xF0, /* ######## */ + 0x0F, 0x00, /* #### */ + 0x03, 0x80, /* ### */ + 0x00, 0xC0, /* ## */ + 0x03, 0x80, /* ### */ + 0x0F, 0x00, /* #### */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x70, /* ### */ + + /* @1584 'x' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x10, /* # # */ + 0x0C, 0x30, /* ## ## */ + 0x0F, 0xF0, /* ######## */ + 0x03, 0xC0, /* #### */ + 0x0F, 0xF0, /* ######## */ + 0x0E, 0x30, /* ### ## */ + 0x08, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @1602 'y' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x40, 0xF0, /* # #### */ + 0x63, 0xE0, /* ## ##### */ + 0x7F, 0x00, /* ####### */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0xF0, /* ##### */ + 0x00, 0x30, /* ## */ + 0x00, 0x00, /* */ + + /* @1620 'z' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x10, /* ## # */ + 0x0E, 0x10, /* ### # */ + 0x0B, 0x10, /* # ## # */ + 0x09, 0x90, /* # ## # */ + 0x08, 0xD0, /* # ## # */ + 0x08, 0x70, /* # ### */ + 0x08, 0x30, /* # ## */ + 0x00, 0x00, /* */ + + /* @1638 '{' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x3E, 0xFC, /* ##### ###### */ + 0x7E, 0xFE, /* ###### ####### */ + 0x40, 0x02, /* # # */ + 0x40, 0x02, /* # # */ + 0x00, 0x00, /* */ + + /* @1656 '|' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0xFF, 0xFE, /* ############### */ + 0xFF, 0xFE, /* ############### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1674 '}' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x40, 0x02, /* # # */ + 0x40, 0x02, /* # # */ + 0x7E, 0xFE, /* ###### ####### */ + 0x3E, 0xFC, /* ##### ###### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1692 '~' (9 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x80, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x00, 0xC0, /* ## */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0xC0, /* ## */ +}; + +/* Character descriptors for Bitstream Vera Sans Mono 11pt */ +/* { [Char width in bits], [Offset into bitstreamVeraSansMonoBold11ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO bitstreamVeraSansMonoBold11ptCharDescriptors[] = +{ + {9, 0}, /* */ + {9, 18}, /* ! */ + {9, 36}, /* " */ + {9, 54}, /* # */ + {9, 72}, /* $ */ + {9, 90}, /* % */ + {9, 108}, /* & */ + {9, 126}, /* ' */ + {9, 144}, /* ( */ + {9, 162}, /* ) */ + {9, 180}, /* * */ + {9, 198}, /* + */ + {9, 216}, /* , */ + {9, 234}, /* - */ + {9, 252}, /* . */ + {9, 270}, /* / */ + {9, 288}, /* 0 */ + {9, 306}, /* 1 */ + {9, 324}, /* 2 */ + {9, 342}, /* 3 */ + {9, 360}, /* 4 */ + {9, 378}, /* 5 */ + {9, 396}, /* 6 */ + {9, 414}, /* 7 */ + {9, 432}, /* 8 */ + {9, 450}, /* 9 */ + {9, 468}, /* : */ + {9, 486}, /* ; */ + {9, 504}, /* < */ + {9, 522}, /* = */ + {9, 540}, /* > */ + {9, 558}, /* ? */ + {9, 576}, /* @ */ + {9, 594}, /* A */ + {9, 612}, /* B */ + {9, 630}, /* C */ + {9, 648}, /* D */ + {9, 666}, /* E */ + {9, 684}, /* F */ + {9, 702}, /* G */ + {9, 720}, /* H */ + {9, 738}, /* I */ + {9, 756}, /* J */ + {9, 774}, /* K */ + {9, 792}, /* L */ + {9, 810}, /* M */ + {9, 828}, /* N */ + {9, 846}, /* O */ + {9, 864}, /* P */ + {9, 882}, /* Q */ + {9, 900}, /* R */ + {9, 918}, /* S */ + {9, 936}, /* T */ + {9, 954}, /* U */ + {9, 972}, /* V */ + {9, 990}, /* W */ + {9, 1008}, /* X */ + {9, 1026}, /* Y */ + {9, 1044}, /* Z */ + {9, 1062}, /* [ */ + {9, 1080}, /* \ */ + {9, 1098}, /* ] */ + {9, 1116}, /* ^ */ + {9, 1134}, /* _ */ + {9, 1152}, /* ` */ + {9, 1170}, /* a */ + {9, 1188}, /* b */ + {9, 1206}, /* c */ + {9, 1224}, /* d */ + {9, 1242}, /* e */ + {9, 1260}, /* f */ + {9, 1278}, /* g */ + {9, 1296}, /* h */ + {9, 1314}, /* i */ + {9, 1332}, /* j */ + {9, 1350}, /* k */ + {9, 1368}, /* l */ + {9, 1386}, /* m */ + {9, 1404}, /* n */ + {9, 1422}, /* o */ + {9, 1440}, /* p */ + {9, 1458}, /* q */ + {9, 1476}, /* r */ + {9, 1494}, /* s */ + {9, 1512}, /* t */ + {9, 1530}, /* u */ + {9, 1548}, /* v */ + {9, 1566}, /* w */ + {9, 1584}, /* x */ + {9, 1602}, /* y */ + {9, 1620}, /* z */ + {9, 1638}, /* { */ + {9, 1656}, /* | */ + {9, 1674}, /* } */ + {9, 1692}, /* ~ */ +}; + +/* Font information for Bitstream Vera Sans Mono 11pt */ +const FONT_INFO bitstreamVeraSansMonoBold11ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + bitstreamVeraSansMonoBold11ptCharDescriptors, /* Character decriptor array */ + bitstreamVeraSansMonoBold11ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/veramonobold11.h b/drivers/lcd/tft/fonts/veramonobold11.h new file mode 100644 index 0000000..bba19e5 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramonobold11.h @@ -0,0 +1,11 @@ +#ifndef __VERA_MONO_BOLD_11__ +#define __VERA_MONO_BOLD_11__ + +#include "bitmapfonts.h" + +/* Font data for Bitstream Vera Sans Mono Bold 11pt */ +extern const uint8_t bitstreamVeraSansMonoBold11ptCharBitmaps[]; +extern const FONT_CHAR_INFO bitstreamVeraSansMonoBold11ptCharDescriptors[]; +extern const FONT_INFO bitstreamVeraSansMonoBold11ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/fonts/veramonobold9.c b/drivers/lcd/tft/fonts/veramonobold9.c new file mode 100644 index 0000000..50a2460 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramonobold9.c @@ -0,0 +1,1069 @@ +#include "veramonobold9.h" + +/* +** Font data for Bitstream Vera Sans Mono Bold 9pt +*/ + +/* Character bitmaps for Bitstream Vera Sans Mono Bold 9pt */ +const uint8_t bitstreamVeraSansMonoBold9ptCharBitmaps[] = +{ + /* @0 ' ' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @16 '!' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1B, 0xF0, /* ## ###### */ + 0x1B, 0xF0, /* ## ###### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @32 '"' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + + /* @48 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x1C, 0xE0, /* ### ### */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x00, 0x80, /* # */ + 0x00, 0x00, /* */ + + /* @64 '$' (8 pixels wide) */ + 0x08, 0xC0, /* # ## */ + 0x19, 0xE0, /* ## #### */ + 0x11, 0x20, /* # # # */ + 0x7F, 0xF0, /* ########### */ + 0x11, 0x20, /* # # # */ + 0x1F, 0x60, /* ##### ## */ + 0x0E, 0x00, /* ### */ + 0x00, 0x00, /* */ + + /* @80 '%' (8 pixels wide) */ + 0x02, 0x60, /* # ## */ + 0x02, 0x90, /* # # # */ + 0x02, 0x90, /* # # # */ + 0x0D, 0x60, /* ## # ## */ + 0x13, 0x00, /* # ## */ + 0x12, 0x80, /* # # # */ + 0x0C, 0x80, /* ## # */ + 0x00, 0x00, /* */ + + /* @96 '&' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0x00, /* ### */ + 0x1F, 0x70, /* ##### ### */ + 0x11, 0xF0, /* # ##### */ + 0x1F, 0x10, /* ##### # */ + 0x1C, 0x00, /* ### */ + 0x17, 0x00, /* # ### */ + 0x00, 0x00, /* */ + + /* @112 ''' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @128 '(' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x1F, 0xF0, /* ######### */ + 0x30, 0x18, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @144 ')' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x30, 0x18, /* ## ## */ + 0x1F, 0xF0, /* ######### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @160 '*' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x20, /* # # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @176 '+' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @192 ',' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x38, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @208 '-' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @224 '.' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @240 '/' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @256 '0' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x1F, 0xF0, /* ######### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @272 '1' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @288 '2' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x20, /* ## # */ + 0x1C, 0x10, /* ### # */ + 0x16, 0x10, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x11, 0xF0, /* # ##### */ + 0x10, 0xE0, /* # ### */ + 0x00, 0x00, /* */ + + /* @304 '3' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1E, 0xF0, /* #### #### */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @320 '4' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x00, /* ### */ + 0x05, 0x80, /* # ## */ + 0x04, 0x60, /* # ## */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @336 '5' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0xF0, /* # #### */ + 0x10, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x11, 0x90, /* # ## # */ + 0x1F, 0x90, /* ###### # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @352 '6' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0xB0, /* # # ## */ + 0x10, 0x90, /* # # # */ + 0x1F, 0x90, /* ###### # */ + 0x0F, 0x00, /* #### */ + 0x00, 0x00, /* */ + + /* @368 '7' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x1E, 0x10, /* #### # */ + 0x0F, 0xD0, /* ###### # */ + 0x01, 0xF0, /* ##### */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + + /* @384 '8' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0E, 0xE0, /* ### ### */ + 0x1E, 0xF0, /* #### #### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1E, 0xF0, /* #### #### */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @400 '9' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0xE0, /* #### */ + 0x13, 0xF0, /* # ###### */ + 0x12, 0x10, /* # # # */ + 0x1A, 0x10, /* ## # # */ + 0x1F, 0xF0, /* ######### */ + 0x07, 0xE0, /* ###### */ + 0x00, 0x00, /* */ + + /* @416 ':' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x19, 0x80, /* ## ## */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @432 ';' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x40, 0x00, /* # */ + 0x39, 0x80, /* ### ## */ + 0x19, 0x80, /* ## ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @448 '<' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x07, 0x80, /* #### */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x00, 0x00, /* */ + + /* @464 '=' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x00, 0x00, /* */ + + /* @480 '>' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0xC0, /* ## ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x07, 0x80, /* #### */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @496 '?' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x20, /* # */ + 0x1B, 0x10, /* ## ## # */ + 0x1B, 0x90, /* ## ### # */ + 0x00, 0xF0, /* #### */ + 0x00, 0x60, /* ## */ + 0x00, 0x00, /* */ + + /* @512 '@' (8 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x30, 0xC0, /* ## ## */ + 0x6F, 0x20, /* ## #### # */ + 0x50, 0xA0, /* # # # # */ + 0x50, 0xA0, /* # # # # */ + 0x50, 0xA0, /* # # # # */ + 0x7F, 0xC0, /* ######### */ + 0x00, 0x00, /* */ + + /* @528 'A' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x00, /* ## */ + 0x1F, 0x80, /* ###### */ + 0x05, 0xF0, /* # ##### */ + 0x05, 0xF0, /* # ##### */ + 0x1F, 0x80, /* ###### */ + 0x18, 0x00, /* ## */ + 0x00, 0x00, /* */ + + /* @544 'B' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x1E, 0xF0, /* #### #### */ + 0x0E, 0xE0, /* ### ### */ + 0x00, 0x00, /* */ + + /* @560 'C' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x00, 0x00, /* */ + + /* @576 'D' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x0F, 0xE0, /* ####### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @592 'E' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @608 'F' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @624 'G' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x0F, 0xE0, /* ####### */ + 0x18, 0x30, /* ## ## */ + 0x12, 0x10, /* # # # */ + 0x1E, 0x10, /* #### # */ + 0x1E, 0x20, /* #### # */ + 0x00, 0x00, /* */ + + /* @640 'H' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @656 'I' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @672 'J' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + + /* @688 'K' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x80, /* ## */ + 0x03, 0xC0, /* #### */ + 0x0E, 0x70, /* ### ### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x00, /* # */ + + /* @704 'L' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @720 'M' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xE0, /* ######## */ + 0x01, 0xC0, /* ### */ + 0x01, 0xC0, /* ### */ + 0x1F, 0xE0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @736 'N' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0xE0, /* #### */ + 0x0F, 0x00, /* #### */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x00, /* */ + + /* @752 'O' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @768 'P' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0xF0, /* ##### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x00, /* */ + + /* @784 'Q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0xC0, /* ##### */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x3F, 0xF0, /* ########## */ + 0x07, 0xC0, /* ##### */ + 0x00, 0x00, /* */ + + /* @800 'R' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x03, 0x10, /* ## # */ + 0x0E, 0xF0, /* ### #### */ + 0x1C, 0xE0, /* ### ### */ + 0x10, 0x00, /* # */ + + /* @816 'S' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x08, 0xE0, /* # ### */ + 0x11, 0xF0, /* # ##### */ + 0x11, 0x90, /* # ## # */ + 0x13, 0x10, /* # ## # */ + 0x1F, 0x10, /* ##### # */ + 0x0E, 0x20, /* ### # */ + 0x00, 0x00, /* */ + + /* @832 'T' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + + /* @848 'U' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xF0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x0F, 0xF0, /* ######## */ + 0x00, 0x00, /* */ + + /* @864 'V' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x30, /* ## */ + 0x07, 0xF0, /* ####### */ + 0x1F, 0x00, /* ##### */ + 0x1F, 0x00, /* ##### */ + 0x07, 0xF0, /* ####### */ + 0x00, 0x30, /* ## */ + 0x00, 0x00, /* */ + + /* @880 'W' (8 pixels wide) */ + 0x00, 0xF0, /* #### */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0x00, /* ##### */ + 0x00, 0xC0, /* ## */ + 0x1F, 0x00, /* ##### */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0xF0, /* ##### */ + 0x00, 0x00, /* */ + + /* @896 'X' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x10, /* # # */ + 0x1C, 0x70, /* ### ### */ + 0x07, 0xC0, /* ##### */ + 0x07, 0xC0, /* ##### */ + 0x1C, 0x70, /* ### ### */ + 0x10, 0x10, /* # # */ + 0x00, 0x00, /* */ + + /* @912 'Y' (8 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x70, /* ### */ + 0x01, 0xE0, /* #### */ + 0x1F, 0x80, /* ###### */ + 0x1F, 0x80, /* ###### */ + 0x01, 0xE0, /* #### */ + 0x00, 0x70, /* ### */ + 0x00, 0x10, /* # */ + + /* @928 'Z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x10, /* ## # */ + 0x1E, 0x10, /* #### # */ + 0x17, 0x10, /* # ### # */ + 0x11, 0xD0, /* # ### # */ + 0x10, 0xF0, /* # #### */ + 0x10, 0x30, /* # ## */ + 0x00, 0x00, /* */ + + /* @944 '[' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x3F, 0xF8, /* ########### */ + 0x3F, 0xF8, /* ########### */ + 0x20, 0x08, /* # # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @960 '\' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x10, /* # */ + 0x00, 0x70, /* ### */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x38, 0x00, /* ### */ + 0x20, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @976 ']' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x20, 0x08, /* # # */ + 0x3F, 0xF8, /* ########### */ + 0x3F, 0xF8, /* ########### */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @992 '^' (8 pixels wide) */ + 0x00, 0x40, /* # */ + 0x00, 0x60, /* ## */ + 0x00, 0x30, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x30, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x40, /* # */ + 0x00, 0x00, /* */ + + /* @1008 '_' (8 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1024 '`' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x08, /* # */ + 0x00, 0x18, /* ## */ + 0x00, 0x10, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1040 'a' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0C, 0x00, /* ## */ + 0x1E, 0x80, /* #### # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + + /* @1056 'b' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + + /* @1072 'c' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x07, 0x00, /* ### */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x08, 0x80, /* # # */ + 0x00, 0x00, /* */ + + /* @1088 'd' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x00, /* */ + + /* @1104 'e' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x13, 0xC0, /* # #### */ + 0x0B, 0x80, /* # ### */ + 0x00, 0x00, /* */ + + /* @1120 'f' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x48, /* # # */ + 0x00, 0x48, /* # # */ + 0x00, 0x00, /* */ + + /* @1136 'g' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x9F, 0xC0, /* # ####### */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0xFF, 0xC0, /* ########## */ + 0x7F, 0xC0, /* ######### */ + 0x00, 0x00, /* */ + + /* @1152 'h' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + + /* @1168 'i' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xD8, /* ####### ## */ + 0x1F, 0xD8, /* ####### ## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1184 'j' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x80, 0x40, /* # # */ + 0x80, 0x40, /* # # */ + 0xFF, 0xD8, /* ########## ## */ + 0x7F, 0xD8, /* ######### ## */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1200 'k' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xF8, /* ########## */ + 0x1F, 0xF8, /* ########## */ + 0x03, 0x00, /* ## */ + 0x0F, 0x80, /* ##### */ + 0x1C, 0xC0, /* ### ## */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1216 'l' (8 pixels wide) */ + 0x00, 0x08, /* # */ + 0x00, 0x08, /* # */ + 0x0F, 0xF8, /* ######### */ + 0x1F, 0xF8, /* ########## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1232 'm' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1248 'n' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x00, /* */ + + /* @1264 'o' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + + /* @1280 'p' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0xFF, 0xC0, /* ########## */ + 0xFF, 0xC0, /* ########## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x1F, 0xC0, /* ####### */ + 0x0F, 0x80, /* ##### */ + 0x00, 0x00, /* */ + + /* @1296 'q' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0x80, /* ##### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0xFF, 0xC0, /* ########## */ + 0xFF, 0xC0, /* ########## */ + 0x00, 0x00, /* */ + + /* @1312 'r' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x00, /* */ + + /* @1328 's' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x09, 0x80, /* # ## */ + 0x13, 0xC0, /* # #### */ + 0x13, 0x40, /* # ## # */ + 0x12, 0x40, /* # # # */ + 0x1E, 0x40, /* #### # */ + 0x0C, 0x80, /* ## # */ + 0x00, 0x00, /* */ + + /* @1344 't' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x0F, 0xF0, /* ######## */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x00, 0x00, /* */ + + /* @1360 'u' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x0F, 0xC0, /* ###### */ + 0x1F, 0xC0, /* ####### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x00, /* */ + + /* @1376 'v' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0xC0, /* ## */ + 0x07, 0xC0, /* ##### */ + 0x1E, 0x00, /* #### */ + 0x1E, 0x00, /* #### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x00, /* */ + + /* @1392 'w' (8 pixels wide) */ + 0x01, 0xC0, /* ### */ + 0x1F, 0xC0, /* ####### */ + 0x1E, 0x00, /* #### */ + 0x01, 0x00, /* # */ + 0x1E, 0x00, /* #### */ + 0x1F, 0xC0, /* ####### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x00, /* */ + + /* @1408 'x' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x10, 0x40, /* # # */ + 0x1D, 0xC0, /* ### ### */ + 0x0F, 0x80, /* ##### */ + 0x0F, 0x80, /* ##### */ + 0x1D, 0xC0, /* ### ### */ + 0x10, 0x40, /* # # */ + 0x00, 0x00, /* */ + + /* @1424 'y' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x80, 0xC0, /* # ## */ + 0x87, 0xC0, /* # ##### */ + 0xFE, 0x00, /* ####### */ + 0x3E, 0x00, /* ##### */ + 0x07, 0xC0, /* ##### */ + 0x00, 0xC0, /* ## */ + 0x00, 0x00, /* */ + + /* @1440 'z' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x18, 0x40, /* ## # */ + 0x1C, 0x40, /* ### # */ + 0x16, 0x40, /* # ## # */ + 0x13, 0x40, /* # ## # */ + 0x11, 0xC0, /* # ### */ + 0x10, 0xC0, /* # ## */ + 0x00, 0x00, /* */ + + /* @1456 '{' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1E, 0xF8, /* #### ##### */ + 0x3E, 0xF8, /* ##### ##### */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x00, 0x00, /* */ + + /* @1472 '|' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x7F, 0xF8, /* ############ */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @1488 '}' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x20, 0x08, /* # # */ + 0x20, 0x08, /* # # */ + 0x3E, 0xF8, /* ##### ##### */ + 0x3E, 0xF8, /* ##### ##### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x00, 0x00, /* */ + + /* @1504 '~' (8 pixels wide) */ + 0x00, 0x00, /* */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x04, 0x00, /* # */ + 0x00, 0x00, /* */ +}; + +/* Character descriptors for Bitstream Vera Sans Mono 9pt */ +/* { [Char width in bits], [Offset into bitstreamVeraSansMonoBold9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO bitstreamVeraSansMonoBold9ptCharDescriptors[] = +{ + {8, 0}, /* */ + {8, 16}, /* ! */ + {8, 32}, /* " */ + {8, 48}, /* # */ + {8, 64}, /* $ */ + {8, 80}, /* % */ + {8, 96}, /* & */ + {8, 112}, /* ' */ + {8, 128}, /* ( */ + {8, 144}, /* ) */ + {8, 160}, /* * */ + {8, 176}, /* + */ + {8, 192}, /* , */ + {8, 208}, /* - */ + {8, 224}, /* . */ + {8, 240}, /* / */ + {8, 256}, /* 0 */ + {8, 272}, /* 1 */ + {8, 288}, /* 2 */ + {8, 304}, /* 3 */ + {8, 320}, /* 4 */ + {8, 336}, /* 5 */ + {8, 352}, /* 6 */ + {8, 368}, /* 7 */ + {8, 384}, /* 8 */ + {8, 400}, /* 9 */ + {8, 416}, /* : */ + {8, 432}, /* ; */ + {8, 448}, /* < */ + {8, 464}, /* = */ + {8, 480}, /* > */ + {8, 496}, /* ? */ + {8, 512}, /* @ */ + {8, 528}, /* A */ + {8, 544}, /* B */ + {8, 560}, /* C */ + {8, 576}, /* D */ + {8, 592}, /* E */ + {8, 608}, /* F */ + {8, 624}, /* G */ + {8, 640}, /* H */ + {8, 656}, /* I */ + {8, 672}, /* J */ + {8, 688}, /* K */ + {8, 704}, /* L */ + {8, 720}, /* M */ + {8, 736}, /* N */ + {8, 752}, /* O */ + {8, 768}, /* P */ + {8, 784}, /* Q */ + {8, 800}, /* R */ + {8, 816}, /* S */ + {8, 832}, /* T */ + {8, 848}, /* U */ + {8, 864}, /* V */ + {8, 880}, /* W */ + {8, 896}, /* X */ + {8, 912}, /* Y */ + {8, 928}, /* Z */ + {8, 944}, /* [ */ + {8, 960}, /* \ */ + {8, 976}, /* ] */ + {8, 992}, /* ^ */ + {8, 1008}, /* _ */ + {8, 1024}, /* ` */ + {8, 1040}, /* a */ + {8, 1056}, /* b */ + {8, 1072}, /* c */ + {8, 1088}, /* d */ + {8, 1104}, /* e */ + {8, 1120}, /* f */ + {8, 1136}, /* g */ + {8, 1152}, /* h */ + {8, 1168}, /* i */ + {8, 1184}, /* j */ + {8, 1200}, /* k */ + {8, 1216}, /* l */ + {8, 1232}, /* m */ + {8, 1248}, /* n */ + {8, 1264}, /* o */ + {8, 1280}, /* p */ + {8, 1296}, /* q */ + {8, 1312}, /* r */ + {8, 1328}, /* s */ + {8, 1344}, /* t */ + {8, 1360}, /* u */ + {8, 1376}, /* v */ + {8, 1392}, /* w */ + {8, 1408}, /* x */ + {8, 1424}, /* y */ + {8, 1440}, /* z */ + {8, 1456}, /* { */ + {8, 1472}, /* | */ + {8, 1488}, /* } */ + {8, 1504}, /* ~ */ +}; + +/* Font information for Bitstream Vera Sans Mono 9pt */ +const FONT_INFO bitstreamVeraSansMonoBold9ptFontInfo = +{ + 2, /* Character height */ + ' ', /* Start character */ + bitstreamVeraSansMonoBold9ptCharDescriptors, /* Character decriptor array */ + bitstreamVeraSansMonoBold9ptCharBitmaps, /* Character bitmap array */ +}; diff --git a/drivers/lcd/tft/fonts/veramonobold9.h b/drivers/lcd/tft/fonts/veramonobold9.h new file mode 100644 index 0000000..3b73134 --- /dev/null +++ b/drivers/lcd/tft/fonts/veramonobold9.h @@ -0,0 +1,11 @@ +#ifndef __VERA_MONO_BOLD_9__ +#define __VERA_MONO_BOLD_9__ + +#include "bitmapfonts.h" + +/* Font data for Bitstream Vera Sans Mono Bold 9pt */ +extern const uint8_t bitstreamVeraSansMonoBold9ptCharBitmaps[]; +extern const FONT_CHAR_INFO bitstreamVeraSansMonoBold9ptCharDescriptors[]; +extern const FONT_INFO bitstreamVeraSansMonoBold9ptFontInfo; + +#endif \ No newline at end of file diff --git a/drivers/lcd/tft/hw/ILI9325.c b/drivers/lcd/tft/hw/ILI9325.c new file mode 100644 index 0000000..d7fc0e1 --- /dev/null +++ b/drivers/lcd/tft/hw/ILI9325.c @@ -0,0 +1,624 @@ +/**************************************************************************/ +/*! + @file ILI9325.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for ILI9325 240x320 pixel TFT LCD displays. + + This driver uses an 8-bit interface and a 16-bit RGB565 colour palette. + Should also work with SPFD5408B or OTM3225A-based LCDs, though + there are sometimes minor differences (for example vertical scrolling + via register 0x6A isn't supported on all controllers). + + @section UPDATES + + 26-11-2010: ili9325ReadData contributed by Adafruit Industries + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "ILI9325.h" +#include "core/systick/systick.h" +#include "drivers/lcd/tft/touchscreen.h" + +static lcdOrientation_t lcdOrientation = LCD_ORIENTATION_PORTRAIT; +static lcdProperties_t ili9325Properties = { 240, 320, TRUE, TRUE, TRUE }; + +/*************************************************/ +/* Private Methods */ +/*************************************************/ + +/**************************************************************************/ +/*! + @brief Causes a brief delay (10 ticks per unit) +*/ +/**************************************************************************/ +void ili9325Delay(unsigned int t) +{ + unsigned char t1; + while(t--) + for ( t1=10; t1 > 0; t1-- ) + { + __asm("nop"); + } +} + +/**************************************************************************/ +/*! + @brief Writes the supplied 16-bit command using an 8-bit interface +*/ +/**************************************************************************/ +void ili9325WriteCmd(uint16_t command) +{ + // Compiled with -Os on GCC 4.4 this works out to 25 cycles + // (versus 36 compiled with no optimisations). I'm not sure it + // can be improved further, so that means 25 cycles/350nS for + // continuous writes (cmd, data, data, data, ...) or ~150 cycles/ + // ~2.1uS for a random pixel (Set X [cmd+data], Set Y [cmd+data], + // Set color [cmd+data]) (times assumes 72MHz clock). + + CLR_CS_CD_SET_RD_WR; // Saves 18 commands compared to "CLR_CS; CLR_CD; SET_RD; SET_WR;" + ILI9325_GPIO2DATA_DATA = (command >> (8 - ILI9325_DATA_OFFSET)); + CLR_WR; + SET_WR; + ILI9325_GPIO2DATA_DATA = command << ILI9325_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR; SET_CS;" +} + +/**************************************************************************/ +/*! + @brief Writes the supplied 16-bit data using an 8-bit interface +*/ +/**************************************************************************/ +void ili9325WriteData(uint16_t data) +{ + CLR_CS_SET_CD_RD_WR; // Saves 18 commands compared to SET_CD; SET_RD; SET_WR; CLR_CS" + ILI9325_GPIO2DATA_DATA = (data >> (8 - ILI9325_DATA_OFFSET)); + CLR_WR; + SET_WR; + ILI9325_GPIO2DATA_DATA = data << ILI9325_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR, SET_CS;" +} + +/**************************************************************************/ +/*! + @brief Reads a 16-bit value from the 8-bit data bus +*/ +/**************************************************************************/ +uint16_t ili9325ReadData(void) +{ + // ToDo: Optimise this method! + + uint16_t high, low; + high = low = 0; + uint16_t d; + + SET_CD_RD_WR; // Saves 14 commands compared to "SET_CD; SET_RD; SET_WR" + CLR_CS; + + // set inputs + ILI9325_GPIO2DATA_SETINPUT; + CLR_RD; + ili9325Delay(100); + high = ILI9325_GPIO2DATA_DATA; + high >>= ILI9325_DATA_OFFSET; + high &= 0xFF; + SET_RD; + + CLR_RD; + ili9325Delay(100); + low = ILI9325_GPIO2DATA_DATA; + low >>= ILI9325_DATA_OFFSET; + low &=0xFF; + SET_RD; + + SET_CS; + ILI9325_GPIO2DATA_SETOUTPUT; + + d = high; + d <<= 8; + d |= low; + + return d; +} + +/**************************************************************************/ +/*! + @brief Reads a 16-bit value +*/ +/**************************************************************************/ +uint16_t ili9325Read(uint16_t addr) +{ + ili9325WriteCmd(addr); + return ili9325ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sends a 16-bit command + 16-bit data +*/ +/**************************************************************************/ +void ili9325Command(uint16_t command, uint16_t data) +{ + ili9325WriteCmd(command); + ili9325WriteData(data); +} + +/**************************************************************************/ +/*! + @brief Returns the 16-bit (4-hexdigit) controller code +*/ +/**************************************************************************/ +uint16_t ili9325Type(void) +{ + ili9325WriteCmd(ILI9325_COMMANDS_DRIVERCODEREAD); + return ili9325ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sets the cursor to the specified X/Y position +*/ +/**************************************************************************/ +void ili9325SetCursor(uint16_t x, uint16_t y) +{ + uint16_t al, ah; + + if (lcdOrientation == LCD_ORIENTATION_LANDSCAPE) + { + al = y; + ah = x; + } + else + { + al = x; + ah = y; + } + + ili9325Command(ILI9325_COMMANDS_HORIZONTALGRAMADDRESSSET, al); + ili9325Command(ILI9325_COMMANDS_VERTICALGRAMADDRESSSET, ah); +} + +/**************************************************************************/ +/*! + @brief Sends the initialisation sequence to the display controller +*/ +/**************************************************************************/ +void ili9325InitDisplay(void) +{ + // Clear data line + GPIO_GPIO2DATA &= ~ILI9325_DATA_MASK; + + SET_RD; + SET_WR; + SET_CS; + SET_CD; + + // Reset display + CLR_RESET; + ili9325Delay(10000); + SET_RESET; + ili9325Delay(500); + + ili9325Command(ILI9325_COMMANDS_DRIVEROUTPUTCONTROL1, 0x0100); // Driver Output Control Register (R01h) + ili9325Command(ILI9325_COMMANDS_LCDDRIVINGCONTROL, 0x0700); // LCD Driving Waveform Control (R02h) + ili9325Command(ILI9325_COMMANDS_ENTRYMODE, 0x1030); // Entry Mode (R03h) + ili9325Command(ILI9325_COMMANDS_DISPLAYCONTROL2, 0x0302); + ili9325Command(ILI9325_COMMANDS_DISPLAYCONTROL3, 0x0000); + ili9325Command(ILI9325_COMMANDS_DISPLAYCONTROL4, 0x0000); // Fmark On + ili9325Command(ILI9325_COMMANDS_POWERCONTROL1, 0x0000); // Power Control 1 (R10h) + ili9325Command(ILI9325_COMMANDS_POWERCONTROL2, 0x0007); // Power Control 2 (R11h) + ili9325Command(ILI9325_COMMANDS_POWERCONTROL3, 0x0000); // Power Control 3 (R12h) + ili9325Command(ILI9325_COMMANDS_POWERCONTROL4, 0x0000); // Power Control 4 (R13h) + ili9325Delay(1000); + ili9325Command(ILI9325_COMMANDS_POWERCONTROL1, 0x14B0); // Power Control 1 (R10h) + ili9325Delay(500); + ili9325Command(ILI9325_COMMANDS_POWERCONTROL2, 0x0007); // Power Control 2 (R11h) + ili9325Delay(500); + ili9325Command(ILI9325_COMMANDS_POWERCONTROL3, 0x008E); // Power Control 3 (R12h) + ili9325Command(ILI9325_COMMANDS_POWERCONTROL4, 0x0C00); // Power Control 4 (R13h) + ili9325Command(ILI9325_COMMANDS_POWERCONTROL7, 0x0015); // NVM read data 2 (R29h) + ili9325Delay(500); + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL1, 0x0000); // Gamma Control 1 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL2, 0x0107); // Gamma Control 2 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL3, 0x0000); // Gamma Control 3 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL4, 0x0203); // Gamma Control 4 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL5, 0x0402); // Gamma Control 5 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL6, 0x0000); // Gamma Control 6 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL7, 0x0207); // Gamma Control 7 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL8, 0x0000); // Gamma Control 8 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL9, 0x0203); // Gamma Control 9 + ili9325Command(ILI9325_COMMANDS_GAMMACONTROL10, 0x0403); // Gamma Control 10 + ili9325Command(ILI9325_COMMANDS_HORIZONTALADDRESSSTARTPOSITION, 0x0000); // Window Horizontal RAM Address Start (R50h) + ili9325Command(ILI9325_COMMANDS_HORIZONTALADDRESSENDPOSITION, ili9325Properties.width - 1); // Window Horizontal RAM Address End (R51h) + ili9325Command(ILI9325_COMMANDS_VERTICALADDRESSSTARTPOSITION, 0X0000); // Window Vertical RAM Address Start (R52h) + ili9325Command(ILI9325_COMMANDS_VERTICALADDRESSENDPOSITION, ili9325Properties.height - 1); // Window Vertical RAM Address End (R53h) + ili9325Command(ILI9325_COMMANDS_DRIVEROUTPUTCONTROL2, 0xa700); // Driver Output Control (R60h) + ili9325Command(ILI9325_COMMANDS_BASEIMAGEDISPLAYCONTROL, 0x0003); // Driver Output Control (R61h) - enable VLE + ili9325Command(ILI9325_COMMANDS_PANELINTERFACECONTROL1, 0X0010); // Panel Interface Control 1 (R90h) + + // Display On + ili9325Command(ILI9325_COMMANDS_DISPLAYCONTROL1, 0x0133); // Display Control (R07h) + ili9325Delay(500); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); +} + +/**************************************************************************/ +/*! + @brief Sets the cursor to the home position (0,0) +*/ +/**************************************************************************/ +void ili9325Home(void) +{ + ili9325SetCursor(0, 0); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) +} + +/**************************************************************************/ +/*! + @brief Sets the window confines +*/ +/**************************************************************************/ +void ili9325SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) +{ + ili9325Command(ILI9325_COMMANDS_HORIZONTALADDRESSSTARTPOSITION, x0); + ili9325Command(ILI9325_COMMANDS_HORIZONTALADDRESSENDPOSITION, x1); + ili9325Command(ILI9325_COMMANDS_VERTICALADDRESSSTARTPOSITION, y0); + ili9325Command(ILI9325_COMMANDS_VERTICALADDRESSENDPOSITION, y1); + ili9325SetCursor(x0, y0); +} + +/*************************************************/ +/* Public Methods */ +/*************************************************/ + +/**************************************************************************/ +/*! + @brief Configures any pins or HW and initialises the LCD controller +*/ +/**************************************************************************/ +void lcdInit(void) +{ + // Set control line pins to output + gpioSetDir(ILI9325_CS_PORT, ILI9325_CS_PIN, 1); + gpioSetDir(ILI9325_CD_PORT, ILI9325_CD_PIN, 1); + gpioSetDir(ILI9325_WR_PORT, ILI9325_WR_PIN, 1); + gpioSetDir(ILI9325_RD_PORT, ILI9325_RD_PIN, 1); + + // Set data port pins to output + ILI9325_GPIO2DATA_SETOUTPUT; + + // Disable pullups + ILI9325_DISABLEPULLUPS(); + + // Set backlight pin to output and turn it on + gpioSetDir(ILI9325_BL_PORT, ILI9325_BL_PIN, 1); // set to output + lcdBacklight(TRUE); + + // Set reset pin to output + gpioSetDir(ILI9325_RES_PORT, ILI9325_RES_PIN, 1); // Set to output + gpioSetValue(ILI9325_RES_PORT, ILI9325_RES_PIN, 0); // Low to reset + systickDelay(50); + gpioSetValue(ILI9325_RES_PORT, ILI9325_RES_PIN, 1); // High to exit + + // Initialize the display + ili9325InitDisplay(); + + systickDelay(50); + + // Set lcd to default orientation + lcdSetOrientation(lcdOrientation); + + // Fill black + lcdFillRGB(COLOR_BLACK); + + // Initialise the touch screen (and calibrate if necessary) + tsInit(); +} + +/**************************************************************************/ +/*! + @brief Enables or disables the LCD backlight +*/ +/**************************************************************************/ +void lcdBacklight(bool state) +{ + // Set the backlight + gpioSetValue(ILI9325_BL_PORT, ILI9325_BL_PIN, state ? 0 : 1); +} + +/**************************************************************************/ +/*! + @brief Renders a simple test pattern on the LCD +*/ +/**************************************************************************/ +void lcdTest(void) +{ + uint32_t i,j; + ili9325Home(); + + for(i=0;i<320;i++) + { + for(j=0;j<240;j++) + { + if(i>279)ili9325WriteData(COLOR_WHITE); + else if(i>239)ili9325WriteData(COLOR_BLUE); + else if(i>199)ili9325WriteData(COLOR_GREEN); + else if(i>159)ili9325WriteData(COLOR_CYAN); + else if(i>119)ili9325WriteData(COLOR_RED); + else if(i>79)ili9325WriteData(COLOR_MAGENTA); + else if(i>39)ili9325WriteData(COLOR_YELLOW); + else ili9325WriteData(COLOR_BLACK); + } + } +} + +/**************************************************************************/ +/*! + @brief Fills the LCD with the specified 16-bit color +*/ +/**************************************************************************/ +void lcdFillRGB(uint16_t data) +{ + unsigned int i; + ili9325Home(); + + uint32_t pixels = 320*240; + for ( i=0; i < pixels; i++ ) + { + ili9325WriteData(data); + } +} + +/**************************************************************************/ +/*! + @brief Draws a single pixel at the specified X/Y location +*/ +/**************************************************************************/ +void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) +{ + ili9325SetCursor(x, y); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) + ili9325WriteData(color); +} + +/**************************************************************************/ +/*! + @brief Draws an array of consecutive RGB565 pixels (much + faster than addressing each pixel individually) +*/ +/**************************************************************************/ +void lcdDrawPixels(uint16_t x, uint16_t y, uint16_t *data, uint32_t len) +{ + uint32_t i = 0; + ili9325SetCursor(x, y); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); + do + { + ili9325WriteData(data[i]); + i++; + } while (i= lcdGetWidth()) + { + x1 = lcdGetWidth() - 1; + } + if (x0 >= lcdGetWidth()) + { + x0 = lcdGetWidth() - 1; + } + + ili9325SetCursor(x0, y); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) + for (pixels = 0; pixels < x1 - x0 + 1; pixels++) + { + ili9325WriteData(color); + } +} + +/**************************************************************************/ +/*! + @brief Optimised routine to draw a vertical line faster than + setting individual pixels +*/ +/**************************************************************************/ +void lcdDrawVLine(uint16_t x, uint16_t y0, uint16_t y1, uint16_t color) +{ + // Allows for slightly better performance than setting individual pixels + lcdOrientation_t orientation = lcdOrientation; + + // Switch orientation + lcdSetOrientation(orientation == LCD_ORIENTATION_PORTRAIT ? LCD_ORIENTATION_LANDSCAPE : LCD_ORIENTATION_PORTRAIT); + + // Draw horizontal line like usual + lcdDrawHLine(y0, y1, lcdGetHeight() - (x + 1), color); + + // Switch orientation back + lcdSetOrientation(orientation); +} + +/**************************************************************************/ +/*! + @brief Gets the 16-bit color of the pixel at the specified location +*/ +/**************************************************************************/ +uint16_t lcdGetPixel(uint16_t x, uint16_t y) +{ + uint16_t preFetch = 0; + + ili9325SetCursor(x, y); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); + preFetch = ili9325ReadData(); + + // Eeek ... why does this need to be done twice for a proper value?!? + ili9325SetCursor(x, y); + ili9325WriteCmd(ILI9325_COMMANDS_WRITEDATATOGRAM); + return ili9325ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sets the LCD orientation to horizontal and vertical +*/ +/**************************************************************************/ +void lcdSetOrientation(lcdOrientation_t orientation) +{ + uint16_t entryMode = 0x1030; + uint16_t outputControl = 0x0100; + + switch (orientation) + { + case LCD_ORIENTATION_PORTRAIT: + entryMode = 0x1030; + outputControl = 0x0100; + break; + case LCD_ORIENTATION_LANDSCAPE: + entryMode = 0x1028; + outputControl = 0x0000; + break; + } + + ili9325Command(ILI9325_COMMANDS_ENTRYMODE, entryMode); + ili9325Command(ILI9325_COMMANDS_DRIVEROUTPUTCONTROL1, outputControl); + lcdOrientation = orientation; + + ili9325SetCursor(0, 0); +} + +/**************************************************************************/ +/*! + @brief Gets the current screen orientation (horizontal or vertical) +*/ +/**************************************************************************/ +lcdOrientation_t lcdGetOrientation(void) +{ + return lcdOrientation; +} + +/**************************************************************************/ +/*! + @brief Gets the width in pixels of the LCD screen (varies depending + on the current screen orientation) +*/ +/**************************************************************************/ +uint16_t lcdGetWidth(void) +{ + switch (lcdOrientation) + { + case LCD_ORIENTATION_PORTRAIT: + return ili9325Properties.width; + break; + case LCD_ORIENTATION_LANDSCAPE: + default: + return ili9325Properties.height; + } +} + +/**************************************************************************/ +/*! + @brief Gets the height in pixels of the LCD screen (varies depending + on the current screen orientation) +*/ +/**************************************************************************/ +uint16_t lcdGetHeight(void) +{ + switch (lcdOrientation) + { + case LCD_ORIENTATION_PORTRAIT: + return ili9325Properties.height; + break; + case LCD_ORIENTATION_LANDSCAPE: + default: + return ili9325Properties.width; + } +} + +/**************************************************************************/ +/*! + @brief Scrolls the contents of the LCD screen vertically the + specified number of pixels using a HW optimised routine +*/ +/**************************************************************************/ +void lcdScroll(int16_t pixels, uint16_t fillColor) +{ + int16_t y = pixels; + while (y < 0) + y += 320; + while (y >= 320) + y -= 320; + ili9325WriteCmd(ILI9325_COMMANDS_VERTICALSCROLLCONTROL); + ili9325WriteData(y); +} + +/**************************************************************************/ +/*! + @brief Gets the controller's 16-bit (4 hexdigit) ID +*/ +/**************************************************************************/ +uint16_t lcdGetControllerID(void) +{ + return ili9325Type(); +} + +/**************************************************************************/ +/*! + @brief Returns the LCDs 'lcdProperties_t' that describes the LCDs + generic capabilities and dimensions +*/ +/**************************************************************************/ +lcdProperties_t lcdGetProperties(void) +{ + return ili9325Properties; +} diff --git a/drivers/lcd/tft/hw/ILI9325.h b/drivers/lcd/tft/hw/ILI9325.h new file mode 100644 index 0000000..a589c40 --- /dev/null +++ b/drivers/lcd/tft/hw/ILI9325.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/*! + @file ILI9325.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __ILI9325_H__ +#define __ILI9325_H__ + +#include "projectconfig.h" + +#include "drivers/lcd/tft/lcd.h" +#include "core/gpio/gpio.h" + +// Control pins +#define ILI9325_CS_PORT 1 // CS (LCD Pin 7) +#define ILI9325_CS_PIN 8 +#define ILI9325_CD_PORT 1 // CS/RS (LCD Pin 8) +#define ILI9325_CD_PIN 9 +#define ILI9325_WR_PORT 1 // WR (LCD Pin 9) +#define ILI9325_WR_PIN 10 +#define ILI9325_RD_PORT 1 // RD (LCD Pin 10) +#define ILI9325_RD_PIN 11 + +// These combined pin definitions are for optimisation purposes. +// If the pin values above are modified the bit equivalents +// below will also need to be updated +#define ILI9325_CS_CD_PINS 0x300 // 8 + 9 +#define ILI9325_RD_WR_PINS 0xC00 // 11 + 10 +#define ILI9325_WR_CS_PINS 0x500 // 10 + 8 +#define ILI9325_CD_RD_WR_PINS 0xE00 // 9 + 11 + 10 +#define ILI9325_CS_CD_RD_WR_PINS 0xF00 // 8 + 9 + 11 + 10 + +// Backlight and Reset pins +#define ILI9325_RES_PORT 3 // LCD Reset (LCD Pin 31) +#define ILI9325_RES_PIN 3 +#define ILI9325_BL_PORT 2 // Backlight Enable (LCD Pin 16) +#define ILI9325_BL_PIN 9 + +// Data pins +// Note: data pins must be consecutive and on the same port +#define ILI9325_DATA_PORT 2 // 8-Pin Data Port +#define ILI9325_DATA_PIN1 1 +#define ILI9325_DATA_PIN2 2 +#define ILI9325_DATA_PIN3 3 +#define ILI9325_DATA_PIN4 4 +#define ILI9325_DATA_PIN5 5 +#define ILI9325_DATA_PIN6 6 +#define ILI9325_DATA_PIN7 7 +#define ILI9325_DATA_PIN8 8 +#define ILI9325_DATA_MASK 0x000001FE +#define ILI9325_DATA_OFFSET 1 // Offset = PIN1 + +// Placed here to try to keep all pin specific values in header file +#define ILI9325_DISABLEPULLUPS() do { gpioSetPullup(&IOCON_PIO2_1, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_2, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_3, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_4, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_5, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_6, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_7, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_8, gpioPullupMode_Inactive); } while (0) + +// These registers allow fast single operation clear+set of bits (see section 8.5.1 of LPC1343 UM) +#define ILI9325_GPIO2DATA_DATA (*(pREG32 (GPIO_GPIO2_BASE + (ILI9325_DATA_MASK << 2)))) +#define ILI9325_GPIO1DATA_WR (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9325_WR_PIN) << 2)))) +#define ILI9325_GPIO1DATA_CD (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9325_CD_PIN) << 2)))) +#define ILI9325_GPIO1DATA_CS (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9325_CS_PIN) << 2)))) +#define ILI9325_GPIO1DATA_RD (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9325_RD_PIN) << 2)))) +#define ILI9325_GPIO3DATA_RES (*(pREG32 (GPIO_GPIO3_BASE + ((1 << ILI9325_RES_PIN) << 2)))) +#define ILI9325_GPIO1DATA_CS_CD (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9325_CS_CD_PINS) << 2)))) +#define ILI9325_GPIO1DATA_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9325_RD_WR_PINS) << 2)))) +#define ILI9325_GPIO1DATA_WR_CS (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9325_WR_CS_PINS) << 2)))) +#define ILI9325_GPIO1DATA_CD_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9325_CD_RD_WR_PINS) << 2)))) +#define ILI9325_GPIO1DATA_CS_CD_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9325_CS_CD_RD_WR_PINS) << 2)))) + +// Macros to set data bus direction to input/output +#define ILI9325_GPIO2DATA_SETINPUT GPIO_GPIO2DIR &= ~ILI9325_DATA_MASK +#define ILI9325_GPIO2DATA_SETOUTPUT GPIO_GPIO2DIR |= ILI9325_DATA_MASK + +// Macros for control line state +#define CLR_CD ILI9325_GPIO1DATA_CD = (0) +#define SET_CD ILI9325_GPIO1DATA_CD = (1 << ILI9325_CD_PIN) +#define CLR_CS ILI9325_GPIO1DATA_CS = (0) +#define SET_CS ILI9325_GPIO1DATA_CS = (1 << ILI9325_CS_PIN) +#define CLR_WR ILI9325_GPIO1DATA_WR = (0) +#define SET_WR ILI9325_GPIO1DATA_WR = (1 << ILI9325_WR_PIN) +#define CLR_RD ILI9325_GPIO1DATA_RD = (0) +#define SET_RD ILI9325_GPIO1DATA_RD = (1 << ILI9325_RD_PIN) +#define CLR_RESET ILI9325_GPIO3DATA_RES = (0) +#define SET_RESET ILI9325_GPIO3DATA_RES = (1 << ILI9325_RES_PIN) + +// These 'combined' macros are defined to improve code performance by +// reducing the number of instructions in heavily used functions +#define CLR_CS_CD ILI9325_GPIO1DATA_CS_CD = (0); +#define SET_RD_WR ILI9325_GPIO1DATA_RD_WR = (ILI9325_RD_WR_PINS); +#define SET_WR_CS ILI9325_GPIO1DATA_WR_CS = (ILI9325_WR_CS_PINS); +#define SET_CD_RD_WR ILI9325_GPIO1DATA_CD_RD_WR = (ILI9325_CD_RD_WR_PINS); +#define CLR_CS_CD_SET_RD_WR ILI9325_GPIO1DATA_CS_CD_RD_WR = (ILI9325_RD_WR_PINS); +#define CLR_CS_SET_CD_RD_WR ILI9325_GPIO1DATA_CS_CD_RD_WR = (ILI9325_CD_RD_WR_PINS); + +enum +{ + ILI9325_COMMANDS_DRIVERCODEREAD = 0x0000, + ILI9325_COMMANDS_DRIVEROUTPUTCONTROL1 = 0x0001, + ILI9325_COMMANDS_LCDDRIVINGCONTROL = 0x0002, + ILI9325_COMMANDS_ENTRYMODE = 0x0003, + ILI9325_COMMANDS_RESIZECONTROL = 0x0004, + ILI9325_COMMANDS_DISPLAYCONTROL1 = 0x0007, + ILI9325_COMMANDS_DISPLAYCONTROL2 = 0x0008, + ILI9325_COMMANDS_DISPLAYCONTROL3 = 0x0009, + ILI9325_COMMANDS_DISPLAYCONTROL4 = 0x000A, + ILI9325_COMMANDS_RGBDISPLAYINTERFACECONTROL1 = 0x000C, + ILI9325_COMMANDS_FRAMEMAKERPOSITION = 0x000D, + ILI9325_COMMANDS_RGBDISPLAYINTERFACECONTROL2 = 0x000F, + ILI9325_COMMANDS_POWERCONTROL1 = 0x0010, + ILI9325_COMMANDS_POWERCONTROL2 = 0x0011, + ILI9325_COMMANDS_POWERCONTROL3 = 0x0012, + ILI9325_COMMANDS_POWERCONTROL4 = 0x0013, + ILI9325_COMMANDS_HORIZONTALGRAMADDRESSSET = 0x0020, + ILI9325_COMMANDS_VERTICALGRAMADDRESSSET = 0x0021, + ILI9325_COMMANDS_WRITEDATATOGRAM = 0x0022, + ILI9325_COMMANDS_POWERCONTROL7 = 0x0029, + ILI9325_COMMANDS_FRAMERATEANDCOLORCONTROL = 0x002B, + ILI9325_COMMANDS_GAMMACONTROL1 = 0x0030, + ILI9325_COMMANDS_GAMMACONTROL2 = 0x0031, + ILI9325_COMMANDS_GAMMACONTROL3 = 0x0032, + ILI9325_COMMANDS_GAMMACONTROL4 = 0x0035, + ILI9325_COMMANDS_GAMMACONTROL5 = 0x0036, + ILI9325_COMMANDS_GAMMACONTROL6 = 0x0037, + ILI9325_COMMANDS_GAMMACONTROL7 = 0x0038, + ILI9325_COMMANDS_GAMMACONTROL8 = 0x0039, + ILI9325_COMMANDS_GAMMACONTROL9 = 0x003C, + ILI9325_COMMANDS_GAMMACONTROL10 = 0x003D, + ILI9325_COMMANDS_HORIZONTALADDRESSSTARTPOSITION = 0x0050, + ILI9325_COMMANDS_HORIZONTALADDRESSENDPOSITION = 0x0051, + ILI9325_COMMANDS_VERTICALADDRESSSTARTPOSITION = 0x0052, + ILI9325_COMMANDS_VERTICALADDRESSENDPOSITION = 0x0053, + ILI9325_COMMANDS_DRIVEROUTPUTCONTROL2 = 0x0060, + ILI9325_COMMANDS_BASEIMAGEDISPLAYCONTROL = 0x0061, + ILI9325_COMMANDS_VERTICALSCROLLCONTROL = 0x006A, + ILI9325_COMMANDS_PARTIALIMAGE1DISPLAYPOSITION = 0x0080, + ILI9325_COMMANDS_PARTIALIMAGE1AREASTARTLINE = 0x0081, + ILI9325_COMMANDS_PARTIALIMAGE1AREAENDLINE = 0x0082, + ILI9325_COMMANDS_PARTIALIMAGE2DISPLAYPOSITION = 0x0083, + ILI9325_COMMANDS_PARTIALIMAGE2AREASTARTLINE = 0x0084, + ILI9325_COMMANDS_PARTIALIMAGE2AREAENDLINE = 0x0085, + ILI9325_COMMANDS_PANELINTERFACECONTROL1 = 0x0090, + ILI9325_COMMANDS_PANELINTERFACECONTROL2 = 0x0092, + ILI9325_COMMANDS_PANELINTERFACECONTROL4 = 0x0095, + ILI9325_COMMANDS_OTPVCMPROGRAMMINGCONTROL = 0x00A1, + ILI9325_COMMANDS_OTPVCMSTATUSANDENABLE = 0x00A2, + ILI9325_COMMANDS_OTPPROGRAMMINGIDKEY = 0x00A5 +}; + +#endif diff --git a/drivers/lcd/tft/hw/ILI9328.c b/drivers/lcd/tft/hw/ILI9328.c new file mode 100644 index 0000000..b7a69eb --- /dev/null +++ b/drivers/lcd/tft/hw/ILI9328.c @@ -0,0 +1,616 @@ +/**************************************************************************/ +/*! + @file ILI9328.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for ILI9328 240x320 pixel TFT LCD displays. + + This driver uses an 8-bit interface and a 16-bit RGB565 colour palette. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "ILI9328.h" +#include "core/systick/systick.h" +#include "drivers/lcd/tft/touchscreen.h" + +static volatile lcdOrientation_t lcdOrientation = LCD_ORIENTATION_PORTRAIT; +static lcdProperties_t ili9328Properties = { 240, 320, TRUE, TRUE, TRUE }; + +/*************************************************/ +/* Private Methods */ +/*************************************************/ + +/**************************************************************************/ +/*! + @brief Causes a brief delay (10 ticks per unit) +*/ +/**************************************************************************/ +void ili9328Delay(unsigned int t) +{ + unsigned char t1; + while(t--) + for ( t1=10; t1 > 0; t1-- ) + { + __asm("nop"); + } +} + +/**************************************************************************/ +/*! + @brief Writes the supplied 16-bit command using an 8-bit interface +*/ +/**************************************************************************/ +void ili9328WriteCmd(uint16_t command) +{ + // Compiled with -Os on GCC 4.4 this works out to 25 cycles + // (versus 36 compiled with no optimisations). I'm not sure it + // can be improved further, so that means 25 cycles/350nS for + // continuous writes (cmd, data, data, data, ...) or ~150 cycles/ + // ~2.1uS for a random pixel (Set X [cmd+data], Set Y [cmd+data], + // Set color [cmd+data]) (times assumes 72MHz clock). + + CLR_CS_CD_SET_RD_WR; // Saves 18 commands compared to "CLR_CS; CLR_CD; SET_RD; SET_WR;" + ILI9328_GPIO2DATA_DATA = (command >> (8 - ILI9328_DATA_OFFSET)); + CLR_WR; + SET_WR; + ILI9328_GPIO2DATA_DATA = command << ILI9328_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR; SET_CS;" +} + +/**************************************************************************/ +/*! + @brief Writes the supplied 16-bit data using an 8-bit interface +*/ +/**************************************************************************/ +void ili9328WriteData(uint16_t data) +{ + CLR_CS_SET_CD_RD_WR; // Saves 18 commands compared to SET_CD; SET_RD; SET_WR; CLR_CS" + ILI9328_GPIO2DATA_DATA = (data >> (8 - ILI9328_DATA_OFFSET)); + CLR_WR; + SET_WR; + ILI9328_GPIO2DATA_DATA = data << ILI9328_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR, SET_CS;" +} + +/**************************************************************************/ +/*! + @brief Reads a 16-bit value from the 8-bit data bus +*/ +/**************************************************************************/ +uint16_t ili9328ReadData(void) +{ + // ToDo: Optimise this method! + + uint16_t high, low; + high = low = 0; + uint16_t d; + + SET_CD_RD_WR; // Saves 14 commands compared to "SET_CD; SET_RD; SET_WR" + CLR_CS; + + // set inputs + ILI9328_GPIO2DATA_SETINPUT; + CLR_RD; + ili9328Delay(100); + high = ILI9328_GPIO2DATA_DATA; + high >>= ILI9328_DATA_OFFSET; + high &= 0xFF; + SET_RD; + + CLR_RD; + ili9328Delay(100); + low = ILI9328_GPIO2DATA_DATA; + low >>= ILI9328_DATA_OFFSET; + low &=0xFF; + SET_RD; + + SET_CS; + ILI9328_GPIO2DATA_SETOUTPUT; + + d = high; + d <<= 8; + d |= low; + + return d; +} + +/**************************************************************************/ +/*! + @brief Reads a 16-bit value +*/ +/**************************************************************************/ +uint16_t ili9328Read(uint16_t addr) +{ + ili9328WriteCmd(addr); + return ili9328ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sends a 16-bit command + 16-bit data +*/ +/**************************************************************************/ +void ili9328Command(uint16_t command, uint16_t data) +{ + ili9328WriteCmd(command); + ili9328WriteData(data); +} + +/**************************************************************************/ +/*! + @brief Returns the 16-bit (4-hexdigit) controller code +*/ +/**************************************************************************/ +uint16_t ili9328Type(void) +{ + ili9328WriteCmd(ILI9328_COMMANDS_DRIVERCODEREAD); + return ili9328ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sets the cursor to the specified X/Y position +*/ +/**************************************************************************/ +void ili9328SetCursor(uint16_t x, uint16_t y) +{ + uint16_t al, ah; + + if (lcdOrientation == LCD_ORIENTATION_LANDSCAPE) + { + al = y; + ah = x; + } + else + { + al = x; + ah = y; + } + + ili9328Command(ILI9328_COMMANDS_HORIZONTALGRAMADDRESSSET, al); + ili9328Command(ILI9328_COMMANDS_VERTICALGRAMADDRESSSET, ah); +} + +/**************************************************************************/ +/*! + @brief Sends the initialisation sequence to the display controller +*/ +/**************************************************************************/ +void ili9328InitDisplay(void) +{ + // Clear data line + GPIO_GPIO2DATA &= ~ILI9328_DATA_MASK; + + SET_RD; + SET_WR; + SET_CS; + SET_CD; + + // Reset display + CLR_RESET; + ili9328Delay(100); + SET_RESET; + ili9328Delay(1000); + + ili9328Command(ILI9328_COMMANDS_DRIVEROUTPUTCONTROL1, 0x0100); // Driver Output Control Register (R01h) + ili9328Command(ILI9328_COMMANDS_LCDDRIVINGCONTROL, 0x0700); // LCD Driving Waveform Control (R02h) + ili9328Command(ILI9328_COMMANDS_ENTRYMODE, 0x1030); // Entry Mode (R03h) + ili9328Command(ILI9328_COMMANDS_DISPLAYCONTROL2, 0x0302); + ili9328Command(ILI9328_COMMANDS_DISPLAYCONTROL3, 0x0000); + ili9328Command(ILI9328_COMMANDS_DISPLAYCONTROL4, 0x0000); // Fmark On + ili9328Command(ILI9328_COMMANDS_POWERCONTROL1, 0x0000); // Power Control 1 (R10h) + ili9328Command(ILI9328_COMMANDS_POWERCONTROL2, 0x0007); // Power Control 2 (R11h) + ili9328Command(ILI9328_COMMANDS_POWERCONTROL3, 0x0000); // Power Control 3 (R12h) + ili9328Command(ILI9328_COMMANDS_POWERCONTROL4, 0x0000); // Power Control 4 (R13h) + ili9328Delay(1000); + ili9328Command(ILI9328_COMMANDS_POWERCONTROL1, 0x14B0); // Power Control 1 (R10h) + ili9328Delay(500); + ili9328Command(ILI9328_COMMANDS_POWERCONTROL2, 0x0007); // Power Control 2 (R11h) + ili9328Delay(500); + ili9328Command(ILI9328_COMMANDS_POWERCONTROL3, 0x008E); // Power Control 3 (R12h) + ili9328Command(ILI9328_COMMANDS_POWERCONTROL4, 0x0C00); // Power Control 4 (R13h) + ili9328Command(ILI9328_COMMANDS_POWERCONTROL7, 0x0015); // NVM read data 2 (R29h) + ili9328Delay(500); + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL1, 0x0000); // Gamma Control 1 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL2, 0x0107); // Gamma Control 2 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL3, 0x0000); // Gamma Control 3 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL4, 0x0203); // Gamma Control 4 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL5, 0x0402); // Gamma Control 5 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL6, 0x0000); // Gamma Control 6 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL7, 0x0207); // Gamma Control 7 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL8, 0x0000); // Gamma Control 8 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL9, 0x0203); // Gamma Control 9 + ili9328Command(ILI9328_COMMANDS_GAMMACONTROL10, 0x0403); // Gamma Control 10 + ili9328Command(ILI9328_COMMANDS_HORIZONTALADDRESSSTARTPOSITION, 0x0000); // Window Horizontal RAM Address Start (R50h) + ili9328Command(ILI9328_COMMANDS_HORIZONTALADDRESSENDPOSITION, ili9328Properties.width - 1); // Window Horizontal RAM Address End (R51h) + ili9328Command(ILI9328_COMMANDS_VERTICALADDRESSSTARTPOSITION, 0X0000); // Window Vertical RAM Address Start (R52h) + ili9328Command(ILI9328_COMMANDS_VERTICALADDRESSENDPOSITION, ili9328Properties.height - 1); // Window Vertical RAM Address End (R53h) + ili9328Command(ILI9328_COMMANDS_DRIVEROUTPUTCONTROL2, 0xa700); // Driver Output Control (R60h) + ili9328Command(ILI9328_COMMANDS_BASEIMAGEDISPLAYCONTROL, 0x0003); // Driver Output Control (R61h) - enable VLE + ili9328Command(ILI9328_COMMANDS_PANELINTERFACECONTROL1, 0X0010); // Panel Interface Control 1 (R90h) + + // Display On + ili9328Command(ILI9328_COMMANDS_DISPLAYCONTROL1, 0x0133); // Display Control (R07h) + ili9328Delay(500); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); +} + +/**************************************************************************/ +/*! + @brief Sets the cursor to the home position (0,0) +*/ +/**************************************************************************/ +void ili9328Home(void) +{ + ili9328SetCursor(0, 0); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) +} + +/**************************************************************************/ +/*! + @brief Sets the window confines +*/ +/**************************************************************************/ +void ili9328SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) +{ + ili9328Command(ILI9328_COMMANDS_HORIZONTALADDRESSSTARTPOSITION, x0); + ili9328Command(ILI9328_COMMANDS_HORIZONTALADDRESSENDPOSITION, x1); + ili9328Command(ILI9328_COMMANDS_VERTICALADDRESSSTARTPOSITION, y0); + ili9328Command(ILI9328_COMMANDS_VERTICALADDRESSENDPOSITION, y1); + ili9328SetCursor(x0, y0); +} + +/*************************************************/ +/* Public Methods */ +/*************************************************/ + +/**************************************************************************/ +/*! + @brief Configures any pins or HW and initialises the LCD controller +*/ +/**************************************************************************/ +void lcdInit(void) +{ + // Set control line pins to output + gpioSetDir(ILI9328_CS_PORT, ILI9328_CS_PIN, 1); + gpioSetDir(ILI9328_CD_PORT, ILI9328_CD_PIN, 1); + gpioSetDir(ILI9328_WR_PORT, ILI9328_WR_PIN, 1); + gpioSetDir(ILI9328_RD_PORT, ILI9328_RD_PIN, 1); + + // Set data port pins to output + ILI9328_GPIO2DATA_SETOUTPUT; + + // Disable pullups + ILI9328_DISABLEPULLUPS(); + + // Set backlight pin to output and turn it on + gpioSetDir(ILI9328_BL_PORT, ILI9328_BL_PIN, 1); // set to output + lcdBacklight(TRUE); + + // Set reset pin to output + gpioSetDir(ILI9328_RES_PORT, ILI9328_RES_PIN, 1); // Set to output + gpioSetValue(ILI9328_RES_PORT, ILI9328_RES_PIN, 0); // Low to reset + systickDelay(50); + gpioSetValue(ILI9328_RES_PORT, ILI9328_RES_PIN, 1); // High to exit + + // Initialize the display + ili9328InitDisplay(); + + systickDelay(50); + + // Set lcd to default orientation + lcdSetOrientation(lcdOrientation); + + // Fill black + lcdFillRGB(COLOR_BLACK); + + // Initialise the touch screen (and calibrate if necessary) + tsInit(); +} + +/**************************************************************************/ +/*! + @brief Enables or disables the LCD backlight +*/ +/**************************************************************************/ +void lcdBacklight(bool state) +{ + // Set the backlight + gpioSetValue(ILI9328_BL_PORT, ILI9328_BL_PIN, state ? 0 : 1); +} + +/**************************************************************************/ +/*! + @brief Renders a simple test pattern on the LCD +*/ +/**************************************************************************/ +void lcdTest(void) +{ + uint32_t i,j; + ili9328Home(); + + for(i=0;i<320;i++) + { + for(j=0;j<240;j++) + { + if(i>279)ili9328WriteData(COLOR_WHITE); + else if(i>239)ili9328WriteData(COLOR_BLUE); + else if(i>199)ili9328WriteData(COLOR_GREEN); + else if(i>159)ili9328WriteData(COLOR_CYAN); + else if(i>119)ili9328WriteData(COLOR_RED); + else if(i>79)ili9328WriteData(COLOR_MAGENTA); + else if(i>39)ili9328WriteData(COLOR_YELLOW); + else ili9328WriteData(COLOR_BLACK); + } + } +} + +/**************************************************************************/ +/*! + @brief Fills the LCD with the specified 16-bit color +*/ +/**************************************************************************/ +void lcdFillRGB(uint16_t data) +{ + unsigned int i; + ili9328Home(); + + uint32_t pixels = 320*240; + for ( i=0; i < pixels; i++ ) + { + ili9328WriteData(data); + } +} + +/**************************************************************************/ +/*! + @brief Draws a single pixel at the specified X/Y location +*/ +/**************************************************************************/ +void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) +{ + ili9328SetCursor(x, y); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) + ili9328WriteData(color); +} + +/**************************************************************************/ +/*! + @brief Draws an array of consecutive RGB565 pixels (much + faster than addressing each pixel individually) +*/ +/**************************************************************************/ +void lcdDrawPixels(uint16_t x, uint16_t y, uint16_t *data, uint32_t len) +{ + uint32_t i = 0; + ili9328SetCursor(x, y); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); + do + { + ili9328WriteData(data[i]); + i++; + } while (i= lcdGetWidth()) + { + x1 = lcdGetWidth() - 1; + } + if (x0 >= lcdGetWidth()) + { + x0 = lcdGetWidth() - 1; + } + + ili9328SetCursor(x0, y); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); // Write Data to GRAM (R22h) + for (pixels = 0; pixels < x1 - x0 + 1; pixels++) + { + ili9328WriteData(color); + } +} + +/**************************************************************************/ +/*! + @brief Optimised routine to draw a vertical line faster than + setting individual pixels +*/ +/**************************************************************************/ +void lcdDrawVLine(uint16_t x, uint16_t y0, uint16_t y1, uint16_t color) +{ + lcdOrientation_t orientation = lcdOrientation; + + // Switch orientation + lcdSetOrientation(orientation == LCD_ORIENTATION_PORTRAIT ? LCD_ORIENTATION_LANDSCAPE : LCD_ORIENTATION_PORTRAIT); + + // Draw horizontal line like usual + lcdDrawHLine(y0, y1, lcdGetHeight() - (x + 1), color); + + // Switch orientation back + lcdSetOrientation(orientation); +} + +/**************************************************************************/ +/*! + @brief Gets the 16-bit color of the pixel at the specified location +*/ +/**************************************************************************/ +uint16_t lcdGetPixel(uint16_t x, uint16_t y) +{ + uint16_t preFetch = 0; + + ili9328SetCursor(x, y); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); + preFetch = ili9328ReadData(); + + // Eeek ... why does this need to be done twice for a proper value?!? + ili9328SetCursor(x, y); + ili9328WriteCmd(ILI9328_COMMANDS_WRITEDATATOGRAM); + return ili9328ReadData(); +} + +/**************************************************************************/ +/*! + @brief Sets the LCD orientation to horizontal and vertical +*/ +/**************************************************************************/ +void lcdSetOrientation(lcdOrientation_t orientation) +{ + uint16_t entryMode = 0x1030; + uint16_t outputControl = 0x0100; + + switch (orientation) + { + case LCD_ORIENTATION_PORTRAIT: + entryMode = 0x1030; + outputControl = 0x0100; + break; + case LCD_ORIENTATION_LANDSCAPE: + entryMode = 0x1028; + outputControl = 0x0000; + break; + } + + ili9328Command(ILI9328_COMMANDS_ENTRYMODE, entryMode); + ili9328Command(ILI9328_COMMANDS_DRIVEROUTPUTCONTROL1, outputControl); + lcdOrientation = orientation; + + ili9328SetCursor(0, 0); +} + +/**************************************************************************/ +/*! + @brief Gets the current screen orientation (horizontal or vertical) +*/ +/**************************************************************************/ +lcdOrientation_t lcdGetOrientation(void) +{ + return lcdOrientation; +} + +/**************************************************************************/ +/*! + @brief Gets the width in pixels of the LCD screen (varies depending + on the current screen orientation) +*/ +/**************************************************************************/ +uint16_t lcdGetWidth(void) +{ + switch (lcdOrientation) + { + case LCD_ORIENTATION_PORTRAIT: + return ili9328Properties.width; + break; + case LCD_ORIENTATION_LANDSCAPE: + default: + return ili9328Properties.height; + } +} + +/**************************************************************************/ +/*! + @brief Gets the height in pixels of the LCD screen (varies depending + on the current screen orientation) +*/ +/**************************************************************************/ +uint16_t lcdGetHeight(void) +{ + switch (lcdOrientation) + { + case LCD_ORIENTATION_PORTRAIT: + return ili9328Properties.height; + break; + case LCD_ORIENTATION_LANDSCAPE: + default: + return ili9328Properties.width; + } +} + +/**************************************************************************/ +/*! + @brief Scrolls the contents of the LCD screen vertically the + specified number of pixels using a HW optimised routine +*/ +/**************************************************************************/ +void lcdScroll(int16_t pixels, uint16_t fillColor) +{ + int16_t y = pixels; + while (y < 0) + y += 320; + while (y >= 320) + y -= 320; + ili9328WriteCmd(ILI9328_COMMANDS_VERTICALSCROLLCONTROL); + ili9328WriteData(y); +} + +/**************************************************************************/ +/*! + @brief Gets the controller's 16-bit (4 hexdigit) ID +*/ +/**************************************************************************/ +uint16_t lcdGetControllerID(void) +{ + return ili9328Type(); +} + +/**************************************************************************/ +/*! + @brief Returns the LCDs 'lcdProperties_t' that describes the LCDs + generic capabilities and dimensions +*/ +/**************************************************************************/ +lcdProperties_t lcdGetProperties(void) +{ + return ili9328Properties; +} diff --git a/drivers/lcd/tft/hw/ILI9328.h b/drivers/lcd/tft/hw/ILI9328.h new file mode 100644 index 0000000..82cedb6 --- /dev/null +++ b/drivers/lcd/tft/hw/ILI9328.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/*! + @file ILI9328.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __ILI9328_H__ +#define __ILI9328_H__ + +#include "projectconfig.h" + +#include "drivers/lcd/tft/lcd.h" +#include "core/gpio/gpio.h" + +// Control pins +#define ILI9328_CS_PORT 1 // CS (LCD Pin 7) +#define ILI9328_CS_PIN 8 +#define ILI9328_CD_PORT 1 // CS/RS (LCD Pin 8) +#define ILI9328_CD_PIN 9 +#define ILI9328_WR_PORT 1 // WR (LCD Pin 9) +#define ILI9328_WR_PIN 10 +#define ILI9328_RD_PORT 1 // RD (LCD Pin 10) +#define ILI9328_RD_PIN 11 + +// These combined pin definitions are for optimisation purposes. +// If the pin values above are modified the bit equivalents +// below will also need to be updated +#define ILI9328_CS_CD_PINS 0x300 // 8 + 9 +#define ILI9328_RD_WR_PINS 0xC00 // 11 + 10 +#define ILI9328_WR_CS_PINS 0x500 // 10 + 8 +#define ILI9328_CD_RD_WR_PINS 0xE00 // 9 + 11 + 10 +#define ILI9328_CS_CD_RD_WR_PINS 0xF00 // 8 + 9 + 11 + 10 + +// Backlight and Reset pins +#define ILI9328_RES_PORT 3 // LCD Reset (LCD Pin 31) +#define ILI9328_RES_PIN 3 +#define ILI9328_BL_PORT 2 // Backlight Enable (LCD Pin 16) +#define ILI9328_BL_PIN 9 + +// Data pins +// Note: data pins must be consecutive and on the same port +#define ILI9328_DATA_PORT 2 // 8-Pin Data Port +#define ILI9328_DATA_PIN1 1 +#define ILI9328_DATA_PIN2 2 +#define ILI9328_DATA_PIN3 3 +#define ILI9328_DATA_PIN4 4 +#define ILI9328_DATA_PIN5 5 +#define ILI9328_DATA_PIN6 6 +#define ILI9328_DATA_PIN7 7 +#define ILI9328_DATA_PIN8 8 +#define ILI9328_DATA_MASK 0x000001FE +#define ILI9328_DATA_OFFSET 1 // Offset = PIN1 + +// Placed here to try to keep all pin specific values in header file +#define ILI9328_DISABLEPULLUPS() do { gpioSetPullup(&IOCON_PIO2_1, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_2, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_3, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_4, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_5, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_6, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_7, gpioPullupMode_Inactive); \ + gpioSetPullup(&IOCON_PIO2_8, gpioPullupMode_Inactive); } while (0) + +// These registers allow fast single operation clear+set of bits (see section 8.5.1 of LPC1343 UM) +#define ILI9328_GPIO2DATA_DATA (*(pREG32 (GPIO_GPIO2_BASE + (ILI9328_DATA_MASK << 2)))) +#define ILI9328_GPIO1DATA_WR (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9328_WR_PIN) << 2)))) +#define ILI9328_GPIO1DATA_CD (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9328_CD_PIN) << 2)))) +#define ILI9328_GPIO1DATA_CS (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9328_CS_PIN) << 2)))) +#define ILI9328_GPIO1DATA_RD (*(pREG32 (GPIO_GPIO1_BASE + ((1 << ILI9328_RD_PIN) << 2)))) +#define ILI9328_GPIO3DATA_RES (*(pREG32 (GPIO_GPIO3_BASE + ((1 << ILI9328_RES_PIN) << 2)))) +#define ILI9328_GPIO1DATA_CS_CD (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9328_CS_CD_PINS) << 2)))) +#define ILI9328_GPIO1DATA_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9328_RD_WR_PINS) << 2)))) +#define ILI9328_GPIO1DATA_WR_CS (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9328_WR_CS_PINS) << 2)))) +#define ILI9328_GPIO1DATA_CD_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9328_CD_RD_WR_PINS) << 2)))) +#define ILI9328_GPIO1DATA_CS_CD_RD_WR (*(pREG32 (GPIO_GPIO1_BASE + ((ILI9328_CS_CD_RD_WR_PINS) << 2)))) + +// Macros to set data bus direction to input/output +#define ILI9328_GPIO2DATA_SETINPUT GPIO_GPIO2DIR &= ~ILI9328_DATA_MASK +#define ILI9328_GPIO2DATA_SETOUTPUT GPIO_GPIO2DIR |= ILI9328_DATA_MASK + +// Macros for control line state +#define CLR_CD ILI9328_GPIO1DATA_CD = (0) +#define SET_CD ILI9328_GPIO1DATA_CD = (1 << ILI9328_CD_PIN) +#define CLR_CS ILI9328_GPIO1DATA_CS = (0) +#define SET_CS ILI9328_GPIO1DATA_CS = (1 << ILI9328_CS_PIN) +#define CLR_WR ILI9328_GPIO1DATA_WR = (0) +#define SET_WR ILI9328_GPIO1DATA_WR = (1 << ILI9328_WR_PIN) +#define CLR_RD ILI9328_GPIO1DATA_RD = (0) +#define SET_RD ILI9328_GPIO1DATA_RD = (1 << ILI9328_RD_PIN) +#define CLR_RESET ILI9328_GPIO3DATA_RES = (0) +#define SET_RESET ILI9328_GPIO3DATA_RES = (1 << ILI9328_RES_PIN) + +// These 'combined' macros are defined to improve code performance by +// reducing the number of instructions in heavily used functions +#define CLR_CS_CD ILI9328_GPIO1DATA_CS_CD = (0); +#define SET_RD_WR ILI9328_GPIO1DATA_RD_WR = (ILI9328_RD_WR_PINS); +#define SET_WR_CS ILI9328_GPIO1DATA_WR_CS = (ILI9328_WR_CS_PINS); +#define SET_CD_RD_WR ILI9328_GPIO1DATA_CD_RD_WR = (ILI9328_CD_RD_WR_PINS); +#define CLR_CS_CD_SET_RD_WR ILI9328_GPIO1DATA_CS_CD_RD_WR = (ILI9328_RD_WR_PINS); +#define CLR_CS_SET_CD_RD_WR ILI9328_GPIO1DATA_CS_CD_RD_WR = (ILI9328_CD_RD_WR_PINS); + +enum +{ + ILI9328_COMMANDS_DRIVERCODEREAD = 0x0000, + ILI9328_COMMANDS_DRIVEROUTPUTCONTROL1 = 0x0001, + ILI9328_COMMANDS_LCDDRIVINGCONTROL = 0x0002, + ILI9328_COMMANDS_ENTRYMODE = 0x0003, + ILI9328_COMMANDS_RESIZECONTROL = 0x0004, + ILI9328_COMMANDS_DISPLAYCONTROL1 = 0x0007, + ILI9328_COMMANDS_DISPLAYCONTROL2 = 0x0008, + ILI9328_COMMANDS_DISPLAYCONTROL3 = 0x0009, + ILI9328_COMMANDS_DISPLAYCONTROL4 = 0x000A, + ILI9328_COMMANDS_RGBDISPLAYINTERFACECONTROL1 = 0x000C, + ILI9328_COMMANDS_FRAMEMAKERPOSITION = 0x000D, + ILI9328_COMMANDS_RGBDISPLAYINTERFACECONTROL2 = 0x000F, + ILI9328_COMMANDS_POWERCONTROL1 = 0x0010, + ILI9328_COMMANDS_POWERCONTROL2 = 0x0011, + ILI9328_COMMANDS_POWERCONTROL3 = 0x0012, + ILI9328_COMMANDS_POWERCONTROL4 = 0x0013, + ILI9328_COMMANDS_HORIZONTALGRAMADDRESSSET = 0x0020, + ILI9328_COMMANDS_VERTICALGRAMADDRESSSET = 0x0021, + ILI9328_COMMANDS_WRITEDATATOGRAM = 0x0022, + ILI9328_COMMANDS_POWERCONTROL7 = 0x0029, + ILI9328_COMMANDS_FRAMERATEANDCOLORCONTROL = 0x002B, + ILI9328_COMMANDS_GAMMACONTROL1 = 0x0030, + ILI9328_COMMANDS_GAMMACONTROL2 = 0x0031, + ILI9328_COMMANDS_GAMMACONTROL3 = 0x0032, + ILI9328_COMMANDS_GAMMACONTROL4 = 0x0035, + ILI9328_COMMANDS_GAMMACONTROL5 = 0x0036, + ILI9328_COMMANDS_GAMMACONTROL6 = 0x0037, + ILI9328_COMMANDS_GAMMACONTROL7 = 0x0038, + ILI9328_COMMANDS_GAMMACONTROL8 = 0x0039, + ILI9328_COMMANDS_GAMMACONTROL9 = 0x003C, + ILI9328_COMMANDS_GAMMACONTROL10 = 0x003D, + ILI9328_COMMANDS_HORIZONTALADDRESSSTARTPOSITION = 0x0050, + ILI9328_COMMANDS_HORIZONTALADDRESSENDPOSITION = 0x0051, + ILI9328_COMMANDS_VERTICALADDRESSSTARTPOSITION = 0x0052, + ILI9328_COMMANDS_VERTICALADDRESSENDPOSITION = 0x0053, + ILI9328_COMMANDS_DRIVEROUTPUTCONTROL2 = 0x0060, + ILI9328_COMMANDS_BASEIMAGEDISPLAYCONTROL = 0x0061, + ILI9328_COMMANDS_VERTICALSCROLLCONTROL = 0x006A, + ILI9328_COMMANDS_PARTIALIMAGE1DISPLAYPOSITION = 0x0080, + ILI9328_COMMANDS_PARTIALIMAGE1AREASTARTLINE = 0x0081, + ILI9328_COMMANDS_PARTIALIMAGE1AREAENDLINE = 0x0082, + ILI9328_COMMANDS_PARTIALIMAGE2DISPLAYPOSITION = 0x0083, + ILI9328_COMMANDS_PARTIALIMAGE2AREASTARTLINE = 0x0084, + ILI9328_COMMANDS_PARTIALIMAGE2AREAENDLINE = 0x0085, + ILI9328_COMMANDS_PANELINTERFACECONTROL1 = 0x0090, + ILI9328_COMMANDS_PANELINTERFACECONTROL2 = 0x0092, + ILI9328_COMMANDS_PANELINTERFACECONTROL4 = 0x0095, + ILI9328_COMMANDS_OTPVCMPROGRAMMINGCONTROL = 0x00A1, + ILI9328_COMMANDS_OTPVCMSTATUSANDENABLE = 0x00A2, + ILI9328_COMMANDS_OTPPROGRAMMINGIDKEY = 0x00A5 +}; + +#endif diff --git a/drivers/lcd/tft/hw/readme.txt b/drivers/lcd/tft/hw/readme.txt new file mode 100644 index 0000000..dd0ec4c --- /dev/null +++ b/drivers/lcd/tft/hw/readme.txt @@ -0,0 +1,12 @@ +HW-Specific LCD Drivers +======================= + +ILI9325 - 240x320 16-bit display (8-bit interface) +ILI9328 - 240x320 16-bit display (8-bit interface) +st7735 - 128x160 16-bit display (Bit-banged SPI interface) +st7783 - 240x320 16-bit displays (8-bit interface) + +NOTE: Only the ILI9325 and ILI9328 drivers have been fully tested. +The others are incomplete or have only been partially tested. (The +ST7783 driver, for example, has issues when the screen orientation +is set to landscape.) \ No newline at end of file diff --git a/drivers/lcd/tft/hw/st7735.c b/drivers/lcd/tft/hw/st7735.c new file mode 100644 index 0000000..939015e --- /dev/null +++ b/drivers/lcd/tft/hw/st7735.c @@ -0,0 +1,444 @@ +/**************************************************************************/ +/*! + @file st7735.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for st7735 128x160 pixel TFT LCD displays. + + This driver uses a bit-banged SPI interface and a 16-bit RGB565 + colour palette. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "st7735.h" +#include "core/systick/systick.h" +#include "core/gpio/gpio.h" + +static lcdOrientation_t lcdOrientation = LCD_ORIENTATION_PORTRAIT; +static lcdProperties_t st7735Properties = { 128, 160, FALSE, FALSE, FALSE }; + +/*************************************************/ +/* Private Methods */ +/*************************************************/ + +/*************************************************/ +void st7735WriteCmd(uint8_t command) +{ + CLR_CS; + CLR_RS; + uint8_t i = 0; + for (i=0; i<8; i++) + { + if (command & 0x80) + { + SET_SDA; + } + else + { + CLR_SDA; + } + CLR_SCL; + command <<= 1; + SET_SCL; + } + SET_CS; +} + +/*************************************************/ +void st7735WriteData(uint8_t data) +{ + CLR_CS; + SET_RS; + uint8_t i = 0; + for (i=0; i<8; i++) + { + if (data & 0x80) + { + SET_SDA; + } + else + { + CLR_SDA; + } + CLR_SCL; + data <<= 1; + SET_SCL; + } + SET_CS; +} + +/*************************************************/ +void st7735SetAddrWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) +{ + st7735WriteCmd(ST7735_CASET); // column addr set + st7735WriteData(0x00); + st7735WriteData(x0+2); // XSTART + st7735WriteData(0x00); + st7735WriteData(x1+2); // XEND + + st7735WriteCmd(ST7735_RASET); // row addr set + st7735WriteData(0x00); + st7735WriteData(y0+1); // YSTART + st7735WriteData(0x00); + st7735WriteData(y1+1); // YEND +} + +/*************************************************/ +void st7735InitDisplay(void) +{ + st7735WriteCmd(ST7735_SWRESET); // software reset + systickDelay(50); + st7735WriteCmd(ST7735_SLPOUT); // out of sleep mode + systickDelay(500); + + st7735WriteCmd(ST7735_COLMOD); // set color mode + st7735WriteData(0x05); // 16-bit color + systickDelay(10); + + st7735WriteCmd(ST7735_FRMCTR1); // frame rate control + st7735WriteData(0x00); // fastest refresh + st7735WriteData(0x06); // 6 lines front porch + st7735WriteData(0x03); // 3 lines backporch + systickDelay(10); + + st7735WriteCmd(ST7735_MADCTL); // memory access control (directions) + st7735WriteData(0xC8); // row address/col address, bottom to top refresh + + st7735WriteCmd(ST7735_DISSET5); // display settings #5 + st7735WriteData(0x15); // 1 clock cycle nonoverlap, 2 cycle gate rise, 3 cycle oscil. equalize + st7735WriteData(0x02); // fix on VTL + + st7735WriteCmd(ST7735_INVCTR); // display inversion control + st7735WriteData(0x0); // line inversion + + st7735WriteCmd(ST7735_PWCTR1); // power control + st7735WriteData(0x02); // GVDD = 4.7V + st7735WriteData(0x70); // 1.0uA + systickDelay(10); + st7735WriteCmd(ST7735_PWCTR2); // power control + st7735WriteData(0x05); // VGH = 14.7V, VGL = -7.35V + st7735WriteCmd(ST7735_PWCTR3); // power control + st7735WriteData(0x01); // Opamp current small + st7735WriteData(0x02); // Boost frequency + + + st7735WriteCmd(ST7735_VMCTR1); // power control + st7735WriteData(0x3C); // VCOMH = 4V + st7735WriteData(0x38); // VCOML = -1.1V + systickDelay(10); + + st7735WriteCmd(ST7735_PWCTR6); // power control + st7735WriteData(0x11); + st7735WriteData(0x15); + + st7735WriteCmd(ST7735_GMCTRP1); + st7735WriteData(0x09); + st7735WriteData(0x16); + st7735WriteData(0x09); + st7735WriteData(0x20); + st7735WriteData(0x21); + st7735WriteData(0x1B); + st7735WriteData(0x13); + st7735WriteData(0x19); + st7735WriteData(0x17); + st7735WriteData(0x15); + st7735WriteData(0x1E); + st7735WriteData(0x2B); + st7735WriteData(0x04); + st7735WriteData(0x05); + st7735WriteData(0x02); + st7735WriteData(0x0E); + st7735WriteCmd(ST7735_GMCTRN1); + st7735WriteData(0x0B); + st7735WriteData(0x14); + st7735WriteData(0x08); + st7735WriteData(0x1E); + st7735WriteData(0x22); + st7735WriteData(0x1D); + st7735WriteData(0x18); + st7735WriteData(0x1E); + st7735WriteData(0x1B); + st7735WriteData(0x1A); + st7735WriteData(0x24); + st7735WriteData(0x2B); + st7735WriteData(0x06); + st7735WriteData(0x06); + st7735WriteData(0x02); + st7735WriteData(0x0F); + systickDelay(10); + + st7735WriteCmd(ST7735_CASET); // column addr set + st7735WriteData(0x00); + st7735WriteData(0x02); // XSTART = 2 + st7735WriteData(0x00); + st7735WriteData(0x81); // XEND = 129 + + st7735WriteCmd(ST7735_RASET); // row addr set + st7735WriteData(0x00); + st7735WriteData(0x02); // XSTART = 1 + st7735WriteData(0x00); + st7735WriteData(0x81); // XEND = 160 + + st7735WriteCmd(ST7735_NORON); // normal display on + systickDelay(10); + + st7735WriteCmd(ST7735_DISPON); + systickDelay(500); +} + +/*************************************************/ +/* Public Methods */ +/*************************************************/ + +/*************************************************/ +void lcdInit(void) +{ + // Set control pins to output + gpioSetDir(ST7735_PORT, ST7735_RS_PIN, 1); + gpioSetDir(ST7735_PORT, ST7735_SDA_PIN, 1); + gpioSetDir(ST7735_PORT, ST7735_SCL_PIN, 1); + gpioSetDir(ST7735_PORT, ST7735_CS_PIN, 1); + gpioSetDir(ST7735_PORT, ST7735_RES_PIN, 1); + gpioSetDir(ST7735_PORT, ST7735_BL_PIN, 1); + + // Set pins low by default (except reset) + CLR_RS; + CLR_SDA; + CLR_SCL; + CLR_CS; + CLR_BL; + SET_RES; + + // Turn backlight on + lcdBacklight(TRUE); + + // Reset display + SET_RES; + systickDelay(50); + CLR_RES; + systickDelay(50); + SET_RES; + systickDelay(50); + + // Run LCD init sequence + st7735InitDisplay(); + + // Fill black + lcdFillRGB(COLOR_BLACK); +} + +/*************************************************/ +void lcdBacklight(bool state) +{ + // Set the backlight + // Note: Depending on the type of transistor used + // to control the backlight, you made need to invert + // the values below + if (state) + // CLR_BL; + SET_BL; + else + // SET_BL; + CLR_BL; +} + +/*************************************************/ +void lcdTest(void) +{ + uint8_t i = 0; + for (i = 0; i < 100; i++) + { + lcdDrawPixel(i, i, 0xFFFF); + } +} + +/*************************************************/ +void lcdFillRGB(uint16_t color) +{ + uint8_t x, y; + st7735SetAddrWindow(0, 0, lcdGetWidth() - 1, lcdGetHeight() - 1); + st7735WriteCmd(ST7735_RAMWR); // write to RAM + for (x=0; x < lcdGetWidth(); x++) + { + for (y=0; y < lcdGetHeight(); y++) + { + st7735WriteData(color >> 8); + st7735WriteData(color); + } + } + st7735WriteCmd(ST7735_NOP); +} + +/*************************************************/ +void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) +{ + st7735SetAddrWindow(x,y,x+1,y+1); + st7735WriteCmd(ST7735_RAMWR); // write to RAM + st7735WriteData(color >> 8); + st7735WriteData(color); +} + +/**************************************************************************/ +/*! + @brief Draws an array of consecutive RGB565 pixels (much + faster than addressing each pixel individually) +*/ +/**************************************************************************/ +void lcdDrawPixels(uint16_t x, uint16_t y, uint16_t *data, uint32_t len) +{ + // ToDo: Optimise this function ... currently only a placeholder + uint32_t i = 0; + do + { + lcdDrawPixel(x+i, y, data[i]); + i++; + } while (i= lcdGetWidth()) + { + x1 = lcdGetWidth() - 1; + } + if (x0 >= lcdGetWidth()) + { + x0 = lcdGetWidth() - 1; + } + + st7735SetAddrWindow(x0, y, lcdGetWidth(), y + 1); + st7735WriteCmd(ST7735_RAMWR); // write to RAM + for (pixels = 0; pixels < x1 - x0 + 1; pixels++) + { + st7735WriteData(color >> 8); + st7735WriteData(color); + } + st7735WriteCmd(ST7735_NOP); +} + +/*************************************************/ +void lcdDrawVLine(uint16_t x, uint16_t y0, uint16_t y1, uint16_t color) +{ + // Allows for slightly better performance than setting individual pixels + uint16_t y, pixels; + + if (y1 < y0) + { + // Switch y1 and y0 + y = y1; + y1 = y0; + y0 = y; + } + + // Check limits + if (y1 >= lcdGetHeight()) + { + y1 = lcdGetHeight() - 1; + } + if (y0 >= lcdGetHeight()) + { + y0 = lcdGetHeight() - 1; + } + + st7735SetAddrWindow(x, y0, x, lcdGetHeight()); + st7735WriteCmd(ST7735_RAMWR); // write to RAM + for (pixels = 0; pixels < y1 - y0 + 1; pixels++) + { + st7735WriteData(color >> 8); + st7735WriteData(color); + } + st7735WriteCmd(ST7735_NOP); +} + +/*************************************************/ +uint16_t lcdGetPixel(uint16_t x, uint16_t y) +{ + // ToDo + return 0; +} + +/*************************************************/ +void lcdSetOrientation(lcdOrientation_t orientation) +{ + // ToDo +} + +/*************************************************/ +lcdOrientation_t lcdGetOrientation(void) +{ + return lcdOrientation; +} + +/*************************************************/ +uint16_t lcdGetWidth(void) +{ + return st7735Properties.width; +} + +/*************************************************/ +uint16_t lcdGetHeight(void) +{ + return st7735Properties.height; +} + +/*************************************************/ +void lcdScroll(int16_t pixels, uint16_t fillColor) +{ + // ToDo +} + +/*************************************************/ +uint16_t lcdGetControllerID(void) +{ + // ToDo + return 0; +} + +/*************************************************/ +lcdProperties_t lcdGetProperties(void) +{ + return st7735Properties; +} diff --git a/drivers/lcd/tft/hw/st7735.h b/drivers/lcd/tft/hw/st7735.h new file mode 100644 index 0000000..483c2cc --- /dev/null +++ b/drivers/lcd/tft/hw/st7735.h @@ -0,0 +1,113 @@ +/**************************************************************************/ +/*! + @file ST7735.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __ST7735_H__ +#define __ST7735_H__ + +#include "projectconfig.h" +#include "drivers/lcd/tft/lcd.h" + +/************************************************************************** + ST7735 CONNECTOR + ----------------------------------------------------------------------- + Pin Function Notes + === ============== =============================== + 1 NC + 2 GND + 3 LED K/- + 4 LED A/+ 3.0V + 5 GND + 6 RESET + 7 RS + 8 SDA Serial Data + 9 SCL Serial Clock + 10 VCC 2.8-3.4V + 11 VCC 2.8-3.4V + 12 CS + 13 GND + 14 NC + + **************************************************************************/ + +// Control pins +#define ST7735_GPIODATAREG (*(pREG32 (0x50023FFC))) // GPIO2DATA +#define ST7735_PORT (2) +#define ST7735_RS_PIN (1) +#define ST7735_SDA_PIN (2) +#define ST7735_SCL_PIN (3) +#define ST7735_CS_PIN (4) +#define ST7735_RES_PIN (5) +#define ST7735_BL_PIN (6) + +// Macros for control line state +#define CLR_RS do { ST7735_GPIODATAREG &= ~(1< 0; t1-- ) + { + __asm("nop"); + } +} + +/*************************************************/ +void st7783WriteCmd(uint16_t command) +{ + // Compiled with -Os on GCC 4.4 this works out to 25 cycles + // (versus 36 compiled with no optimisations). I'm not sure it + // can be improved further, so that means 25 cycles/350nS for + // continuous writes (cmd, data, data, data, ...) or ~150 cycles/ + // ~2.1uS for a random pixel (Set X [cmd+data], Set Y [cmd+data], + // Set color [cmd+data]) (times assumes 72MHz clock). + + CLR_CS_CD_SET_RD_WR; // Saves 18 commands compared to "CLR_CS; CLR_CD; SET_RD; SET_WR;" + ST7783_GPIO2DATA_DATA = (command >> (8 - ST7783_DATA_OFFSET)); + CLR_WR; + SET_WR; + ST7783_GPIO2DATA_DATA = command << ST7783_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR; SET_CS;" +} + +/*************************************************/ +void st7783WriteData(uint16_t data) +{ + CLR_CS_SET_CD_RD_WR; // Saves 18 commands compared to SET_CD; SET_RD; SET_WR; CLR_CS" + ST7783_GPIO2DATA_DATA = (data >> (8 - ST7783_DATA_OFFSET)); + CLR_WR; + SET_WR; + ST7783_GPIO2DATA_DATA = data << ST7783_DATA_OFFSET; + CLR_WR; + SET_WR_CS; // Saves 7 commands compared to "SET_WR, SET_CS;" +} + +/*************************************************/ +uint16_t st7783ReadData(void) +{ + // ToDo: Optimise this method! + + uint16_t high, low; + high = low = 0; + uint16_t d; + + SET_CD_RD_WR; // Saves 14 commands compared to "SET_CD; SET_RD; SET_WR" + CLR_CS; + + // set inputs + ST7783_GPIO2DATA_SETINPUT; + CLR_RD; + st7783Delay(100); + high = ST7783_GPIO2DATA_DATA; + high >>= ST7783_DATA_OFFSET; + high &= 0xFF; + SET_RD; + + CLR_RD; + st7783Delay(100); + low = ST7783_GPIO2DATA_DATA; + low >>= ST7783_DATA_OFFSET; + low &=0xFF; + SET_RD; + + SET_CS; + ST7783_GPIO2DATA_SETOUTPUT; + + d = high; + d <<= 8; + d |= low; + + return d; +} + +/*************************************************/ +uint16_t st7783Read(uint16_t addr) +{ + st7783WriteCmd(addr); + return st7783ReadData(); +} + +/*************************************************/ +void st7783Command(uint16_t command, uint16_t data) +{ + st7783WriteCmd(command); + st7783WriteData(data); +} + +/*************************************************/ +/* Returns the 4-hexdigit controller code */ +/*************************************************/ +uint16_t st7783Type(void) +{ + st7783WriteCmd(0x0); + return st7783ReadData(); +} + +/*************************************************/ +void st7783SetCursor(uint16_t x, uint16_t y) +{ + uint16_t he, ve, al, ah; + + switch (lcdOrientation) + { + case LCD_ORIENTATION_LANDSCAPE: + he = st7783Properties.width-1-y; + ve = st7783Properties.height-1-x; + al = y; + ah = x; + break; + case LCD_ORIENTATION_PORTRAIT: + default: + he = st7783Properties.width-1; + ve = st7783Properties.height-1; + al = x; + ah = y; + break; + } + st7783Command(0x0051, he); + st7783Command(0x0053, ve); + st7783Command(0x0020, al); + st7783Command(0x0021, ah); +} + +/*************************************************/ +void st7783InitDisplay(void) +{ + // Clear data line + GPIO_GPIO2DATA &= ~ST7783_DATA_MASK; + + SET_RD; + SET_WR; + SET_CS; + SET_CD; + + // Reset display + CLR_RESET; + st7783Delay(10000); + SET_RESET; + st7783Delay(500); + + st7783Command(0x00FF, 0x0001); + st7783Command(0x00F3, 0x0008); + st7783WriteCmd(0x00F3); + + st7783Command(0x0001, 0x0100); // Driver Output Control Register (R01h) + st7783Command(0x0002, 0x0700); // LCD Driving Waveform Control (R02h) + st7783Command(0x0003, 0x1030); // Entry Mode (R03h) + st7783Command(0x0008, 0x0302); + st7783Command(0x0009, 0x0000); + st7783Command(0x0010, 0x0000); // Power Control 1 (R10h) + st7783Command(0x0011, 0x0007); // Power Control 2 (R11h) + st7783Command(0x0012, 0x0000); // Power Control 3 (R12h) + st7783Command(0x0013, 0x0000); // Power Control 4 (R13h) + st7783Delay(1000); + st7783Command(0x0010, 0x14B0); // Power Control 1 (R10h) + st7783Delay(500); + st7783Command(0x0011, 0x0007); // Power Control 2 (R11h) + st7783Delay(500); + st7783Command(0x0012, 0x008E); // Power Control 3 (R12h) + st7783Command(0x0013, 0x0C00); // Power Control 4 (R13h) + st7783Command(0x0029, 0x0015); // NVM read data 2 (R29h) + st7783Delay(500); + st7783Command(0x0030, 0x0000); // Gamma Control 1 + st7783Command(0x0031, 0x0107); // Gamma Control 2 + st7783Command(0x0032, 0x0000); // Gamma Control 3 + st7783Command(0x0035, 0x0203); // Gamma Control 6 + st7783Command(0x0036, 0x0402); // Gamma Control 7 + st7783Command(0x0037, 0x0000); // Gamma Control 8 + st7783Command(0x0038, 0x0207); // Gamma Control 9 + st7783Command(0x0039, 0x0000); // Gamma Control 10 + st7783Command(0x003C, 0x0203); // Gamma Control 13 + st7783Command(0x003D, 0x0403); // Gamma Control 14 + st7783Command(0x0050, 0x0000); // Window Horizontal RAM Address Start (R50h) + st7783Command(0x0051, st7783Properties.width - 1); // Window Horizontal RAM Address End (R51h) + st7783Command(0x0052, 0X0000); // Window Vertical RAM Address Start (R52h) + st7783Command(0x0053, st7783Properties.height - 1); // Window Vertical RAM Address End (R53h) + st7783Command(0x0060, 0xa700); // Driver Output Control (R60h) + st7783Command(0x0061, 0x0001); // Driver Output Control (R61h) + st7783Command(0x0090, 0X0029); // Panel Interface Control 1 (R90h) + + // Display On + st7783Command(0x0007, 0x0133); // Display Control (R07h) + st7783Delay(500); + st7783WriteCmd(0x0022); +} + +/*************************************************/ +void st7783Home(void) +{ + st7783SetCursor(0, 0); + st7783WriteCmd(0x0022); // Write Data to GRAM (R22h) +} + +/*************************************************/ +void st7783SetWindow(uint16_t x, uint16_t y, uint16_t height, uint16_t width) +{ + // Window horizontal RAM address start + if (x >= height) st7783Command(0x50, (x - height + 1)); + else st7783Command(0x50, 0); + // Window horizontal GRAM address end + st7783Command(0x51, x); + // Window vertical GRAM address start + if (y >= width) st7783Command(0x52, (y - width + 1)); + else st7783Command(0x52, 0); + // Window vertical GRAM address end + st7783Command(0x53, y); + + st7783SetCursor(x, y); +} + +/*************************************************/ +/* Public Methods */ +/*************************************************/ + +/*************************************************/ +void lcdInit(void) +{ + // Set control line pins to output + gpioSetDir(ST7783_CS_PORT, ST7783_CS_PIN, 1); + gpioSetDir(ST7783_CD_PORT, ST7783_CD_PIN, 1); + gpioSetDir(ST7783_WR_PORT, ST7783_WR_PIN, 1); + gpioSetDir(ST7783_RD_PORT, ST7783_RD_PIN, 1); + + // Set data port pins to output + ST7783_GPIO2DATA_SETOUTPUT; + + // Disable pullups + ST7783_DISABLEPULLUPS(); + + // Set backlight pin to output and turn it on + gpioSetDir(ST7783_BL_PORT, ST7783_BL_PIN, 1); // set to output + lcdBacklight(TRUE); + + // Set reset pin to output + gpioSetDir(ST7783_RES_PORT, ST7783_RES_PIN, 1); // Set to output + gpioSetValue(ST7783_RES_PORT, ST7783_RES_PIN, 0); // Low to reset + systickDelay(50); + gpioSetValue(ST7783_RES_PORT, ST7783_RES_PIN, 1); // High to exit + + // Initialize the display + st7783InitDisplay(); + + // Set lcd to default orientation + lcdSetOrientation(lcdOrientation); + + // Fill black + lcdFillRGB(COLOR_BLACK); + + // Initialise the touch screen (and calibrate if necessary) + tsInit(); +} + +/*************************************************/ +void lcdBacklight(bool state) +{ + // Set the backlight + gpioSetValue(ST7783_BL_PORT, ST7783_BL_PIN, state ? 0 : 1); +} + +/*************************************************/ +void lcdTest(void) +{ + uint32_t i,j; + st7783Home(); + + for(i=0;i<320;i++) + { + for(j=0;j<240;j++) + { + if(i>279)st7783WriteData(COLOR_WHITE); + else if(i>239)st7783WriteData(COLOR_BLUE); + else if(i>199)st7783WriteData(COLOR_GREEN); + else if(i>159)st7783WriteData(COLOR_CYAN); + else if(i>119)st7783WriteData(COLOR_RED); + else if(i>79)st7783WriteData(COLOR_MAGENTA); + else if(i>39)st7783WriteData(COLOR_YELLOW); + else st7783WriteData(COLOR_BLACK); + } + } +} + +/*************************************************/ +void lcdFillRGB(uint16_t data) +{ + unsigned int i; + st7783Home(); + + uint32_t pixels = 320*240; + for ( i=0; i < pixels; i++ ) + { + st7783WriteData(data); + } +} + +/*************************************************/ +void lcdDrawPixel(uint16_t x, uint16_t y, uint16_t color) +{ + st7783SetCursor(x, y); + st7783WriteCmd(0x0022); // Write Data to GRAM (R22h) + st7783WriteData(color); +} + +/**************************************************************************/ +/*! + @brief Draws an array of consecutive RGB565 pixels (much + faster than addressing each pixel individually) +*/ +/**************************************************************************/ +void lcdDrawPixels(uint16_t x, uint16_t y, uint16_t *data, uint32_t len) +{ + uint32_t i = 0; + st7783SetCursor(x, y); + st7783WriteCmd(0x0022); // Write Data to GRAM (R22h) + do + { + st7783WriteData(data[i]); + i++; + } while (i GENERIC DRAWING --> SPECIFIC HW + CODE --> CODE --> DRIVER +---------------- ---------------- ---------------- + + +drawing.c Generic drawing routines such as drawing pixels, lines, + rectangles, as well as basic text-rendering. + +lcd.h This file contains the prototypes of HW-specific functions + that must be implemented in the LCD driver, since + drawing.c will redirect all requests to these lower level + functions. + +touchscreen.c Contains a very simple example of how to use ADC to read + the current position on a touchscreen. No signal debouncing + takes places, and this code will need to be improved for us + in a real-world situation. + +hw\* HW-specific drivers based on lcd.h \ No newline at end of file diff --git a/drivers/lcd/tft/touchscreen.c b/drivers/lcd/tft/touchscreen.c new file mode 100644 index 0000000..20e9a19 --- /dev/null +++ b/drivers/lcd/tft/touchscreen.c @@ -0,0 +1,609 @@ +/**************************************************************************/ +/*! + @file touchscreen.c + @author K. Townsend (microBuilder.eu) + + Parts copyright (c) 2001, Carlos E. Vidales. All rights reserved. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "touchscreen.h" + +#include "core/adc/adc.h" +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" +#include "drivers/eeprom/eeprom.h" +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/fonts/dejavusans9.h" + +#define TS_LINE1 "Touch the center of" +#define TS_LINE2 "the red circle using" +#define TS_LINE3 "a pen or stylus" + +static bool _tsInitialised = FALSE; +static uint8_t _tsThreshhold = CFG_TFTLCD_TS_DEFAULTTHRESHOLD; +tsPoint_t _tsLCDPoints[3]; +tsPoint_t _tsTSPoints[3]; +tsMatrix_t _tsMatrix; + +/**************************************************************************/ +/* */ +/* ----------------------- Private Methods ------------------------------ */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Reads the current Z/pressure level using the ADC +*/ +/**************************************************************************/ +void tsReadZ(uint32_t* z1, uint32_t* z2) +{ + if (!_tsInitialised) tsInit(); + + // XP = ADC + // XM = GPIO Output Low + // YP = GPIO Output High + // YM = GPIO Input + + TS_XM_FUNC_GPIO; + TS_YP_FUNC_GPIO; + TS_YM_FUNC_GPIO; + + gpioSetDir (TS_XM_PORT, TS_XM_PIN, 1); + gpioSetDir (TS_YP_PORT, TS_YP_PIN, 1); + gpioSetDir (TS_YM_PORT, TS_YM_PIN, 0); + + gpioSetValue(TS_XM_PORT, TS_XM_PIN, 0); // GND + gpioSetValue(TS_YP_PORT, TS_YP_PIN, 1); // 3.3V + + TS_XP_FUNC_ADC; + *z1 = adcRead(TS_XP_ADC_CHANNEL); + + // XP = GPIO Input + // XM = GPIO Output Low + // YP = GPIO Output High + // YM = ADC + + TS_XP_FUNC_GPIO; + gpioSetDir (TS_YM_PORT, TS_YM_PIN, 0); + + TS_YM_FUNC_ADC; + *z2 = adcRead(TS_YM_ADC_CHANNEL); +} + +/**************************************************************************/ +/*! + @brief Reads the current X position using the ADC +*/ +/**************************************************************************/ +uint32_t tsReadX(void) +{ + if (!_tsInitialised) tsInit(); + + // XP = GPIO Output High + // XM = GPIO Output Low + // YP = ADC + // YM = GPIO Input + + TS_XP_FUNC_GPIO; + TS_XM_FUNC_GPIO; + TS_YM_FUNC_GPIO; + + gpioSetDir (TS_XP_PORT, TS_XP_PIN, 1); + gpioSetDir (TS_XM_PORT, TS_XM_PIN, 1); + gpioSetDir (TS_YM_PORT, TS_YM_PIN, 0); + + gpioSetValue(TS_XP_PORT, TS_XP_PIN, 1); // 3.3V + gpioSetValue(TS_XM_PORT, TS_XM_PIN, 0); // GND + + TS_YP_FUNC_ADC; + + // Return the ADC results + return adcRead(TS_YP_ADC_CHANNEL); +} + +/**************************************************************************/ +/*! + @brief Reads the current Y position using the ADC +*/ +/**************************************************************************/ +uint32_t tsReadY(void) +{ + if (!_tsInitialised) tsInit(); + + // YP = GPIO Output High + // YM = GPIO Output Low + // XP = GPIO Input + // XM = ADC + + TS_YP_FUNC_GPIO; + TS_YM_FUNC_GPIO; + TS_XP_FUNC_GPIO; + + gpioSetDir (TS_YP_PORT, TS_YP_PIN, 1); + gpioSetDir (TS_YM_PORT, TS_YM_PIN, 1); + gpioSetDir (TS_XP_PORT, TS_XP_PIN, 0); + + gpioSetValue(TS_YP_PORT, TS_YP_PIN, 1); // 3.3V + gpioSetValue(TS_YM_PORT, TS_YM_PIN, 0); // GND + + TS_XM_FUNC_ADC; + + // Return the ADC results + return adcRead(TS_XM_ADC_CHANNEL); +} + +/**************************************************************************/ +/*! + @brief Centers a line of text horizontally +*/ +/**************************************************************************/ +void tsCalibCenterText(char* text, uint16_t y, uint16_t color) +{ + drawString((lcdGetWidth() - drawGetStringWidth(&dejaVuSans9ptFontInfo, text)) / 2, y, color, &dejaVuSans9ptFontInfo, text); +} + +/**************************************************************************/ +/*! + @brief Renders the calibration screen with an appropriately + placed test point and waits for a touch event +*/ +/**************************************************************************/ +tsTouchData_t tsRenderCalibrationScreen(uint16_t x, uint16_t y, uint16_t radius) +{ + drawFill(COLOR_WHITE); + tsCalibCenterText(TS_LINE1, 50, COLOR_GRAY_50); + tsCalibCenterText(TS_LINE2, 65, COLOR_GRAY_50); + tsCalibCenterText(TS_LINE3, 80, COLOR_GRAY_50); + drawCircle(x, y, radius, COLOR_RED); + drawCircle(x, y, radius + 2, COLOR_GRAY_128); + + // Wait for a valid touch events + tsTouchData_t data; + tsTouchError_t error; + bool valid = false; + while (!valid) + { + error = tsRead(&data); + if (!error && data.valid) + { + valid = true; + } + } + + return data; +} + +/**************************************************************************/ +/*! + @brief Calculates the difference between the touch screen and the + actual screen co-ordinates, taking into account misalignment + and any physical offset of the touch screen. + + @note This is based on the public domain touch screen calibration code + written by Carlos E. Vidales (copyright (c) 2001). + + For more inforormation, see the following app notes: + + - AN2173 - Touch Screen Control and Calibration + Svyatoslav Paliy, Cypress Microsystems + - Calibration in touch-screen systems + Wendy Fang and Tony Chang, + Analog Applications Journal, 3Q 2007 (Texas Instruments) +*/ +/**************************************************************************/ +int setCalibrationMatrix( tsPoint_t * displayPtr, tsPoint_t * screenPtr, tsMatrix_t * matrixPtr) +{ + int retValue = 0; + + matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + if( matrixPtr->Divider == 0 ) + { + retValue = -1 ; + } + else + { + matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - + ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; + + matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - + ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; + + // Persist data to EEPROM + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_AN, matrixPtr->An); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_BN, matrixPtr->Bn); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_CN, matrixPtr->Cn); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DN, matrixPtr->Dn); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_EN, matrixPtr->En); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN, matrixPtr->Fn); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN, matrixPtr->Fn); + eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER, matrixPtr->Divider); + eepromWriteU8(CFG_EEPROM_TOUCHSCREEN_CALIBRATED, 1); + } + + return( retValue ) ; +} + +/**************************************************************************/ +/*! + @brief Converts the supplied touch screen location (screenPtr) to + a pixel location on the display (displayPtr) using the + supplied matrix. The screen orientation is also taken into + account when converting the touch screen co-ordinate to + a pixel location on the LCD. + + @note This is based on the public domain touch screen calibration code + written by Carlos E. Vidales (copyright (c) 2001). +*/ +/**************************************************************************/ +int getDisplayPoint( tsPoint_t * displayPtr, tsPoint_t * screenPtr, tsMatrix_t * matrixPtr ) +{ + int retValue = 0 ; + + if( matrixPtr->Divider != 0 ) + { + displayPtr->x = ( (matrixPtr->An * screenPtr->x) + + (matrixPtr->Bn * screenPtr->y) + + matrixPtr->Cn + ) / matrixPtr->Divider ; + + displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + + (matrixPtr->En * screenPtr->y) + + matrixPtr->Fn + ) / matrixPtr->Divider ; + } + else + { + retValue = -1 ; + } + + // Adjust value if the screen is in landscape mode + lcdOrientation_t orientation; + orientation = lcdGetOrientation(); + if (orientation == LCD_ORIENTATION_LANDSCAPE) + { + uint32_t oldx, oldy; + oldx = displayPtr->x; + oldy = displayPtr->y; + displayPtr->x = oldy; + displayPtr->y = lcdGetHeight() - oldx; + } + + return( retValue ) ; +} + +/**************************************************************************/ +/* */ +/* ----------------------- Public Methods ------------------------------- */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/*! + @brief Initialises the appropriate GPIO pins and ADC for the + touchscreen +*/ +/**************************************************************************/ +void tsInit(void) +{ + // Make sure that ADC is initialised + adcInit(); + + // Set initialisation flag + _tsInitialised = TRUE; + _tsThreshhold = tsGetThreshhold(); + + // Load values from EEPROM if touch screen has already been calibrated + if (eepromReadU8(CFG_EEPROM_TOUCHSCREEN_CALIBRATED) == 1) + { + // Load calibration data + _tsMatrix.An = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_AN); + _tsMatrix.Bn = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_BN); + _tsMatrix.Cn = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_CN); + _tsMatrix.Dn = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_DN); + _tsMatrix.En = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_EN); + _tsMatrix.Fn = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN); + _tsMatrix.Divider = eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER); + } +} + +/**************************************************************************/ +/*! + @brief Reads the current X, Y and Z co-ordinates of the touch screen +*/ +/**************************************************************************/ +tsTouchError_t tsRead(tsTouchData_t* data) +{ + uint32_t x1, x2, y1, y2, z1, z2; + + // Assign pressure levels regardless of touch state + tsReadZ(&z1, &z2); + data->z1 = z1; + data->z2 = z2; + data->xraw = 0; + data->yraw = 0; + data->xlcd = 0; + data->ylcd = 0; + + // Abort if the screen is not being touched (0 levels reported) + if (z1 < _tsThreshhold) + { + data->valid = false; + return TS_ERROR_NONE; + } + + // Get two X/Y readings and compare results + x1 = tsReadX(); + x2 = tsReadX(); + y1 = tsReadY(); + y2 = tsReadY(); + + // Throw an error if both readings aren't identical + if (x1 != x2 || y1 != y2) + { + data->valid = false; + data->xraw = x1; + data->yraw = y1; + return TS_ERROR_XYMISMATCH; + } + + // X/Y seems to be valid and reading has been confirmed twice + data->xraw = x1; + data->yraw = y1; + + // Convert x/y values to pixel location with matrix multiply + tsPoint_t location, touch; + touch.x = x1; + touch.y = y1; + getDisplayPoint( &location, &touch, &_tsMatrix) ; + data->xlcd = location.x; + data->ylcd = location.y; + data->valid = true; + + return TS_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Starts the screen calibration process. Each corner will be + tested, meaning that each boundary (top, left, right and + bottom) will be tested twice and the readings averaged. +*/ +/**************************************************************************/ +void tsCalibrate(void) +{ + tsTouchData_t data; + + /* --------------- Welcome Screen --------------- */ + data = tsRenderCalibrationScreen(lcdGetWidth() / 2, lcdGetHeight() / 2, 5); + systickDelay(250); + + /* ----------------- First Dot ------------------ */ + // 10% over and 10% down + data = tsRenderCalibrationScreen(lcdGetWidth() / 10, lcdGetHeight() / 10, 5); + _tsLCDPoints[0].x = lcdGetWidth() / 10; + _tsLCDPoints[0].y = lcdGetHeight() / 10; + _tsTSPoints[0].x = data.xraw; + _tsTSPoints[0].y = data.yraw; + printf("Point 1 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n", + (int)_tsLCDPoints[0].x, (int)_tsLCDPoints[0].y, (int)_tsTSPoints[0].x, (int)_tsTSPoints[0].y); + systickDelay(250); + + /* ---------------- Second Dot ------------------ */ + // 50% over and 90% down + data = tsRenderCalibrationScreen(lcdGetWidth() / 2, lcdGetHeight() - lcdGetHeight() / 10, 5); + _tsLCDPoints[1].x = lcdGetWidth() / 2; + _tsLCDPoints[1].y = lcdGetHeight() - lcdGetHeight() / 10; + _tsTSPoints[1].x = data.xraw; + _tsTSPoints[1].y = data.yraw; + printf("Point 2 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n", + (int)_tsLCDPoints[1].x, (int)_tsLCDPoints[1].y, (int)_tsTSPoints[1].x, (int)_tsTSPoints[1].y); + systickDelay(250); + + /* ---------------- Third Dot ------------------- */ + // 90% over and 50% down + data = tsRenderCalibrationScreen(lcdGetWidth() - lcdGetWidth() / 10, lcdGetHeight() / 2, 5); + _tsLCDPoints[2].x = lcdGetWidth() - lcdGetWidth() / 10; + _tsLCDPoints[2].y = lcdGetHeight() / 2; + _tsTSPoints[2].x = data.xraw; + _tsTSPoints[2].y = data.yraw; + printf("Point 3 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n", + (int)_tsLCDPoints[2].x, (int)_tsLCDPoints[2].y, (int)_tsTSPoints[2].x, (int)_tsTSPoints[2].y); + systickDelay(250); + + // Do matrix calculations for calibration and store to EEPROM + setCalibrationMatrix(&_tsLCDPoints[0], &_tsTSPoints[0], &_tsMatrix); +} + +/**************************************************************************/ +/*! + @brief Causes a blocking delay until a valid touch event occurs + + @note Thanks to 'rossum' and limor for this nifty little tidbit on + debouncing the signals via pressure sensitivity (using Z) + + @section Example + + @code + #include "drivers/lcd/tft/touchscreen.h" + ... + tsTouchData_t data; + tsTouchError_t error; + + while (1) + { + // Cause a blocking delay until a touch event occurs or 5s passes + error = tsWaitForEvent(&data, 5000); + + if (error) + { + switch(error) + { + case TS_ERROR_TIMEOUT: + printf("Timeout occurred %s", CFG_PRINTF_NEWLINE); + break; + default: + break; + } + } + else + { + // A valid touch event occurred ... display data + printf("Touch Event: X = %04u, Y = %04u %s", + data.xlcd, + data.ylcd, + CFG_PRINTF_NEWLINE); + } + } + + @endcode +*/ +/**************************************************************************/ +tsTouchError_t tsWaitForEvent(tsTouchData_t* data, uint32_t timeoutMS) +{ + if (!_tsInitialised) tsInit(); + + tsRead(data); + + // Return the results right away if reading is valid + if (data->valid) + { + return TS_ERROR_NONE; + } + + // Handle timeout if delay > 0 milliseconds + if (timeoutMS) + { + uint32_t startTick = systickGetTicks(); + // Systick rollover may occur while waiting for timeout + if (startTick > 0xFFFFFFFF - timeoutMS) + { + while (data->valid == false) + { + // Throw alert if timeout delay has been passed + if ((systickGetTicks() < startTick) && (systickGetTicks() >= (timeoutMS - (0xFFFFFFFF - startTick)))) + { + return TS_ERROR_TIMEOUT; + } + tsRead(data); + } + } + // No systick rollover will occur ... calculate timeout the simple way + else + { + // Wait in infinite loop + while (data->valid == false) + { + // Throw timeout if delay has been passed + if ((systickGetTicks() - startTick) > timeoutMS) + { + return TS_ERROR_TIMEOUT; + } + tsRead(data); + } + } + } + // No timeout requested ... wait forever + else + { + while (data->valid == false) + { + tsRead(data); + } + } + + // Indicate correct reading + return TS_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Updates the touch screen threshhold level and saves it + to EEPROM +*/ +/**************************************************************************/ +int tsSetThreshhold(uint8_t value) +{ + if ((value < 0) || (value > 254)) + { + return -1; + } + + // Update threshhold value + _tsThreshhold = value; + + // Persist to EEPROM + eepromWriteU8(CFG_EEPROM_TOUCHSCREEN_THRESHHOLD, value); + + return 0; +} + +/**************************************************************************/ +/*! + @brief Gets the current touch screen threshhold level from EEPROM + (if present) or returns the default value from projectconfig.h +*/ +/**************************************************************************/ +uint8_t tsGetThreshhold(void) +{ + // Check if custom threshold has been set in eeprom + uint8_t thold = eepromReadU8(CFG_EEPROM_TOUCHSCREEN_THRESHHOLD); + if (thold != 0xFF) + { + // Use value from EEPROM + _tsThreshhold = thold; + } + else + { + // Use the default value from projectconfig.h + _tsThreshhold = CFG_TFTLCD_TS_DEFAULTTHRESHOLD; + } + + return _tsThreshhold; +} diff --git a/drivers/lcd/tft/touchscreen.h b/drivers/lcd/tft/touchscreen.h new file mode 100644 index 0000000..ae49eb2 --- /dev/null +++ b/drivers/lcd/tft/touchscreen.h @@ -0,0 +1,109 @@ +/**************************************************************************/ +/*! + @file touchscreen.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __TOUCHSCREEN_H__ +#define __TOUCHSCREEN_H__ + +#include "projectconfig.h" + +#define TS_XP_PORT (1) +#define TS_XP_PIN (0) +#define TS_XP_FUNC_GPIO do {IOCON_JTAG_TMS_PIO1_0 &= ~(IOCON_JTAG_TMS_PIO1_0_FUNC_MASK | IOCON_JTAG_TMS_PIO1_0_ADMODE_MASK); IOCON_JTAG_TMS_PIO1_0 |= IOCON_JTAG_TMS_PIO1_0_FUNC_GPIO;} while (0) +#define TS_XP_FUNC_ADC do {IOCON_JTAG_TMS_PIO1_0 &= ~(IOCON_JTAG_TMS_PIO1_0_FUNC_MASK | IOCON_JTAG_TMS_PIO1_0_ADMODE_MASK); IOCON_JTAG_TMS_PIO1_0 |= IOCON_JTAG_TMS_PIO1_0_FUNC_AD1;} while (0) + +#define TS_XM_PORT (1) +#define TS_XM_PIN (1) +#define TS_XM_FUNC_GPIO do {IOCON_JTAG_TDO_PIO1_1 &= ~(IOCON_JTAG_TDO_PIO1_1_FUNC_MASK | IOCON_JTAG_TDO_PIO1_1_ADMODE_MASK); IOCON_JTAG_TDO_PIO1_1 |= IOCON_JTAG_TDO_PIO1_1_FUNC_GPIO;} while (0) +#define TS_XM_FUNC_ADC do {IOCON_JTAG_TDO_PIO1_1 &= ~(IOCON_JTAG_TDO_PIO1_1_FUNC_MASK | IOCON_JTAG_TDO_PIO1_1_ADMODE_MASK); IOCON_JTAG_TDO_PIO1_1 |= IOCON_JTAG_TDO_PIO1_1_FUNC_AD2;} while (0) + +#define TS_YP_PORT (0) +#define TS_YP_PIN (11) +#define TS_YP_FUNC_GPIO do {IOCON_JTAG_TDI_PIO0_11 &= ~(IOCON_JTAG_TDI_PIO0_11_FUNC_MASK | IOCON_JTAG_TDI_PIO0_11_ADMODE_MASK); IOCON_JTAG_TDI_PIO0_11 |= IOCON_JTAG_TDI_PIO0_11_FUNC_GPIO;} while (0) +#define TS_YP_FUNC_ADC do {IOCON_JTAG_TDI_PIO0_11 &= ~(IOCON_JTAG_TDI_PIO0_11_FUNC_MASK | IOCON_JTAG_TDI_PIO0_11_ADMODE_MASK); IOCON_JTAG_TDI_PIO0_11 |= IOCON_JTAG_TDI_PIO0_11_FUNC_AD0;} while (0) + +#define TS_YM_PORT (1) +#define TS_YM_PIN (2) +#define TS_YM_FUNC_GPIO do {IOCON_JTAG_nTRST_PIO1_2 &= ~(IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK | IOCON_JTAG_nTRST_PIO1_2_ADMODE_MASK); IOCON_JTAG_nTRST_PIO1_2 |= IOCON_JTAG_nTRST_PIO1_2_FUNC_GPIO;} while (0) +#define TS_YM_FUNC_ADC do {IOCON_JTAG_nTRST_PIO1_2 &= ~(IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK | IOCON_JTAG_nTRST_PIO1_2_ADMODE_MASK); IOCON_JTAG_nTRST_PIO1_2 |= IOCON_JTAG_nTRST_PIO1_2_FUNC_AD3;} while (0) + +#define TS_YP_ADC_CHANNEL (0) // ADC0.0 +#define TS_XP_ADC_CHANNEL (1) // ADC0.1 +#define TS_XM_ADC_CHANNEL (2) // ADC0.2 +#define TS_YM_ADC_CHANNEL (3) // ADC0.3 + +typedef struct Point +{ + int32_t x; + int32_t y; +} tsPoint_t; + +typedef struct Matrix +{ + int32_t An, + Bn, + Cn, + Dn, + En, + Fn, + Divider ; +} tsMatrix_t; + +typedef struct +{ + uint32_t xraw; // Touch screen x + uint32_t yraw; // Touch screen Y + uint16_t xlcd; // LCD co-ordinate X + uint16_t ylcd; // LCD co-ordinate Y + uint32_t z1; + uint32_t z2; + bool valid; // Whether this is a valid reading or not +} tsTouchData_t; + +typedef enum +{ + TS_ERROR_NONE = 0, + TS_ERROR_TIMEOUT = -1, // Timeout occured before a valid reading + TS_ERROR_XYMISMATCH = -2 // Unable to get a stable X/Y value +} tsTouchError_t; + +// Method Prototypes +void tsInit ( void ); +tsTouchError_t tsRead(tsTouchData_t* data); +void tsCalibrate ( void ); +tsTouchError_t tsWaitForEvent(tsTouchData_t* data, uint32_t timeoutMS); +int tsSetThreshhold(uint8_t value); +uint8_t tsGetThreshhold(void); + +#endif diff --git a/drivers/motor/stepper/stepper.c b/drivers/motor/stepper/stepper.c new file mode 100644 index 0000000..5112fe3 --- /dev/null +++ b/drivers/motor/stepper/stepper.c @@ -0,0 +1,300 @@ +/**************************************************************************/ +/*! + @file stepper.c + @author Based on original code by Tom Igoe + Modified by K. Townsend (microBuilder.eu) + + @brief Simple bi-polar stepper motor controller, based on the + Arduino stepper library by Tom Igoe. Includes simple + position handling methods to keep track of the motor's + relative position and the spindle's current rotation. + + @section Example + + @code + + #include "sysinit.h" + #include "core/systick/systick.h" + #include "drivers/motor/stepper/stepper.h" + + ... + + systemInit(); // Configure cpu and mandatory peripherals + + stepperInit(200); // Initialise driver for 200-step motor + stepperSetSpeed(60); // Set speed to 60 rpm (1 revolution per second) + + while (1) + { + stepperStep(400); // Move forward 400 steps + stepperStep(-200); // Move backward 200 steps + systickDelay(1000); // Wait one second + + // Move 'home' after 10 loops (current position = 2000) + if (stepperGetPosition() == 2000) + { + stepperMoveHome(); // Move back to the starting position + systickDelay(1000); // Wait one second + } + } + + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "stepper.h" +#include "core/gpio/gpio.h" +#include "core/timer32/timer32.h" + +static int64_t stepperPosition = 0; // The current position (in steps) relative to 'Home' +static uint32_t stepperStepNumber = 0; // The current position (in steps) relative to 0° +static uint32_t stepperStepsPerRotation = 0; // Number of steps in a full 360° rotation +static uint32_t stepperStepDelay = 0; // Delay in CPU ticks between individual steps + +/**************************************************************************/ +/*! + Private - Cause the motor to step forward or backward one step +*/ +/**************************************************************************/ +void stepMotor(uint32_t thisStep) +{ + switch (thisStep) + { + case 0: // 1010 + gpioSetValue(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 1); + gpioSetValue(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 0); + gpioSetValue(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 1); + gpioSetValue(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 0); + break; + case 1: // 0110 + gpioSetValue(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 0); + gpioSetValue(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 1); + gpioSetValue(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 1); + gpioSetValue(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 0); + break; + case 2: // 0101 + gpioSetValue(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 0); + gpioSetValue(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 1); + gpioSetValue(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 0); + gpioSetValue(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 1); + break; + case 3: // 1001 + gpioSetValue(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 1); + gpioSetValue(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 0); + gpioSetValue(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 0); + gpioSetValue(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 1); + break; + } +} + +/**************************************************************************/ +/*! + @brief Initialises the GPIO pins and delay timer and sets any + default values. + + @param[in] steps + The number of steps per rotation (typically 200 or 400) +*/ +/**************************************************************************/ +void stepperInit(uint32_t steps) +{ + // Setup motor control pins + gpioSetDir(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 1); + gpioSetDir(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 1); + gpioSetDir(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 1); + gpioSetDir(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 1); + + gpioSetValue(STEPPER_IN1_PORT, STEPPER_IN1_PIN, 0); + gpioSetValue(STEPPER_IN2_PORT, STEPPER_IN2_PIN, 0); + gpioSetValue(STEPPER_IN3_PORT, STEPPER_IN3_PIN, 0); + gpioSetValue(STEPPER_IN4_PORT, STEPPER_IN4_PIN, 0); + + // Set the number of steps per rotation + stepperStepsPerRotation = steps; + + // Set the default speed (2 rotations per second) + stepperSetSpeed(120); +} + +/**************************************************************************/ +/*! + @brief Gets the current position (in steps) relative to 'Home'. + + @return The difference (in steps) of the motor's current position + from the original 'Home' position. Value can be negative or + positive depending on the direction of previous movements. +*/ +/**************************************************************************/ +int64_t stepperGetPosition() +{ + return stepperPosition; +} + +/**************************************************************************/ +/*! + @brief Gets the motor's current rotation (in steps) relative to + the spindle's 'Zero' position. + + @return The current step (0 .. steps per rotation) on the motor's + spindle relative to 0°. Value is always positive. +*/ +/**************************************************************************/ +uint32_t stepperGetRotation() +{ + return stepperStepNumber; +} + +/**************************************************************************/ +/*! + @brief Sets the motor's current position to 'Home', meaning that + any future movement will be relative to the current + position. +*/ +/**************************************************************************/ +void stepperSetHome() +{ + stepperPosition = 0; +} + +/**************************************************************************/ +/*! + @brief Moves the motor back to the original 'Home' position. +*/ +/**************************************************************************/ +void stepperMoveHome() +{ + stepperStep(stepperPosition * -1); +} + +/**************************************************************************/ +/*! + @brief Saves the spindle's current angle/position as 0°. Each + step the spindle takes will now be relative to the spindle's + current position. +*/ +/**************************************************************************/ +void stepperSetZero() +{ + stepperStepNumber = 0; +} + +/**************************************************************************/ +/*! + @brief Moves the motor to its original rotation value. For example, + if a 200-step motor is currently rotated to step 137, it + will move the motor forward 63 steps to end at step 0 or 0°. +*/ +/**************************************************************************/ +void stepperMoveZero() +{ + if (!stepperStepNumber) + { + stepperStep(stepperStepsPerRotation - stepperStepNumber); + } +} + +/**************************************************************************/ +/*! + @brief Sets the motor speed in rpm, meaning the number of times the + motor will fully rotate in a one minute period. + + @param[in] rpm + Motor speed in revolutions per minute (RPM) + + @warning Not all motors will function at all speeds, and some trial + and error may be required to find an appropriate speed for + the motor. +*/ +/**************************************************************************/ +void stepperSetSpeed(uint32_t rpm) +{ + uint32_t ticksOneRPM = ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / stepperStepsPerRotation) * 60; + + // Set stepper RPM + stepperStepDelay = ticksOneRPM / rpm; + + // Initialise 32-bit timer 0 with the appropriate delay + timer32Init(0, stepperStepDelay); + timer32Enable(0); +} + +/**************************************************************************/ +/*! + @brief Moves the motor forward or backward the specified number + of steps. A positive number moves the motor forward, + while a negative number moves the motor backwards. + + @param[in] steps + The number of steps to move foreward (positive) or + backward (negative) +*/ +/**************************************************************************/ +void stepperStep(int32_t steps) +{ + uint32_t stepsLeft = abs(steps); // Force number to be positive + + while (stepsLeft > 0) + { + // Wait 1 tick between individual steps + timer32Delay(0, 1); + + // Increment or decrement step counters (depending on direction) + if (steps > 0) + { + stepperPosition++; // Increment global position counter + stepperStepNumber++; // Increment single rotation counter + if (stepperStepNumber == stepperStepsPerRotation) + { + stepperStepNumber = 0; + } + } + else + { + stepperPosition--; // Decrement global position counter + if (stepperStepNumber == 0) + { + stepperStepNumber = stepperStepsPerRotation; + } + stepperStepNumber--; // Decrement single rotation counter + } + + // Decrement number of remaining steps + stepsLeft--; + + // Step the motor one step + stepMotor(stepperStepNumber % 4); + } +} + + + diff --git a/drivers/motor/stepper/stepper.h b/drivers/motor/stepper/stepper.h new file mode 100644 index 0000000..7e4f3d2 --- /dev/null +++ b/drivers/motor/stepper/stepper.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/*! + @file stepper.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _STEPPER_H_ +#define _STEPPER_H_ + +#include "projectconfig.h" + +#define STEPPER_IN1_PORT (3) +#define STEPPER_IN1_PIN (0) +#define STEPPER_IN2_PORT (3) +#define STEPPER_IN2_PIN (1) +#define STEPPER_IN3_PORT (3) +#define STEPPER_IN3_PIN (2) +#define STEPPER_IN4_PORT (3) +#define STEPPER_IN4_PIN (3) + +void stepperInit( uint32_t steps ); +void stepperSetSpeed( uint32_t rpm ); +int64_t stepperGetPosition(); +uint32_t stepperGetRotation(); +void stepperMoveHome(); +void stepperSetHome(); +void stepperMoveZero(); +void stepperSetZero(); +void stepperStep( int32_t steps ); + +#endif \ No newline at end of file diff --git a/drivers/rsa/rsa.c b/drivers/rsa/rsa.c new file mode 100644 index 0000000..9fda5be --- /dev/null +++ b/drivers/rsa/rsa.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/*! + @file rsa.c + @author Kyle Loudon + modified: microBuilder.eu + @date 4 January, 2010 + @version 1.0 + + Basic RSA-encryption using 64-bit math (32-bit keys). + + Based on the examples from "Mastering Algorithms with C" by + Kyle Loudon (O'Reilly, 1999). + + For details on how to generate a valid RSA key pair, see: + http://www.microbuilder.eu/Tutorials/SoftwareDevelopment/RSAEncryption.aspx + + @warning Most versions of libc used for embedded systems do not + include support for 64-bit integers with printf, etc. (to + keep the compiled code size and memory usage as small as + possible). Unless you have explicitly added long long + support for printf, you should not try to display 64-bit + values with it (%lld, etc.). Using 32-bit values (changing + the definition of huge_t to uint32_t) will avoid this issue + entirely, though at the expense of weaker encryption. +*/ +/**************************************************************************/ + +#include "rsa.h" + +huge_t modexp(huge_t a, huge_t b, huge_t n) +{ + huge_t y; + y = 1; + + /* Compute pow(a, b) % n using the binary square and multiply method. */ + while (b != 0) + { + /* For each 1 in b, accumulate y. */ + if (b & 1) + { + y = (y * a) % n; + } + + /* Square a for each bit in b. */ + a = (a * a) % n; + + /* Prepare for the next bit in b. */ + b = b >> 1; + } + + return y; +} + +void rsaTest() +{ + huge_t rsaOrig, rsaDecrypted, rsaEncrypted; + rsaPubKey_t publicKey; + rsaPriKey_t privateKey; + int i; + + printf("Encrypting with RSA %s", CFG_PRINTF_NEWLINE); + + #if CFG_RSA_BITS == 64 + // Values based on 64-bit math (huge_t = uint64_t) + // which will result in more secure encryption, but also + // increases the size of the encrypted text + publicKey.e = 21; + publicKey.n = 16484947; + privateKey.d = 15689981; + privateKey.n = 16484947; + #endif + + #if CFG_RSA_BITS == 32 + // Alternative values with 32-bit math (huge_t = uint32_t) + // or when smaller encrypted text is desired + publicKey.e = 17; + publicKey.n = 209; + privateKey.d = 53; + privateKey.n = 209; + #endif + + #if CFG_RSA_BITS == 64 + printf("Starting RSA encryption/decryption test %s", CFG_PRINTF_NEWLINE); + #endif + #if CFG_RSA_BITS == 32 + printf("d=%u, e=%u, n=%u %s", (unsigned int)privateKey.d, (unsigned int)publicKey.e, (unsigned int)publicKey.n, CFG_PRINTF_NEWLINE); + #endif + + for (i = 0; i < 128; i++) + { + rsaOrig = i; + rsaEncrypt(rsaOrig, &rsaEncrypted, publicKey); + rsaDecrypt(rsaEncrypted, &rsaDecrypted, privateKey); + + if (rsaOrig == rsaDecrypted) + { + #if CFG_RSA_BITS == 64 + printf("Encrypted and decrypted %d %s", i, CFG_PRINTF_NEWLINE); + #endif + #if CFG_RSA_BITS == 32 + printf("In=%5u, Encrypted=%5u, Out=%5u (OK) %s", (unsigned int)rsaOrig, (unsigned int)rsaEncrypted, (unsigned int)rsaDecrypted, CFG_PRINTF_NEWLINE); + #endif + } + else + { + #if CFG_RSA_BITS == 64 + printf("Failed to decrypt %d %s", i, CFG_PRINTF_NEWLINE); + #endif + #if CFG_RSA_BITS == 32 + printf("In=%5u, Encrypted=%5u, Out=%5u (ERROR) %s", (unsigned int)rsaOrig, (unsigned int)rsaEncrypted, (unsigned int)rsaDecrypted, CFG_PRINTF_NEWLINE); + #endif + } + } +} + +void rsaEncrypt(huge_t plaintext, huge_t *ciphertext, rsaPubKey_t pubkey) +{ + *ciphertext = modexp(plaintext, pubkey.e, pubkey.n); + + return; +} + +void rsaDecrypt(huge_t ciphertext, huge_t *plaintext, rsaPriKey_t prikey) +{ + *plaintext = modexp(ciphertext, prikey.d, prikey.n); + + return; +} diff --git a/drivers/rsa/rsa.h b/drivers/rsa/rsa.h new file mode 100644 index 0000000..a879f0b --- /dev/null +++ b/drivers/rsa/rsa.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/*! + @file rsa.h + @author Kyle Loudon + modified: microBuilder.eu + @date 4 January, 2010 + @version 1.0 + + Basic RSA-encryption using 64-bit math (32-bit keys). + + Based on the examples from "Mastering Algorithms with C" by + Kyle Loudon (O'Reilly, 1999). +*/ +/**************************************************************************/ + +#ifndef _RSA_H_ +#define _RSA_H_ + +#include "projectconfig.h" + +/* In a secure implementation, huge_t should be at least 400 decimal digits, * + * instead of the 20 provided by a 64-bit value. This means that key values * + * can be no longer than 10 digits in length in the current implementation. */ +#if CFG_RSA_BITS == 64 + typedef uint64_t huge_t; +#endif +#if CFG_RSA_BITS == 32 + typedef uint32_t huge_t; +#endif + +/* Structure for RSA public keys. */ +typedef struct rsaPubKey_s +{ + huge_t e; + huge_t n; +} +rsaPubKey_t; + +/* Define a structure for RSA private keys. */ +typedef struct rsaPriKey_s +{ + huge_t d; + huge_t n; +} +rsaPriKey_t; + +void rsaTest(); +void rsaEncrypt(huge_t plaintext, huge_t *ciphertext, rsaPubKey_t pubkey); +void rsaDecrypt(huge_t ciphertext, huge_t *plaintext, rsaPriKey_t prikey); + +#endif diff --git a/drivers/sensors/analogjoystick/analogjoystick.c b/drivers/sensors/analogjoystick/analogjoystick.c new file mode 100644 index 0000000..3a1cd5c --- /dev/null +++ b/drivers/sensors/analogjoystick/analogjoystick.c @@ -0,0 +1,138 @@ +/**************************************************************************/ +/*! + @file analogjoystick.c + @author K. Townsend (microBuilder.eu) + + @section DESCRIPTION + + Driver for a simple four-way analog joystick. + + @section Example + + @code + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "analogjoystick.h" +#include "core/adc/adc.h" +#include "core/gpio/gpio.h" + +static bool _joystickInitialised = false; + +/**************************************************************************/ +/*! + @brief Initialises the analog joystick, setting the appropriate + pins to analog and digital input. +*/ +/**************************************************************************/ +void joystickInit(void) +{ + // Make sure SEL is set to GPIO and input + JOYSTICK_SEL_FUNC_GPIO; + gpioSetDir (JOYSTICK_SEL_PORT, JOYSTICK_SEL_PIN, 0); + + // Make sure that ADC is initialised + adcInit(); + + // Set HORIZ/VERT pins to ADC + JOYSTICK_VERT_FUNC_ADC; + JOYSTICK_HORIZ_FUNC_ADC; + + _joystickInitialised = true; +} + +/**************************************************************************/ +/*! + @brief Gets the raw ADC values for the horizontal and vertical + axis, as well as the current state of the select button. + + @param[out] horizontal + pointer to the uint32_t variable that will hold the + results of the horizontal analog conversion + @param[out] vertical + pointer to the uint32_t variable that will hold the + results of the vertical analog conversion + @param[out] select + pointer to the bool variable that will hold the + current state of the digital select button +*/ +/**************************************************************************/ +void joystickGetValues(uint32_t *horizontal, uint32_t *vertical, bool *select) +{ + if (!_joystickInitialised) joystickInit(); + + // Get current ADC values + *horizontal = adcRead(JOYSTICK_HORIZ_ADCPORT); + *vertical = adcRead(JOYSTICK_VERT_ADCPORT); + *select = gpioGetValue(JOYSTICK_SEL_PORT, JOYSTICK_SEL_PIN); +} + +/**************************************************************************/ +/*! + @brief Gets the rough direction that the joystick is currently + positioned at + + @param[out] direction + The joystick_direction_t (enum) that will be used to + identify the joysticks approximate current direction. +*/ +/**************************************************************************/ +void joystickGetDirection(joystick_direction_t *direction) +{ + if (!_joystickInitialised) joystickInit(); + + uint32_t hor, ver; + bool sel; + + // Get current joystick position + joystickGetValues(&hor, &ver, &sel); + + // ToDo: Process values + direction = JOYSTICK_DIR_NONE; +} + +/**************************************************************************/ +/*! + @brief Get the approximate rotation of the joystick from 0-359° in + a clock-wise direction. + + @param[out] rotation + Pointer to the uint32_t variable that will hold the + rotation value. +*/ +/**************************************************************************/ +void joystickGetRotation(uint32_t *rotation) +{ + if (!_joystickInitialised) joystickInit(); + + // ToDo +} diff --git a/drivers/sensors/analogjoystick/analogjoystick.h b/drivers/sensors/analogjoystick/analogjoystick.h new file mode 100644 index 0000000..9574bd6 --- /dev/null +++ b/drivers/sensors/analogjoystick/analogjoystick.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/*! + @file analogjoystick.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _ANALOGJOYSTICK_H_ +#define _ANALOGJOYSTICK_H_ + +#include "projectconfig.h" + +#define JOYSTICK_VERT_ADCPORT (2) // ADC2 +#define JOYSTICK_HORIZ_ADCPORT (3) // ADC3 +#define JOYSTICK_SEL_PORT (0) // Use UART CTS for SEL input (P0.7) +#define JOYSTICK_SEL_PIN (7) + +#define JOYSTICK_SEL_FUNC_GPIO do {IOCON_PIO0_7 &= ~IOCON_PIO0_7_FUNC_MASK; IOCON_PIO0_7 |= IOCON_PIO0_7_FUNC_GPIO;} while (0) +#define JOYSTICK_VERT_FUNC_ADC do {IOCON_JTAG_TMS_PIO1_0 &= ~IOCON_JTAG_TMS_PIO1_0_FUNC_MASK; IOCON_JTAG_TMS_PIO1_0 |= IOCON_JTAG_TMS_PIO1_0_FUNC_AD1;} while (0) +#define JOYSTICK_HORIZ_FUNC_ADC do {IOCON_JTAG_nTRST_PIO1_2 &= ~IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK; IOCON_JTAG_nTRST_PIO1_2 |= IOCON_JTAG_nTRST_PIO1_2_FUNC_AD3;} while (0) + +typedef enum +{ + JOYSTICK_DIR_NONE = 0, + JOYSTICK_DIR_NORTH = 1, + JOYSTICK_DIR_NORTHEAST = 2, + JOYSTICK_DIR_EAST = 3, + JOYSTICK_DIR_SOUTHEAST = 4, + JOYSTICK_DIR_SOUTH = 5, + JOYSTICK_DIR_SOUTHWEST = 6, + JOYSTICK_DIR_WEST = 7, + JOYSTICK_DIR_NORTHWEST = 8 +} joystick_direction_t; + +void joystickInit(void); +void joystickGetValues(uint32_t *horizontal, uint32_t *vertical, bool *select); +void joystickGetDirection(joystick_direction_t *direction); +void joystickGetRotation(uint32_t *rotation); + +#endif diff --git a/drivers/sensors/lm75b/lm75b.c b/drivers/sensors/lm75b/lm75b.c new file mode 100644 index 0000000..d5633d9 --- /dev/null +++ b/drivers/sensors/lm75b/lm75b.c @@ -0,0 +1,208 @@ +/**************************************************************************/ +/*! + @file lm75b.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + Driver for NXP's LM75B I2C temperature sensor. This temperature + sensor has an accuracy of 0.125°C, and returns a temperature value + in degrees celsius where each unit is equal to 0.125°C. For example, + if the temperature reading is 198, it means that the temperature in + degree celsius is: 198 / 8 = 24.75°C. + + @section Example + + @code + #include "core/cpu/cpu.h" + #include "drivers/sensors/lm75b/lm75b.h" + + int main(void) + { + cpuInit(); + + int32_t temp = 0; + + // Initialise the LM75B + lm75bInit(); + + while (1) + { + // Get the current temperature (in 0.125°C units) + lm75bGetTemperature(&temp); + + // Multiply value by 125 for fixed-point math (0.125°C per unit) + temp *= 125; + + // Use modulus operator to display decimal value + printf("Current Temperature: %d.%d C\n", temp / 1000, temp % 1000); + + // Alternatively, you could also use floating point math, though + // this will result in larger compiled code if you add in floating + // point support for printf, etc. + // + // float tempFloat = 0.0F; + // lm75bGetTemperature(&temp); + // tempFloat = (float)temp / 8.0F; + } + } + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "lm75b.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +uint32_t i; + +static bool _lm75bInitialised = false; + +/**************************************************************************/ +/*! + @brief Writes an 8 bit values over I2C +*/ +/**************************************************************************/ +lm75bError_e lm75bWrite8 (uint8_t reg, uint32_t value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 3; + I2CReadLength = 0; + I2CMasterBuffer[0] = LM75B_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + I2CMasterBuffer[2] = (value & 0xFF); // Value to write + i2cEngine(); + return LM75B_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Reads a 16 bit values over I2C +*/ +/**************************************************************************/ +lm75bError_e lm75bRead16(uint8_t reg, int32_t *value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 2; + I2CReadLength = 2; + I2CMasterBuffer[0] = LM75B_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + // Append address w/read bit + I2CMasterBuffer[2] = LM75B_ADDRESS | LM75B_READBIT; + i2cEngine(); + + // Shift values to create properly formed integer + *value = ((I2CSlaveBuffer[0] << 8) | I2CSlaveBuffer[1]) >> 5; + + // Sign extend negative numbers + if (I2CSlaveBuffer[0] & 0x80) + { + // Negative number + *value |= 0xFFFFFC00; + } + return LM75B_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Initialises the I2C block +*/ +/**************************************************************************/ +lm75bError_e lm75bInit(void) +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + return LM75B_ERROR_I2CINIT; /* Fatal error */ + } + + _lm75bInitialised = true; + + return LM75B_ERROR_OK; + + // Set device to shutdown mode by default (saves power) + return lm75bConfigWrite (LM75B_CONFIG_SHUTDOWN_SHUTDOWN); +} + +/**************************************************************************/ +/*! + @brief Reads the current temperature from the LM75B + + @note This method will assign a signed 32-bit value (int32) to 'temp', + where each unit represents +/- 0.125°C. To convert the numeric + value to degrees celsius, you must divide the value of 'temp' + by 8. This conversion is not done automatically, since you may + or may not want to use floating point math for the calculations. +*/ +/**************************************************************************/ +lm75bError_e lm75bGetTemperature (int32_t *temp) +{ + if (!_lm75bInitialised) lm75bInit(); + + // Turn device on + lm75bConfigWrite (LM75B_CONFIG_SHUTDOWN_POWERON); + + // Read temperature + lm75bError_e error = LM75B_ERROR_OK; + error = lm75bRead16 (LM75B_REGISTER_TEMPERATURE, temp); + + // Shut device back down + lm75bConfigWrite (LM75B_CONFIG_SHUTDOWN_SHUTDOWN); + + return error; +} + +/**************************************************************************/ +/*! + @brief Writes the supplied 8-bit value to the LM75B config register +*/ +/**************************************************************************/ +lm75bError_e lm75bConfigWrite (uint8_t configValue) +{ + if (!_lm75bInitialised) lm75bInit(); + + return lm75bWrite8 (LM75B_REGISTER_CONFIGURATION, configValue); +} diff --git a/drivers/sensors/lm75b/lm75b.h b/drivers/sensors/lm75b/lm75b.h new file mode 100644 index 0000000..5d6c67c --- /dev/null +++ b/drivers/sensors/lm75b/lm75b.h @@ -0,0 +1,70 @@ +/**************************************************************************/ +/*! + @file lm75b.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _LM75B_H_ +#define _LM75B_H_ + +#include "projectconfig.h" +#include "core/i2c/i2c.h" + +#define LM75B_ADDRESS (0x90) // 100 1000 shifted left 1 bit = 0x90 +#define LM75B_READBIT (0x01) + +#define LM75B_REGISTER_TEMPERATURE (0x00) +#define LM75B_REGISTER_CONFIGURATION (0x01) + +#define LM75B_CONFIG_SHUTDOWN_MASK (0x01) +#define LM75B_CONFIG_SHUTDOWN_POWERON (0x00) +#define LM75B_CONFIG_SHUTDOWN_SHUTDOWN (0x01) + +typedef enum +{ + LM75B_ERROR_OK = 0, // Everything executed normally + LM75B_ERROR_I2CINIT, // Unable to initialise I2C + LM75B_ERROR_I2CBUSY, // I2C already in use + LM75B_ERROR_LAST +} +lm75bError_e; + +lm75bError_e lm75bInit(void); +lm75bError_e lm75bGetTemperature (int32_t *temp); +lm75bError_e lm75bConfigWrite (uint8_t configValue); + +#endif + + diff --git a/drivers/sensors/pn532/pn532.c b/drivers/sensors/pn532/pn532.c new file mode 100644 index 0000000..350d4cb --- /dev/null +++ b/drivers/sensors/pn532/pn532.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/*! + @file pn532.c +*/ +/**************************************************************************/ +#include + +#include "pn532.h" +#include "pn532_drvr.h" +#include "core/systick/systick.h" +#include "core/uart/uart.h" + +static pn532_pcb_t pcb; + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters + + @param pbtData Pointer to the byte data + @param szBytes Data length in bytes +*/ +/**************************************************************************/ +void pn532PrintHex(const byte_t * pbtData, const size_t szBytes) +{ + size_t szPos; + for (szPos=0; szPos < szBytes; szPos++) + { + PN532_DEBUG("%02x ", pbtData[szPos]); + } + PN532_DEBUG(CFG_PRINTF_NEWLINE); +} + +/**************************************************************************/ +/*! + @brief Gets a reference to the PN532 peripheral control block, + which can be used to determine that state of the PN532 + IC, buffers, etc. +*/ +/**************************************************************************/ +pn532_pcb_t * pn532GetPCB() +{ + return &pcb; +} + +/**************************************************************************/ +/*! + @brief Initialises the appropriate serial bus (UART, etc.),and + sets up any buffers or peripherals required by the PN532. +*/ +/**************************************************************************/ +void pn532Init(void) +{ + // Clear protocol control blocks + memset(&pcb, 0, sizeof(pn532_pcb_t)); + + // Initialise the underlying HW + pn532HWInit(); + + // Set the PCB flags to an appropriate state + pcb.initialised = TRUE; +} + +/**************************************************************************/ +/*! + @brief Configures the PN532 for a specific modulation and + baud rate +*/ +/**************************************************************************/ +pn532_error_t pn532Configure(pn532_modulation_t mod) +{ + // ToDo + + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Reads the response buffer from the PN532 + + @param abtCommand + The byte array containg the command and any + optional paramaters + @param szLen + The number of bytes in abtCommand +*/ +/**************************************************************************/ +pn532_error_t pn532Read(byte_t * abtResponse, size_t * pszLen) +{ + if (!pcb.initialised) pn532Init(); + + // Try to wake the device up if it's in sleep mode + if (pcb.state == PN532_STATE_SLEEP) + { + pn532_error_t wakeupError = pn532Wakeup(); + if (wakeupError) + { + return wakeupError; + } + } + + // Read the response if the device is in an appropriate state + if (pcb.state == PN532_STATE_READY) + { + return pn532ReadResponse(abtResponse, pszLen); + } + else + { + PN532_DEBUG("Init Failed%s", CFG_PRINTF_NEWLINE); + return PN532_ERROR_UNABLETOINIT; + } +} + +/**************************************************************************/ +/*! + @brief Sends a byte array of command and parameter data to the + PN532, starting with the command byte. The frame's + preamble, checksums, postamble and frame identifier (0xD4) + will all be automatically added. + + @param abtCommand + The byte array containg the command and any + optional paramaters + @param szLen + The number of bytes in abtCommand +*/ +/**************************************************************************/ +pn532_error_t pn532Write(byte_t * abtCommand, size_t szLen) +{ + if (!pcb.initialised) pn532Init(); + + // Try to wake the device up if it's in sleep mode + if (pcb.state == PN532_STATE_SLEEP) + { + pn532_error_t wakeupError = pn532Wakeup(); + if (wakeupError) + { + return wakeupError; + } + } + + // Send the command if the device is in an appropriate state + if (pcb.state == PN532_STATE_READY) + { + return pn532SendCommand(abtCommand, szLen); + } + else + { + PN532_DEBUG("Init Failed%s", CFG_PRINTF_NEWLINE); + return PN532_ERROR_UNABLETOINIT; + } +} + diff --git a/drivers/sensors/pn532/pn532.h b/drivers/sensors/pn532/pn532.h new file mode 100644 index 0000000..a88f06b --- /dev/null +++ b/drivers/sensors/pn532/pn532.h @@ -0,0 +1,65 @@ +/**************************************************************************/ +/*! + @file pn532.h +*/ +/**************************************************************************/ + +#ifndef __PN532_H__ +#define __PN532_H__ + +#include "projectconfig.h" + +#define PN532_DEBUG(fmt, args...) printf(fmt, ##args) + +typedef enum pn532_state_e +{ + PN532_STATE_SLEEP, + PN532_STATE_READY, + PN532_STATE_BUSY +} +pn532_state_t; + +/* Error messages generate by the stack (not to be confused with app level errors from the PN532) */ +typedef enum pn532_error_e +{ + PN532_ERROR_NONE = 0x00, + PN532_ERROR_UNABLETOINIT = 0x01, // Unable to initialise or wakeup the device + PN532_ERROR_APPLEVELERROR = 0x02, // Application level error detected + PN532_ERROR_BUSY = 0x03, // Busy executing a previous command + PN532_ERROR_NOACK = 0x04, // No ack message received + PN532_ERROR_INVALIDACK = 0x05, // Ack != 00 00 FF 00 FF 00 + PN532_ERROR_PREAMBLEMISMATCH = 0x06, // Frame preamble + start code mismatch + PN532_ERROR_EXTENDEDFRAME = 0x07, // Extended frames currently unsupported + PN532_ERROR_LENCHECKSUMMISMATCH = 0x08, + PN532_ERROR_RESPONSEBUFFEREMPTY = 0x09, // No response data received + PN532_ERROR_SPIREADYSTATUSTIMEOUT = 0x0A // Timeout waiting for 'ready' status (SPI only) + +} pn532_error_t; + +typedef enum pn532_modulation_e +{ + PN532_MODULATION_ISO14443A_106KBPS = 0x00, + PN532_MODULATION_FELICA_212KBPS = 0x01, + PN532_MODULATION_FELICA_424KBPS = 0x02, + PN532_MODULATION_ISO14443B_106KBPS = 0x03, + PN532_MODULATION_JEWEL_106KBPS = 0x04 +} pn532_modulation_t; + +/* PN532 Protocol control block */ +typedef struct +{ + BOOL initialised; + pn532_state_t state; + pn532_modulation_t modulation; + uint32_t lastCommand; + uint32_t appError; +} pn532_pcb_t; + +void pn532Init(); +pn532_pcb_t * pn532GetPCB(); +pn532_error_t pn532SetModulation(pn532_modulation_t mod); +pn532_error_t pn532Write(byte_t *abtCommand, size_t szLen); +pn532_error_t pn532Read(byte_t *abtResponse, size_t * pszLen); +void pn532PrintHex(const byte_t * pbtData, const size_t szBytes); + +#endif diff --git a/drivers/sensors/pn532/pn532_drvr.h b/drivers/sensors/pn532/pn532_drvr.h new file mode 100644 index 0000000..f314fa1 --- /dev/null +++ b/drivers/sensors/pn532/pn532_drvr.h @@ -0,0 +1,103 @@ +/**************************************************************************/ +/*! + @file pn532_drvr.h +*/ +/**************************************************************************/ + +#ifndef __PN532_DRV_H__ +#define __PN532_DRV_H__ + +#include "projectconfig.h" +#include "pn532.h" + +#define PN532_UART +// #define PN532_SPI + +#define PN532_DEBUG(fmt, args...) printf(fmt, ##args) + +#define PN532_RSTPD_PORT (2) +#define PN532_RSTPD_PIN (2) +#define PN532_SPI_CSPORT (0) +#define PN532_SPI_CSPIN (2) + +#define PN532_NORMAL_FRAME__DATA_MAX_LEN (254) +#define PN532_NORMAL_FRAME__OVERHEAD (8) +#define PN532_EXTENDED_FRAME__DATA_MAX_LEN (264) +#define PN532_EXTENDED_FRAME__OVERHEAD (11) +#define PN532_BUFFER_LEN (PN532_EXTENDED_FRAME__DATA_MAX_LEN + PN532_EXTENDED_FRAME__OVERHEAD) +#define PN532_UART_BAUDRATE (115200) + +enum +{ + PN532_COMMAND_DIAGNOSE = 0x00, + PN532_COMMAND_GETFIRMWAREVERSION = 0x02, + PN532_COMMAND_GETGENERALSTATUS = 0x04, + PN532_COMMAND_READREGISTER = 0x06, + PN532_COMMAND_WRITEREGISTER = 0x08, + PN532_COMMAND_READGPIO = 0x0C, + PN532_COMMAND_WRITEGPIO = 0x0E, + PN532_COMMAND_SETSERIALBAUDRATE = 0x10, + PN532_COMMAND_SETPARAMETERS = 0x12, + PN532_COMMAND_SAMCONFIGURATION = 0x14, + PN532_COMMAND_POWERDOWN = 0x16, + PN532_COMMAND_RFCONFIGURATION = 0x32, + PN532_COMMAND_RFREGULATIONTEST = 0x58, + PN532_COMMAND_INJUMPFORDEP = 0x56, + PN532_COMMAND_INJUMPFORPSL = 0x46, + PN532_COMMAND_INLISTPASSIVETARGET = 0x4A, + PN532_COMMAND_INATR = 0x50, + PN532_COMMAND_INPSL = 0x4E, + PN532_COMMAND_INDATAEXCHANGE = 0x40, + PN532_COMMAND_INCOMMUNICATETHRU = 0x42, + PN532_COMMAND_INDESELECT = 0x44, + PN532_COMMAND_INRELEASE = 0x52, + PN532_COMMAND_INSELECT = 0x54, + PN532_COMMAND_INAUTOPOLL = 0x60, + PN532_COMMAND_TGINITASTARGET = 0x8C, + PN532_COMMAND_TGSETGENERALBYTES = 0x92, + PN532_COMMAND_TGGETDATA = 0x86, + PN532_COMMAND_TGSETDATA = 0x8E, + PN532_COMMAND_TGSETMETADATA = 0x94, + PN532_COMMAND_TGGETINITIATORCOMMAND = 0x88, + PN532_COMMAND_TGRESPONSETOINITIATOR = 0x90, + PN532_COMMAND_TGGETTARGETSTATUS = 0x8A +}; + +/* Application level errors generated by the PN532 chip */ +enum +{ + PN532_APPERROR_NONE = 0x00, + PN532_APPERROR_TIMEOUT = 0x01, + PN532_APPERROR_CRCERROR = 0x02, + PN532_APPERROR_PARITYERROR = 0x04, + PN532_APPERROR_FRAMINGERROR = 0x05, + PN532_APPERROR_BITCOLLISION = 0x06, + PN532_APPERROR_INSUFFICIENTBUFFER = 0x07, + PN532_APPERROR_RFBUFFEROVERFLOW = 0x09, + PN532_APPERROR_RFFIELDTIMEOUT = 0x0A, + PN532_APPERROR_RFPROTOCOLERROR = 0x0B, + PN532_APPERROR_TEMPERROR = 0x0D, + PN532_APPERROR_INTERNBUFFEROVERFLOW = 0x0E, + PN532_APPERROR_INVALIDPARAMETER = 0x10, + PN532_APPERROR_DEP_UNSUPPORTEDCMD = 0x12, + PN532_APPERROR_DEP_INVALIDOFORMAT = 0x13, + PN532_APPERROR_AUTHENTERR = 0x14, + PN532_APPERROR_UIDCCHECKERROR = 0x23, + PN532_APPERROR_DEP_INVALIDDEVSTATE = 0x25, + PN532_APPERROR_OPERATIONNOTALLOWED = 0x26, + PN532_APPERROR_CMDNOTACCEPTABLE = 0x27, + PN532_APPERROR_TARGETRELEASED = 0x29, + PN532_APPERROR_IDMISMATCH = 0x2A, + PN532_APPERROR_CARDDISAPPEARED = 0x2B, + PN532_APPERROR_NFCID3MISMATCH = 0x2C, + PN532_APPERROR_OVERCURRENTEVENT = 0x2D, + PN532_APPERROR_NADMISSINGINDEP = 0x2E +}; + +void pn532HWInit(void); +pn532_error_t pn532BuildFrame(byte_t * pbtFrame, size_t * pszFrame, const byte_t * pbtData, const size_t szData); +pn532_error_t pn532SendCommand(const byte_t * pbtData, const size_t szData); +pn532_error_t pn532ReadResponse(byte_t * pbtResponse, size_t * pszRxLen); +pn532_error_t pn532Wakeup(void); + +#endif diff --git a/drivers/sensors/pn532/pn532_drvr_spi.c b/drivers/sensors/pn532/pn532_drvr_spi.c new file mode 100644 index 0000000..f55621a --- /dev/null +++ b/drivers/sensors/pn532/pn532_drvr_spi.c @@ -0,0 +1,410 @@ +/**************************************************************************/ +/*! + @file pn532_drvr_spi.c +*/ +/**************************************************************************/ +#include "pn532_drvr.h" + +#ifdef PN532_SPI + +#include +#include "core/systick/systick.h" +#include "core/gpio/gpio.h" +#include "core/ssp/ssp.h" + +#define PN532_SELECT gpioSetValue(PN532_SPI_CSPORT, PN532_SPI_CSPIN, 0); +#define PN532_DESELECT gpioSetValue(PN532_SPI_CSPORT, PN532_SPI_CSPIN, 1); + +#define PN532_SPI_STATREAD 0x02 +#define PN532_SPI_DATAWRITE 0x01 +#define PN532_SPI_DATAREAD 0x03 +#define PN532_SPI_READY 0x01 + +// The number of attempts to make while waiting for the status ready bit +// Each attempt is 1ms +#define PN532_SPI_TIMEOUT 100 + +/**************************************************************************/ +/*! + @brief Writes a single byte via SPI +*/ +/**************************************************************************/ +uint8_t pn532SPIWrite(unsigned char data) +{ + // Note: bits have to be reversed since SPI is LSB on the PN532 + // Thankfully the M3 has a quick HW reverse command (RBIT) + while ((SSP_SSP0SR & SSP_SSP0SR_TNF_NOTFULL) == 0); + SSP_SSP0DR = ((unsigned char)(RBIT(data)>>24)); // Write + while ((SSP_SSP0SR & SSP_SSP0SR_RNE_NOTEMPTY) == 0); + data = ((unsigned char)(RBIT(SSP_SSP0DR)>>24)); // Read + return data; +} + +/**************************************************************************/ +/*! + @brief Writes a byte array via SPI +*/ +/**************************************************************************/ +void pn532SPIWriteArray(byte_t * pbtData, size_t len) +{ + while (len != 0) + { + pn532SPIWrite(*pbtData); + pbtData++; + len--; + } +} + +/**************************************************************************/ +/*! + @brief Reads a single byte via SPI +*/ +/**************************************************************************/ +uint8_t pn532SPIRead(void) +{ + return pn532SPIWrite(0x00); +} + +/**************************************************************************/ +/*! + @brief Reads a byte array via SPI +*/ +/**************************************************************************/ +void pn532SPIReadArray(uint8_t* buff, size_t len) +{ + size_t i; + for (i=0; i PN532_NORMAL_FRAME__DATA_MAX_LEN) + { + // Extended frames currently unsupported + return PN532_ERROR_EXTENDEDFRAME; + } + + // LEN - Packet length = data length (len) + checksum (1) + end of stream marker (1) + pbtFrame[3] = szData + 1; + // LCS - Packet length checksum + pbtFrame[4] = 256 - (szData + 1); + // TFI + pbtFrame[5] = 0xD4; + // DATA - Copy the PN53X command into the packet buffer + memcpy (pbtFrame + 6, pbtData, szData); + + // DCS - Calculate data payload checksum + byte_t btDCS = (256 - 0xD4); + size_t szPos; + for (szPos = 0; szPos < szData; szPos++) + { + btDCS -= pbtData[szPos]; + } + pbtFrame[6 + szData] = btDCS; + + // 0x00 - End of stream marker + pbtFrame[szData + 7] = 0x00; + + (*pszFrame) = szData + PN532_NORMAL_FRAME__OVERHEAD; + + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Sends the specified command to the PN532, automatically + creating an appropriate frame for it + + @param pdbData Pointer to the byte data to send + @param szData Length in bytes of the data to send + + @note Possible error messages are: + + - PN532_ERROR_BUSY + - PN532_ERROR_NOACK + - PN532_ERROR_INVALIDACK + - PN532_ERROR_SPIREADYSTATUSTIMEOUT +*/ +/**************************************************************************/ +pn532_error_t pn532SendCommand(const byte_t * pbtData, const size_t szData) +{ + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check busy flag + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + pn532->state = PN532_STATE_BUSY; + + // Every packet must start with "00 00 ff" + byte_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; + size_t szFrame = 0; + + // Build the frame + pn532BuildFrame (abtFrame, &szFrame, pbtData, szData); + + // Register the last command that was sent + pn532->lastCommand = pbtData[0]; + + // Output the frame data for debugging if requested + PN532_DEBUG("Sending (%02d): ", szFrame); + pn532PrintHex(abtFrame, szFrame); + + PN532_SELECT; + systickDelay(5); + + // Send data to the PN532 + pn532SPIWrite(PN532_SPI_DATAWRITE); + pn532SPIWriteArray(abtFrame, szFrame); + + // Wait for READY status + size_t t = 0; + while (pn532GetStatus() != PN532_SPI_READY) + { + if(t++>PN532_SPI_TIMEOUT) + { + PN532_DEBUG("Timeout waiting for READY status%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_SPIREADYSTATUSTIMEOUT; + } + systickDelay(1); + } + + // Read ACK + PN532_SELECT; + pn532SPIWrite(PN532_SPI_DATAREAD); + const byte_t abtAck[6] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 }; + byte_t abtRxBuf[6]; + pn532SPIReadArray(abtRxBuf, 6); + PN532_DESELECT; + + // Make sure the received ACK matches the prototype + if (0 != (memcmp (abtRxBuf, abtAck, 6))) + { + PN532_DEBUG ("Invalid ACK: "); + pn532PrintHex(abtRxBuf, 6); + PN532_DEBUG("%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_INVALIDACK; + } + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Reads a response from the PN532 + + @note Possible error message are: + + - PN532_ERROR_BUSY + - PN532_ERROR_RESPONSEBUFFEREMPTY + - PN532_ERROR_PREAMBLEMISMATCH + - PN532_ERROR_APPLEVELERROR + - PN532_ERROR_EXTENDEDFRAME + - PN532_ERROR_LENCHECKSUMMISMATCH + - PN532_ERROR_SPIREADYSTATUSTIMEOUT +*/ +/**************************************************************************/ +pn532_error_t pn532ReadResponse(byte_t * pbtResponse, size_t * pszRxLen) +{ + size_t t, i; + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check if we're busy + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + pn532->state = PN532_STATE_BUSY; + pn532->appError = PN532_APPERROR_NONE; + + // Wait for the response ready signal + t = 0; + while (pn532GetStatus() != PN532_SPI_READY) + { + if(t++>PN532_SPI_TIMEOUT) + { + PN532_DEBUG("Timeout waiting for READY status%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_SPIREADYSTATUSTIMEOUT; + } + systickDelay(1); + } + + PN532_SELECT; + systickDelay(1); + + pn532SPIWrite(PN532_SPI_DATAREAD); + + // Check preamble + pbtResponse[0] = pn532SPIRead(); + pbtResponse[1] = pn532SPIRead(); + pbtResponse[2] = pn532SPIRead(); + const byte_t pn53x_preamble[3] = { 0x00, 0x00, 0xff }; + if (0 != (memcmp (pbtResponse, pn53x_preamble, 3))) + { + PN532_DEBUG("Frame preamble + start code mismatch%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_PREAMBLEMISMATCH; + } + + // Check the frame type + pbtResponse[3] = pn532SPIRead(); + pbtResponse[4] = pn532SPIRead(); + pbtResponse[5] = pn532SPIRead(); + if ((0x01 == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Error frame + PN532_DEBUG("Application level error (%02d)%s", pbtResponse[5], CFG_PRINTF_NEWLINE); + // Set application error message ID + pn532->appError = pbtResponse[5]; + pn532->state = PN532_STATE_READY; + return PN532_ERROR_APPLEVELERROR; + } + else if ((0xff == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Extended frame + PN532_DEBUG("Extended frames currently unsupported%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_EXTENDEDFRAME; + } + else + { + // Check checksum, unless this is a response to the wakeup command + if (pn532->lastCommand = PN532_COMMAND_SAMCONFIGURATION) + { + *pszRxLen = 6; + } + else + { + // Normal frame + if (256 != (pbtResponse[3] + pbtResponse[4])) + { + // TODO: Retry + PN532_DEBUG("Length checksum mismatch%s", CFG_PRINTF_NEWLINE); + pn532PrintHex(pbtResponse, 6); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_LENCHECKSUMMISMATCH; + } + // Read payload + size_t szPayloadLen = pbtResponse[3] - 2; + for (i=0; istate = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Sends the wakeup sequence to the PN532. +*/ +/**************************************************************************/ +pn532_error_t pn532Wakeup(void) +{ + size_t szLen; + byte_t abtWakeUp[] = { 0x01,0x55,0x55,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 }; + // byte_t abtWakeUp[] = { 0x01,0x00,0x00,0xff,0x03,0xfd,0xd4,PN532_COMMAND_SAMCONFIGURATION,0x01,0x17,0x00 }; + + pn532_pcb_t *pn532 = pn532GetPCB(); + + PN532_DEBUG("Sending Wakeup Sequence%s", CFG_PRINTF_NEWLINE); + + PN532_SELECT; + systickDelay(2); + + // Transmit wakeup sequence + pn532SPIWriteArray(abtWakeUp, sizeof(abtWakeUp)); + systickDelay(100); + + // Register the last command that was sent + pn532->lastCommand = PN532_COMMAND_SAMCONFIGURATION; + + byte_t response[32]; + pn532ReadResponse(response, &szLen); + PN532_DESELECT; + + // Todo: Check for error ... currently throws a checksum error + // that isn't really an error + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +#endif // #ifdef PN532_SPI diff --git a/drivers/sensors/pn532/pn532_drvr_uart.c b/drivers/sensors/pn532/pn532_drvr_uart.c new file mode 100644 index 0000000..abe029f --- /dev/null +++ b/drivers/sensors/pn532/pn532_drvr_uart.c @@ -0,0 +1,276 @@ +/**************************************************************************/ +/*! + @file pn532_drvr_uart.c +*/ +/**************************************************************************/ +#include + +#include "pn532.h" +#include "pn532_drvr.h" + +#ifdef PN532_UART + +#include "core/systick/systick.h" +#include "core/gpio/gpio.h" +#include "core/uart/uart.h" + +/**************************************************************************/ +/*! + @brief Initialises UART and configures the PN532 +*/ +/**************************************************************************/ +void pn532HWInit(void) +{ + PN532_DEBUG("Initialising UART (%d)%s", PN532_UART_BAUDRATE, CFG_PRINTF_NEWLINE); + uartInit(PN532_UART_BAUDRATE); + + // Set reset pin as output and reset device + gpioSetDir(PN532_RSTPD_PORT, PN532_RSTPD_PIN, gpioDirection_Output); + PN532_DEBUG("Resetting the PN532...\r\n"); + gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 0); + systickDelay(400); + gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 1); + + // Wait for the PN532 to finish booting + systickDelay(100); +} + +/**************************************************************************/ +/*! + @brief Builds a standard PN532 frame using the supplied data + + @param pbtFrame Pointer to the field that will hold the frame data + @param pszFrame Pointer to the field that will hold the frame length + @param pbtData Pointer to the data to insert in a frame + @param swData Length of the data to insert in bytes + + @note Possible error messages are: + + - PN532_ERROR_EXTENDEDFRAME +*/ +/**************************************************************************/ +pn532_error_t pn532BuildFrame(byte_t * pbtFrame, size_t * pszFrame, const byte_t * pbtData, const size_t szData) +{ + if (szData > PN532_NORMAL_FRAME__DATA_MAX_LEN) + { + // Extended frames currently unsupported + return PN532_ERROR_EXTENDEDFRAME; + } + + // LEN - Packet length = data length (len) + checksum (1) + end of stream marker (1) + pbtFrame[3] = szData + 1; + // LCS - Packet length checksum + pbtFrame[4] = 256 - (szData + 1); + // TFI + pbtFrame[5] = 0xD4; + // DATA - Copy the PN53X command into the packet buffer + memcpy (pbtFrame + 6, pbtData, szData); + + // DCS - Calculate data payload checksum + byte_t btDCS = (256 - 0xD4); + size_t szPos; + for (szPos = 0; szPos < szData; szPos++) + { + btDCS -= pbtData[szPos]; + } + pbtFrame[6 + szData] = btDCS; + + // 0x00 - End of stream marker + pbtFrame[szData + 7] = 0x00; + + (*pszFrame) = szData + PN532_NORMAL_FRAME__OVERHEAD; + + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Sends the specified command to the PN532, automatically + creating an appropriate frame for it + + @param pdbData Pointer to the byte data to send + @param szData Length in bytes of the data to send + + @note Possible error messages are: + + - PN532_ERROR_BUSY + - PN532_ERROR_NOACK + - PN532_ERROR_INVALIDACK +*/ +/**************************************************************************/ +pn532_error_t pn532SendCommand(const byte_t * pbtData, const size_t szData) +{ + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check if we're busy + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + + // Flag the stack as busy + pn532->state = PN532_STATE_BUSY; + + // Every packet must start with "00 00 ff" + byte_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; + size_t szFrame = 0; + + // Build the frame + pn532BuildFrame (abtFrame, &szFrame, pbtData, szData); + + // Keep track of the last command that was sent + pn532->lastCommand = pbtData[0]; + + // Output the frame data for debugging if requested + PN532_DEBUG("Sending (%02d): ", szFrame); + pn532PrintHex(abtFrame, szFrame); + + // Send data to the PN532 + uartSend (abtFrame, szFrame); + + // Wait for ACK + byte_t abtRxBuf[6]; + uart_pcb_t *uart = uartGetPCB(); + systickDelay(10); // FIXME: How long should we wait for ACK? + if (uart->rxfifo.len < 6) + { + // Unable to read ACK + PN532_DEBUG ("Unable to read ACK%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NOACK; + } + + // Read ACK ... this will also remove it from the buffer + const byte_t abtAck[6] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 }; + abtRxBuf[0] = uartRxBufferRead(); + abtRxBuf[1] = uartRxBufferRead(); + abtRxBuf[2] = uartRxBufferRead(); + abtRxBuf[3] = uartRxBufferRead(); + abtRxBuf[4] = uartRxBufferRead(); + abtRxBuf[5] = uartRxBufferRead(); + + // Make sure the received ACK matches the prototype + if (0 != (memcmp (abtRxBuf, abtAck, 6))) + { + PN532_DEBUG ("Invalid ACK: "); + pn532PrintHex(abtRxBuf, 6); + PN532_DEBUG("%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_INVALIDACK; + } + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Reads a response from the PN532 + + @note Possible error message are: + + - PN532_ERROR_BUSY + - PN532_ERROR_RESPONSEBUFFEREMPTY + - PN532_ERROR_PREAMBLEMISMATCH + - PN532_ERROR_APPLEVELERROR + - PN532_ERROR_EXTENDEDFRAME + - PN532_ERROR_LENCHECKSUMMISMATCH +*/ +/**************************************************************************/ +pn532_error_t pn532ReadResponse(byte_t * pbtResponse, size_t * pszRxLen) +{ + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check if we're busy + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + + // Flag the stack as busy + pn532->state = PN532_STATE_BUSY; + + // Reset the app error flag + pn532->appError = PN532_APPERROR_NONE; + + // Read response from uart + if (!uartRxBufferReadArray(pbtResponse, pszRxLen)) + { + pn532->state = PN532_STATE_READY; + return PN532_ERROR_RESPONSEBUFFEREMPTY; + } + + // Display the raw response data for debugging if requested + PN532_DEBUG("Received (%02d): ", *pszRxLen); + pn532PrintHex(pbtResponse, *pszRxLen); + + // Check preamble + const byte_t pn53x_preamble[3] = { 0x00, 0x00, 0xff }; + if (0 != (memcmp (pbtResponse, pn53x_preamble, 3))) + { + PN532_DEBUG("Frame preamble + start code mismatch%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_PREAMBLEMISMATCH; + } + + // Check the frame type + if ((0x01 == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Error frame + PN532_DEBUG("Application level error (%02d)%s", pbtResponse[5], CFG_PRINTF_NEWLINE); + // Set application error message ID + pn532->appError = pbtResponse[5]; + pn532->state = PN532_STATE_READY; + return PN532_ERROR_APPLEVELERROR; + } + else if ((0xff == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Extended frame + PN532_DEBUG("Extended frames currently unsupported%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_EXTENDEDFRAME; + } + else + { + // Normal frame + if (256 != (pbtResponse[3] + pbtResponse[4])) + { + // TODO: Retry + PN532_DEBUG("Length checksum mismatch%s", CFG_PRINTF_NEWLINE); + pn532->state = PN532_STATE_READY; + return PN532_ERROR_LENCHECKSUMMISMATCH; + } + // size_t szPayloadLen = abtRx[3] - 2; + } + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Sends the wakeup sequence to the PN532. +*/ +/**************************************************************************/ +pn532_error_t pn532Wakeup(void) +{ + size_t szLen; + byte_t abtWakeUp[] = { 0x55,0x55,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 }; + + pn532_pcb_t *pn532 = pn532GetPCB(); + + PN532_DEBUG("Sending Wakeup Sequence%s", CFG_PRINTF_NEWLINE); + uartSend(abtWakeUp,sizeof(abtWakeUp)); + systickDelay(100); + + byte_t response[32]; + pn532ReadResponse(response, &szLen); + + // Todo: Check for error ... currently throws a checksum error + // that isn't really an error + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +#endif // #ifdef PN532_UART \ No newline at end of file diff --git a/drivers/sensors/pn532/pn532_mifare.c b/drivers/sensors/pn532/pn532_mifare.c new file mode 100644 index 0000000..1176833 --- /dev/null +++ b/drivers/sensors/pn532/pn532_mifare.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/*! + @file pn532_mifare.c +*/ +/**************************************************************************/ +#include + +#include "pn532.h" +#include "pn532_mifare.h" + +bool pn532MifareCmd(const pn532_mifare_cmd_t mc, const uint8_t ui8Block, pn532_mifare_param_t * pmp) +{ + byte_t abtRx[265]; + size_t szRx = sizeof(abtRx); + size_t szParamLen; + byte_t abtCmd[265]; + bool bEasyFraming; + + abtCmd[0] = mc; // The MIFARE Classic command + abtCmd[1] = ui8Block; // The block address (1K=0x00..0x39, 4K=0x00..0xff) + + switch (mc) { + // Read and store command have no parameter + case PN532_MIFARE_CMD_READ: + case PN532_MIFARE_CMD_STORE: + szParamLen = 0; + break; + + // Authenticate command + case PN532_MIFARE_CMD_AUTH_A: + case PN532_MIFARE_CMD_AUTH_B: + szParamLen = sizeof (pn532_mifare_param_auth_t); + break; + + // Data command + case PN532_MIFARE_CMD_WRITE: + szParamLen = sizeof (pn532_mifare_param_data_t); + break; + + // Value command + case PN532_MIFARE_CMD_DECREMENT: + case PN532_MIFARE_CMD_INCREMENT: + case PN532_MIFARE_CMD_TRANSFER: + szParamLen = sizeof (pn532_mifare_param_value_t); + break; + + // Please fix your code, you never should reach this statement + default: + return false; + break; + } + + // When available, copy the parameter bytes + if (szParamLen) + memcpy (abtCmd + 2, (byte_t *) pmp, szParamLen); + +// bEasyFraming = pnd->bEasyFraming; +// if (!nfc_configure (pnd, NDO_EASY_FRAMING, true)) { +// nfc_perror (pnd, "nfc_configure"); +// return false; +// } +// +// // Fire the mifare command +// if (!nfc_initiator_transceive_bytes (pnd, abtCmd, 2 + szParamLen, abtRx, &szRx)) { +// if (pnd->iLastError == EINVRXFRAM) { +// // "Invalid received frame" AKA EINVRXFRAM, usual means we are +// // authenticated on a sector but the requested MIFARE cmd (read, write) +// // is not permitted by current acces bytes; +// // So there is nothing to do here. +// } else { +// nfc_perror (pnd, "nfc_initiator_transceive_bytes"); +// } +// nfc_configure (pnd, NDO_EASY_FRAMING, bEasyFraming); +// return false; +// } +// if (!nfc_configure (pnd, NDO_EASY_FRAMING, bEasyFraming)) { +// nfc_perror (pnd, "nfc_configure"); +// return false; +// } +// +// // When we have executed a read command, copy the received bytes into the param +// if (mc == MC_READ) { +// if (szRx == 16) { +// memcpy (pmp->mpd.abtData, abtRx, 16); +// } else { +// return false; +// } +// } + + // Command succesfully executed + return true; +} diff --git a/drivers/sensors/pn532/pn532_mifare.h b/drivers/sensors/pn532/pn532_mifare.h new file mode 100644 index 0000000..0ea8464 --- /dev/null +++ b/drivers/sensors/pn532/pn532_mifare.h @@ -0,0 +1,123 @@ +/**************************************************************************/ +/*! + @file pn532_mifare.h +*/ +/**************************************************************************/ + +#ifndef __PN532_MIFARE_H__ +#define __PN532_MIFARE_H__ + +#include "projectconfig.h" + +typedef enum pn532_mifare_cmd_e +{ + PN532_MIFARE_CMD_AUTH_A = 0x60, + PN532_MIFARE_CMD_AUTH_B = 0x61, + PN532_MIFARE_CMD_READ = 0x30, + PN532_MIFARE_CMD_WRITE = 0xA0, + PN532_MIFARE_CMD_TRANSFER = 0xB0, + PN532_MIFARE_CMD_DECREMENT = 0xC0, + PN532_MIFARE_CMD_INCREMENT = 0xC1, + PN532_MIFARE_CMD_STORE = 0xC2 +} +pn532_mifare_cmd_t; + +typedef struct +{ + byte_t abtKey[6]; + byte_t abtUid[4]; +} +pn532_mifare_param_auth_t; + +typedef struct +{ + byte_t abtData[16]; +} +pn532_mifare_param_data_t; + +typedef struct +{ + byte_t abtValue[4]; +} +pn532_mifare_param_value_t; + +typedef union +{ + pn532_mifare_param_auth_t mpa; + pn532_mifare_param_data_t mpd; + pn532_mifare_param_value_t mpv; +} +pn532_mifare_param_t; + +// MIFARE Classic +typedef struct +{ + byte_t abtUID[4]; + byte_t btBCC; + byte_t btUnknown; + byte_t abtATQA[2]; + byte_t abtUnknown[8]; +} +pn532_mifare_classic_block_manufacturer_t; + +typedef struct +{ + byte_t abtData[16]; +} +pn532_mifare_classic_block_data_t; + +typedef struct +{ + byte_t abtKeyA[6]; + byte_t abtAccessBits[4]; + byte_t abtKeyB[6]; +} +pn532_mifare_classic_block_trailer_t; + +typedef union +{ + pn532_mifare_classic_block_manufacturer_t mbm; + pn532_mifare_classic_block_data_t mbd; + pn532_mifare_classic_block_trailer_t mbt; +} +pn532_mifare_classic_block_t; + +typedef struct +{ + pn532_mifare_classic_block_t amb[256]; +} +pn532_mifare_classic_tag_t; + +// MIFARE Ultralight +typedef struct +{ + byte_t sn0[3]; + byte_t btBCC0; + byte_t sn1[4]; + byte_t btBCC1; + byte_t internal; + byte_t lock[2]; + byte_t otp[4]; +} +pn532_mifareul_block_manufacturer_t; + +typedef struct +{ + byte_t abtData[16]; +} +pn532_mifareul_block_data_t; + +typedef union +{ + pn532_mifareul_block_manufacturer_t mbm; + pn532_mifareul_block_data_t mbd; +} +pn532_mifareul_block_t; + +typedef struct +{ + pn532_mifareul_block_t amb[4]; +} +pn532_mifareul_tag_t; + +#endif diff --git a/drivers/sensors/tcs3414/tcs3414.c b/drivers/sensors/tcs3414/tcs3414.c new file mode 100644 index 0000000..2a81c71 --- /dev/null +++ b/drivers/sensors/tcs3414/tcs3414.c @@ -0,0 +1,260 @@ +/**************************************************************************/ +/*! + @file tcs3414.c + @author K. Townsend (microBuilder.eu) + Morten Hjerde (tcs3414CalculateCCT) + + @brief Drivers for the TAOS TCS3414 I2C RGB sensor + + @section DESCRIPTION + + The TAOS TCS3414 is a digital color/light sensor that can be used + to derive the color chromaticity and illuminance of ambient light + with 16-bit resolution. The device has an array of filtered + photo-diodes with 4 red, 4 green, 4 blue and 4 unfiltered + captors. The sensor has digital gain and prescalar support so that + the sensitivty of the device can be dynamically adjusted with a + 1,000,000:1 dynamic range. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "tcs3414.h" +#include "core/systick/systick.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +uint32_t i; + +static bool _tcs3414Initialised = false; + +/**************************************************************************/ +/*! + @brief Sends a single command byte over I2C +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414WriteCmd (uint8_t cmd) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 2; + I2CReadLength = 0; + I2CMasterBuffer[0] = TCS3414_ADDRESS; // I2C device address + I2CMasterBuffer[1] = cmd; // Command register + i2cEngine(); + return TCS3414_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Writes an 8 bit values over I2C +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414Write8 (uint8_t reg, uint32_t value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 3; + I2CReadLength = 0; + I2CMasterBuffer[0] = TCS3414_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + I2CMasterBuffer[2] = (value & 0xFF); // Value to write + i2cEngine(); + return TCS3414_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Reads a 16 bit values over I2C +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414Read16(uint8_t reg, uint16_t *value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 2; + I2CReadLength = 2; + I2CMasterBuffer[0] = TCS3414_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + // Append address w/read bit + I2CMasterBuffer[2] = TCS3414_ADDRESS | TCS3414_READBIT; + i2cEngine(); + + // Shift values to create properly formed integer (low byte first) + *value = (I2CSlaveBuffer[0] | (I2CSlaveBuffer[1] << 8)); + + return TCS3414_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Initialises the I2C block +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414Init(void) +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + return TCS3414_ERROR_I2CINIT; /* Fatal error */ + } + + _tcs3414Initialised = true; + + // Note: by default, the device is in power down mode on bootup + + return TCS3414_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Sets the gain and prescalar to control sensitivty +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414SetSensitivity(tcs3414Gain_t gain, tcs3414Prescalar_t prescalar) +{ + if (!_tcs3414Initialised) tcs3414Init(); + + tcs3414Error_e error = TCS3414_ERROR_OK; + + // Enable the device by setting the control bit to 0x03 (power + ADC on) + error = tcs3414Write8(TCS3414_COMMAND_BIT | TCS3414_REGISTER_CONTROL, TCS3414_CONTROL_POWERON); + if (error) return error; + + // Set the gani and prescalar values using the GAIN register + error = tcs3414Write8(TCS3414_COMMAND_BIT | TCS3414_REGISTER_GAIN, gain | prescalar); + if (error) return error; + + // Turn the device off to save power + error = tcs3414Write8(TCS3414_COMMAND_BIT | TCS3414_REGISTER_CONTROL, TCS3414_CONTROL_POWEROFF); + if (error) return error; + + return error; +} + +/**************************************************************************/ +/*! + @brief Reads the RGB and clear luminosity from the TCS3414 +*/ +/**************************************************************************/ +tcs3414Error_e tcs3414GetRGBL(uint16_t *red, uint16_t *green, uint16_t *blue, uint16_t *clear) +{ + if (!_tcs3414Initialised) tcs3414Init(); + + tcs3414Error_e error = TCS3414_ERROR_OK; + + // Enable the device by setting the control bit to 0x03 (power + ADC on) + error = tcs3414Write8(TCS3414_COMMAND_BIT | TCS3414_REGISTER_CONTROL, TCS3414_CONTROL_POWERON); + if (error) return error; + + // Wait >12ms for ADC to complete + systickDelay(13); + + // Reads two byte red value + error = tcs3414Read16(TCS3414_COMMAND_BIT | TCS3414_WORD_BIT | TCS3414_REGISTER_REDLOW, red); + if (error) return error; + + // Reads two byte green value + error = tcs3414Read16(TCS3414_COMMAND_BIT | TCS3414_WORD_BIT | TCS3414_REGISTER_GREENLOW, green); + if (error) return error; + + // Reads two byte blue value + error = tcs3414Read16(TCS3414_COMMAND_BIT | TCS3414_WORD_BIT | TCS3414_REGISTER_BLUELOW, blue); + if (error) return error; + + // Reads two byte clear value + error = tcs3414Read16(TCS3414_COMMAND_BIT | TCS3414_WORD_BIT | TCS3414_REGISTER_CLEARLOW, clear); + if (error) return error; + + // Turn the device off to save power + error = tcs3414Write8(TCS3414_COMMAND_BIT | TCS3414_REGISTER_CONTROL, TCS3414_CONTROL_POWEROFF); + if (error) return error; + + return error; +} + +/**************************************************************************/ +/*! + @brief Reads the RGB values from the TCS3414 color sensor and + calculates CCT (Correlated Color Temperature) + + @return The Correlated Color Temperature in Kelvin +*/ +/**************************************************************************/ +uint32_t tcs3414CalculateCCT (uint16_t red, uint16_t green, uint16_t blue) +{ + volatile float R; + volatile float G; + volatile float B; + volatile float X; + volatile float Y; + volatile float Z; + volatile float x; + volatile float y; + volatile float n; + volatile float CCT; + + // Convert RGB values to a 0-100 scale + R = (((float) red) / 65536) * 100; + G = (((float) green) / 65536) * 100; + B = (((float) blue)/ 65536) * 100; + + //do matrix transformation + X = (-0.14282 * R) + (1.54924 * G) + (-0.95641 * B); + Y = (-0.32466 * R) + (1.57837 * G) + (-0.73191 * B); + Z = (-0.68202 * R) + (0.77073 * G) + (0.56332 * B); + + //calc chromaticity coordinates + x = (X)/(X + Y + Z); + y = (Y)/(X + Y + Z); + + //use McCamy’s formula to get CCT: + n = (x - 0.3320) / (0.1858 - y); + CCT = (449 * n * n * n); // we don't have pow + CCT += (3525 * n * n); + CCT += (6823.3 * n); + CCT += 5520.33; + + return ((uint32_t) CCT); +} diff --git a/drivers/sensors/tcs3414/tcs3414.h b/drivers/sensors/tcs3414/tcs3414.h new file mode 100644 index 0000000..146c870 --- /dev/null +++ b/drivers/sensors/tcs3414/tcs3414.h @@ -0,0 +1,130 @@ +/**************************************************************************/ +/*! + @file tcs3414.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _TCS3414_H_ +#define _TCS3414_H_ + +#include "projectconfig.h" +#include "core/i2c/i2c.h" + +#define TCS3414_ADDRESS (0x72) // 0111001 shifted left 1 bit = 0x72 (ADDR = GND or floating) +#define TCS3414_READBIT (0x01) + +#define TCS3414_COMMAND_BIT (0x80) // Must be 1 +#define TCS3414_WORD_BIT (0x20) // 1 = read/write word (rather than byte) + +#define TCS3414_REGISTER_CONTROL (0x00) +#define TCS3414_REGISTER_TIMING (0x01) +#define TCS3414_REGISTER_INTERRUPT (0x02) +#define TCS3414_REGISTER_INTSOURCE (0x03) +#define TCS3414_REGISTER_PARTNO_REVID (0x04) +#define TCS3414_REGISTER_GAIN (0x07) +#define TCS3414_REGISTER_LOWTHRESHOLD_LOWBYTE (0x08) +#define TCS3414_REGISTER_LOWTHRESHOLD_HIGHBYTE (0x09) +#define TCS3414_REGISTER_HIGHTHRESHOLD_LOWBYTE (0x0A) +#define TCS3414_REGISTER_HIGHTHRESHOLD_HIGHBYTE (0x0B) +#define TCS3414_REGISTER_GREENLOW (0x10) +#define TCS3414_REGISTER_GREENHIGH (0x11) +#define TCS3414_REGISTER_REDLOW (0x12) +#define TCS3414_REGISTER_REDHIGH (0x13) +#define TCS3414_REGISTER_BLUELOW (0x14) +#define TCS3414_REGISTER_BLUEHIGH (0x15) +#define TCS3414_REGISTER_CLEARLOW (0x16) +#define TCS3414_REGISTER_CLEARHIGH (0x17) + +#define TCS3414_CONTROL_POWERON (0x03) +#define TCS3414_CONTROL_POWEROFF (0x00) + +#define TCS3414_GAIN_GAINMASK (0x30) +#define TCS3414_GAIN_PRESCALARMASK (0x07) + +/**************************************************************************/ +/*! + The Gain setting (bit [5:4] in the GAIN register) multiplies the output + by the specified amount, allowing you to adjust it's sensitivity and + dynamic range. +*/ +/**************************************************************************/ +typedef enum +{ + tcs3414Gain_1 = 0x00, + tcs3414Gain_4 = 0x10, + tcs3414Gain_16 = 0x20, + tcs3414Gain_64 = 0x30 +} +tcs3414Gain_t; + +/**************************************************************************/ +/*! + The Prescalar (bits [2:0] in the GAIN register) divides down the + output by the specified amount, allowing you to adjust it's + sensitivity and dynamic range. +*/ +/**************************************************************************/ +typedef enum +{ + tcs3414Prescalar_1 = 0x00, + tcs3414Prescalar_2 = 0x01, + tcs3414Prescalar_4 = 0x02, + tcs3414Prescalar_8 = 0x03, + tcs3414Prescalar_16 = 0x04, + tcs3414Prescalar_32 = 0x05, + tcs3414Prescalar_64 = 0x06 +} +tcs3414Prescalar_t; + +/**************************************************************************/ +/*! + Possible I2C error messages +*/ +/**************************************************************************/ +typedef enum +{ + TCS3414_ERROR_OK = 0, // Everything executed normally + TCS3414_ERROR_I2CINIT, // Unable to initialise I2C + TCS3414_ERROR_I2CBUSY, // I2C already in use + TCS3414_ERROR_LAST +} +tcs3414Error_e; + +tcs3414Error_e tcs3414Init(void); +tcs3414Error_e tcs3414SetSensitivity(tcs3414Gain_t gain, tcs3414Prescalar_t prescalar); +tcs3414Error_e tcs3414GetRGBL (uint16_t *red, uint16_t *green, uint16_t *blue, uint16_t *clear); +uint32_t tcs3414CalculateCCT (uint16_t red, uint16_t green, uint16_t blue); + +#endif + + diff --git a/drivers/sensors/tsl2561/tsl2561.c b/drivers/sensors/tsl2561/tsl2561.c new file mode 100644 index 0000000..79027b5 --- /dev/null +++ b/drivers/sensors/tsl2561/tsl2561.c @@ -0,0 +1,364 @@ +/**************************************************************************/ +/*! + @file tsl2561.c + @author K. Townsend (microBuilder.eu) + + @brief Drivers for the TAOS TSL2561 I2C digital luminosity sensor + + @section DESCRIPTION + + The TSL2561 is a 16-bit digital luminosity sensor the approximates + the human eye's response to light. It contains one broadband + photodiode that measures visible plus infrared light (channel 0) + and one infrared photodiode (channel 1). + + @section EXAMPLE + + @code + #include "drivers/sensors/tsl2561/tsl2561.h" + ... + uint16_t broadband, ir; + uint32_t lux; + + // Initialise luminosity sensor + tsl2561Init(); + + // Optional ... default setting is 400ms with no gain + // Set timing to 101ms with no gain + tsl2561SetTiming(TSL2561_INTEGRATIONTIME_101MS, TSL2561_GAIN_0X); + + // Check luminosity level and calculate lux + tsl2561GetLuminosity(&broadband, &ir); + lux = tsl2561CalculateLux(broadband, ir); + printf("Broadband: %u, IR: %u, Lux: %d %s", broadband, ir, lux, CFG_PRINTF_NEWLINE); + + @endcode + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "tsl2561.h" +#include "core/systick/systick.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; +extern volatile uint32_t I2CReadLength, I2CWriteLength; + +uint32_t i; + +static bool _tsl2561Initialised = false; +static tsl2561IntegrationTime_t _tsl2561IntegrationTime = TSL2561_INTEGRATIONTIME_402MS; +static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_0X; + +/**************************************************************************/ +/*! + @brief Sends a single command byte over I2C +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561WriteCmd (uint8_t cmd) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 2; + I2CReadLength = 0; + I2CMasterBuffer[0] = TSL2561_ADDRESS; // I2C device address + I2CMasterBuffer[1] = cmd; // Command register + i2cEngine(); + return TSL2561_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Writes an 8 bit values over I2C +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561Write8 (uint8_t reg, uint32_t value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 3; + I2CReadLength = 0; + I2CMasterBuffer[0] = TSL2561_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + I2CMasterBuffer[2] = (value & 0xFF); // Value to write + i2cEngine(); + return TSL2561_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Reads a 16 bit values over I2C +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561Read16(uint8_t reg, uint16_t *value) +{ + // Clear write buffers + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 2; + I2CReadLength = 2; + I2CMasterBuffer[0] = TSL2561_ADDRESS; // I2C device address + I2CMasterBuffer[1] = reg; // Command register + // Append address w/read bit + I2CMasterBuffer[2] = TSL2561_ADDRESS | TSL2561_READBIT; + i2cEngine(); + + // Shift values to create properly formed integer (low byte first) + *value = (I2CSlaveBuffer[0] | (I2CSlaveBuffer[1] << 8)); + + return TSL2561_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Enables the device +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561Enable(void) +{ + if (!_tsl2561Initialised) tsl2561Init(); + + // Enable the device by setting the control bit to 0x03 + return tsl2561Write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON); +} + +/**************************************************************************/ +/*! + @brief Disables the device (putting it in lower power sleep mode) +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561Disable(void) +{ + if (!_tsl2561Initialised) tsl2561Init(); + + // Turn the device off to save power + return tsl2561Write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF); +} + +/**************************************************************************/ +/*! + @brief Initialises the I2C block +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561Init(void) +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + return TSL2561_ERROR_I2CINIT; /* Fatal error */ + } + + _tsl2561Initialised = true; + + // Set default integration time and gain + tsl2561SetTiming(_tsl2561IntegrationTime, _tsl2561Gain); + + // Note: by default, the device is in power down mode on bootup + + return TSL2561_ERROR_OK; +} + +/**************************************************************************/ +/*! + @brief Sets the integration time and gain (controls sensitivity) +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561SetTiming(tsl2561IntegrationTime_t integration, tsl2561Gain_t gain) +{ + if (!_tsl2561Initialised) tsl2561Init(); + + tsl2561Error_t error = TSL2561_ERROR_OK; + + // Enable the device by setting the control bit to 0x03 + error = tsl2561Enable(); + if (error) return error; + + // Turn the device off to save power + error = tsl2561Write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, integration | gain); + if (error) return error; + + // Update value placeholders + _tsl2561IntegrationTime = integration; + _tsl2561Gain = gain; + + // Turn the device off to save power + error = tsl2561Disable(); + if (error) return error; + + return error; +} + +/**************************************************************************/ +/*! + @brief Reads the luminosity on both channels from the TSL2561 +*/ +/**************************************************************************/ +tsl2561Error_t tsl2561GetLuminosity (uint16_t *broadband, uint16_t *ir) +{ + if (!_tsl2561Initialised) tsl2561Init(); + + tsl2561Error_t error = TSL2561_ERROR_OK; + + // Enable the device by setting the control bit to 0x03 + error = tsl2561Enable(); + if (error) return error; + + // Wait x ms for ADC to complete + switch (_tsl2561IntegrationTime) + { + case TSL2561_INTEGRATIONTIME_13MS: + systickDelay(14); + break; + case TSL2561_INTEGRATIONTIME_101MS: + systickDelay(102); + break; + default: + systickDelay(400); + break; + } + + // Reads two byte value from channel 0 (visible + infrared) + error = tsl2561Read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW, broadband); + if (error) return error; + + // Reads two byte value from channel 1 (infrared) + error = tsl2561Read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW, ir); + if (error) return error; + + // Turn the device off to save power + error = tsl2561Disable(); + if (error) return error; + + return error; +} + +/**************************************************************************/ +/*! + @brief Calculates LUX from the supplied ch0 (broadband) and ch1 + (IR) readings +*/ +/**************************************************************************/ +uint32_t tsl2561CalculateLux(uint16_t ch0, uint16_t ch1) +{ + unsigned long chScale; + unsigned long channel1; + unsigned long channel0; + + switch (_tsl2561IntegrationTime) + { + case TSL2561_INTEGRATIONTIME_13MS: + chScale = TSL2561_LUX_CHSCALE_TINT0; + break; + case TSL2561_INTEGRATIONTIME_101MS: + chScale = TSL2561_LUX_CHSCALE_TINT1; + break; + default: // No scaling ... integration time = 402ms + chScale = (1 << TSL2561_LUX_CHSCALE); + break; + } + + // Scale for gain (1x or 16x) + if (!_tsl2561Gain) chScale = chScale << 4; + + // scale the channel values + channel0 = (ch0 * chScale) >> TSL2561_LUX_CHSCALE; + channel1 = (ch1 * chScale) >> TSL2561_LUX_CHSCALE; + + // find the ratio of the channel values (Channel1/Channel0) + unsigned long ratio1 = 0; + if (channel0 != 0) ratio1 = (channel1 << (TSL2561_LUX_RATIOSCALE+1)) / channel0; + + // round the ratio value + unsigned long ratio = (ratio1 + 1) >> 1; + + unsigned int b, m; + +#ifdef TSL2561_PACKAGE_CS + if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C)) + {b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C;} + else if (ratio <= TSL2561_LUX_K2C) + {b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C;} + else if (ratio <= TSL2561_LUX_K3C) + {b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C;} + else if (ratio <= TSL2561_LUX_K4C) + {b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C;} + else if (ratio <= TSL2561_LUX_K5C) + {b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C;} + else if (ratio <= TSL2561_LUX_K6C) + {b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C;} + else if (ratio <= TSL2561_LUX_K7C) + {b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C;} + else if (ratio > TSL2561_LUX_K8C) + {b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C;} +#else + if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T)) + {b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T;} + else if (ratio <= TSL2561_LUX_K2T) + {b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T;} + else if (ratio <= TSL2561_LUX_K3T) + {b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T;} + else if (ratio <= TSL2561_LUX_K4T) + {b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T;} + else if (ratio <= TSL2561_LUX_K5T) + {b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T;} + else if (ratio <= TSL2561_LUX_K6T) + {b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T;} + else if (ratio <= TSL2561_LUX_K7T) + {b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T;} + else if (ratio > TSL2561_LUX_K8T) + {b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T;} +#endif + + unsigned long temp; + temp = ((channel0 * b) - (channel1 * m)); + + // do not allow negative lux value + if (temp < 0) temp = 0; + + // round lsb (2^(LUX_SCALE-1)) + temp += (1 << (TSL2561_LUX_LUXSCALE-1)); + + // strip off fractional portion + uint32_t lux = temp >> TSL2561_LUX_LUXSCALE; + + // Signal I2C had no errors + return lux; +} diff --git a/drivers/sensors/tsl2561/tsl2561.h b/drivers/sensors/tsl2561/tsl2561.h new file mode 100644 index 0000000..9cad43a --- /dev/null +++ b/drivers/sensors/tsl2561/tsl2561.h @@ -0,0 +1,163 @@ +/**************************************************************************/ +/*! + @file tsl2561.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _TSL2561_H_ +#define _TSL2561_H_ + +#include "projectconfig.h" +#include "core/i2c/i2c.h" + +#define TSL2561_PACKAGE_CS // Lux calculations differ slightly for CS package +// #define TSL2561_PACKAGE_T_FN_CL + +#define TSL2561_ADDRESS (0x72) // 0111001 shifted left 1 bit = 0x72 (ADDR = GND or floating) +#define TSL2561_READBIT (0x01) + +#define TSL2561_COMMAND_BIT (0x80) // Must be 1 +#define TSL2561_CLEAR_BIT (0x40) // Clears any pending interrupt (write 1 to clear) +#define TSL2561_WORD_BIT (0x20) // 1 = read/write word (rather than byte) +#define TSL2561_BLOCK_BIT (0x10) // 1 = using block read/write + +#define TSL2561_CONTROL_POWERON (0x03) +#define TSL2561_CONTROL_POWEROFF (0x00) + +#define TSL2561_LUX_LUXSCALE (14) // Scale by 2^14 +#define TSL2561_LUX_RATIOSCALE (9) // Scale ratio by 2^9 +#define TSL2561_LUX_CHSCALE (10) // Scale channel values by 2^10 +#define TSL2561_LUX_CHSCALE_TINT0 (0x7517) // 322/11 * 2^TSL2561_LUX_CHSCALE +#define TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) // 322/81 * 2^TSL2561_LUX_CHSCALE + +// T, FN and CL package values +#define TSL2561_LUX_K1T (0x0040) // 0.125 * 2^RATIO_SCALE +#define TSL2561_LUX_B1T (0x01f2) // 0.0304 * 2^LUX_SCALE +#define TSL2561_LUX_M1T (0x01be) // 0.0272 * 2^LUX_SCALE +#define TSL2561_LUX_K2T (0x0080) // 0.250 * 2^RATIO_SCALE +#define TSL2561_LUX_B2T (0x0214) // 0.0325 * 2^LUX_SCALE +#define TSL2561_LUX_M2T (0x02d1) // 0.0440 * 2^LUX_SCALE +#define TSL2561_LUX_K3T (0x00c0) // 0.375 * 2^RATIO_SCALE +#define TSL2561_LUX_B3T (0x023f) // 0.0351 * 2^LUX_SCALE +#define TSL2561_LUX_M3T (0x037b) // 0.0544 * 2^LUX_SCALE +#define TSL2561_LUX_K4T (0x0100) // 0.50 * 2^RATIO_SCALE +#define TSL2561_LUX_B4T (0x0270) // 0.0381 * 2^LUX_SCALE +#define TSL2561_LUX_M4T (0x03fe) // 0.0624 * 2^LUX_SCALE +#define TSL2561_LUX_K5T (0x0138) // 0.61 * 2^RATIO_SCALE +#define TSL2561_LUX_B5T (0x016f) // 0.0224 * 2^LUX_SCALE +#define TSL2561_LUX_M5T (0x01fc) // 0.0310 * 2^LUX_SCALE +#define TSL2561_LUX_K6T (0x019a) // 0.80 * 2^RATIO_SCALE +#define TSL2561_LUX_B6T (0x00d2) // 0.0128 * 2^LUX_SCALE +#define TSL2561_LUX_M6T (0x00fb) // 0.0153 * 2^LUX_SCALE +#define TSL2561_LUX_K7T (0x029a) // 1.3 * 2^RATIO_SCALE +#define TSL2561_LUX_B7T (0x0018) // 0.00146 * 2^LUX_SCALE +#define TSL2561_LUX_M7T (0x0012) // 0.00112 * 2^LUX_SCALE +#define TSL2561_LUX_K8T (0x029a) // 1.3 * 2^RATIO_SCALE +#define TSL2561_LUX_B8T (0x0000) // 0.000 * 2^LUX_SCALE +#define TSL2561_LUX_M8T (0x0000) // 0.000 * 2^LUX_SCALE + +// CS package values +#define TSL2561_LUX_K1C (0x0043) // 0.130 * 2^RATIO_SCALE +#define TSL2561_LUX_B1C (0x0204) // 0.0315 * 2^LUX_SCALE +#define TSL2561_LUX_M1C (0x01ad) // 0.0262 * 2^LUX_SCALE +#define TSL2561_LUX_K2C (0x0085) // 0.260 * 2^RATIO_SCALE +#define TSL2561_LUX_B2C (0x0228) // 0.0337 * 2^LUX_SCALE +#define TSL2561_LUX_M2C (0x02c1) // 0.0430 * 2^LUX_SCALE +#define TSL2561_LUX_K3C (0x00c8) // 0.390 * 2^RATIO_SCALE +#define TSL2561_LUX_B3C (0x0253) // 0.0363 * 2^LUX_SCALE +#define TSL2561_LUX_M3C (0x0363) // 0.0529 * 2^LUX_SCALE +#define TSL2561_LUX_K4C (0x010a) // 0.520 * 2^RATIO_SCALE +#define TSL2561_LUX_B4C (0x0282) // 0.0392 * 2^LUX_SCALE +#define TSL2561_LUX_M4C (0x03df) // 0.0605 * 2^LUX_SCALE +#define TSL2561_LUX_K5C (0x014d) // 0.65 * 2^RATIO_SCALE +#define TSL2561_LUX_B5C (0x0177) // 0.0229 * 2^LUX_SCALE +#define TSL2561_LUX_M5C (0x01dd) // 0.0291 * 2^LUX_SCALE +#define TSL2561_LUX_K6C (0x019a) // 0.80 * 2^RATIO_SCALE +#define TSL2561_LUX_B6C (0x0101) // 0.0157 * 2^LUX_SCALE +#define TSL2561_LUX_M6C (0x0127) // 0.0180 * 2^LUX_SCALE +#define TSL2561_LUX_K7C (0x029a) // 1.3 * 2^RATIO_SCALE +#define TSL2561_LUX_B7C (0x0037) // 0.00338 * 2^LUX_SCALE +#define TSL2561_LUX_M7C (0x002b) // 0.00260 * 2^LUX_SCALE +#define TSL2561_LUX_K8C (0x029a) // 1.3 * 2^RATIO_SCALE +#define TSL2561_LUX_B8C (0x0000) // 0.000 * 2^LUX_SCALE +#define TSL2561_LUX_M8C (0x0000) // 0.000 * 2^LUX_SCALE + +enum +{ + TSL2561_REGISTER_CONTROL = 0x00, + TSL2561_REGISTER_TIMING = 0x01, + TSL2561_REGISTER_THRESHHOLDL_LOW = 0x02, + TSL2561_REGISTER_THRESHHOLDL_HIGH = 0x03, + TSL2561_REGISTER_THRESHHOLDH_LOW = 0x04, + TSL2561_REGISTER_THRESHHOLDH_HIGH = 0x05, + TSL2561_REGISTER_INTERRUPT = 0x06, + TSL2561_REGISTER_CRC = 0x08, + TSL2561_REGISTER_ID = 0x0A, + TSL2561_REGISTER_CHAN0_LOW = 0x0C, + TSL2561_REGISTER_CHAN0_HIGH = 0x0D, + TSL2561_REGISTER_CHAN1_LOW = 0x0E, + TSL2561_REGISTER_CHAN1_HIGH = 0x0F +}; + +typedef enum +{ + TSL2561_INTEGRATIONTIME_13MS = 0x00, // 13.7ms + TSL2561_INTEGRATIONTIME_101MS = 0x01, // 101ms + TSL2561_INTEGRATIONTIME_402MS = 0x02 // 402ms +} +tsl2561IntegrationTime_t; + +typedef enum +{ + TSL2561_GAIN_0X = 0x00, // No gain + TSL2561_GAIN_16X = 0x10, // 16x gain +} +tsl2561Gain_t; + +typedef enum +{ + TSL2561_ERROR_OK = 0, // Everything executed normally + TSL2561_ERROR_I2CINIT, // Unable to initialise I2C + TSL2561_ERROR_I2CBUSY, // I2C already in use + TSL2561_ERROR_LAST +} +tsl2561Error_t; + +tsl2561Error_t tsl2561Init(void); +tsl2561Error_t tsl2561SetTiming(tsl2561IntegrationTime_t integration, tsl2561Gain_t gain); +tsl2561Error_t tsl2561GetLuminosity (uint16_t *broadband, uint16_t *ir); +uint32_t tsl2561CalculateLux(uint16_t ch0, uint16_t ch1); + +#endif + + diff --git a/lpc134x-vcom.inf b/lpc134x-vcom.inf new file mode 100644 index 0000000..c5a5384 --- /dev/null +++ b/lpc134x-vcom.inf @@ -0,0 +1,65 @@ +; +; Keil - An ARM Company Comunication Device Class driver installation file +; (C)2007 Copyright +; + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%Keil% +;LayoutFile=layout.inf +DriverVer=01/06/07 + +[Manufacturer] +%Keil%=DeviceList + +[DestinationDirs] +DefaultDestDir=12 + +[SourceDisksFiles] + +[SourceDisksNames] + +[DeviceList] +%DESCRIPTION%=LPC134xUSB, USB\VID_239A&PID_1002 + +;------------------------------------------------------------------------------ +; Windows 2000/XP Sections +;------------------------------------------------------------------------------ + +[LPC134xUSB.nt] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles +AddReg=LPC134xUSB.nt.AddReg + +[DriverCopyFiles] +usbser.sys,,,0x20 + +[LPC134xUSB.nt.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[LPC134xUSB.nt.Services] +include=mdmcpq.inf +AddService=usbser, 0x00000002, DriverService + + +[LPC134xUSB.nt.HW] +include=mdmcpq.inf + +[DriverService] +DisplayName=%DESCRIPTION% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + +;------------------------------------------------------------------------------ +; String Definitions +;------------------------------------------------------------------------------ + +[Strings] +NXP="NXP - Founded by Philips" +DESCRIPTION="LPC134x USB VCom Port" diff --git a/lpc134x.h b/lpc134x.h new file mode 100644 index 0000000..e69af30 --- /dev/null +++ b/lpc134x.h @@ -0,0 +1,3534 @@ +/**************************************************************************/ +/*! + @file lpc134x.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section DESCRIPTION + + LPC1343 header file, based on V0.10 of the LPC1343 User Manual. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _LPC134X_H_ +#define _LPC134X_H_ + +#include "sysdefs.h" +#include "projectconfig.h" + +/*############################################################################## +## System Control Block +##############################################################################*/ + +#define SCB_BASE_ADDRESS (*(pREG32 (0x40048000))) // System control block base address + +#define SCB_MEMREMAP (*(pREG32 (0x40048000))) // System memory remap +#define SCB_PRESETCTRL (*(pREG32 (0x40048004))) // Peripheral reset control +#define SCB_PLLCTRL (*(pREG32 (0x40048008))) // System PLL control +#define SCB_PLLSTAT (*(pREG32 (0x4004800C))) // System PLL status +#define SCB_USBPLLCTRL (*(pREG32 (0x40048010))) // USB PLL control +#define SCB_USBPLLSTAT (*(pREG32 (0x40048014))) // USB PLL status +#define SCB_SYSOSCCTRL (*(pREG32 (0x40048020))) // System oscillator control +#define SCB_WDTOSCCTRL (*(pREG32 (0x40048024))) // Watchdog oscillator control +#define SCB_IRCCTRL (*(pREG32 (0x40048028))) // IRC control +#define SCB_RESETSTAT (*(pREG32 (0x40048030))) // System reset status register +#define SCB_PLLCLKSEL (*(pREG32 (0x40048040))) // System PLL clock source select +#define SCB_PLLCLKUEN (*(pREG32 (0x40048044))) // System PLL clock source update enable +#define SCB_USBPLLCLKSEL (*(pREG32 (0x40048048))) // USB PLL clock source select +#define SCB_USBPLLCLKUEN (*(pREG32 (0x4004804C))) // USB PLL clock source update enable +#define SCB_MAINCLKSEL (*(pREG32 (0x40048070))) // Main clock source select +#define SCB_MAINCLKUEN (*(pREG32 (0x40048074))) // Main clock source update enable +#define SCB_SYSAHBCLKDIV (*(pREG32 (0x40048078))) // System AHB clock divider +#define SCB_SYSAHBCLKCTRL (*(pREG32 (0x40048080))) // System AHB clock control +#define SCB_SSP0CLKDIV (*(pREG32 (0x40048094))) // SSP0 clock divider +#define SCB_UARTCLKDIV (*(pREG32 (0x40048098))) // UART clock divider +#define SCB_SYSTICKCLKDIV (*(pREG32 (0x400480B0))) // System tick clock divider +#define SCB_USBCLKSEL (*(pREG32 (0x400480C0))) // USB clock source select +#define SCB_USBCLKUEN (*(pREG32 (0x400480C4))) // USB clock source update enable +#define SCB_USBCLKDIV (*(pREG32 (0x400480C8))) // USB clock divider +#define SCB_WDTCLKSEL (*(pREG32 (0x400480D0))) // Watchdog clock source select +#define SCB_WDTCLKUEN (*(pREG32 (0x400480D4))) // Watchdog clock source update enable +#define SCB_WDTCLKDIV (*(pREG32 (0x400480D8))) // Watchdog clock divider +#define SCB_CLKOUTCLKSEL (*(pREG32 (0x400480E0))) // CLKOUT clock source select +#define SCB_CLKOUTCLKUEN (*(pREG32 (0x400480E4))) // CLKOUT clock source update enable +#define SCB_CLKOUTCLKDIV (*(pREG32 (0x400480E8))) // CLKOUT clock divider +#define SCB_PIOPORCAP0 (*(pREG32 (0x40048100))) // POR captured PIO status 0 +#define SCB_PIOPORCAP1 (*(pREG32 (0x40048104))) // POR captured PIO status 1 +#define SCB_BODCTRL (*(pREG32 (0x40048150))) // Brown-out detector control +#define SCB_SYSTICKCCAL (*(pREG32 (0x40048158))) // System tick counter calibration +#define SCB_STARTAPRP0 (*(pREG32 (0x40048200))) // Start logic edge control register 0; bottom 32 interrupts +#define SCB_STARTERP0 (*(pREG32 (0x40048204))) // Start logic signal enable register 0; bottom 32 interrupts +#define SCB_STARTRSRP0CLR (*(pREG32 (0x40048208))) // Start logic reset register 0; bottom 32 interrupts +#define SCB_STARTSRP0 (*(pREG32 (0x4004820C))) // Start logic status register 0; bottom 32 interrupts +#define SCB_STARTAPRP1 (*(pREG32 (0x40048210))) // Start logic edge control register 1; top 8 interrupts +#define SCB_STARTERP1 (*(pREG32 (0x40048214))) // Start logic signal enable register 1; top 8 interrupts +#define SCB_STARTRSRP1CLR (*(pREG32 (0x40048218))) // Start logic reset register 1; top 8 interrupts +#define SCB_STARTSRP1 (*(pREG32 (0x4004821C))) // Start logic status register 1; top 8 interrupts +#define SCB_PDSLEEPCFG (*(pREG32 (0x40048230))) // Power-down states in Deep-sleep mode +#define SCB_PDAWAKECFG (*(pREG32 (0x40048234))) // Power-down states after wake-up from Deep-sleep mode +#define SCB_PDRUNCFG (*(pREG32 (0x40048238))) // Power-down configuration register +#define SCB_DEVICEID (*(pREG32 (0x400483F4))) // Device ID +#define SCB_MMFAR (*(pREG32 (0xE000ED34))) // Memory Manage Address Register (MMAR) +#define SCB_BFAR (*(pREG32 (0xE000ED38))) // Bus Fault Manage Address Register (BFAR) +#define SCB_DEMCR (*(pREG32 (0xE000EDFC))) + +/* CPU ID Base Register */ +#define SCB_CPUID (*(pREG32 (0xE000ED00))) +#define SCB_CPUID_REVISION_MASK ((unsigned int) 0x0000000F) // Revision Code +#define SCB_CPUID_PARTNO_MASK ((unsigned int) 0x0000FFF0) // Part Number +#define SCB_CPUID_CONSTANT_MASK ((unsigned int) 0x000F0000) // Constant +#define SCB_CPUID_VARIANT_MASK ((unsigned int) 0x00F00000) // Variant +#define SCB_CPUID_IMPLEMENTER_MASK ((unsigned int) 0xFF000000) // Implementer + +/* System Control Register */ + +#define SCB_SCR (*(pREG32 (0xE000ED10))) +#define SCB_SCR_SLEEPONEXIT_MASK ((unsigned int) 0x00000002) // Enable sleep on exit +#define SCB_SCR_SLEEPONEXIT ((unsigned int) 0x00000002) +#define SCB_SCR_SLEEPDEEP_MASK ((unsigned int) 0x00000004) +#define SCB_SCR_SLEEPDEEP ((unsigned int) 0x00000004) // Enable deep sleep +#define SCB_SCR_SEVONPEND_MASK ((unsigned int) 0x00000010) // Wake up from WFE is new int is pended regardless of priority +#define SCB_SCR_SEVONPEND ((unsigned int) 0x00000010) + +/* Application Interrupt and Reset Control Register */ + +#define SCB_AIRCR (*(pREG32 (0xE000ED0C))) +#define SCB_AIRCR_VECTKEY_VALUE ((unsigned int) 0x05FA0000) // Vect key needs to be set to 05FA for reset to work +#define SCB_AIRCR_VECTKEY_MASK ((unsigned int) 0xFFFF0000) +#define SCB_AIRCR_ENDIANESS ((unsigned int) 0x00008000) // Read Endianness (1=Big, 0=Little) +#define SCB_AIRCR_ENDIANESS_MASK ((unsigned int) 0x00008000) +#define SCB_AIRCR_PRIGROUP ((unsigned int) 0x00000700) +#define SCB_AIRCR_PRIGROUP_MASK ((unsigned int) 0x00000700) +#define SCB_AIRCR_SYSRESETREQ ((unsigned int) 0x00000004) // Request system reset +#define SCB_AIRCR_SYSRESETREQ_MASK ((unsigned int) 0x00000004) +#define SCB_AIRCR_VECTCLRACTIVE ((unsigned int) 0x00000002) // Used to prevent accidental reset +#define SCB_AIRCR_VECTCLRACTIVE_MASK ((unsigned int) 0x00000002) +#define SCB_AIRCR_VECTRESET ((unsigned int) 0x00000001) +#define SCB_AIRCR_VECTRESET_MASK ((unsigned int) 0x00000001) + +/* Memory Management Fault Status Register */ + +#define SCB_MMFSR (*(pREG32 (0xE000ED28))) +#define SCB_MMFSR_IACCVIOL_MASK ((unsigned int) 0x00000001) // Instruction access violation +#define SCB_MMFSR_IACCVIOL ((unsigned int) 0x00000001) +#define SCB_MMFSR_DACCVIOL_MASK ((unsigned int) 0x00000002) // Data access violation +#define SCB_MMFSR_DACCVIOL ((unsigned int) 0x00000002) +#define SCB_MMFSR_MUNSTKERR_MASK ((unsigned int) 0x00000008) // Unstacking error +#define SCB_MMFSR_MUNSTKERR ((unsigned int) 0x00000008) +#define SCB_MMFSR_MSTKERR_MASK ((unsigned int) 0x00000010) // Stacking error +#define SCB_MMFSR_MSTKERR ((unsigned int) 0x00000010) +#define SCB_MMFSR_MMARVALID_MASK ((unsigned int) 0x00000080) // Indicates MMAR is valid +#define SCB_MMFSR_MMARVALID ((unsigned int) 0x00000080) + +/* Bus Fault Status Register */ + +#define SCB_BFSR (*(pREG32 (0xE000ED29))) +#define SCB_BFSR_IBUSERR_MASK ((unsigned int) 0x00000001) // Instruction access violation +#define SCB_BFSR_IBUSERR ((unsigned int) 0x00000001) +#define SCB_BFSR_PRECISERR_MASK ((unsigned int) 0x00000002) // Precise data access violation +#define SCB_BFSR_PRECISERR ((unsigned int) 0x00000002) +#define SCB_BFSR_IMPRECISERR_MASK ((unsigned int) 0x00000004) // Imprecise data access violation +#define SCB_BFSR_IMPRECISERR ((unsigned int) 0x00000004) +#define SCB_BFSR_UNSTKERR_MASK ((unsigned int) 0x00000008) // Unstacking error +#define SCB_BFSR_UNSTKERR ((unsigned int) 0x00000008) +#define SCB_BFSR_STKERR_MASK ((unsigned int) 0x00000010) // Stacking error +#define SCB_BFSR_STKERR ((unsigned int) 0x00000010) +#define SCB_BFSR_BFARVALID_MASK ((unsigned int) 0x00000080) // Indicates BFAR is valid +#define SCB_BFSR_BFARVALID ((unsigned int) 0x00000080) + +/* Usage Fault Status Register */ + +#define SCB_UFSR (*(pREG32 (0xE000ED2A))) +#define SCB_UFSR_UNDEFINSTR_MASK ((unsigned int) 0x00000001) // Attempt to execute an undefined instruction +#define SCB_UFSR_UNDEFINSTR ((unsigned int) 0x00000001) +#define SCB_UFSR_INVSTATE_MASK ((unsigned int) 0x00000002) // Attempt to switch to invalid state (i.e. ARM) +#define SCB_UFSR_INVSTATE ((unsigned int) 0x00000002) +#define SCB_UFSR_INVPC_MASK ((unsigned int) 0x00000004) // Attempt to do exception with bad value in EXC_RETURN number +#define SCB_UFSR_INVPC ((unsigned int) 0x00000004) +#define SCB_UFSR_NOCP_MASK ((unsigned int) 0x00000008) // Attempt to execute a coprocessor instruction +#define SCB_UFSR_NOCP ((unsigned int) 0x00000008) +#define SCB_UFSR_UNALIGNED_MASK ((unsigned int) 0x00000100) // Unaligned access +#define SCB_UFSR_UNALIGNED ((unsigned int) 0x00000100) +#define SCB_UFSR_DIVBYZERO_MASK ((unsigned int) 0x00000200) // Divide by zero +#define SCB_UFSR_DIVBYZERO ((unsigned int) 0x00000200) + +/* Hard Fault Status Register */ + +#define SCB_HFSR (*(pREG32 (0xE000ED2C))) +#define SCB_HFSR_VECTTBL_MASK ((unsigned int) 0x00000002) // Hard fault caused by failed vector fetch +#define SCB_HFSR_VECTTBL ((unsigned int) 0x00000002) +#define SCB_HFSR_FORCED_MASK ((unsigned int) 0x40000000) // Hard fault taken because of bus/mem man/usage fault +#define SCB_HFSR_FORCED ((unsigned int) 0x40000000) +#define SCB_HFSR_DEBUGEVT_MASK ((unsigned int) 0x80000000) // Hard fault triggered by debug event +#define SCB_HFSR_DEBUGEVT ((unsigned int) 0x80000000) + +/* Debug Fault Status Register */ + +#define SCB_DFSR (*(pREG32 (0xE000ED30))) +#define SCB_DFSR_HALTED_MASK ((unsigned int) 0x00000001) // Halt requested in NVIC +#define SCB_DFSR_HALTED ((unsigned int) 0x00000001) +#define SCB_DFSR_BKPT_MASK ((unsigned int) 0x00000002) // BKPT instruction executed +#define SCB_DFSR_BKPT ((unsigned int) 0x00000002) +#define SCB_DFSR_DWTTRAP_MASK ((unsigned int) 0x00000004) // DWT match occurred +#define SCB_DFSR_DWTTRAP ((unsigned int) 0x00000004) +#define SCB_DFSR_VCATCH_MASK ((unsigned int) 0x00000008) // Vector fetch occurred +#define SCB_DFSR_VCATCH ((unsigned int) 0x00000008) +#define SCB_DFSR_EXTERNAL_MASK ((unsigned int) 0x00000010) // EDBGRQ signal asserted +#define SCB_DFSR_EXTERNAL ((unsigned int) 0x00000010) + +/* SCB_MEMREMAP (System memory remap register) + The system memory remap register selects whether the ARM interrupt vectors are read + from the boot ROM, the flash, or the SRAM. */ + +#define SCB_MEMREMAP_MODE_BOOTLOADER ((unsigned int) 0x00000000) // Interrupt vectors are remapped to Boot ROM +#define SCB_MEMREMAP_MODE_RAM ((unsigned int) 0x00000001) // Interrupt vectors are remapped to Static ROM +#define SCB_MEMREMAP_MODE_FLASH ((unsigned int) 0x00000002) // Interrupt vectors are not remapped and reside in Flash +#define SCB_MEMREMAP_MASK ((unsigned int) 0x00000003) + +/* PRESETCTRL (Peripheral reset control register) */ + +#define SCB_PRESETCTRL_SSP0_RESETENABLED ((unsigned int) 0x00000000) +#define SCB_PRESETCTRL_SSP0_RESETDISABLED ((unsigned int) 0x00000001) +#define SCB_PRESETCTRL_SSP0_MASK ((unsigned int) 0x00000001) +#define SCB_PRESETCTRL_I2C_RESETENABLED ((unsigned int) 0x00000000) +#define SCB_PRESETCTRL_I2C_RESETDISABLED ((unsigned int) 0x00000002) +#define SCB_PRESETCTRL_I2C_MASK ((unsigned int) 0x00000002) + +/* SYSPLLCTRL (System PLL control register) + This register connects and enables the system PLL and configures the PLL multiplier and + divider values. The PLL accepts an input frequency from 10 MHz to 25 MHz from various + clock sources. The input frequency is multiplied up to a high frequency, then divided down + to provide the actual clock used by the CPU, peripherals, and optionally the USB + subsystem. Note that the USB subsystem has its own dedicated PLL. The PLL can + produce a clock up to the maximum allowed for the CPU, which is 72 MHz. */ + +#define SCB_PLLCTRL_MULT_1 ((unsigned int) 0x00000000) +#define SCB_PLLCTRL_MULT_2 ((unsigned int) 0x00000001) +#define SCB_PLLCTRL_MULT_3 ((unsigned int) 0x00000002) +#define SCB_PLLCTRL_MULT_4 ((unsigned int) 0x00000003) +#define SCB_PLLCTRL_MULT_5 ((unsigned int) 0x00000004) +#define SCB_PLLCTRL_MULT_6 ((unsigned int) 0x00000005) +#define SCB_PLLCTRL_MULT_7 ((unsigned int) 0x00000006) +#define SCB_PLLCTRL_MULT_8 ((unsigned int) 0x00000007) +#define SCB_PLLCTRL_MULT_9 ((unsigned int) 0x00000008) +#define SCB_PLLCTRL_MULT_10 ((unsigned int) 0x00000009) +#define SCB_PLLCTRL_MULT_11 ((unsigned int) 0x0000000A) +#define SCB_PLLCTRL_MULT_12 ((unsigned int) 0x0000000B) +#define SCB_PLLCTRL_MULT_13 ((unsigned int) 0x0000000C) +#define SCB_PLLCTRL_MULT_14 ((unsigned int) 0x0000000D) +#define SCB_PLLCTRL_MULT_15 ((unsigned int) 0x0000000E) +#define SCB_PLLCTRL_MULT_16 ((unsigned int) 0x0000000F) +#define SCB_PLLCTRL_MULT_17 ((unsigned int) 0x00000010) +#define SCB_PLLCTRL_MULT_18 ((unsigned int) 0x00000011) +#define SCB_PLLCTRL_MULT_19 ((unsigned int) 0x00000012) +#define SCB_PLLCTRL_MULT_20 ((unsigned int) 0x00000013) +#define SCB_PLLCTRL_MULT_21 ((unsigned int) 0x00000014) +#define SCB_PLLCTRL_MULT_22 ((unsigned int) 0x00000015) +#define SCB_PLLCTRL_MULT_23 ((unsigned int) 0x00000016) +#define SCB_PLLCTRL_MULT_24 ((unsigned int) 0x00000017) +#define SCB_PLLCTRL_MULT_25 ((unsigned int) 0x00000018) +#define SCB_PLLCTRL_MULT_26 ((unsigned int) 0x00000019) +#define SCB_PLLCTRL_MULT_27 ((unsigned int) 0x0000001A) +#define SCB_PLLCTRL_MULT_28 ((unsigned int) 0x0000001B) +#define SCB_PLLCTRL_MULT_29 ((unsigned int) 0x0000001C) +#define SCB_PLLCTRL_MULT_30 ((unsigned int) 0x0000001D) +#define SCB_PLLCTRL_MULT_31 ((unsigned int) 0x0000001E) +#define SCB_PLLCTRL_MULT_32 ((unsigned int) 0x0000001F) +#define SCB_PLLCTRL_MULT_MASK ((unsigned int) 0x0000001F) +#define SCB_PLLCTRL_DIV_2 ((unsigned int) 0x00000000) +#define SCB_PLLCTRL_DIV_4 ((unsigned int) 0x00000020) +#define SCB_PLLCTRL_DIV_8 ((unsigned int) 0x00000040) +#define SCB_PLLCTRL_DIV_16 ((unsigned int) 0x00000060) +#define SCB_PLLCTRL_DIV_BIT (5) +#define SCB_PLLCTRL_DIV_MASK ((unsigned int) 0x00000060) +#define SCB_PLLCTRL_DIRECT_MASK ((unsigned int) 0x00000080) // Direct CCO clock output control +#define SCB_PLLCTRL_BYPASS_MASK ((unsigned int) 0x00000100) // Input clock bypass control +#define SCB_PLLCTRL_MASK ((unsigned int) 0x000001FF) + +/* SYSPLLSTAT (System PLL status register) + This register is a Read-only register and supplies the PLL lock status */ + +#define SCB_PLLSTAT_LOCK ((unsigned int) 0x00000001) // 0 = PLL not locked, 1 = PLL locked +#define SCB_PLLSTAT_LOCK_MASK ((unsigned int) 0x00000001) + +/* USBPLLCTRL (USB PLL control register) + The USB PLL is identical to the system PLL and is used to provide a dedicated clock to + the USB block if available. The USB PLL should be always connected to the system + oscillator to produce a stable USB clock. */ + +#define SCB_USBPLLCTRL_MULT_1 ((unsigned int) 0x00000000) +#define SCB_USBPLLCTRL_MULT_2 ((unsigned int) 0x00000001) +#define SCB_USBPLLCTRL_MULT_3 ((unsigned int) 0x00000002) +#define SCB_USBPLLCTRL_MULT_4 ((unsigned int) 0x00000003) +#define SCB_USBPLLCTRL_MULT_5 ((unsigned int) 0x00000004) +#define SCB_USBPLLCTRL_MULT_6 ((unsigned int) 0x00000005) +#define SCB_USBPLLCTRL_MULT_7 ((unsigned int) 0x00000006) +#define SCB_USBPLLCTRL_MULT_8 ((unsigned int) 0x00000007) +#define SCB_USBPLLCTRL_MULT_9 ((unsigned int) 0x00000008) +#define SCB_USBPLLCTRL_MULT_10 ((unsigned int) 0x00000009) +#define SCB_USBPLLCTRL_MULT_11 ((unsigned int) 0x0000000A) +#define SCB_USBPLLCTRL_MULT_12 ((unsigned int) 0x0000000B) +#define SCB_USBPLLCTRL_MULT_13 ((unsigned int) 0x0000000C) +#define SCB_USBPLLCTRL_MULT_14 ((unsigned int) 0x0000000D) +#define SCB_USBPLLCTRL_MULT_15 ((unsigned int) 0x0000000E) +#define SCB_USBPLLCTRL_MULT_16 ((unsigned int) 0x0000000F) +#define SCB_USBPLLCTRL_MULT_17 ((unsigned int) 0x00000010) +#define SCB_USBPLLCTRL_MULT_18 ((unsigned int) 0x00000011) +#define SCB_USBPLLCTRL_MULT_19 ((unsigned int) 0x00000012) +#define SCB_USBPLLCTRL_MULT_20 ((unsigned int) 0x00000013) +#define SCB_USBPLLCTRL_MULT_21 ((unsigned int) 0x00000014) +#define SCB_USBPLLCTRL_MULT_22 ((unsigned int) 0x00000015) +#define SCB_USBPLLCTRL_MULT_23 ((unsigned int) 0x00000016) +#define SCB_USBPLLCTRL_MULT_24 ((unsigned int) 0x00000017) +#define SCB_USBPLLCTRL_MULT_25 ((unsigned int) 0x00000018) +#define SCB_USBPLLCTRL_MULT_26 ((unsigned int) 0x00000019) +#define SCB_USBPLLCTRL_MULT_27 ((unsigned int) 0x0000001A) +#define SCB_USBPLLCTRL_MULT_28 ((unsigned int) 0x0000001B) +#define SCB_USBPLLCTRL_MULT_29 ((unsigned int) 0x0000001C) +#define SCB_USBPLLCTRL_MULT_30 ((unsigned int) 0x0000001D) +#define SCB_USBPLLCTRL_MULT_31 ((unsigned int) 0x0000001E) +#define SCB_USBPLLCTRL_MULT_32 ((unsigned int) 0x0000001F) +#define SCB_USBPLLCTRL_MULT_MASK ((unsigned int) 0x0000001F) +#define SCB_USBPLLCTRL_DIV_2 ((unsigned int) 0x00000000) +#define SCB_USBPLLCTRL_DIV_4 ((unsigned int) 0x00000020) +#define SCB_USBPLLCTRL_DIV_8 ((unsigned int) 0x00000040) +#define SCB_USBPLLCTRL_DIV_16 ((unsigned int) 0x00000060) +#define SCB_USBPLLCTRL_DIV_BIT (5) +#define SCB_USBPLLCTRL_DIV_MASK ((unsigned int) 0x00000060) +#define SCB_USBPLLCTRL_DIRECT_MASK ((unsigned int) 0x00000080) // Direct CCO clock output control +#define SCB_USBPLLCTRL_BYPASS_MASK ((unsigned int) 0x00000100) // Input clock bypass control +#define SCB_USBPLLCTRL_MASK ((unsigned int) 0x000001FF) + +/* USBPLLSTAT (System PLL status register) + This register is a Read-only register and supplies the PLL lock status. */ + +#define SCB_USBPLLSTAT_LOCK ((unsigned int) 0x00000001) // 0 = PLL not locked, 1 = PLL locked +#define SCB_USBPLLSTAT_LOCK_MASK ((unsigned int) 0x00000001) + +/* SYSOSCCTRL (System oscillator control register) + This register configures the frequency range for the system oscillator. */ + +#define SCB_SYSOSCCTRL_BYPASS_DISABLED ((unsigned int) 0x00000000) // Oscillator is not bypassed. +#define SCB_SYSOSCCTRL_BYPASS_ENABLED ((unsigned int) 0x00000001) // Bypass enabled +#define SCB_SYSOSCCTRL_BYPASS_MASK ((unsigned int) 0x00000001) +#define SCB_SYSOSCCTRL_FREQRANGE_1TO20MHZ ((unsigned int) 0x00000000) // 1-20 MHz frequency range +#define SCB_SYSOSCCTRL_FREQRANGE_15TO25MHZ ((unsigned int) 0x00000002) // 15-25 MHz frequency range +#define SCB_SYSOSCCTRL_FREQRANGE_MASK ((unsigned int) 0x00000002) + +/* WDTOSCTRL (Watchdog oscillator control register) + This register configures the watchdog oscillator. The oscillator consists of an analog and a + digital part. The analog part contains the oscillator function and generates an analog clock + (Fclkana). With the digital part, the analog output clock (Fclkana) can be divided to the + required output clock frequency wdt_osc_clk. The analog output frequency (Fclkana) can + be adjusted with the FREQSEL bits between 500 kHz and 3.7 MHz. With the digital part + Fclkana will be divided (divider ratios = 2, 4,...,64) to wdt_osc_clk using the DIVSEL bits.*/ + +#define SCB_WDTOSCCTRL_DIVSEL_DIV2 ((unsigned int) 0x00000000) // Reset value +#define SCB_WDTOSCCTRL_DIVSEL_DIV4 ((unsigned int) 0x00000001) +#define SCB_WDTOSCCTRL_DIVSEL_DIV6 ((unsigned int) 0x00000002) +#define SCB_WDTOSCCTRL_DIVSEL_DIV8 ((unsigned int) 0x00000003) +#define SCB_WDTOSCCTRL_DIVSEL_DIV10 ((unsigned int) 0x00000004) +#define SCB_WDTOSCCTRL_DIVSEL_DIV12 ((unsigned int) 0x00000005) +#define SCB_WDTOSCCTRL_DIVSEL_DIV14 ((unsigned int) 0x00000006) +#define SCB_WDTOSCCTRL_DIVSEL_DIV16 ((unsigned int) 0x00000007) +#define SCB_WDTOSCCTRL_DIVSEL_DIV18 ((unsigned int) 0x00000008) +#define SCB_WDTOSCCTRL_DIVSEL_DIV20 ((unsigned int) 0x00000009) +#define SCB_WDTOSCCTRL_DIVSEL_DIV22 ((unsigned int) 0x0000000A) +#define SCB_WDTOSCCTRL_DIVSEL_DIV24 ((unsigned int) 0x0000000B) +#define SCB_WDTOSCCTRL_DIVSEL_DIV26 ((unsigned int) 0x0000000C) +#define SCB_WDTOSCCTRL_DIVSEL_DIV28 ((unsigned int) 0x0000000D) +#define SCB_WDTOSCCTRL_DIVSEL_DIV30 ((unsigned int) 0x0000000E) +#define SCB_WDTOSCCTRL_DIVSEL_DIV32 ((unsigned int) 0x0000000F) +#define SCB_WDTOSCCTRL_DIVSEL_DIV34 ((unsigned int) 0x00000010) +#define SCB_WDTOSCCTRL_DIVSEL_DIV36 ((unsigned int) 0x00000011) +#define SCB_WDTOSCCTRL_DIVSEL_DIV38 ((unsigned int) 0x00000012) +#define SCB_WDTOSCCTRL_DIVSEL_DIV40 ((unsigned int) 0x00000013) +#define SCB_WDTOSCCTRL_DIVSEL_DIV42 ((unsigned int) 0x00000014) +#define SCB_WDTOSCCTRL_DIVSEL_DIV44 ((unsigned int) 0x00000015) +#define SCB_WDTOSCCTRL_DIVSEL_DIV46 ((unsigned int) 0x00000016) +#define SCB_WDTOSCCTRL_DIVSEL_DIV48 ((unsigned int) 0x00000017) +#define SCB_WDTOSCCTRL_DIVSEL_DIV50 ((unsigned int) 0x00000018) +#define SCB_WDTOSCCTRL_DIVSEL_DIV52 ((unsigned int) 0x00000019) +#define SCB_WDTOSCCTRL_DIVSEL_DIV54 ((unsigned int) 0x0000001A) +#define SCB_WDTOSCCTRL_DIVSEL_DIV56 ((unsigned int) 0x0000001B) +#define SCB_WDTOSCCTRL_DIVSEL_DIV58 ((unsigned int) 0x0000001C) +#define SCB_WDTOSCCTRL_DIVSEL_DIV60 ((unsigned int) 0x0000001D) +#define SCB_WDTOSCCTRL_DIVSEL_DIV62 ((unsigned int) 0x0000001E) +#define SCB_WDTOSCCTRL_DIVSEL_DIV64 ((unsigned int) 0x0000001F) +#define SCB_WDTOSCCTRL_DIVSEL_MASK ((unsigned int) 0x0000001F) +#define SCB_WDTOSCCTRL_FREQSEL_0_5MHZ ((unsigned int) 0x00000020) +#define SCB_WDTOSCCTRL_FREQSEL_0_8MHZ ((unsigned int) 0x00000040) +#define SCB_WDTOSCCTRL_FREQSEL_1_1MHZ ((unsigned int) 0x00000060) +#define SCB_WDTOSCCTRL_FREQSEL_1_4MHZ ((unsigned int) 0x00000080) +#define SCB_WDTOSCCTRL_FREQSEL_1_6MHZ ((unsigned int) 0x000000A0) // Reset value +#define SCB_WDTOSCCTRL_FREQSEL_1_8MHZ ((unsigned int) 0x000000C0) +#define SCB_WDTOSCCTRL_FREQSEL_2_0MHZ ((unsigned int) 0x000000E0) +#define SCB_WDTOSCCTRL_FREQSEL_2_2MHZ ((unsigned int) 0x00000100) +#define SCB_WDTOSCCTRL_FREQSEL_2_4MHZ ((unsigned int) 0x00000120) +#define SCB_WDTOSCCTRL_FREQSEL_2_6MHZ ((unsigned int) 0x00000140) +#define SCB_WDTOSCCTRL_FREQSEL_2_7MHZ ((unsigned int) 0x00000160) +#define SCB_WDTOSCCTRL_FREQSEL_2_9MHZ ((unsigned int) 0x00000180) +#define SCB_WDTOSCCTRL_FREQSEL_3_1MHZ ((unsigned int) 0x000001A0) +#define SCB_WDTOSCCTRL_FREQSEL_3_2MHZ ((unsigned int) 0x000001C0) +#define SCB_WDTOSCCTRL_FREQSEL_3_4MHZ ((unsigned int) 0x000001E0) +#define SCB_WDTOSCCTRL_FREQSEL_MASK ((unsigned int) 0x000001E0) + +/* IRCCTRL (Internal resonant crystal control register) + This register is used to trim the on-chip 12 MHz oscillator. The trim value is factory-preset + and written by the boot code on start-up. */ + +#define SCB_IRCCTRL_MASK ((unsigned int) 0x000000FF) + +/* SYSRSTSTAT (System reset status register) + The SYSRSTSTAT register shows the source of the latest reset event. The bits are + cleared by writing a one to any of the bits. The POR event clears all other bits in this + register, but if another reset signal (e.g., EXTRST) remains asserted after the POR signal + is negated, then its bit is set to detected. */ + +#define SCB_RESETSTAT_POR_MASK ((unsigned int) 0x00000001) // POR reset status +#define SCB_RESETSTAT_EXTRST_MASK ((unsigned int) 0x00000002) // Status of the external reset pin +#define SCB_RESETSTAT_WDT_MASK ((unsigned int) 0x00000004) // Status of the watchdog reset +#define SCB_RESETSTAT_BOD_MASK ((unsigned int) 0x00000008) // Status of the brown-out detect reset +#define SCB_RESETSTAT_SYSRST_MASK ((unsigned int) 0x00000010) // Status of the software system reset +#define SCB_RESETSTAT_MASK ((unsigned int) 0x00000010) + +/* SYSPLLCLKSEL (System PLL clock source select register) + This register selects the clock source for the system PLL. The SYSPLLCLKUEN register + must be toggled from LOW to HIGH for the update to take effect. + Remark: The system oscillator must be selected if the system PLL is used to generate a + 48 MHz clock to the USB block. +*/ + +#define SCB_CLKSEL_SOURCE_INTERNALOSC ((unsigned int) 0x00000000) +#define SCB_CLKSEL_SOURCE_MAINOSC ((unsigned int) 0x00000001) +#define SCB_CLKSEL_SOURCE_RTCOSC ((unsigned int) 0x00000002) +#define SCB_CLKSEL_SOURCE_MASK ((unsigned int) 0x00000002) + +/* SYSPLLUEN (System PLL clock source update enable register) + This register updates the clock source of the system PLL with the new input clock after the + SYSPLLCLKSEL register has been written to. In order for the update to take effect, first + write a zero to the SYSPLLUEN register and then write a one to SYSPLLUEN. */ + +#define SCB_PLLCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_PLLCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_PLLCLKUEN_MASK ((unsigned int) 0x00000001) + +/* USBPLLCLKSEL (USB PLL clock source select register) + his register selects the clock source for the dedicated USB PLL. The SYSPLLCLKUEN + register must be toggled from LOW to HIGH for the update to take effect. + Remark: Always select the system oscillator to produce a stable 48 MHz clock for + the USB block. */ + +#define SCB_USBPLLCLKSEL_SOURCE_INTERNALOSC ((unsigned int) 0x00000000) // Do NOT use (even though this is the default value) +#define SCB_USBPLLCLKSEL_SOURCE_MAINOSC ((unsigned int) 0x00000001) // Main oscillator should always be used for USB clock +#define SCB_USBPLLCLKSEL_SOURCE_MASK ((unsigned int) 0x00000002) + +/* USBPLLUEN (USB PLL clock source update enable register) + This register updates the clock source of the USB PLL with the new input clock after the + USBPLLCLKSEL register has been written to. In order for the update to take effect at the + USB PLL input, first write a zero to the USBPLLUEN register and then write a one to + USBPLLUEN. */ + +#define SCB_USBPLLCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_USBPLLCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_USBPLLCLKUEN_MASK ((unsigned int) 0x00000001) + +/* MAINCLKSEL (Main clock source select register) + This register selects the main system clock which can be either the output from the + system PLL or the IRC, system, or Watchdog oscillators directly. The main system clock + clocks the core, the peripherals, and optionally the USB block. + The MAINCLKUEN register must be toggled from LOW to HIGH for the update to take effect.*/ + +#define SCB_MAINCLKSEL_SOURCE_INTERNALOSC ((unsigned int) 0x00000000) // Use IRC oscillator for main clock source +#define SCB_MAINCLKSEL_SOURCE_INPUTCLOCK ((unsigned int) 0x00000001) // Use Input clock to system PLL for main clock source +#define SCB_MAINCLKSEL_SOURCE_WDTOSC ((unsigned int) 0x00000002) // Use watchdog oscillator for main clock source +#define SCB_MAINCLKSEL_SOURCE_SYSPLLCLKOUT ((unsigned int) 0x00000003) // Use system PLL clock out for main clock source +#define SCB_MAINCLKSEL_MASK ((unsigned int) 0x00000003) + +/* MAINCLKUEN (Main clock source update enable register) + This register updates the clock source of the main clock with the new input clock after the + MAINCLKSEL register has been written to. In order for the update to take effect, first write + a zero to the MAINUEN register and then write a one to MAINCLKUEN. */ + +#define SCB_MAINCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_MAINCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_MAINCLKUEN_MASK ((unsigned int) 0x00000001) + +/* SYSAHBCLKDIV (System AHB clock divider register) + This register divides the main clock to provide the system clock to the core, memories, + and the peripherals. The system clock can be shut down completely by setting the DIV + bits to 0x0. */ + +#define SCB_SYSAHBCLKDIV_DISABLE ((unsigned int) 0x00000000) // 0 will shut the system clock down completely +#define SCB_SYSAHBCLKDIV_DIV1 ((unsigned int) 0x00000001) // 1, 2 or 4 are the most common values +#define SCB_SYSAHBCLKDIV_DIV2 ((unsigned int) 0x00000002) +#define SCB_SYSAHBCLKDIV_DIV4 ((unsigned int) 0x00000004) +#define SCB_SYSAHBCLKDIV_MASK ((unsigned int) 0x000000FF) // AHB clock divider can be from 0 to 255 + +/* AHBCLKCTRL (System AHB clock control register) + The AHBCLKCTRL register enables the clocks to individual system and peripheral blocks. + The system clock (sys_ahb_clk[0], bit 0 in the AHBCLKCTRL register) provides the clock + for the AHB to APB bridge, the AHB matrix, the ARM Cortex-M3, the Syscon block, and + the PMU. This clock cannot be disabled. */ + +#define SCB_SYSAHBCLKCTRL_SYS ((unsigned int) 0x00000001) // Enables clock for AHB and APB bridges, FCLK, HCLK, SysCon and PMU +#define SCB_SYSAHBCLKCTRL_SYS_MASK ((unsigned int) 0x00000001) +#define SCB_SYSAHBCLKCTRL_ROM ((unsigned int) 0x00000002) // Enables clock for ROM +#define SCB_SYSAHBCLKCTRL_ROM_MASK ((unsigned int) 0x00000002) +#define SCB_SYSAHBCLKCTRL_RAM ((unsigned int) 0x00000004) // Enables clock for SRAM +#define SCB_SYSAHBCLKCTRL_RAM_MASK ((unsigned int) 0x00000004) +#define SCB_SYSAHBCLKCTRL_FLASH1 ((unsigned int) 0x00000008) // Enables clock for flash1 +#define SCB_SYSAHBCLKCTRL_FLASH1_MASK ((unsigned int) 0x00000008) +#define SCB_SYSAHBCLKCTRL_FLASH2 ((unsigned int) 0x00000010) // Enables clock for flash2 +#define SCB_SYSAHBCLKCTRL_FLASH2_MASK ((unsigned int) 0x00000010) +#define SCB_SYSAHBCLKCTRL_I2C ((unsigned int) 0x00000020) // Enables clock for I2C +#define SCB_SYSAHBCLKCTRL_I2C_MASK ((unsigned int) 0x00000020) +#define SCB_SYSAHBCLKCTRL_GPIO ((unsigned int) 0x00000040) // Enables clock for GPIO +#define SCB_SYSAHBCLKCTRL_GPIO_MASK ((unsigned int) 0x00000040) +#define SCB_SYSAHBCLKCTRL_CT16B0 ((unsigned int) 0x00000080) // Enables clock for 16-bit counter/timer 0 +#define SCB_SYSAHBCLKCTRL_CT16B0_MASK ((unsigned int) 0x00000080) +#define SCB_SYSAHBCLKCTRL_CT16B1 ((unsigned int) 0x00000100) // Enables clock for 16-bit counter/timer 1 +#define SCB_SYSAHBCLKCTRL_CT16B1_MASK ((unsigned int) 0x00000100) +#define SCB_SYSAHBCLKCTRL_CT32B0 ((unsigned int) 0x00000200) // Enables clock for 32-bit counter/timer 0 +#define SCB_SYSAHBCLKCTRL_CT32B0_MASK ((unsigned int) 0x00000200) +#define SCB_SYSAHBCLKCTRL_CT32B1 ((unsigned int) 0x00000400) // Enables clock for 32-bit counter/timer 1 +#define SCB_SYSAHBCLKCTRL_CT32B1_MASK ((unsigned int) 0x00000400) +#define SCB_SYSAHBCLKCTRL_SSP0 ((unsigned int) 0x00000800) // Enables clock for SSP0 +#define SCB_SYSAHBCLKCTRL_SSP0_MASK ((unsigned int) 0x00000800) +#define SCB_SYSAHBCLKCTRL_UART ((unsigned int) 0x00001000) // Enables clock for UART. UART pins must be configured +#define SCB_SYSAHBCLKCTRL_UART_MASK ((unsigned int) 0x00001000) // in the IOCON block before the UART clock can be enabled. +#define SCB_SYSAHBCLKCTRL_ADC ((unsigned int) 0x00002000) // Enables clock for ADC +#define SCB_SYSAHBCLKCTRL_ADC_MASK ((unsigned int) 0x00002000) +#define SCB_SYSAHBCLKCTRL_USB_REG ((unsigned int) 0x00004000) // Enables clock for USB_REG +#define SCB_SYSAHBCLKCTRL_USB_REG_MASK ((unsigned int) 0x00004000) +#define SCB_SYSAHBCLKCTRL_WDT ((unsigned int) 0x00008000) // Enables clock for watchdog timer +#define SCB_SYSAHBCLKCTRL_WDT_MASK ((unsigned int) 0x00008000) +#define SCB_SYSAHBCLKCTRL_IOCON ((unsigned int) 0x00010000) // Enables clock for IO configuration block +#define SCB_SYSAHBCLKCTRL_IOCON_MASK ((unsigned int) 0x00010000) +#define SCB_SYSAHBCLKCTRL_ALL_MASK ((unsigned int) 0x0001FFFF) + +/* SSP0CLKDIV (SSP0 clock divider register) + This register configures the SSP0 peripheral clock SSP_PCLK. The SSP_PCLK can be + shut down by setting the DIV bits to 0x0. It can be set from 1..255. */ + +#define SCB_SSP0CLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_SSP0CLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide SSP0 clock by 1 (can be set from 1..255) +#define SCB_SSP0CLKDIV_DIV2 ((unsigned int) 0x00000002) +#define SCB_SSP0CLKDIV_DIV3 ((unsigned int) 0x00000003) +#define SCB_SSP0CLKDIV_DIV4 ((unsigned int) 0x00000004) +#define SCB_SSP0CLKDIV_DIV6 ((unsigned int) 0x00000006) +#define SCB_SSP0CLKDIV_DIV10 ((unsigned int) 0x0000000A) +#define SCB_SSP0CLKDIV_DIV12 ((unsigned int) 0x0000000C) +#define SCB_SSP0CLKDIV_DIV20 ((unsigned int) 0x00000014) +#define SCB_SSP0CLKDIV_DIV40 ((unsigned int) 0x00000028) +#define SCB_SSP0CLKDIV_MASK ((unsigned int) 0x000000FF) + +/* UARTCLKDIV (UART clock divider register) + This register configures the UART peripheral. The UART_PCLK can be shut down by + setting the DIV bits to 0x0. + Remark: Note that the UART pins must be configured in the IOCON block before the + UART clock can be enabled. */ + +#define SCB_UARTCLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_UARTCLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide UART clock by 1 (can be set from 1..255) +#define SCB_UARTCLKDIV_DIV2 ((unsigned int) 0x00000002) +#define SCB_UARTCLKDIV_DIV4 ((unsigned int) 0x00000004) +#define SCB_UARTCLKDIV_MASK ((unsigned int) 0x000000FF) + +/* SYSTICKCLKDIV (SYSTICK clock divider register) + This register configures the SYSTICK peripheral clock. The SYSTICK timer clock can be + shut down by setting the DIV bits to 0x0. */ + +#define SCB_SYSTICKCLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_SYSTICKCLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide SYSTICK clock by 1 (can be set from 1..255) +#define SCB_SYSTICKCLKDIV_DIV2 ((unsigned int) 0x00000002) // Divide SYSTICK clock by 2 +#define SCB_SYSTICKCLKDIV_DIV4 ((unsigned int) 0x00000004) // Divide SYSTICK clock by 4 +#define SCB_SYSTICKCLKDIV_DIV8 ((unsigned int) 0x00000008) // Divide SYSTICK clock by 8 +#define SCB_SYSTICKCLKDIV_MASK ((unsigned int) 0x000000FF) + +/* USBCLKSEL (USB clock source select register) + This register selects the clock source for the USB usb_clk. The clock source can be either + the USB PLL output or the main clock, and the clock can be further divided by the + USBCLKDIV register to obtain a 48 MHz clock. The USBCLKUEN register must be toggled from + LOW to HIGH for the update to take effect. */ + +#define SCB_USBCLKSEL_SOURCE_USBPLLOUT ((unsigned int) 0x00000000) // USB PLL output +#define SCB_USBCLKSEL_SOURCE_INPUTCLOCK ((unsigned int) 0x00000001) // Use the main clock +#define SCB_USBCLKSEL_MASK ((unsigned int) 0x00000003) + +/* USBCLKUEN (USB clock source update enable register) + This register updates the clock source of the USB with the new input clock after the + USBCLKSEL register has been written to. In order for the update to take effect, first write + a zero to the USBCLKUEN register and then write a one to USBCLKUEN. */ + +#define SCB_USBCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_USBCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_USBCLKUEN_MASK ((unsigned int) 0x00000001) + +/* USBCLKDIV (USB clock divider register) + This register allows the USB clock usb_clk to be divided to 48 MHz. The usb_clk can be + shut down by setting the DIV bits to 0x0. */ + +#define SCB_USBCLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_USBCLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide USB clock by 1 (can be set from 1..255) +#define SCB_USBCLKDIV_MASK ((unsigned int) 0x000000FF) + +/* WDTCLKSEL (WDT clock source select register) + This register selects the clock source for the watchdog timer. The WDTCLKUEN register + must be toggled from LOW to HIGH for the update to take effect. */ + +#define SCB_WDTCLKSEL_SOURCE_INTERNALOSC ((unsigned int) 0x00000000) // Use the internal oscillator +#define SCB_WDTCLKSEL_SOURCE_INPUTCLOCK ((unsigned int) 0x00000001) // Use the main clock +#define SCB_WDTCLKSEL_SOURCE_WATCHDOGOSC ((unsigned int) 0x00000002) // Use the watchdog oscillator +#define SCB_WDTCLKSEL_MASK ((unsigned int) 0x00000003) + +/* WDTCLKUEN (WDT clock source update enable register) + This register updates the clock source of the watchdog timer with the new input clock after + the WDTCLKSEL register has been written to. In order for the update to take effect at the + input of the watchdog timer, first write a zero to the WDTCLKUEN register and then write + a one to WDTCLKUEN. */ + +#define SCB_WDTCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_WDTCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_WDTCLKUEN_MASK ((unsigned int) 0x00000001) + +/* WDTCLKDIV (WDT clock divider register) + This register determines the divider values for the watchdog clock wdt_clk. */ + +#define SCB_WDTCLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_WDTCLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide clock by 1 (can be set from 1..255) +#define SCB_WDTCLKDIV_MASK ((unsigned int) 0x000000FF) + +/* CLKOUTCLKSEL (CLKOUT clock source select register) + This register configures the clkout_clk signal to be output on the CLKOUT pin. All three + oscillators and the main clock can be selected for the clkout_clk clock. + The CLKOUTCLKUEN register must be toggled from LOW to HIGH for the update to take effect. */ + +#define SCB_CLKOUTCLKSEL_SOURCE_USBPLLOUT ((unsigned int) 0x00000000) // USB PLL output +#define SCB_CLKOUTCLKSEL_SOURCE_INPUTCLOCK ((unsigned int) 0x00000001) // Use the main clock +#define SCB_CLKOUTCLKSEL_MASK ((unsigned int) 0x00000003) + +/* CLKOUTUEN (CLKOUT clock source update enable register) + This register updates the clock source of the CLKOUT pin with the new clock after the + CLKOUTCLKSEL register has been written to. In order for the update to take effect at the + input of the CLKOUT pin, first write a zero to the CLKCLKUEN register and then write a + one to CLKCLKUEN. */ + +#define SCB_CLKOUTCLKUEN_DISABLE ((unsigned int) 0x00000000) +#define SCB_CLKOUTCLKUEN_UPDATE ((unsigned int) 0x00000001) +#define SCB_CLKOUTCLKUEN_MASK ((unsigned int) 0x00000001) + +/* CLKOUTCLKDIV (CLKOUT clock divider register) + This register determines the divider value for the clkout_clk signal on the CLKOUT pin. */ + +#define SCB_CLKOUTCLKDIV_DISABLE ((unsigned int) 0x00000000) +#define SCB_CLKOUTCLKDIV_DIV1 ((unsigned int) 0x00000001) // Divide clock by 1 (can be set from 1..255) +#define SCB_CLKOUTCLKDIV_MASK ((unsigned int) 0x000000FF) + + +/* PIOPORCAP0 (POR captured PIO status register 0) + The PIOPORCAP0 register captures the state (HIGH or LOW) of the PIO pins of ports 0,1, + and 2 (pins PIO2_0 to PIO2_7) at power-on-reset. Each bit represents the reset state of + one GPIO pin. This register is a read-only status register. */ + +#define SCB_PIOPORCAP0_PIO0_0 ((unsigned int) 0x00000001) +#define SCB_PIOPORCAP0_PIO0_0_MASK ((unsigned int) 0x00000001) +#define SCB_PIOPORCAP0_PIO0_1 ((unsigned int) 0x00000002) +#define SCB_PIOPORCAP0_PIO0_1_MASK ((unsigned int) 0x00000002) +#define SCB_PIOPORCAP0_PIO0_2 ((unsigned int) 0x00000004) +#define SCB_PIOPORCAP0_PIO0_2_MASK ((unsigned int) 0x00000004) +#define SCB_PIOPORCAP0_PIO0_3 ((unsigned int) 0x00000008) +#define SCB_PIOPORCAP0_PIO0_3_MASK ((unsigned int) 0x00000008) +#define SCB_PIOPORCAP0_PIO0_4 ((unsigned int) 0x00000010) +#define SCB_PIOPORCAP0_PIO0_4_MASK ((unsigned int) 0x00000010) +#define SCB_PIOPORCAP0_PIO0_5 ((unsigned int) 0x00000020) +#define SCB_PIOPORCAP0_PIO0_5_MASK ((unsigned int) 0x00000020) +#define SCB_PIOPORCAP0_PIO0_6 ((unsigned int) 0x00000040) +#define SCB_PIOPORCAP0_PIO0_6_MASK ((unsigned int) 0x00000040) +#define SCB_PIOPORCAP0_PIO0_7 ((unsigned int) 0x00000080) +#define SCB_PIOPORCAP0_PIO0_7_MASK ((unsigned int) 0x00000080) +#define SCB_PIOPORCAP0_PIO0_8 ((unsigned int) 0x00000100) +#define SCB_PIOPORCAP0_PIO0_8_MASK ((unsigned int) 0x00000100) +#define SCB_PIOPORCAP0_PIO0_9 ((unsigned int) 0x00000200) +#define SCB_PIOPORCAP0_PIO0_9_MASK ((unsigned int) 0x00000200) +#define SCB_PIOPORCAP0_PIO0_10 ((unsigned int) 0x00000400) +#define SCB_PIOPORCAP0_PIO0_10_MASK ((unsigned int) 0x00000400) +#define SCB_PIOPORCAP0_PIO0_11 ((unsigned int) 0x00000800) +#define SCB_PIOPORCAP0_PIO0_11_MASK ((unsigned int) 0x00000800) +#define SCB_PIOPORCAP0_PIO1_0 ((unsigned int) 0x00001000) +#define SCB_PIOPORCAP0_PIO1_0_MASK ((unsigned int) 0x00001000) +#define SCB_PIOPORCAP0_PIO1_1 ((unsigned int) 0x00002000) +#define SCB_PIOPORCAP0_PIO1_1_MASK ((unsigned int) 0x00002000) +#define SCB_PIOPORCAP0_PIO1_2 ((unsigned int) 0x00004000) +#define SCB_PIOPORCAP0_PIO1_2_MASK ((unsigned int) 0x00004000) +#define SCB_PIOPORCAP0_PIO1_3 ((unsigned int) 0x00008000) +#define SCB_PIOPORCAP0_PIO1_3_MASK ((unsigned int) 0x00008000) +#define SCB_PIOPORCAP0_PIO1_4 ((unsigned int) 0x00010000) +#define SCB_PIOPORCAP0_PIO1_4_MASK ((unsigned int) 0x00010000) +#define SCB_PIOPORCAP0_PIO1_5 ((unsigned int) 0x00020000) +#define SCB_PIOPORCAP0_PIO1_5_MASK ((unsigned int) 0x00020000) +#define SCB_PIOPORCAP0_PIO1_6 ((unsigned int) 0x00040000) +#define SCB_PIOPORCAP0_PIO1_6_MASK ((unsigned int) 0x00040000) +#define SCB_PIOPORCAP0_PIO1_7 ((unsigned int) 0x00080000) +#define SCB_PIOPORCAP0_PIO1_7_MASK ((unsigned int) 0x00080000) +#define SCB_PIOPORCAP0_PIO1_8 ((unsigned int) 0x00100000) +#define SCB_PIOPORCAP0_PIO1_8_MASK ((unsigned int) 0x00100000) +#define SCB_PIOPORCAP0_PIO1_9 ((unsigned int) 0x00200000) +#define SCB_PIOPORCAP0_PIO1_9_MASK ((unsigned int) 0x00200000) +#define SCB_PIOPORCAP0_PIO1_10 ((unsigned int) 0x00400000) +#define SCB_PIOPORCAP0_PIO1_10_MASK ((unsigned int) 0x00400000) +#define SCB_PIOPORCAP0_PIO1_11 ((unsigned int) 0x00800000) +#define SCB_PIOPORCAP0_PIO1_11_MASK ((unsigned int) 0x00800000) +#define SCB_PIOPORCAP0_PIO2_0 ((unsigned int) 0x01000000) +#define SCB_PIOPORCAP0_PIO2_0_MASK ((unsigned int) 0x01000000) +#define SCB_PIOPORCAP0_PIO2_1 ((unsigned int) 0x02000000) +#define SCB_PIOPORCAP0_PIO2_1_MASK ((unsigned int) 0x02000000) +#define SCB_PIOPORCAP0_PIO2_2 ((unsigned int) 0x04000000) +#define SCB_PIOPORCAP0_PIO2_2_MASK ((unsigned int) 0x04000000) +#define SCB_PIOPORCAP0_PIO2_3 ((unsigned int) 0x08000000) +#define SCB_PIOPORCAP0_PIO2_3_MASK ((unsigned int) 0x08000000) +#define SCB_PIOPORCAP0_PIO2_4 ((unsigned int) 0x10000000) +#define SCB_PIOPORCAP0_PIO2_4_MASK ((unsigned int) 0x10000000) +#define SCB_PIOPORCAP0_PIO2_5 ((unsigned int) 0x20000000) +#define SCB_PIOPORCAP0_PIO2_5_MASK ((unsigned int) 0x20000000) +#define SCB_PIOPORCAP0_PIO2_6 ((unsigned int) 0x40000000) +#define SCB_PIOPORCAP0_PIO2_6_MASK ((unsigned int) 0x40000000) +#define SCB_PIOPORCAP0_PIO2_7 ((unsigned int) 0x80000000) +#define SCB_PIOPORCAP0_PIO2_7_MASK ((unsigned int) 0x80000000) + +/* PIOPORCAP1 (POR captured PIO status register 1) + The PIOPORCAP1 register captures the state (HIGH or LOW) of the PIO pins of port 2 + (PIO2_8 to PIO2_11) and port 3 at power-on-reset. Each bit represents the reset state of + one PIO pin. This register is a read-only status register. */ + +#define SCB_PIOPORCAP1_PIO2_8 ((unsigned int) 0x00000001) +#define SCB_PIOPORCAP1_PIO2_8_MASK ((unsigned int) 0x00000001) +#define SCB_PIOPORCAP1_PIO2_9 ((unsigned int) 0x00000002) +#define SCB_PIOPORCAP1_PIO2_9_MASK ((unsigned int) 0x00000002) +#define SCB_PIOPORCAP1_PIO2_10 ((unsigned int) 0x00000004) +#define SCB_PIOPORCAP1_PIO2_10_MASK ((unsigned int) 0x00000004) +#define SCB_PIOPORCAP1_PIO2_11 ((unsigned int) 0x00000008) +#define SCB_PIOPORCAP1_PIO2_11_MASK ((unsigned int) 0x00000008) +#define SCB_PIOPORCAP1_PIO3_0 ((unsigned int) 0x00000010) +#define SCB_PIOPORCAP1_PIO3_0_MASK ((unsigned int) 0x00000010) +#define SCB_PIOPORCAP1_PIO3_1 ((unsigned int) 0x00000020) +#define SCB_PIOPORCAP1_PIO3_1_MASK ((unsigned int) 0x00000020) +#define SCB_PIOPORCAP1_PIO3_2 ((unsigned int) 0x00000040) +#define SCB_PIOPORCAP1_PIO3_2_MASK ((unsigned int) 0x00000040) +#define SCB_PIOPORCAP1_PIO3_3 ((unsigned int) 0x00000080) +#define SCB_PIOPORCAP1_PIO3_3_MASK ((unsigned int) 0x00000080) +#define SCB_PIOPORCAP1_PIO3_4 ((unsigned int) 0x00000100) +#define SCB_PIOPORCAP1_PIO3_4_MASK ((unsigned int) 0x00000100) +#define SCB_PIOPORCAP1_PIO3_5 ((unsigned int) 0x00000200) +#define SCB_PIOPORCAP1_PIO3_5_MASK ((unsigned int) 0x00000200) + +/* BODCTRL (Brown-out detection control register) + The BOD control register selects four separate threshold values for sending a BOD + interrupt to the NVIC. Only one level is allowed for forced reset. */ + +#define SCB_BODCTRL_RSTLEVEL_MASK ((unsigned int) 0x00000003) +#define SCB_BODCTRL_INTLEVEL_1_69V_1_84V ((unsigned int) 0x00000000) +#define SCB_BODCTRL_INTLEVEL_2_29V_2_44V ((unsigned int) 0x00000004) +#define SCB_BODCTRL_INTLEVEL_2_59V_2_74V ((unsigned int) 0x00000008) +#define SCB_BODCTRL_INTLEVEL_2_87V_2_98V ((unsigned int) 0x0000000C) +#define SCB_BODCTRL_INTLEVEL_MASK ((unsigned int) 0x0000000C) +#define SCB_BODCTRL_RSTENABLE_DISABLE ((unsigned int) 0x00000000) +#define SCB_BODCTRL_RSTENABLE_ENABLE ((unsigned int) 0x00000010) +#define SCB_BODCTRL_RSTENABLE_MASK ((unsigned int) 0x00000010) + +/* SYSTCKCAL (System tick counter calibration register) */ + +#define SCB_SYSTICKCCAL_MASK ((unsigned int) 0x03FFFFFF) // Undefined as of v0.07 of the LPC1343 User Manual + +/* STARTAPRP0 (Start logic edge control register 0) + The STARTAPRP0 register controls the start logic inputs of ports 0 (PIO0_0 to PIO0_11) + and 1 (PIO1_0 to PIO1_11) and the lower 8 inputs of port 2 (PIO2_0 to PIO2_7). This + register selects a falling or rising edge on the corresponding PIO input to produce a falling + or rising clock edge, respectively, for the start logic (see Section 3–9.3). + Every bit in the STARTAPRP0 register controls one port input and is connected to one + wake-up interrupt in the NVIC. Bit 0 in the STARTAPRP0 register corresponds to interrupt + 0, bit 1 to interrupt 1, etc.. The bottom 32 interrupts are contained this register, + the top 8 interrupts are contained in the STARTAPRP1 register for total of 40 wake-up + interrupts. + Remark: Each interrupt connected to a start logic input must be enabled in the NVIC if the + corresponding PIO pin is used to wake up the chip from Deep-sleep mode. */ + +#define SCB_STARTAPRP0_APRPIO0_0 ((unsigned int) 0x00000001) +#define SCB_STARTAPRP0_APRPIO0_0_MASK ((unsigned int) 0x00000001) +#define SCB_STARTAPRP0_APRPIO0_1 ((unsigned int) 0x00000002) +#define SCB_STARTAPRP0_APRPIO0_1_MASK ((unsigned int) 0x00000002) +#define SCB_STARTAPRP0_APRPIO0_2 ((unsigned int) 0x00000004) +#define SCB_STARTAPRP0_APRPIO0_2_MASK ((unsigned int) 0x00000004) +#define SCB_STARTAPRP0_APRPIO0_3 ((unsigned int) 0x00000008) +#define SCB_STARTAPRP0_APRPIO0_3_MASK ((unsigned int) 0x00000008) +#define SCB_STARTAPRP0_APRPIO0_4 ((unsigned int) 0x00000010) +#define SCB_STARTAPRP0_APRPIO0_4_MASK ((unsigned int) 0x00000010) +#define SCB_STARTAPRP0_APRPIO0_5 ((unsigned int) 0x00000020) +#define SCB_STARTAPRP0_APRPIO0_5_MASK ((unsigned int) 0x00000020) +#define SCB_STARTAPRP0_APRPIO0_6 ((unsigned int) 0x00000040) +#define SCB_STARTAPRP0_APRPIO0_6_MASK ((unsigned int) 0x00000040) +#define SCB_STARTAPRP0_APRPIO0_7 ((unsigned int) 0x00000080) +#define SCB_STARTAPRP0_APRPIO0_7_MASK ((unsigned int) 0x00000080) +#define SCB_STARTAPRP0_APRPIO0_8 ((unsigned int) 0x00000100) +#define SCB_STARTAPRP0_APRPIO0_8_MASK ((unsigned int) 0x00000100) +#define SCB_STARTAPRP0_APRPIO0_9 ((unsigned int) 0x00000200) +#define SCB_STARTAPRP0_APRPIO0_9_MASK ((unsigned int) 0x00000200) +#define SCB_STARTAPRP0_APRPIO0_10 ((unsigned int) 0x00000400) +#define SCB_STARTAPRP0_APRPIO0_10_MASK ((unsigned int) 0x00000400) +#define SCB_STARTAPRP0_APRPIO0_11 ((unsigned int) 0x00000800) +#define SCB_STARTAPRP0_APRPIO0_11_MASK ((unsigned int) 0x00000800) +#define SCB_STARTAPRP0_APRPIO1_0 ((unsigned int) 0x00001000) +#define SCB_STARTAPRP0_APRPIO1_0_MASK ((unsigned int) 0x00001000) +#define SCB_STARTAPRP0_APRPIO1_1 ((unsigned int) 0x00002000) +#define SCB_STARTAPRP0_APRPIO1_1_MASK ((unsigned int) 0x00002000) +#define SCB_STARTAPRP0_APRPIO1_2 ((unsigned int) 0x00004000) +#define SCB_STARTAPRP0_APRPIO1_2_MASK ((unsigned int) 0x00004000) +#define SCB_STARTAPRP0_APRPIO1_3 ((unsigned int) 0x00008000) +#define SCB_STARTAPRP0_APRPIO1_3_MASK ((unsigned int) 0x00008000) +#define SCB_STARTAPRP0_APRPIO1_4 ((unsigned int) 0x00010000) +#define SCB_STARTAPRP0_APRPIO1_4_MASK ((unsigned int) 0x00010000) +#define SCB_STARTAPRP0_APRPIO1_5 ((unsigned int) 0x00020000) +#define SCB_STARTAPRP0_APRPIO1_5_MASK ((unsigned int) 0x00020000) +#define SCB_STARTAPRP0_APRPIO1_6 ((unsigned int) 0x00040000) +#define SCB_STARTAPRP0_APRPIO1_6_MASK ((unsigned int) 0x00040000) +#define SCB_STARTAPRP0_APRPIO1_7 ((unsigned int) 0x00080000) +#define SCB_STARTAPRP0_APRPIO1_7_MASK ((unsigned int) 0x00080000) +#define SCB_STARTAPRP0_APRPIO1_8 ((unsigned int) 0x00100000) +#define SCB_STARTAPRP0_APRPIO1_8_MASK ((unsigned int) 0x00100000) +#define SCB_STARTAPRP0_APRPIO1_9 ((unsigned int) 0x00200000) +#define SCB_STARTAPRP0_APRPIO1_9_MASK ((unsigned int) 0x00200000) +#define SCB_STARTAPRP0_APRPIO1_10 ((unsigned int) 0x00400000) +#define SCB_STARTAPRP0_APRPIO1_10_MASK ((unsigned int) 0x00400000) +#define SCB_STARTAPRP0_APRPIO1_11 ((unsigned int) 0x00800000) +#define SCB_STARTAPRP0_APRPIO1_11_MASK ((unsigned int) 0x00800000) +#define SCB_STARTAPRP0_APRPIO2_0 ((unsigned int) 0x01000000) +#define SCB_STARTAPRP0_APRPIO2_0_MASK ((unsigned int) 0x01000000) +#define SCB_STARTAPRP0_APRPIO2_1 ((unsigned int) 0x02000000) +#define SCB_STARTAPRP0_APRPIO2_1_MASK ((unsigned int) 0x02000000) +#define SCB_STARTAPRP0_APRPIO2_2 ((unsigned int) 0x04000000) +#define SCB_STARTAPRP0_APRPIO2_2_MASK ((unsigned int) 0x04000000) +#define SCB_STARTAPRP0_APRPIO2_3 ((unsigned int) 0x08000000) +#define SCB_STARTAPRP0_APRPIO2_3_MASK ((unsigned int) 0x08000000) +#define SCB_STARTAPRP0_APRPIO2_4 ((unsigned int) 0x10000000) +#define SCB_STARTAPRP0_APRPIO2_4_MASK ((unsigned int) 0x10000000) +#define SCB_STARTAPRP0_APRPIO2_5 ((unsigned int) 0x20000000) +#define SCB_STARTAPRP0_APRPIO2_5_MASK ((unsigned int) 0x20000000) +#define SCB_STARTAPRP0_APRPIO2_6 ((unsigned int) 0x40000000) +#define SCB_STARTAPRP0_APRPIO2_6_MASK ((unsigned int) 0x40000000) +#define SCB_STARTAPRP0_APRPIO2_7 ((unsigned int) 0x80000000) +#define SCB_STARTAPRP0_APRPIO2_7_MASK ((unsigned int) 0x80000000) +#define SCB_STARTAPRP0_MASK ((unsigned int) 0xFFFFFFFF) + +/* STARTERP0 (Start logic signal enable register 0) + This STARTERP0 register enables or disables the start signal bits in the start logic. */ + +#define SCB_STARTERP0_ERPIO0_0 ((unsigned int) 0x00000001) +#define SCB_STARTERP0_ERPIO0_0_MASK ((unsigned int) 0x00000001) +#define SCB_STARTERP0_ERPIO0_1 ((unsigned int) 0x00000002) +#define SCB_STARTERP0_ERPIO0_1_MASK ((unsigned int) 0x00000002) +#define SCB_STARTERP0_ERPIO0_2 ((unsigned int) 0x00000004) +#define SCB_STARTERP0_ERPIO0_2_MASK ((unsigned int) 0x00000004) +#define SCB_STARTERP0_ERPIO0_3 ((unsigned int) 0x00000008) +#define SCB_STARTERP0_ERPIO0_3_MASK ((unsigned int) 0x00000008) +#define SCB_STARTERP0_ERPIO0_4 ((unsigned int) 0x00000010) +#define SCB_STARTERP0_ERPIO0_4_MASK ((unsigned int) 0x00000010) +#define SCB_STARTERP0_ERPIO0_5 ((unsigned int) 0x00000020) +#define SCB_STARTERP0_ERPIO0_5_MASK ((unsigned int) 0x00000020) +#define SCB_STARTERP0_ERPIO0_6 ((unsigned int) 0x00000040) +#define SCB_STARTERP0_ERPIO0_6_MASK ((unsigned int) 0x00000040) +#define SCB_STARTERP0_ERPIO0_7 ((unsigned int) 0x00000080) +#define SCB_STARTERP0_ERPIO0_7_MASK ((unsigned int) 0x00000080) +#define SCB_STARTERP0_ERPIO0_8 ((unsigned int) 0x00000100) +#define SCB_STARTERP0_ERPIO0_8_MASK ((unsigned int) 0x00000100) +#define SCB_STARTERP0_ERPIO0_9 ((unsigned int) 0x00000200) +#define SCB_STARTERP0_ERPIO0_9_MASK ((unsigned int) 0x00000200) +#define SCB_STARTERP0_ERPIO0_10 ((unsigned int) 0x00000400) +#define SCB_STARTERP0_ERPIO0_10_MASK ((unsigned int) 0x00000400) +#define SCB_STARTERP0_ERPIO0_11 ((unsigned int) 0x00000800) +#define SCB_STARTERP0_ERPIO0_11_MASK ((unsigned int) 0x00000800) +#define SCB_STARTERP0_ERPIO1_0 ((unsigned int) 0x00001000) +#define SCB_STARTERP0_ERPIO1_0_MASK ((unsigned int) 0x00001000) +#define SCB_STARTERP0_ERPIO1_1 ((unsigned int) 0x00002000) +#define SCB_STARTERP0_ERPIO1_1_MASK ((unsigned int) 0x00002000) +#define SCB_STARTERP0_ERPIO1_2 ((unsigned int) 0x00004000) +#define SCB_STARTERP0_ERPIO1_2_MASK ((unsigned int) 0x00004000) +#define SCB_STARTERP0_ERPIO1_3 ((unsigned int) 0x00008000) +#define SCB_STARTERP0_ERPIO1_3_MASK ((unsigned int) 0x00008000) +#define SCB_STARTERP0_ERPIO1_4 ((unsigned int) 0x00010000) +#define SCB_STARTERP0_ERPIO1_4_MASK ((unsigned int) 0x00010000) +#define SCB_STARTERP0_ERPIO1_5 ((unsigned int) 0x00020000) +#define SCB_STARTERP0_ERPIO1_5_MASK ((unsigned int) 0x00020000) +#define SCB_STARTERP0_ERPIO1_6 ((unsigned int) 0x00040000) +#define SCB_STARTERP0_ERPIO1_6_MASK ((unsigned int) 0x00040000) +#define SCB_STARTERP0_ERPIO1_7 ((unsigned int) 0x00080000) +#define SCB_STARTERP0_ERPIO1_7_MASK ((unsigned int) 0x00080000) +#define SCB_STARTERP0_ERPIO1_8 ((unsigned int) 0x00100000) +#define SCB_STARTERP0_ERPIO1_8_MASK ((unsigned int) 0x00100000) +#define SCB_STARTERP0_ERPIO1_9 ((unsigned int) 0x00200000) +#define SCB_STARTERP0_ERPIO1_9_MASK ((unsigned int) 0x00200000) +#define SCB_STARTERP0_ERPIO1_10 ((unsigned int) 0x00400000) +#define SCB_STARTERP0_ERPIO1_10_MASK ((unsigned int) 0x00400000) +#define SCB_STARTERP0_ERPIO1_11 ((unsigned int) 0x00800000) +#define SCB_STARTERP0_ERPIO1_11_MASK ((unsigned int) 0x00800000) +#define SCB_STARTERP0_ERPIO2_0 ((unsigned int) 0x01000000) +#define SCB_STARTERP0_ERPIO2_0_MASK ((unsigned int) 0x01000000) +#define SCB_STARTERP0_ERPIO2_1 ((unsigned int) 0x02000000) +#define SCB_STARTERP0_ERPIO2_1_MASK ((unsigned int) 0x02000000) +#define SCB_STARTERP0_ERPIO2_2 ((unsigned int) 0x04000000) +#define SCB_STARTERP0_ERPIO2_2_MASK ((unsigned int) 0x04000000) +#define SCB_STARTERP0_ERPIO2_3 ((unsigned int) 0x08000000) +#define SCB_STARTERP0_ERPIO2_3_MASK ((unsigned int) 0x08000000) +#define SCB_STARTERP0_ERPIO2_4 ((unsigned int) 0x10000000) +#define SCB_STARTERP0_ERPIO2_4_MASK ((unsigned int) 0x10000000) +#define SCB_STARTERP0_ERPIO2_5 ((unsigned int) 0x20000000) +#define SCB_STARTERP0_ERPIO2_5_MASK ((unsigned int) 0x20000000) +#define SCB_STARTERP0_ERPIO2_6 ((unsigned int) 0x40000000) +#define SCB_STARTERP0_ERPIO2_6_MASK ((unsigned int) 0x40000000) +#define SCB_STARTERP0_ERPIO2_7 ((unsigned int) 0x80000000) +#define SCB_STARTERP0_ERPIO2_7_MASK ((unsigned int) 0x80000000) +#define SCB_STARTERP0_MASK ((unsigned int) 0xFFFFFFFF) + +/* STARTRSRP0CLR (Start logic reset register 0) + Writing a one to a bit in the STARTRSRP0CLR register resets the start logic state. The + start-up logic uses the input signals to generate a clock edge for registering a start + signal. This clock edge (falling or rising) sets the interrupt for waking up from + Deep-sleep mode. Therefore, the start-up logic states must be cleared before being used. */ + +#define SCB_STARTRSRP0CLR_RSRPIO0_0 ((unsigned int) 0x00000001) +#define SCB_STARTRSRP0CLR_RSRPIO0_0_MASK ((unsigned int) 0x00000001) +#define SCB_STARTRSRP0CLR_RSRPIO0_1 ((unsigned int) 0x00000002) +#define SCB_STARTRSRP0CLR_RSRPIO0_1_MASK ((unsigned int) 0x00000002) +#define SCB_STARTRSRP0CLR_RSRPIO0_2 ((unsigned int) 0x00000004) +#define SCB_STARTRSRP0CLR_RSRPIO0_2_MASK ((unsigned int) 0x00000004) +#define SCB_STARTRSRP0CLR_RSRPIO0_3 ((unsigned int) 0x00000008) +#define SCB_STARTRSRP0CLR_RSRPIO0_3_MASK ((unsigned int) 0x00000008) +#define SCB_STARTRSRP0CLR_RSRPIO0_4 ((unsigned int) 0x00000010) +#define SCB_STARTRSRP0CLR_RSRPIO0_4_MASK ((unsigned int) 0x00000010) +#define SCB_STARTRSRP0CLR_RSRPIO0_5 ((unsigned int) 0x00000020) +#define SCB_STARTRSRP0CLR_RSRPIO0_5_MASK ((unsigned int) 0x00000020) +#define SCB_STARTRSRP0CLR_RSRPIO0_6 ((unsigned int) 0x00000040) +#define SCB_STARTRSRP0CLR_RSRPIO0_6_MASK ((unsigned int) 0x00000040) +#define SCB_STARTRSRP0CLR_RSRPIO0_7 ((unsigned int) 0x00000080) +#define SCB_STARTRSRP0CLR_RSRPIO0_7_MASK ((unsigned int) 0x00000080) +#define SCB_STARTRSRP0CLR_RSRPIO0_8 ((unsigned int) 0x00000100) +#define SCB_STARTRSRP0CLR_RSRPIO0_8_MASK ((unsigned int) 0x00000100) +#define SCB_STARTRSRP0CLR_RSRPIO0_9 ((unsigned int) 0x00000200) +#define SCB_STARTRSRP0CLR_RSRPIO0_9_MASK ((unsigned int) 0x00000200) +#define SCB_STARTRSRP0CLR_RSRPIO0_10 ((unsigned int) 0x00000400) +#define SCB_STARTRSRP0CLR_RSRPIO0_10_MASK ((unsigned int) 0x00000400) +#define SCB_STARTRSRP0CLR_RSRPIO0_11 ((unsigned int) 0x00000800) +#define SCB_STARTRSRP0CLR_RSRPIO0_11_MASK ((unsigned int) 0x00000800) +#define SCB_STARTRSRP0CLR_RSRPIO1_0 ((unsigned int) 0x00001000) +#define SCB_STARTRSRP0CLR_RSRPIO1_0_MASK ((unsigned int) 0x00001000) +#define SCB_STARTRSRP0CLR_RSRPIO1_1 ((unsigned int) 0x00002000) +#define SCB_STARTRSRP0CLR_RSRPIO1_1_MASK ((unsigned int) 0x00002000) +#define SCB_STARTRSRP0CLR_RSRPIO1_2 ((unsigned int) 0x00004000) +#define SCB_STARTRSRP0CLR_RSRPIO1_2_MASK ((unsigned int) 0x00004000) +#define SCB_STARTRSRP0CLR_RSRPIO1_3 ((unsigned int) 0x00008000) +#define SCB_STARTRSRP0CLR_RSRPIO1_3_MASK ((unsigned int) 0x00008000) +#define SCB_STARTRSRP0CLR_RSRPIO1_4 ((unsigned int) 0x00010000) +#define SCB_STARTRSRP0CLR_RSRPIO1_4_MASK ((unsigned int) 0x00010000) +#define SCB_STARTRSRP0CLR_RSRPIO1_5 ((unsigned int) 0x00020000) +#define SCB_STARTRSRP0CLR_RSRPIO1_5_MASK ((unsigned int) 0x00020000) +#define SCB_STARTRSRP0CLR_RSRPIO1_6 ((unsigned int) 0x00040000) +#define SCB_STARTRSRP0CLR_RSRPIO1_6_MASK ((unsigned int) 0x00040000) +#define SCB_STARTRSRP0CLR_RSRPIO1_7 ((unsigned int) 0x00080000) +#define SCB_STARTRSRP0CLR_RSRPIO1_7_MASK ((unsigned int) 0x00080000) +#define SCB_STARTRSRP0CLR_RSRPIO1_8 ((unsigned int) 0x00100000) +#define SCB_STARTRSRP0CLR_RSRPIO1_8_MASK ((unsigned int) 0x00100000) +#define SCB_STARTRSRP0CLR_RSRPIO1_9 ((unsigned int) 0x00200000) +#define SCB_STARTRSRP0CLR_RSRPIO1_9_MASK ((unsigned int) 0x00200000) +#define SCB_STARTRSRP0CLR_RSRPIO1_10 ((unsigned int) 0x00400000) +#define SCB_STARTRSRP0CLR_RSRPIO1_10_MASK ((unsigned int) 0x00400000) +#define SCB_STARTRSRP0CLR_RSRPIO1_11 ((unsigned int) 0x00800000) +#define SCB_STARTRSRP0CLR_RSRPIO1_11_MASK ((unsigned int) 0x00800000) +#define SCB_STARTRSRP0CLR_RSRPIO2_0 ((unsigned int) 0x01000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_0_MASK ((unsigned int) 0x01000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_1 ((unsigned int) 0x02000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_1_MASK ((unsigned int) 0x02000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_2 ((unsigned int) 0x04000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_2_MASK ((unsigned int) 0x04000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_3 ((unsigned int) 0x08000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_3_MASK ((unsigned int) 0x08000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_4 ((unsigned int) 0x10000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_4_MASK ((unsigned int) 0x10000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_5 ((unsigned int) 0x20000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_5_MASK ((unsigned int) 0x20000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_6 ((unsigned int) 0x40000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_6_MASK ((unsigned int) 0x40000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_7 ((unsigned int) 0x80000000) +#define SCB_STARTRSRP0CLR_RSRPIO2_7_MASK ((unsigned int) 0x80000000) +#define SCB_STARTRSRP0CLR_MASK ((unsigned int) 0xFFFFFFFF) + +/* (Start logic status register 0) + This register reflects the status of the enabled start signal bits. Each bit + (if enabled) reflects the state of the start logic, i.e. whether or not a + wake-up signal has been received for a given pin. */ + +#define SCB_STARTSRP0_SRPIO0_0 ((unsigned int) 0x00000001) +#define SCB_STARTSRP0_SRPIO0_0_MASK ((unsigned int) 0x00000001) +#define SCB_STARTSRP0_SRPIO0_1 ((unsigned int) 0x00000002) +#define SCB_STARTSRP0_SRPIO0_1_MASK ((unsigned int) 0x00000002) +#define SCB_STARTSRP0_SRPIO0_2 ((unsigned int) 0x00000004) +#define SCB_STARTSRP0_SRPIO0_2_MASK ((unsigned int) 0x00000004) +#define SCB_STARTSRP0_SRPIO0_3 ((unsigned int) 0x00000008) +#define SCB_STARTSRP0_SRPIO0_3_MASK ((unsigned int) 0x00000008) +#define SCB_STARTSRP0_SRPIO0_4 ((unsigned int) 0x00000010) +#define SCB_STARTSRP0_SRPIO0_4_MASK ((unsigned int) 0x00000010) +#define SCB_STARTSRP0_SRPIO0_5 ((unsigned int) 0x00000020) +#define SCB_STARTSRP0_SRPIO0_5_MASK ((unsigned int) 0x00000020) +#define SCB_STARTSRP0_SRPIO0_6 ((unsigned int) 0x00000040) +#define SCB_STARTSRP0_SRPIO0_6_MASK ((unsigned int) 0x00000040) +#define SCB_STARTSRP0_SRPIO0_7 ((unsigned int) 0x00000080) +#define SCB_STARTSRP0_SRPIO0_7_MASK ((unsigned int) 0x00000080) +#define SCB_STARTSRP0_SRPIO0_8 ((unsigned int) 0x00000100) +#define SCB_STARTSRP0_SRPIO0_8_MASK ((unsigned int) 0x00000100) +#define SCB_STARTSRP0_SRPIO0_9 ((unsigned int) 0x00000200) +#define SCB_STARTSRP0_SRPIO0_9_MASK ((unsigned int) 0x00000200) +#define SCB_STARTSRP0_SRPIO0_10 ((unsigned int) 0x00000400) +#define SCB_STARTSRP0_SRPIO0_10_MASK ((unsigned int) 0x00000400) +#define SCB_STARTSRP0_SRPIO0_11 ((unsigned int) 0x00000800) +#define SCB_STARTSRP0_SRPIO0_11_MASK ((unsigned int) 0x00000800) +#define SCB_STARTSRP0_SRPIO1_0 ((unsigned int) 0x00001000) +#define SCB_STARTSRP0_SRPIO1_0_MASK ((unsigned int) 0x00001000) +#define SCB_STARTSRP0_SRPIO1_1 ((unsigned int) 0x00002000) +#define SCB_STARTSRP0_SRPIO1_1_MASK ((unsigned int) 0x00002000) +#define SCB_STARTSRP0_SRPIO1_2 ((unsigned int) 0x00004000) +#define SCB_STARTSRP0_SRPIO1_2_MASK ((unsigned int) 0x00004000) +#define SCB_STARTSRP0_SRPIO1_3 ((unsigned int) 0x00008000) +#define SCB_STARTSRP0_SRPIO1_3_MASK ((unsigned int) 0x00008000) +#define SCB_STARTSRP0_SRPIO1_4 ((unsigned int) 0x00010000) +#define SCB_STARTSRP0_SRPIO1_4_MASK ((unsigned int) 0x00010000) +#define SCB_STARTSRP0_SRPIO1_5 ((unsigned int) 0x00020000) +#define SCB_STARTSRP0_SRPIO1_5_MASK ((unsigned int) 0x00020000) +#define SCB_STARTSRP0_SRPIO1_6 ((unsigned int) 0x00040000) +#define SCB_STARTSRP0_SRPIO1_6_MASK ((unsigned int) 0x00040000) +#define SCB_STARTSRP0_SRPIO1_7 ((unsigned int) 0x00080000) +#define SCB_STARTSRP0_SRPIO1_7_MASK ((unsigned int) 0x00080000) +#define SCB_STARTSRP0_SRPIO1_8 ((unsigned int) 0x00100000) +#define SCB_STARTSRP0_SRPIO1_8_MASK ((unsigned int) 0x00100000) +#define SCB_STARTSRP0_SRPIO1_9 ((unsigned int) 0x00200000) +#define SCB_STARTSRP0_SRPIO1_9_MASK ((unsigned int) 0x00200000) +#define SCB_STARTSRP0_SRPIO1_10 ((unsigned int) 0x00400000) +#define SCB_STARTSRP0_SRPIO1_10_MASK ((unsigned int) 0x00400000) +#define SCB_STARTSRP0_SRPIO1_11 ((unsigned int) 0x00800000) +#define SCB_STARTSRP0_SRPIO1_11_MASK ((unsigned int) 0x00800000) +#define SCB_STARTSRP0_SRPIO2_0 ((unsigned int) 0x01000000) +#define SCB_STARTSRP0_SRPIO2_0_MASK ((unsigned int) 0x01000000) +#define SCB_STARTSRP0_SRPIO2_1 ((unsigned int) 0x02000000) +#define SCB_STARTSRP0_SRPIO2_1_MASK ((unsigned int) 0x02000000) +#define SCB_STARTSRP0_SRPIO2_2 ((unsigned int) 0x04000000) +#define SCB_STARTSRP0_SRPIO2_2_MASK ((unsigned int) 0x04000000) +#define SCB_STARTSRP0_SRPIO2_3 ((unsigned int) 0x08000000) +#define SCB_STARTSRP0_SRPIO2_3_MASK ((unsigned int) 0x08000000) +#define SCB_STARTSRP0_SRPIO2_4 ((unsigned int) 0x10000000) +#define SCB_STARTSRP0_SRPIO2_4_MASK ((unsigned int) 0x10000000) +#define SCB_STARTSRP0_SRPIO2_5 ((unsigned int) 0x20000000) +#define SCB_STARTSRP0_SRPIO2_5_MASK ((unsigned int) 0x20000000) +#define SCB_STARTSRP0_SRPIO2_6 ((unsigned int) 0x40000000) +#define SCB_STARTSRP0_SRPIO2_6_MASK ((unsigned int) 0x40000000) +#define SCB_STARTSRP0_SRPIO2_7 ((unsigned int) 0x80000000) +#define SCB_STARTSRP0_SRPIO2_7_MASK ((unsigned int) 0x80000000) +#define SCB_STARTSRP0_MASK ((unsigned int) 0xFFFFFFFF) + + +/* STARTAPRP1 (Start logic edge control register 1) + The STARTAPRP1 register controls the start logic inputs of ports 2 (PIO2_8 to PIO2_11) + and 3 (PIO3_0 to PIO3_3). This register selects a falling or rising edge on the + corresponding PIO input to produce a falling or rising clock edge, respectively, for the + start-up logic. + Every bit in the STARTAPRP1 register controls one port input and is connected to one + wake-up interrupt in the NVIC. Bit 0 in the STARTAPRP1 register corresponds to interrupt + 32, bit 1 to interrupt 33, up to bit 7 corresponding to interrupt 39. + Remark: Each interrupt connected to a start logic input must be enabled in the NVIC if the + corresponding PIO pin is used to wake up the chip from Deep-sleep mode.*/ + +#define SCB_STARTAPRP1_APRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTAPRP1_APRPIO2_8_MASK ((unsigned int) 0x00000001) +#define SCB_STARTAPRP1_APRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTAPRP1_APRPIO2_9_MASK ((unsigned int) 0x00000002) +#define SCB_STARTAPRP1_APRPIO2_10 ((unsigned int) 0x00000004) +#define SCB_STARTAPRP1_APRPIO2_10_MASK ((unsigned int) 0x00000004) +#define SCB_STARTAPRP1_APRPIO2_11 ((unsigned int) 0x00000008) +#define SCB_STARTAPRP1_APRPIO2_11_MASK ((unsigned int) 0x00000008) +#define SCB_STARTAPRP1_APRPIO3_0 ((unsigned int) 0x00000010) +#define SCB_STARTAPRP1_APRPIO3_0_MASK ((unsigned int) 0x00000010) +#define SCB_STARTAPRP1_APRPIO3_1 ((unsigned int) 0x00000020) +#define SCB_STARTAPRP1_APRPIO3_1_MASK ((unsigned int) 0x00000020) +#define SCB_STARTAPRP1_APRPIO3_2 ((unsigned int) 0x00000040) +#define SCB_STARTAPRP1_APRPIO3_2_MASK ((unsigned int) 0x00000040) +#define SCB_STARTAPRP1_APRPIO3_3 ((unsigned int) 0x00000080) +#define SCB_STARTAPRP1_APRPIO3_3_MASK ((unsigned int) 0x00000080) +#define SCB_STARTAPRP1_MASK ((unsigned int) 0x000000FF) + +/* STARTERP1 (Start logic signal enable register 1) + This STARTERP1 register enables or disables the start signal bits in the start logic. */ + +#define SCB_STARTERP1_ERPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTERP1_ERPIO2_8_MASK ((unsigned int) 0x00000001) +#define SCB_STARTERP1_ERPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTERP1_ERPIO2_9_MASK ((unsigned int) 0x00000002) +#define SCB_STARTERP1_ERPIO2_10 ((unsigned int) 0x00000004) +#define SCB_STARTERP1_ERPIO2_10_MASK ((unsigned int) 0x00000004) +#define SCB_STARTERP1_ERPIO2_11 ((unsigned int) 0x00000008) +#define SCB_STARTERP1_ERPIO2_11_MASK ((unsigned int) 0x00000008) +#define SCB_STARTERP1_ERPIO3_0 ((unsigned int) 0x00000010) +#define SCB_STARTERP1_ERPIO3_0_MASK ((unsigned int) 0x00000010) +#define SCB_STARTERP1_ERPIO3_1 ((unsigned int) 0x00000020) +#define SCB_STARTERP1_ERPIO3_1_MASK ((unsigned int) 0x00000020) +#define SCB_STARTERP1_ERPIO3_2 ((unsigned int) 0x00000040) +#define SCB_STARTERP1_ERPIO3_2_MASK ((unsigned int) 0x00000040) +#define SCB_STARTERP1_ERPIO3_3 ((unsigned int) 0x00000080) +#define SCB_STARTERP1_ERPIO3_3_MASK ((unsigned int) 0x00000080) +#define SCB_STARTERP1_MASK ((unsigned int) 0x000000FF) + +/* (Start logic reset register 1) + Writing a one to a bit in the STARTRSRP1CLR register resets the start logic state. The + start-up logic uses the input signals to generate a clock edge for registering a start + signal. This clock edge (falling or rising) sets the interrupt for waking up from + Deep-sleep mode. Therefore, the start-up logic states must be cleared before being used. */ + +#define SCB_STARTRSRP1CLR_RSRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTRSRP1CLR_RSRPIO2_8_MASK ((unsigned int) 0x00000001) +#define SCB_STARTRSRP1CLR_RSRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTRSRP1CLR_RSRPIO2_9_MASK ((unsigned int) 0x00000002) +#define SCB_STARTRSRP1CLR_RSRPIO2_10 ((unsigned int) 0x00000004) +#define SCB_STARTRSRP1CLR_RSRPIO2_10_MASK ((unsigned int) 0x00000004) +#define SCB_STARTRSRP1CLR_RSRPIO2_11 ((unsigned int) 0x00000008) +#define SCB_STARTRSRP1CLR_RSRPIO2_11_MASK ((unsigned int) 0x00000008) +#define SCB_STARTRSRP1CLR_RSRPIO3_0 ((unsigned int) 0x00000010) +#define SCB_STARTRSRP1CLR_RSRPIO3_0_MASK ((unsigned int) 0x00000010) +#define SCB_STARTRSRP1CLR_RSRPIO3_1 ((unsigned int) 0x00000020) +#define SCB_STARTRSRP1CLR_RSRPIO3_1_MASK ((unsigned int) 0x00000020) +#define SCB_STARTRSRP1CLR_RSRPIO3_2 ((unsigned int) 0x00000040) +#define SCB_STARTRSRP1CLR_RSRPIO3_2_MASK ((unsigned int) 0x00000040) +#define SCB_STARTRSRP1CLR_RSRPIO3_3 ((unsigned int) 0x00000080) +#define SCB_STARTRSRP1CLR_RSRPIO3_3_MASK ((unsigned int) 0x00000080) +#define SCB_STARTRSRP1CLR_MASK ((unsigned int) 0x000000FF) + +/* STARTSRP1 (Start logic status register 1) + This register reflects the status of the enabled start signals. */ + +#define SCB_STARTSRP1_SRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTSRP1_SRPIO2_8_MASK ((unsigned int) 0x00000001) +#define SCB_STARTSRP1_SRPIO2_8 ((unsigned int) 0x00000001) +#define SCB_STARTSRP1_SRPIO2_9_MASK ((unsigned int) 0x00000002) +#define SCB_STARTSRP1_SRPIO2_10 ((unsigned int) 0x00000004) +#define SCB_STARTSRP1_SRPIO2_10_MASK ((unsigned int) 0x00000004) +#define SCB_STARTSRP1_SRPIO2_11 ((unsigned int) 0x00000008) +#define SCB_STARTSRP1_SRPIO2_11_MASK ((unsigned int) 0x00000008) +#define SCB_STARTSRP1_SRPIO3_0 ((unsigned int) 0x00000010) +#define SCB_STARTSRP1_SRPIO3_0_MASK ((unsigned int) 0x00000010) +#define SCB_STARTSRP1_SRPIO3_1 ((unsigned int) 0x00000020) +#define SCB_STARTSRP1_SRPIO3_1_MASK ((unsigned int) 0x00000020) +#define SCB_STARTSRP1_SRPIO3_2 ((unsigned int) 0x00000040) +#define SCB_STARTSRP1_SRPIO3_2_MASK ((unsigned int) 0x00000040) +#define SCB_STARTSRP1_SRPIO3_3 ((unsigned int) 0x00000080) +#define SCB_STARTSRP1_SRPIO3_3_MASK ((unsigned int) 0x00000080) +#define SCB_STARTSRP1_MASK ((unsigned int) 0x000000FF) + +/* PDSLEEPCFG (Deep-sleep mode configuration register) + The bits in this register can be programmed to indicate the state the chip must enter when + the Deep-sleep mode is asserted by the ARM. The value of the PDSLEEPCFG register + will be automatically loaded into the PDRUNCFG register when the Sleep mode is + entered. */ + +#define SCB_PDSLEEPCFG_IRCOUT_PD ((unsigned int) 0x00000001) +#define SCB_PDSLEEPCFG_IRCOUT_PD_MASK ((unsigned int) 0x00000001) +#define SCB_PDSLEEPCFG_IRC_PD ((unsigned int) 0x00000002) +#define SCB_PDSLEEPCFG_IRC_PD_MASK ((unsigned int) 0x00000002) +#define SCB_PDSLEEPCFG_FLASH_PD ((unsigned int) 0x00000004) +#define SCB_PDSLEEPCFG_FLASH_PD_MASK ((unsigned int) 0x00000004) +#define SCB_PDSLEEPCFG_BOD_PD ((unsigned int) 0x00000008) +#define SCB_PDSLEEPCFG_BOD_PD_MASK ((unsigned int) 0x00000008) +#define SCB_PDSLEEPCFG_ADC_PD ((unsigned int) 0x00000010) +#define SCB_PDSLEEPCFG_ADC_PD_MASK ((unsigned int) 0x00000010) +#define SCB_PDSLEEPCFG_SYSOSC_PD ((unsigned int) 0x00000020) +#define SCB_PDSLEEPCFG_SYSOSC_PD_MASK ((unsigned int) 0x00000020) +#define SCB_PDSLEEPCFG_WDTOSC_PD ((unsigned int) 0x00000040) +#define SCB_PDSLEEPCFG_WDTOSC_PD_MASK ((unsigned int) 0x00000040) +#define SCB_PDSLEEPCFG_SYSPLL_PD ((unsigned int) 0x00000080) +#define SCB_PDSLEEPCFG_SYSPLL_PD_MASK ((unsigned int) 0x00000080) +#define SCB_PDSLEEPCFG_USBPLL_PD ((unsigned int) 0x00000100) +#define SCB_PDSLEEPCFG_USBPLL_PD_MASK ((unsigned int) 0x00000100) +#define SCB_PDSLEEPCFG_USBPAD_PD ((unsigned int) 0x00000400) +#define SCB_PDSLEEPCFG_USBPAD_PD_MASK ((unsigned int) 0x00000400) + +/* PDAWAKECFG (Wake-up configuration register) + The bits in this register can be programmed to indicate the state the chip must enter when + it is waking up from Deep-sleep mode. */ + +#define SCB_PDAWAKECFG_IRCOUT_PD ((unsigned int) 0x00000001) +#define SCB_PDAWAKECFG_IRCOUT_PD_MASK ((unsigned int) 0x00000001) +#define SCB_PDAWAKECFG_IRC_PD ((unsigned int) 0x00000002) +#define SCB_PDAWAKECFG_IRC_PD_MASK ((unsigned int) 0x00000002) +#define SCB_PDAWAKECFG_FLASH_PD ((unsigned int) 0x00000004) +#define SCB_PDAWAKECFG_FLASH_PD_MASK ((unsigned int) 0x00000004) +#define SCB_PDAWAKECFG_BOD_PD ((unsigned int) 0x00000008) +#define SCB_PDAWAKECFG_BOD_PD_MASK ((unsigned int) 0x00000008) +#define SCB_PDAWAKECFG_ADC_PD ((unsigned int) 0x00000010) +#define SCB_PDAWAKECFG_ADC_PD_MASK ((unsigned int) 0x00000010) +#define SCB_PDAWAKECFG_SYSOSC_PD ((unsigned int) 0x00000020) +#define SCB_PDAWAKECFG_SYSOSC_PD_MASK ((unsigned int) 0x00000020) +#define SCB_PDAWAKECFG_WDTOSC_PD ((unsigned int) 0x00000040) +#define SCB_PDAWAKECFG_WDTOSC_PD_MASK ((unsigned int) 0x00000040) +#define SCB_PDAWAKECFG_SYSPLL_PD ((unsigned int) 0x00000080) +#define SCB_PDAWAKECFG_SYSPLL_PD_MASK ((unsigned int) 0x00000080) +#define SCB_PDAWAKECFG_USBPLL_PD ((unsigned int) 0x00000100) +#define SCB_PDAWAKECFG_USBPLL_PD_MASK ((unsigned int) 0x00000100) +#define SCB_PDAWAKECFG_USBPAD_PD ((unsigned int) 0x00000400) +#define SCB_PDAWAKECFG_USBPAD_PD_MASK ((unsigned int) 0x00000400) + +/* PDRUNCFG (Power-down configuration register) + The bits in the PDRUNCFG register control the power to the various analog blocks. This + register can be written to at any time while the chip is running, and a write will take effect + immediately with the exception of the power-down signal to the IRC. Setting a 1 powers-down + a peripheral and 0 enables it. */ + +#define SCB_PDRUNCFG_IRCOUT ((unsigned int) 0x00000001) // IRC oscillator output power-down +#define SCB_PDRUNCFG_IRCOUT_MASK ((unsigned int) 0x00000001) +#define SCB_PDRUNCFG_IRC ((unsigned int) 0x00000002) // IRC oscillator power-down +#define SCB_PDRUNCFG_IRC_MASK ((unsigned int) 0x00000002) +#define SCB_PDRUNCFG_FLASH ((unsigned int) 0x00000004) // Flash power-down +#define SCB_PDRUNCFG_FLASH_MASK ((unsigned int) 0x00000004) +#define SCB_PDRUNCFG_BOD ((unsigned int) 0x00000008) // Brown-out detector power-down +#define SCB_PDRUNCFG_BOD_MASK ((unsigned int) 0x00000008) +#define SCB_PDRUNCFG_ADC ((unsigned int) 0x00000010) // ADC power-down +#define SCB_PDRUNCFG_ADC_MASK ((unsigned int) 0x00000010) +#define SCB_PDRUNCFG_SYSOSC ((unsigned int) 0x00000020) // System oscillator power-down +#define SCB_PDRUNCFG_SYSOSC_MASK ((unsigned int) 0x00000020) +#define SCB_PDRUNCFG_WDTOSC ((unsigned int) 0x00000040) // Watchdog oscillator power-down +#define SCB_PDRUNCFG_WDTOSC_MASK ((unsigned int) 0x00000040) +#define SCB_PDRUNCFG_SYSPLL ((unsigned int) 0x00000080) // System PLL power-down +#define SCB_PDRUNCFG_SYSPLL_MASK ((unsigned int) 0x00000080) +#define SCB_PDRUNCFG_USBPLL ((unsigned int) 0x00000100) // USB PLL power-down +#define SCB_PDRUNCFG_USBPLL_MASK ((unsigned int) 0x00000100) +#define SCB_PDRUNCFG_USBPAD ((unsigned int) 0x00000400) // USB PHY power-down +#define SCB_PDRUNCFG_USBPAD_MASK ((unsigned int) 0x00000400) + +/* DEVICE_ID (Device ID Register) + This device ID register is a read-only register and contains the device ID for each + LPC13xx part. This register is also read by the ISP/IAP commands. */ + +#define SCB_DEVICEID_LPC1311FHN33 ((unsigned int) 0x2C42502B) +#define SCB_DEVICEID_LPC1313FHN33 ((unsigned int) 0x2C40102B) +#define SCB_DEVICEID_LPC1313FBD48 ((unsigned int) 0x2C40102B) +#define SCB_DEVICEID_LPC1342FHN33 ((unsigned int) 0x3D01402B) +#define SCB_DEVICEID_LPC1343FHN33 ((unsigned int) 0x3D00002B) +#define SCB_DEVICEID_LPC1343FBD48 ((unsigned int) 0x3D00002B) + +/*############################################################################## +## Data Watchpoint and Trace Unit (DWT) +##############################################################################*/ +// For more information, see Cortex-M3 Technical Reference Manual 8.3 +// This block is optional and not all comparators or functionality may +// be present on all chips, though basic DWT functionality is present +// on the LPC1343 since CYCNT works + +#define DWT_CTRL (*(pREG32 (0xE0001000))) // Control register +#define DWT_CYCCNT (*(pREG32 (0xE0001004))) // Cycle counter (useful for rough performance testing) +#define DWT_CPICNT (*(pREG32 (0xE0001008))) // CPI Count Register +#define DWT_EXCCNT (*(pREG32 (0xE000100C))) // Exception overhead count register +#define DWT_SLEEPCNT (*(pREG32 (0xE0001010))) // Sleep count register +#define DWT_LSUCNT (*(pREG32 (0xE0001014))) // LSU count register +#define DWT_FOLDCNT (*(pREG32 (0xE0001018))) // Folder-instruction count register +#define DWT_PCSR (*(pREG32 (0xE000101C))) // Program counter sample register +#define DWT_COMP0 (*(pREG32 (0xE0001020))) // Comparator register 0 +#define DWT_MASK0 (*(pREG32 (0xE0001024))) // Mask register 0 +#define DWT_FUNCTION0 (*(pREG32 (0xE0001028))) // Function register 0 +#define DWT_COMP1 (*(pREG32 (0xE0001030))) // Comparator register 1 +#define DWT_MASK1 (*(pREG32 (0xE0001034))) // Mask register 1 +#define DWT_FUNCTION1 (*(pREG32 (0xE0001038))) // Function register 1 +#define DWT_COMP2 (*(pREG32 (0xE0001040))) // Comparator register 2 +#define DWT_MASK2 (*(pREG32 (0xE0001044))) // Mask register 2 +#define DWT_FUNCTION2 (*(pREG32 (0xE0001048))) // Function register 2 +#define DWT_COMP3 (*(pREG32 (0xE0001050))) // Comparator register 3 +#define DWT_MASK3 (*(pREG32 (0xE0001054))) // Mask register 3 +#define DWT_FUNCTION3 (*(pREG32 (0xE0001058))) // Function register 3 + +/*############################################################################## +## Power Management Unit (PMU) +##############################################################################*/ + +#define PMU_BASE_ADDRESS (0x40038000) + +#define PMU_PMUCTRL (*(pREG32 (0x40038000))) // Power control register +#define PMU_GPREG0 (*(pREG32 (0x40038004))) // General purpose register 0 +#define PMU_GPREG1 (*(pREG32 (0x40038008))) // General purpose register 1 +#define PMU_GPREG2 (*(pREG32 (0x4003800C))) // General purpose register 2 +#define PMU_GPREG3 (*(pREG32 (0x40038010))) // General purpose register 3 +#define PMU_GPREG4 (*(pREG32 (0x40038014))) // General purpose register 4 + +#define PMU_PMUCTRL_DPDEN_MASK ((unsigned int) 0x00000002) // Deep power-down enable +#define PMU_PMUCTRL_DPDEN_DEEPPOWERDOWN ((unsigned int) 0x00000002) // WFI will enter deep power-down mode +#define PMU_PMUCTRL_DPDEN_SLEEP ((unsigned int) 0x00000000) // WFI will enter sleep mode +#define PMU_PMUCTRL_DPDFLAG_MASK ((unsigned int) 0x00000800) // Deep power-down flag +#define PMU_PMUCTRL_DPDFLAG ((unsigned int) 0x00000800) + +/* GPREG0..3 (General purpose registers 0 to 3) + The general purpose registers retain data through the Deep power-down mode when + power is still applied to the VDD(3V3) pin but the chip has entered Deep power-down mode. + Only a “cold” boot when all power has been completely removed from the chip will reset + the general purpose registers. */ + +#define PMU_GPREG0_GPDATA_MASK ((unsigned int) 0xFFFFFFFF) +#define PMU_GPREG1_GPDATA_MASK ((unsigned int) 0xFFFFFFFF) +#define PMU_GPREG2_GPDATA_MASK ((unsigned int) 0xFFFFFFFF) +#define PMU_GPREG3_GPDATA_MASK ((unsigned int) 0xFFFFFFFF) + +/* GPREG4 (General purpose register 4) + The general purpose register 4 retains data through the Deep power-down mode when + power is still applied to the VDD(3V3) pin but the chip has entered Deep power-down mode. + Only a “cold” boot, when all power has been completely removed from the chip, will reset + the general purpose registers. + + Remark: If the external voltage applied on pin VDD(3V3) drops below V, the + hysteresis of the WAKEUP input pin has to be disabled in order for the chip to wake up + from Deep power-down mode. */ + +#define PMU_GPREG4_GPDATA_MASK ((unsigned int) 0xFFFFF800) +#define PMU_GPREG4_WAKEUPHYS_MASK ((unsigned int) 0x00000400) +#define PMU_GPREG4_WAKEUPHYS_HYSTERESISENABLED ((unsigned int) 0x00000400) +#define PMU_GPREG4_WAKEUPHYS_HYSTERESISDISABLED ((unsigned int) 0x00000000) +#define PMU_GPREG4_GPDATA_MASK ((unsigned int) 0xFFFFF800) + +/*############################################################################## +## I/O Control (IOCON) +##############################################################################*/ + +#define IOCON_BASE_ADDRESS (0x40044000) + +/* Values that should be common to all pins, though they are also defined + on the individual pin level in case they change with a pin on any future + device */ + +#define IOCON_COMMON_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_COMMON_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_COMMON_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_COMMON_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_COMMON_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_COMMON_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_COMMON_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_COMMON_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_COMMON_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_6 (*(pREG32 (0x40044000))) +#define IOCON_PIO2_6_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_6_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_6_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_6_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_6_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_6_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_6_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_6_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_6_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_6_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_0 (*(pREG32 (0x40044008))) +#define IOCON_PIO2_0_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_0_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_0_FUNC_DTR ((unsigned int) 0x00000001) +#define IOCON_PIO2_0_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_0_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_0_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_0_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_0_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_0_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_0_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_0_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_nRESET_PIO0_0 (*(pREG32 (0x4004400C))) +#define IOCON_nRESET_PIO0_0_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_nRESET_PIO0_0_FUNC_RESET ((unsigned int) 0x00000000) +#define IOCON_nRESET_PIO0_0_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_nRESET_PIO0_0_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_nRESET_PIO0_0_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_nRESET_PIO0_0_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_nRESET_PIO0_0_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_nRESET_PIO0_0_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_nRESET_PIO0_0_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_nRESET_PIO0_0_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_nRESET_PIO0_0_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_1 (*(pREG32 (0x40044010))) +#define IOCON_PIO0_1_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_1_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_1_FUNC_CLKOUT ((unsigned int) 0x00000001) +#define IOCON_PIO0_1_FUNC_CT32B0_MAT2 ((unsigned int) 0x00000002) +#define IOCON_PIO0_1_FUNC_USB_FTOGGLE ((unsigned int) 0x00000003) +#define IOCON_PIO0_1_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_1_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_1_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_1_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_1_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_1_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_1_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_1_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO1_8 (*(pREG32 (0x40044014))) +#define IOCON_PIO1_8_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_8_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_8_FUNC_CT16B1_CAP0 ((unsigned int) 0x00000001) +#define IOCON_PIO1_8_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_8_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_8_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_8_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_8_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_8_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_8_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_8_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_2 (*(pREG32 (0x4004401C))) +#define IOCON_PIO0_2_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_2_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_2_FUNC_SSEL ((unsigned int) 0x00000001) +#define IOCON_PIO0_2_FUNC_CT16B0_CAP0 ((unsigned int) 0x00000002) +#define IOCON_PIO0_2_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_2_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_2_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_2_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_2_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_2_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_2_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_2_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_7 (*(pREG32 (0x40044020))) +#define IOCON_PIO2_7_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_7_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_7_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_7_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_7_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_7_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_7_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_7_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_7_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_7_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_8 (*(pREG32 (0x40044024))) +#define IOCON_PIO2_8_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_8_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_8_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_8_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_8_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_8_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_8_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_8_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_8_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_8_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_1 (*(pREG32 (0x40044028))) +#define IOCON_PIO2_1_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_1_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_1_FUNC_DSR ((unsigned int) 0x00000001) +#define IOCON_PIO2_1_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_1_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_1_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_1_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_1_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_1_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_1_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_1_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_3 (*(pREG32 (0x4004402C))) +#define IOCON_PIO0_3_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_3_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_3_FUNC_USB_VBUS ((unsigned int) 0x00000001) +#define IOCON_PIO0_3_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_3_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_3_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_3_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_3_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_3_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_3_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_3_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_4 (*(pREG32 (0x40044030))) +#define IOCON_PIO0_4_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_4_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_4_FUNC_I2CSCL ((unsigned int) 0x00000001) +#define IOCON_PIO0_4_I2CMODE_MASK ((unsigned int) 0x00000300) +#define IOCON_PIO0_4_I2CMODE_STANDARDI2C ((unsigned int) 0x00000000) +#define IOCON_PIO0_4_I2CMODE_STANDARDIO ((unsigned int) 0x00000100) +#define IOCON_PIO0_4_I2CMODE_FASTPLUSI2C ((unsigned int) 0x00000200) + +#define IOCON_PIO0_5 (*(pREG32 (0x40044034))) +#define IOCON_PIO0_5_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_5_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_5_FUNC_I2CSDA ((unsigned int) 0x00000001) +#define IOCON_PIO0_5_I2CMODE_MASK ((unsigned int) 0x00000300) +#define IOCON_PIO0_5_I2CMODE_STANDARDI2C ((unsigned int) 0x00000000) +#define IOCON_PIO0_5_I2CMODE_STANDARDIO ((unsigned int) 0x00000100) +#define IOCON_PIO0_5_I2CMODE_FASTPLUSI2C ((unsigned int) 0x00000200) + +#define IOCON_PIO1_9 (*(pREG32 (0x40044038))) +#define IOCON_PIO1_9_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_9_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_9_FUNC_CT16B1_MAT0 ((unsigned int) 0x00000001) +#define IOCON_PIO1_9_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_9_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_9_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_9_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_9_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_9_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_9_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_9_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO3_4 (*(pREG32 (0x4004403C))) +#define IOCON_PIO3_4_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_4_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_4_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_4_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_4_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_4_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_4_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_4_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_4_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_4_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_4 (*(pREG32 (0x40044040))) +#define IOCON_PIO2_4_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_4_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_4_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_4_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_4_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_4_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_4_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_4_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_4_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_4_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_5 (*(pREG32 (0x40044044))) +#define IOCON_PIO2_5_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_5_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_5_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_5_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_5_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_5_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_5_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_5_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_5_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_5_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO3_5 (*(pREG32 (0x40044048))) +#define IOCON_PIO3_5_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_5_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_5_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_5_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_5_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_5_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_5_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_5_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_5_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_5_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_6 (*(pREG32 (0x4004404C))) +#define IOCON_PIO0_6_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_6_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_6_FUNC_USB_CONNECT ((unsigned int) 0x00000001) +#define IOCON_PIO0_6_FUNC_SCK ((unsigned int) 0x00000002) +#define IOCON_PIO0_6_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_6_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_6_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_6_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_6_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_6_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_6_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_6_HYS_ENABLE ((unsigned int) 0x00000020) + + +#define IOCON_PIO0_7 (*(pREG32 (0x40044050))) +#define IOCON_PIO0_7_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_7_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_7_FUNC_CTS ((unsigned int) 0x00000001) +#define IOCON_PIO0_7_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_7_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_7_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_7_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_7_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_7_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_7_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_7_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_9 (*(pREG32 (0x40044054))) +#define IOCON_PIO2_9_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_9_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_9_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_9_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_9_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_9_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_9_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_9_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_9_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_9_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_10 (*(pREG32 (0x40044058))) +#define IOCON_PIO2_10_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_10_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_10_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_10_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_10_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_10_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_10_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_10_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_10_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_10_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_2 (*(pREG32 (0x4004405C))) +#define IOCON_PIO2_2_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_2_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_2_FUNC_DCD ((unsigned int) 0x00000001) +#define IOCON_PIO2_2_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_2_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_2_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_2_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_2_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_2_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_2_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_2_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_8 (*(pREG32 (0x40044060))) +#define IOCON_PIO0_8_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_8_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_8_FUNC_MISO0 ((unsigned int) 0x00000001) +#define IOCON_PIO0_8_FUNC_CT16B0_MAT0 ((unsigned int) 0x00000002) +#define IOCON_PIO0_8_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_8_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_8_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_8_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_8_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_8_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_8_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_8_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO0_9 (*(pREG32 (0x40044064))) +#define IOCON_PIO0_9_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO0_9_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO0_9_FUNC_MOSI0 ((unsigned int) 0x00000001) +#define IOCON_PIO0_9_FUNC_CT16B0_MAT1 ((unsigned int) 0x00000002) +#define IOCON_PIO0_9_FUNC_SWO ((unsigned int) 0x00000003) +#define IOCON_PIO0_9_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO0_9_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO0_9_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO0_9_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO0_9_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO0_9_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO0_9_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO0_9_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_JTAG_TCK_PIO0_10 (*(pREG32 (0x40044068))) +#define IOCON_JTAG_TCK_PIO0_10_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_JTAG_TCK_PIO0_10_FUNC_SWCLK ((unsigned int) 0x00000000) +#define IOCON_JTAG_TCK_PIO0_10_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_JTAG_TCK_PIO0_10_FUNC_SCK ((unsigned int) 0x00000002) +#define IOCON_JTAG_TCK_PIO0_10_FUNC_CT16B0_MAT2 ((unsigned int) 0x00000003) +#define IOCON_JTAG_TCK_PIO0_10_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_JTAG_TCK_PIO0_10_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TCK_PIO0_10_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_JTAG_TCK_PIO0_10_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_JTAG_TCK_PIO0_10_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_JTAG_TCK_PIO0_10_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_JTAG_TCK_PIO0_10_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TCK_PIO0_10_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO1_10 (*(pREG32 (0x4004406C))) +#define IOCON_PIO1_10_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_10_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_10_FUNC_AD6 ((unsigned int) 0x00000001) +#define IOCON_PIO1_10_FUNC_CT16B1_MAT1 ((unsigned int) 0x00000002) +#define IOCON_PIO1_10_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_10_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_10_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_10_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_10_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_10_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_10_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_10_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_PIO1_10_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_PIO1_10_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_PIO1_10_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_PIO2_11 (*(pREG32 (0x40044070))) +#define IOCON_PIO2_11_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO2_11_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO2_11_FUNC_SCK0 ((unsigned int) 0x00000001) +#define IOCON_PIO2_11_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO2_11_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO2_11_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO2_11_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO2_11_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO2_11_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO2_11_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO2_11_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_JTAG_TDI_PIO0_11 (*(pREG32 (0x40044074))) +#define IOCON_JTAG_TDI_PIO0_11_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_JTAG_TDI_PIO0_11_FUNC_TDI ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDI_PIO0_11_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_JTAG_TDI_PIO0_11_FUNC_AD0 ((unsigned int) 0x00000002) +#define IOCON_JTAG_TDI_PIO0_11_FUNC_CT32B0_MAT3 ((unsigned int) 0x00000003) +#define IOCON_JTAG_TDI_PIO0_11_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_JTAG_TDI_PIO0_11_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDI_PIO0_11_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_JTAG_TDI_PIO0_11_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_JTAG_TDI_PIO0_11_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_JTAG_TDI_PIO0_11_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_JTAG_TDI_PIO0_11_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDI_PIO0_11_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_JTAG_TDI_PIO0_11_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_JTAG_TDI_PIO0_11_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDI_PIO0_11_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_JTAG_TMS_PIO1_0 (*(pREG32 (0x40044078))) +#define IOCON_JTAG_TMS_PIO1_0_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_JTAG_TMS_PIO1_0_FUNC_TMS ((unsigned int) 0x00000000) +#define IOCON_JTAG_TMS_PIO1_0_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_JTAG_TMS_PIO1_0_FUNC_AD1 ((unsigned int) 0x00000002) +#define IOCON_JTAG_TMS_PIO1_0_FUNC_CT32B1_CAP0 ((unsigned int) 0x00000003) +#define IOCON_JTAG_TMS_PIO1_0_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_JTAG_TMS_PIO1_0_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TMS_PIO1_0_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_JTAG_TMS_PIO1_0_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_JTAG_TMS_PIO1_0_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_JTAG_TMS_PIO1_0_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_JTAG_TMS_PIO1_0_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TMS_PIO1_0_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_JTAG_TMS_PIO1_0_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_JTAG_TMS_PIO1_0_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_JTAG_TMS_PIO1_0_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_JTAG_TDO_PIO1_1 (*(pREG32 (0x4004407C))) +#define IOCON_JTAG_TDO_PIO1_1_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_JTAG_TDO_PIO1_1_FUNC_TDO ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDO_PIO1_1_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_JTAG_TDO_PIO1_1_FUNC_AD2 ((unsigned int) 0x00000002) +#define IOCON_JTAG_TDO_PIO1_1_FUNC_CT32B1_MAT0 ((unsigned int) 0x00000003) +#define IOCON_JTAG_TDO_PIO1_1_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_JTAG_TDO_PIO1_1_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDO_PIO1_1_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_JTAG_TDO_PIO1_1_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_JTAG_TDO_PIO1_1_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_JTAG_TDO_PIO1_1_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_JTAG_TDO_PIO1_1_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDO_PIO1_1_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_JTAG_TDO_PIO1_1_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_JTAG_TDO_PIO1_1_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_JTAG_TDO_PIO1_1_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_JTAG_nTRST_PIO1_2 (*(pREG32 (0x40044080))) +#define IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_JTAG_nTRST_PIO1_2_FUNC_TRST ((unsigned int) 0x00000000) +#define IOCON_JTAG_nTRST_PIO1_2_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_JTAG_nTRST_PIO1_2_FUNC_AD3 ((unsigned int) 0x00000002) +#define IOCON_JTAG_nTRST_PIO1_2_FUNC_CT32B1_MAT1 ((unsigned int) 0x00000003) +#define IOCON_JTAG_nTRST_PIO1_2_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_JTAG_nTRST_PIO1_2_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_JTAG_nTRST_PIO1_2_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_JTAG_nTRST_PIO1_2_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_JTAG_nTRST_PIO1_2_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_JTAG_nTRST_PIO1_2_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_JTAG_nTRST_PIO1_2_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_JTAG_nTRST_PIO1_2_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_JTAG_nTRST_PIO1_2_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_JTAG_nTRST_PIO1_2_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_JTAG_nTRST_PIO1_2_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_PIO3_0 (*(pREG32 (0x40044084))) +#define IOCON_PIO3_0_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_0_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_0_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_0_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_0_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_0_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_0_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_0_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_0_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_0_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO3_1 (*(pREG32 (0x40044088))) +#define IOCON_PIO3_1_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_1_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_1_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_1_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_1_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_1_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_1_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_1_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_1_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_1_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO2_3 (*(pREG32 (0x4004408C))) +#define IOCON_PIO2_3_FUNC_MASK 0x7 +#define IOCON_PIO2_3_MODE_MASK 0x18 +#define IOCON_PIO2_3_HYS_MASK 0x20 +#define IOCON_PIO2_3_HYS 0x20 + +#define IOCON_SWDIO_PIO1_3 (*(pREG32 (0x40044090))) +#define IOCON_SWDIO_PIO1_3_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_SWDIO_PIO1_3_FUNC_SWDIO ((unsigned int) 0x00000000) +#define IOCON_SWDIO_PIO1_3_FUNC_GPIO ((unsigned int) 0x00000001) +#define IOCON_SWDIO_PIO1_3_FUNC_AD4 ((unsigned int) 0x00000002) +#define IOCON_SWDIO_PIO1_3_FUNC_CT32B1_MAT2 ((unsigned int) 0x00000004) +#define IOCON_SWDIO_PIO1_3_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_SWDIO_PIO1_3_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_SWDIO_PIO1_3_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_SWDIO_PIO1_3_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_SWDIO_PIO1_3_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_SWDIO_PIO1_3_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_PIO1_4 (*(pREG32 (0x40044094))) +#define IOCON_PIO1_4_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_4_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_4_FUNC_AD5 ((unsigned int) 0x00000001) +#define IOCON_PIO1_4_FUNC_CT32B1_MAT3 ((unsigned int) 0x00000002) +#define IOCON_PIO1_4_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_4_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_4_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_4_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_4_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_4_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_4_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_4_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_PIO1_4_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_PIO1_4_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_PIO1_4_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_PIO1_11 (*(pREG32 (0x40044098))) +#define IOCON_PIO1_11_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_11_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_11_FUNC_AD7 ((unsigned int) 0x00000001) +#define IOCON_PIO1_11_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_11_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_11_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_11_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_11_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_11_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_11_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_11_HYS_ENABLE ((unsigned int) 0x00000020) +#define IOCON_PIO1_11_ADMODE_MASK ((unsigned int) 0x00000080) +#define IOCON_PIO1_11_ADMODE_ANALOG ((unsigned int) 0x00000000) +#define IOCON_PIO1_11_ADMODE_DIGITAL ((unsigned int) 0x00000080) + +#define IOCON_PIO3_2 (*(pREG32 (0x4004409C))) +#define IOCON_PIO3_2_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_2_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_2_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_2_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_2_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_2_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_2_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_2_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_2_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_2_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO1_5 (*(pREG32 (0x400440A0))) +#define IOCON_PIO1_5_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_5_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_5_FUNC_RTS ((unsigned int) 0x00000001) +#define IOCON_PIO1_5_FUNC_CT32B0_CAP0 ((unsigned int) 0x00000002) +#define IOCON_PIO1_5_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_5_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_5_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_5_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_5_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_5_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_5_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_5_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO1_6 (*(pREG32 (0x400440A4))) +#define IOCON_PIO1_6_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_6_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_6_FUNC_UART_RXD ((unsigned int) 0x00000001) +#define IOCON_PIO1_6_FUNC_CT32B0_MAT0 ((unsigned int) 0x00000002) +#define IOCON_PIO1_6_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_6_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_6_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_6_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_6_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_6_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_6_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_6_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO1_7 (*(pREG32 (0x400440A8))) +#define IOCON_PIO1_7_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO1_7_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO1_7_FUNC_UART_TXD ((unsigned int) 0x00000001) +#define IOCON_PIO1_7_FUNC_CT32B0_MAT1 ((unsigned int) 0x00000002) +#define IOCON_PIO1_7_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO1_7_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO1_7_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO1_7_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO1_7_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO1_7_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO1_7_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO1_7_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_PIO3_3 (*(pREG32 (0x400440AC))) +#define IOCON_PIO3_3_FUNC_MASK ((unsigned int) 0x00000007) +#define IOCON_PIO3_3_FUNC_GPIO ((unsigned int) 0x00000000) +#define IOCON_PIO3_3_MODE_MASK ((unsigned int) 0x00000018) +#define IOCON_PIO3_3_MODE_INACTIVE ((unsigned int) 0x00000000) +#define IOCON_PIO3_3_MODE_PULLDOWN ((unsigned int) 0x00000008) +#define IOCON_PIO3_3_MODE_PULLUP ((unsigned int) 0x00000010) +#define IOCON_PIO3_3_MODE_REPEATER ((unsigned int) 0x00000018) +#define IOCON_PIO3_3_HYS_MASK ((unsigned int) 0x00000020) +#define IOCON_PIO3_3_HYS_DISABLE ((unsigned int) 0x00000000) +#define IOCON_PIO3_3_HYS_ENABLE ((unsigned int) 0x00000020) + +#define IOCON_SCKLOC (*(pREG32 (0x400440B0))) +#define IOCON_SCKLOC_SCKPIN_MASK ((unsigned int) 0x00000003) +#define IOCON_SCKLOC_SCKPIN_PIO0_10 ((unsigned int) 0x00000000) // Set SCK function to pin 0.10 +#define IOCON_SCKLOC_SCKPIN_PIO2_11 ((unsigned int) 0x00000001) // Set SCK function to pin 2.11 +#define IOCON_SCKLOC_SCKPIN_PIO0_6 ((unsigned int) 0x00000003) // Set SCK function to pin 0.6 + +/*############################################################################## +## Nested Vectored Interrupt Controller +##############################################################################*/ + +#define NVIC_BASE_ADDRESS (0xE000E100) + +typedef struct +{ + volatile uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + volatile uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + volatile uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + volatile uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + volatile uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + volatile uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + volatile uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */ +} NVIC_Type; + +#define NVIC ((NVIC_Type *) NVIC_BASE_ADDRESS) + +static inline void __enable_irq() { __asm volatile ("cpsie i"); } +static inline void __disable_irq() { __asm volatile ("cpsid i"); } + +typedef enum IRQn +{ +/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ + +/****** LPC13xx Specific Interrupt Numbers *******************************************************/ + WAKEUP0_IRQn = 0, /*!< All I/O pins can be used as wakeup source. */ + WAKEUP1_IRQn = 1, /*!< There are 40 pins in total for LPC17xx */ + WAKEUP2_IRQn = 2, + WAKEUP3_IRQn = 3, + WAKEUP4_IRQn = 4, + WAKEUP5_IRQn = 5, + WAKEUP6_IRQn = 6, + WAKEUP7_IRQn = 7, + WAKEUP8_IRQn = 8, + WAKEUP9_IRQn = 9, + WAKEUP10_IRQn = 10, + WAKEUP11_IRQn = 11, + WAKEUP12_IRQn = 12, + WAKEUP13_IRQn = 13, + WAKEUP14_IRQn = 14, + WAKEUP15_IRQn = 15, + WAKEUP16_IRQn = 16, + WAKEUP17_IRQn = 17, + WAKEUP18_IRQn = 18, + WAKEUP19_IRQn = 19, + WAKEUP20_IRQn = 20, + WAKEUP21_IRQn = 21, + WAKEUP22_IRQn = 22, + WAKEUP23_IRQn = 23, + WAKEUP24_IRQn = 24, + WAKEUP25_IRQn = 25, + WAKEUP26_IRQn = 26, + WAKEUP27_IRQn = 27, + WAKEUP28_IRQn = 28, + WAKEUP29_IRQn = 29, + WAKEUP30_IRQn = 30, + WAKEUP31_IRQn = 31, + WAKEUP32_IRQn = 32, + WAKEUP33_IRQn = 33, + WAKEUP34_IRQn = 34, + WAKEUP35_IRQn = 35, + WAKEUP36_IRQn = 36, + WAKEUP37_IRQn = 37, + WAKEUP38_IRQn = 38, + WAKEUP39_IRQn = 39, + I2C_IRQn = 40, /*!< I2C Interrupt */ + TIMER_16_0_IRQn = 41, /*!< 16-bit Timer0 Interrupt */ + TIMER_16_1_IRQn = 42, /*!< 16-bit Timer1 Interrupt */ + TIMER_32_0_IRQn = 43, /*!< 32-bit Timer0 Interrupt */ + TIMER_32_1_IRQn = 44, /*!< 32-bit Timer1 Interrupt */ + SSP_IRQn = 45, /*!< SSP Interrupt */ + UART_IRQn = 46, /*!< UART Interrupt */ + USB_IRQn = 47, /*!< USB Regular Interrupt */ + USB_FIQn = 48, /*!< USB Fast Interrupt */ + ADC_IRQn = 49, /*!< A/D Converter Interrupt */ + WDT_IRQn = 50, /*!< Watchdog timer Interrupt */ + BOD_IRQn = 51, /*!< Brown Out Detect(BOD) Interrupt */ + EINT3_IRQn = 53, /*!< External Interrupt 3 Interrupt */ + EINT2_IRQn = 54, /*!< External Interrupt 2 Interrupt */ + EINT1_IRQn = 55, /*!< External Interrupt 1 Interrupt */ + EINT0_IRQn = 56, /*!< External Interrupt 0 Interrupt */ +} IRQn_t; + +static inline void NVIC_EnableIRQ(IRQn_t IRQn) +{ + NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +static inline void NVIC_DisableIRQ(IRQn_t IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); +} + +/*############################################################################## +## GPIO - General Purpose I/O +##############################################################################*/ + +#define GPIO_GPIO0_BASE (0x50000000) +#define GPIO_GPIO1_BASE (0x50010000) +#define GPIO_GPIO2_BASE (0x50020000) +#define GPIO_GPIO3_BASE (0x50030000) + +#define GPIO_GPIO0DATA (*(pREG32 (0x50003FFC))) // Port data register +#define GPIO_GPIO0DIR (*(pREG32 (0x50008000))) // Data direction register +#define GPIO_GPIO0IS (*(pREG32 (0x50008004))) // Interrupt sense register +#define GPIO_GPIO0IBE (*(pREG32 (0x50008008))) // Interrupt both edges register +#define GPIO_GPIO0IEV (*(pREG32 (0x5000800C))) // Interrupt event register +#define GPIO_GPIO0IE (*(pREG32 (0x50008010))) // Interrupt mask register +#define GPIO_GPIO0RIS (*(pREG32 (0x50008014))) // Raw interrupt status register +#define GPIO_GPIO0MIS (*(pREG32 (0x50008018))) // Masked interrupt status register +#define GPIO_GPIO0IC (*(pREG32 (0x5000801C))) // Interrupt clear register + +#define GPIO_GPIO1DATA (*(pREG32 (0x50013FFC))) // Port data register +#define GPIO_GPIO1DIR (*(pREG32 (0x50018000))) // Data direction register +#define GPIO_GPIO1IS (*(pREG32 (0x50018004))) // Interrupt sense register +#define GPIO_GPIO1IBE (*(pREG32 (0x50018008))) // Interrupt both edges register +#define GPIO_GPIO1IEV (*(pREG32 (0x5001800C))) // Interrupt event register +#define GPIO_GPIO1IE (*(pREG32 (0x50018010))) // Interrupt mask register +#define GPIO_GPIO1RIS (*(pREG32 (0x50018014))) // Raw interrupt status register +#define GPIO_GPIO1MIS (*(pREG32 (0x50018018))) // Masked interrupt status register +#define GPIO_GPIO1IC (*(pREG32 (0x5001801C))) // Interrupt clear register + +#define GPIO_GPIO2DATA (*(pREG32 (0x50023FFC))) // Port data register +#define GPIO_GPIO2DIR (*(pREG32 (0x50028000))) // Data direction register +#define GPIO_GPIO2IS (*(pREG32 (0x50028004))) // Interrupt sense register +#define GPIO_GPIO2IBE (*(pREG32 (0x50028008))) // Interrupt both edges register +#define GPIO_GPIO2IEV (*(pREG32 (0x5002800C))) // Interrupt event register +#define GPIO_GPIO2IE (*(pREG32 (0x50028010))) // Interrupt mask register +#define GPIO_GPIO2RIS (*(pREG32 (0x50028014))) // Raw interrupt status register +#define GPIO_GPIO2MIS (*(pREG32 (0x50028018))) // Masked interrupt status register +#define GPIO_GPIO2IC (*(pREG32 (0x5002801C))) // Interrupt clear register + +#define GPIO_GPIO3DATA (*(pREG32 (0x50033FFC))) // Port data register +#define GPIO_GPIO3DIR (*(pREG32 (0x50038000))) // Data direction register +#define GPIO_GPIO3IS (*(pREG32 (0x50038004))) // Interrupt sense register +#define GPIO_GPIO3IBE (*(pREG32 (0x50038008))) // Interrupt both edges register +#define GPIO_GPIO3IEV (*(pREG32 (0x5003800C))) // Interrupt event register +#define GPIO_GPIO3IE (*(pREG32 (0x50038010))) // Interrupt mask register +#define GPIO_GPIO3RIS (*(pREG32 (0x50038014))) // Raw interrupt status register +#define GPIO_GPIO3MIS (*(pREG32 (0x50038018))) // Masked interrupt status register +#define GPIO_GPIO3IC (*(pREG32 (0x5003801C))) // Interrupt clear register + +#define GPIO_IO_P0 ((unsigned int) 0x00000001) +#define GPIO_IO_P1 ((unsigned int) 0x00000002) +#define GPIO_IO_P2 ((unsigned int) 0x00000004) +#define GPIO_IO_P3 ((unsigned int) 0x00000008) +#define GPIO_IO_P4 ((unsigned int) 0x00000010) +#define GPIO_IO_P5 ((unsigned int) 0x00000020) +#define GPIO_IO_P6 ((unsigned int) 0x00000040) +#define GPIO_IO_P7 ((unsigned int) 0x00000080) +#define GPIO_IO_P8 ((unsigned int) 0x00000100) +#define GPIO_IO_P9 ((unsigned int) 0x00000200) +#define GPIO_IO_P10 ((unsigned int) 0x00000400) +#define GPIO_IO_P11 ((unsigned int) 0x00000800) +#define GPIO_IO_ALL ((unsigned int) 0x00000FFF) + +/*############################################################################## +## USB +##############################################################################*/ + +/* USB registers are defined in USB code */ +#define USB_BASE_ADDRESS (0x40020000) + +/* USB Device Interrupt Status Register */ +#define USB_DEVINTST (*(pREG32 (0x40020000))) +#define USB_DEVINTST_FRAME_MASK ((unsigned int) 0x00000001) +#define USB_DEVINTST_FRAME ((unsigned int) 0x00000001) // Frame interrupt +#define USB_DEVINTST_EP0_MASK ((unsigned int) 0x00000002) +#define USB_DEVINTST_EP0 ((unsigned int) 0x00000002) // USB core interrupt for EP0 +#define USB_DEVINTST_EP1_MASK ((unsigned int) 0x00000004) +#define USB_DEVINTST_EP1 ((unsigned int) 0x00000004) // USB core interrupt for EP1 +#define USB_DEVINTST_EP2_MASK ((unsigned int) 0x00000008) +#define USB_DEVINTST_EP2 ((unsigned int) 0x00000008) // USB core interrupt for EP2 +#define USB_DEVINTST_EP3_MASK ((unsigned int) 0x00000010) +#define USB_DEVINTST_EP3 ((unsigned int) 0x00000010) // USB core interrupt for EP3 +#define USB_DEVINTST_EP4_MASK ((unsigned int) 0x00000020) +#define USB_DEVINTST_EP4 ((unsigned int) 0x00000020) // USB core interrupt for EP4 +#define USB_DEVINTST_EP5_MASK ((unsigned int) 0x00000040) +#define USB_DEVINTST_EP5 ((unsigned int) 0x00000040) // USB core interrupt for EP5 +#define USB_DEVINTST_EP6_MASK ((unsigned int) 0x00000080) +#define USB_DEVINTST_EP6 ((unsigned int) 0x00000080) // USB core interrupt for EP6 +#define USB_DEVINTST_EP7_MASK ((unsigned int) 0x00000100) +#define USB_DEVINTST_EP7 ((unsigned int) 0x00000100) // USB core interrupt for EP7 +#define USB_DEVINTST_DEV_START_MASK ((unsigned int) 0x00000200) +#define USB_DEVINTST_DEV_START ((unsigned int) 0x00000200) +#define USB_DEVINTST_CC_EMPTY_MASK ((unsigned int) 0x00000400) +#define USB_DEVINTST_CC_EMPTY ((unsigned int) 0x00000400) +#define USB_DEVINTST_CD_FULL_MASK ((unsigned int) 0x00000800) +#define USB_DEVINTST_CD_FULL ((unsigned int) 0x00000800) +#define USB_DEVINTST_RxENDPKT_MASK ((unsigned int) 0x00001000) +#define USB_DEVINTST_RxENDPKT ((unsigned int) 0x00001000) +#define USB_DEVINTST_TxENDPKT_MASK ((unsigned int) 0x00002000) +#define USB_DEVINTST_TxENDPKT ((unsigned int) 0x00002000) + +/* USB Device Interrupt Enable Register */ +#define USB_DEVINTEN (*(pREG32 (0x40020004))) +#define USB_DEVINTEN_FRAME_MASK ((unsigned int) 0x00000001) +#define USB_DEVINTEN_FRAME ((unsigned int) 0x00000001) +#define USB_DEVINTEN_EP0_MASK ((unsigned int) 0x00000002) +#define USB_DEVINTEN_EP0 ((unsigned int) 0x00000002) +#define USB_DEVINTEN_EP1_MASK ((unsigned int) 0x00000004) +#define USB_DEVINTEN_EP1 ((unsigned int) 0x00000004) +#define USB_DEVINTEN_EP2_MASK ((unsigned int) 0x00000008) +#define USB_DEVINTEN_EP2 ((unsigned int) 0x00000008) +#define USB_DEVINTEN_EP3_MASK ((unsigned int) 0x00000010) +#define USB_DEVINTEN_EP3 ((unsigned int) 0x00000010) +#define USB_DEVINTEN_EP4_MASK ((unsigned int) 0x00000020) +#define USB_DEVINTEN_EP4 ((unsigned int) 0x00000020) +#define USB_DEVINTEN_EP5_MASK ((unsigned int) 0x00000040) +#define USB_DEVINTEN_EP5 ((unsigned int) 0x00000040) +#define USB_DEVINTEN_EP6_MASK ((unsigned int) 0x00000080) +#define USB_DEVINTEN_EP6 ((unsigned int) 0x00000080) +#define USB_DEVINTEN_EP7_MASK ((unsigned int) 0x00000100) +#define USB_DEVINTEN_EP7 ((unsigned int) 0x00000100) +#define USB_DEVINTEN_DEV_START_MASK ((unsigned int) 0x00000200) +#define USB_DEVINTEN_DEV_START ((unsigned int) 0x00000200) +#define USB_DEVINTEN_CC_EMPTY_MASK ((unsigned int) 0x00000400) +#define USB_DEVINTEN_CC_EMPTY ((unsigned int) 0x00000400) +#define USB_DEVINTEN_CD_FULL_MASK ((unsigned int) 0x00000800) +#define USB_DEVINTEN_CD_FULL ((unsigned int) 0x00000800) +#define USB_DEVINTEN_RxENDPKT_MASK ((unsigned int) 0x00001000) +#define USB_DEVINTEN_RxENDPKT ((unsigned int) 0x00001000) +#define USB_DEVINTEN_TxENDPKT_MASK ((unsigned int) 0x00002000) +#define USB_DEVINTEN_TxENDPKT ((unsigned int) 0x00002000) + +/* USB Device Interrupt Clear Register */ +#define USB_DEVINTCLR (*(pREG32 (0x40020008))) +#define USB_DEVINTCLR_FRAME_MASK ((unsigned int) 0x00000001) +#define USB_DEVINTCLR_FRAME ((unsigned int) 0x00000001) +#define USB_DEVINTCLR_EP0_MASK ((unsigned int) 0x00000002) +#define USB_DEVINTCLR_EP0 ((unsigned int) 0x00000002) +#define USB_DEVINTCLR_EP1_MASK ((unsigned int) 0x00000004) +#define USB_DEVINTCLR_EP1 ((unsigned int) 0x00000004) +#define USB_DEVINTCLR_EP2_MASK ((unsigned int) 0x00000008) +#define USB_DEVINTCLR_EP2 ((unsigned int) 0x00000008) +#define USB_DEVINTCLR_EP3_MASK ((unsigned int) 0x00000010) +#define USB_DEVINTCLR_EP3 ((unsigned int) 0x00000010) +#define USB_DEVINTCLR_EP4_MASK ((unsigned int) 0x00000020) +#define USB_DEVINTCLR_EP4 ((unsigned int) 0x00000020) +#define USB_DEVINTCLR_EP5_MASK ((unsigned int) 0x00000040) +#define USB_DEVINTCLR_EP5 ((unsigned int) 0x00000040) +#define USB_DEVINTCLR_EP6_MASK ((unsigned int) 0x00000080) +#define USB_DEVINTCLR_EP6 ((unsigned int) 0x00000080) +#define USB_DEVINTCLR_EP7_MASK ((unsigned int) 0x00000100) +#define USB_DEVINTCLR_EP7 ((unsigned int) 0x00000100) +#define USB_DEVINTCLR_DEV_START_MASK ((unsigned int) 0x00000200) +#define USB_DEVINTCLR_DEV_START ((unsigned int) 0x00000200) +#define USB_DEVINTCLR_CC_EMPTY_MASK ((unsigned int) 0x00000400) +#define USB_DEVINTCLR_CC_EMPTY ((unsigned int) 0x00000400) +#define USB_DEVINTCLR_CD_FULL_MASK ((unsigned int) 0x00000800) +#define USB_DEVINTCLR_CD_FULL ((unsigned int) 0x00000800) +#define USB_DEVINTCLR_RxENDPKT_MASK ((unsigned int) 0x00001000) +#define USB_DEVINTCLR_RxENDPKT ((unsigned int) 0x00001000) +#define USB_DEVINTCLR_TxENDPKT_MASK ((unsigned int) 0x00002000) +#define USB_DEVINTCLR_TxENDPKT ((unsigned int) 0x00002000) + +/* USB Device Interrupt Set Register */ +#define USB_DEVINTSET (*(pREG32 (0x4002000C))) +#define USB_DEVINTSET_FRAME_MASK ((unsigned int) 0x00000001) +#define USB_DEVINTSET_FRAME ((unsigned int) 0x00000001) +#define USB_DEVINTSET_EP0_MASK ((unsigned int) 0x00000002) +#define USB_DEVINTSET_EP0 ((unsigned int) 0x00000002) +#define USB_DEVINTSET_EP1_MASK ((unsigned int) 0x00000004) +#define USB_DEVINTSET_EP1 ((unsigned int) 0x00000004) +#define USB_DEVINTSET_EP2_MASK ((unsigned int) 0x00000008) +#define USB_DEVINTSET_EP2 ((unsigned int) 0x00000008) +#define USB_DEVINTSET_EP3_MASK ((unsigned int) 0x00000010) +#define USB_DEVINTSET_EP3 ((unsigned int) 0x00000010) +#define USB_DEVINTSET_EP4_MASK ((unsigned int) 0x00000020) +#define USB_DEVINTSET_EP4 ((unsigned int) 0x00000020) +#define USB_DEVINTSET_EP5_MASK ((unsigned int) 0x00000040) +#define USB_DEVINTSET_EP5 ((unsigned int) 0x00000040) +#define USB_DEVINTSET_EP6_MASK ((unsigned int) 0x00000080) +#define USB_DEVINTSET_EP6 ((unsigned int) 0x00000080) +#define USB_DEVINTSET_EP7_MASK ((unsigned int) 0x00000100) +#define USB_DEVINTSET_EP7 ((unsigned int) 0x00000100) +#define USB_DEVINTSET_DEV_START_MASK ((unsigned int) 0x00000200) +#define USB_DEVINTSET_DEV_START ((unsigned int) 0x00000200) +#define USB_DEVINTSET_CC_EMPTY_MASK ((unsigned int) 0x00000400) +#define USB_DEVINTSET_CC_EMPTY ((unsigned int) 0x00000400) +#define USB_DEVINTSET_CD_FULL_MASK ((unsigned int) 0x00000800) +#define USB_DEVINTSET_CD_FULL ((unsigned int) 0x00000800) +#define USB_DEVINTSET_RxENDPKT_MASK ((unsigned int) 0x00001000) +#define USB_DEVINTSET_RxENDPKT ((unsigned int) 0x00001000) +#define USB_DEVINTSET_TxENDPKT_MASK ((unsigned int) 0x00002000) +#define USB_DEVINTSET_TxENDPKT ((unsigned int) 0x00002000) + +/* USB Command Code Register */ +#define USB_CMDCODE (*(pREG32 (0x40020010))) +#define USB_CMDCODE_CMD_PHASE_WRITE ((unsigned int) 0x00000100) +#define USB_CMDCODE_CMD_PHASE_READ ((unsigned int) 0x00000200) +#define USB_CMDCODE_CMD_PHASE_COMMAND ((unsigned int) 0x00000500) +#define USB_CMDCODE_CMD_PHASE_MASK ((unsigned int) 0x0000FF00) +#define USB_CMDCODE_CMD_CODE_MASK ((unsigned int) 0x00FF0000) +#define USB_CMDCODE_CMD_WDATA_MASK ((unsigned int) 0x00FF0000) + +/* USB Command Data Register */ +#define USB_CMDDATA (*(pREG32 (0x40020014))) +#define USB_CMDDATA_CMD_RDATA_MASK ((unsigned int) 0x000000FF) + +/* USB Receive Data Register */ +#define USB_RXDATA (*(pREG32 (0x40020018))) + +/* USB Transmit Data Register */ +#define USB_TXDATA (*(pREG32 (0x4002001C))) + +/* USB Receive Packet Length Register */ +#define USB_RXPLEN (*(pREG32 (0x40020020))) +#define USB_RXPLEN_PKT_LNGTH_MASK ((unsigned int) 0x000003FF) +#define USB_RXPLEN_DV_MASK ((unsigned int) 0x00000400) +#define USB_RXPLEN_DV ((unsigned int) 0x00000400) + +/* USB Transmit Packet Length Register */ +#define USB_TXPLEN (*(pREG32 (0x40020024))) +#define USB_TXPLEN_PKT_LNGTH_MASK 0x3FF + +/* USB Control Register */ +#define USB_CTRL (*(pREG32 (0x40020028))) +#define USB_CTRL_RD_EN_MASK ((unsigned int) 0x00000001) +#define USB_CTRL_RD_EN ((unsigned int) 0x00000001) +#define USB_CTRL_WR_EN_MASK ((unsigned int) 0x00000002) +#define USB_CTRL_WR_EN ((unsigned int) 0x00000002) +#define USB_CTRL_LOG_ENDPOINT_MASK ((unsigned int) 0x0000003C) + +/* USB Device FIQ Select Register */ +#define USB_DEVFIQSEL (*(pREG32 (0x4002002C))) +#define USB_DEVFIQSEL_FRAME_MASK ((unsigned int) 0x00000001) +#define USB_DEVFIQSEL_FRAME ((unsigned int) 0x00000001) +#define USB_DEVFIQSEL_BULKOUT_MASK ((unsigned int) 0x00000002) +#define USB_DEVFIQSEL_BULKOUT ((unsigned int) 0x00000002) +#define USB_DEVFIQSEL_BULKIN_MASK ((unsigned int) 0x00000004) +#define USB_DEVFIQSEL_BULKIN ((unsigned int) 0x00000004) + +/*############################################################################## +## UART +##############################################################################*/ + +#define UART_BASE_ADDRESS (0x40008000) + +#define UART_U0RBR (*(pREG32 (0x40008000))) // Receive buffer +#define UART_U0THR (*(pREG32 (0x40008000))) // Transmitter holding register +#define UART_U0DLL (*(pREG32 (0x40008000))) // Divisor latch LSB +#define UART_U0DLM (*(pREG32 (0x40008004))) // Divisor latch MSB +#define UART_U0IER (*(pREG32 (0x40008004))) // Interrupt enable +#define UART_U0IIR (*(pREG32 (0x40008008))) // Interrupt identification +#define UART_U0FCR (*(pREG32 (0x40008008))) // FIFO control +#define UART_U0MCR (*(pREG32 (0x40008010))) // Modem control +#define UART_U0LCR (*(pREG32 (0x4000800C))) // Line control +#define UART_U0LSR (*(pREG32 (0x40008014))) // Line status +#define UART_U0MSR (*(pREG32 (0x40008018))) // Modem status +#define UART_U0SCR (*(pREG32 (0x4000801C))) // Scratch pad +#define UART_U0ACR (*(pREG32 (0x40008020))) // Auto-baud control +#define UART_U0FDR (*(pREG32 (0x40008028))) // Fractional divider +#define UART_U0TER (*(pREG32 (0x40008030))) // Transmit enable +#define UART_U0RS485CTRL (*(pREG32 (0x4000804C))) // RS485 control register +#define UART_U0RS485ADRMATCH (*(pREG32 (0x40008050))) // RS485 address match +#define UART_U0RS485DLY (*(pREG32 (0x40008054))) // RS485 Delay value +#define UART_U0FIFOLVL (*(pREG32 (0x40008058))) // UART FIFO level + +#define UART_U0RBR_MASK ((unsigned int) 0x000000FF) + +#define UART_U0IER_RBR_Interrupt_MASK ((unsigned int) 0x00000001) // Enables the received data available interrupt +#define UART_U0IER_RBR_Interrupt_Enabled ((unsigned int) 0x00000001) +#define UART_U0IER_RBR_Interrupt_Disabled ((unsigned int) 0x00000000) +#define UART_U0IER_THRE_Interrupt_MASK ((unsigned int) 0x00000002) // Enables the THRE interrupt +#define UART_U0IER_THRE_Interrupt_Enabled ((unsigned int) 0x00000002) +#define UART_U0IER_THRE_Interrupt_Disabled ((unsigned int) 0x00000000) +#define UART_U0IER_RLS_Interrupt_MASK ((unsigned int) 0x00000004) // Enables the Rx line status interrupt +#define UART_U0IER_RLS_Interrupt_Enabled ((unsigned int) 0x00000004) +#define UART_U0IER_RLS_Interrupt_Disabled ((unsigned int) 0x00000000) +#define UART_U0IER_ABEOIntEn_MASK ((unsigned int) 0x00000100) // End of auto-baud interrupt +#define UART_U0IER_ABEOIntEn_Enabled ((unsigned int) 0x00000100) +#define UART_U0IER_ABEOIntEn_Disabled ((unsigned int) 0x00000000) +#define UART_U0IER_ABTOIntEn_MASK ((unsigned int) 0x00000200) // Auto-baud timeout interrupt +#define UART_U0IER_ABTOIntEn_Enabled ((unsigned int) 0x00000200) +#define UART_U0IER_ABTOIntEn_Disabled ((unsigned int) 0x00000000) + +#define UART_U0IIR_IntStatus_MASK ((unsigned int) 0x00000001) // Interrupt status +#define UART_U0IIR_IntStatus_InterruptPending ((unsigned int) 0x00000001) +#define UART_U0IIR_IntStatus_NoInterruptPending ((unsigned int) 0x00000000) +#define UART_U0IIR_IntId_MASK ((unsigned int) 0x0000000E) // Interrupt identification +#define UART_U0IIR_IntId_RLS ((unsigned int) 0x00000006) // Receive line status +#define UART_U0IIR_IntId_RDA ((unsigned int) 0x00000004) // Receive data available +#define UART_U0IIR_IntId_CTI ((unsigned int) 0x0000000C) // Character time-out indicator +#define UART_U0IIR_IntId_THRE ((unsigned int) 0x00000002) // THRE interrupt +#define UART_U0IIR_IntId_MODEM ((unsigned int) 0x00000000) // Modem interrupt +#define UART_U0IIR_FIFO_Enable_MASK ((unsigned int) 0x000000C0) +#define UART_U0IIR_ABEOInt_MASK ((unsigned int) 0x00000100) // End of auto-baud interrupt +#define UART_U0IIR_ABEOInt ((unsigned int) 0x00000100) +#define UART_U0IIR_ABTOInt_MASK ((unsigned int) 0x00000200) // Auto-baud time-out interrupt +#define UART_U0IIR_ABTOInt ((unsigned int) 0x00000200) + +#define UART_U0FCR_FIFO_Enable_MASK ((unsigned int) 0x00000001) // UART FIFOs enabled/disabled +#define UART_U0FCR_FIFO_Enabled ((unsigned int) 0x00000001) +#define UART_U0FCR_FIFO_Disabled ((unsigned int) 0x00000000) +#define UART_U0FCR_Rx_FIFO_Reset_MASK ((unsigned int) 0x00000002) +#define UART_U0FCR_Rx_FIFO_Reset ((unsigned int) 0x00000002) // Clear Rx FIFO +#define UART_U0FCR_Tx_FIFO_Reset_MASK ((unsigned int) 0x00000004) +#define UART_U0FCR_Tx_FIFO_Reset ((unsigned int) 0x00000004) // Clear Tx FIFO +#define UART_U0FCR_Rx_Trigger_Level_Select_MASK ((unsigned int) 0x000000C0) // Chars written before before interrupt +#define UART_U0FCR_Rx_Trigger_Level_Select_1Char ((unsigned int) 0x00000000) +#define UART_U0FCR_Rx_Trigger_Level_Select_4Char ((unsigned int) 0x00000040) +#define UART_U0FCR_Rx_Trigger_Level_Select_8Char ((unsigned int) 0x00000080) +#define UART_U0FCR_Rx_Trigger_Level_Select_12Char ((unsigned int) 0x000000C0) + +#define UART_U0MCR_DTR_Control_MASK ((unsigned int) 0x00000001) // Source for modem output pin DTR +#define UART_U0MCR_DTR_Control ((unsigned int) 0x00000001) +#define UART_U0MCR_RTS_Control_MASK ((unsigned int) 0x00000002) // Source for modem output pin RTS +#define UART_U0MCR_RTS_Control ((unsigned int) 0x00000002) +#define UART_U0MCR_Loopback_Mode_Select_MASK ((unsigned int) 0x00000010) // Diagnostic loopback mode +#define UART_U0MCR_Loopback_Mode_Select_Enabled ((unsigned int) 0x00000010) +#define UART_U0MCR_Loopback_Mode_Select_Disabled ((unsigned int) 0x00000000) +#define UART_U0MCR_RTSen_MASK ((unsigned int) 0x00000040) // Disable auto-rts flow control +#define UART_U0MCR_RTSen_Enabled ((unsigned int) 0x00000040) +#define UART_U0MCR_RTSen_Disabled ((unsigned int) 0x00000000) +#define UART_U0MCR_CTSen_MASK ((unsigned int) 0x00000080) // Disable auto-cts flow control +#define UART_U0MCR_CTSen_Enabled ((unsigned int) 0x00000080) +#define UART_U0MCR_CTSen_Disabled ((unsigned int) 0x00000000) + +#define UART_U0LCR_Word_Length_Select_MASK ((unsigned int) 0x00000003) // Word Length Selector +#define UART_U0LCR_Word_Length_Select_5Chars ((unsigned int) 0x00000000) +#define UART_U0LCR_Word_Length_Select_6Chars ((unsigned int) 0x00000001) +#define UART_U0LCR_Word_Length_Select_7Chars ((unsigned int) 0x00000002) +#define UART_U0LCR_Word_Length_Select_8Chars ((unsigned int) 0x00000003) +#define UART_U0LCR_Stop_Bit_Select_MASK ((unsigned int) 0x00000004) // Stop bit select +#define UART_U0LCR_Stop_Bit_Select_1Bits ((unsigned int) 0x00000000) +#define UART_U0LCR_Stop_Bit_Select_2Bits ((unsigned int) 0x00000004) +#define UART_U0LCR_Parity_Enable_MASK ((unsigned int) 0x00000008) // Parity enable +#define UART_U0LCR_Parity_Enabled ((unsigned int) 0x00000008) +#define UART_U0LCR_Parity_Disabled ((unsigned int) 0x00000000) +#define UART_U0LCR_Parity_Select_MASK ((unsigned int) 0x00000030) // Parity select +#define UART_U0LCR_Parity_Select_OddParity ((unsigned int) 0x00000000) +#define UART_U0LCR_Parity_Select_EvenParity ((unsigned int) 0x00000010) +#define UART_U0LCR_Parity_Select_Forced1 ((unsigned int) 0x00000020) +#define UART_U0LCR_Parity_Select_Forced0 ((unsigned int) 0x00000030) +#define UART_U0LCR_Break_Control_MASK ((unsigned int) 0x00000040) // Break transmission control +#define UART_U0LCR_Break_Control_Enabled ((unsigned int) 0x00000040) +#define UART_U0LCR_Break_Control_Disabled ((unsigned int) 0x00000000) +#define UART_U0LCR_Divisor_Latch_Access_MASK ((unsigned int) 0x00000080) // Divisor latch access +#define UART_U0LCR_Divisor_Latch_Access_Enabled ((unsigned int) 0x00000080) +#define UART_U0LCR_Divisor_Latch_Access_Disabled ((unsigned int) 0x00000000) + +#define UART_U0LSR_RDR_MASK ((unsigned int) 0x00000001) // Receiver data ready +#define UART_U0LSR_RDR_EMPTY ((unsigned int) 0x00000000) // U0RBR is empty +#define UART_U0LSR_RDR_DATA ((unsigned int) 0x00000001) // U0RBR contains valid data +#define UART_U0LSR_OE_MASK ((unsigned int) 0x00000002) // Overrun error +#define UART_U0LSR_OE ((unsigned int) 0x00000002) +#define UART_U0LSR_PE_MASK ((unsigned int) 0x00000004) // Parity error +#define UART_U0LSR_PE ((unsigned int) 0x00000004) +#define UART_U0LSR_FE_MASK ((unsigned int) 0x00000008) // Framing error +#define UART_U0LSR_FE ((unsigned int) 0x00000008) +#define UART_U0LSR_BI_MASK ((unsigned int) 0x00000010) // Break interrupt +#define UART_U0LSR_BI ((unsigned int) 0x00000010) +#define UART_U0LSR_THRE_MASK ((unsigned int) 0x00000020) // Transmitter holding register empty +#define UART_U0LSR_THRE ((unsigned int) 0x00000020) +#define UART_U0LSR_TEMT_MASK ((unsigned int) 0x00000040) // Transmitter empty +#define UART_U0LSR_TEMT ((unsigned int) 0x00000040) +#define UART_U0LSR_RXFE_MASK ((unsigned int) 0x00000080) // Error in Rx FIFO +#define UART_U0LSR_RXFE ((unsigned int) 0x00000080) + +#define UART_U0MSR_Delta_CTS_MASK ((unsigned int) 0x00000001) // State change of input CTS +#define UART_U0MSR_Delta_CTS ((unsigned int) 0x00000001) +#define UART_U0MSR_Delta_DSR_MASK ((unsigned int) 0x00000002) // State change of input DSR +#define UART_U0MSR_Delta_DSR ((unsigned int) 0x00000002) +#define UART_U0MSR_Trailing_Edge_RI_MASK ((unsigned int) 0x00000004) // Low to high transition of input RI +#define UART_U0MSR_Trailing_Edge_RI ((unsigned int) 0x00000004) +#define UART_U0MSR_Delta_DCD_MASK ((unsigned int) 0x00000008) // State change of input DCD +#define UART_U0MSR_Delta_DCD ((unsigned int) 0x00000008) +#define UART_U0MSR_CTS_MASK ((unsigned int) 0x00000010) // Clear to send state +#define UART_U0MSR_CTS ((unsigned int) 0x00000010) +#define UART_U0MSR_DSR_MASK ((unsigned int) 0x00000020) // Data set ready state +#define UART_U0MSR_DSR ((unsigned int) 0x00000020) +#define UART_U0MSR_RI_MASK ((unsigned int) 0x00000040) // Ring indicator state +#define UART_U0MSR_RI ((unsigned int) 0x00000040) +#define UART_U0MSR_DCD_MASK ((unsigned int) 0x00000080) // Data carrier detect state +#define UART_U0MSR_DCD ((unsigned int) 0x00000080) + +#define UART_U0ACR_Start_MASK ((unsigned int) 0x00000001) // Auto-baud start/stop +#define UART_U0ACR_Start ((unsigned int) 0x00000001) +#define UART_U0ACR_Stop ((unsigned int) 0x00000000) +#define UART_U0ACR_Mode_MASK ((unsigned int) 0x00000002) // Auto-baud mode select +#define UART_U0ACR_Mode_Mode1 ((unsigned int) 0x00000000) +#define UART_U0ACR_Mode_Mode2 ((unsigned int) 0x00000002) +#define UART_U0ACR_AutoRestart_MASK ((unsigned int) 0x00000004) +#define UART_U0ACR_AutoRestart_NoRestart ((unsigned int) 0x00000000) +#define UART_U0ACR_AutoRestart_Restart ((unsigned int) 0x00000004) // Restart in case of time-out +#define UART_U0ACR_ABEOIntClr_MASK ((unsigned int) 0x00000100) // End of auto-baud interrupt clear bit +#define UART_U0ACR_ABEOIntClr ((unsigned int) 0x00000100) +#define UART_U0ACR_ABTOIntClr_MASK ((unsigned int) 0x00000200) // Auto-baud timeout interrupt clear bit +#define UART_U0ACR_ABTOIntClr ((unsigned int) 0x00000200) + +#define UART_U0FDR_DIVADDVAL_MASK ((unsigned int) 0x0000000F) // Fractional divider: prescaler register +#define UART_U0FDR_MULVAL_MASK ((unsigned int) 0x000000F0) // Fractional divider: prescaler multiplier + +#define UART_U0TER_TXEN_MASK ((unsigned int) 0x00000080) // UART transmit enable +#define UART_U0TER_TXEN_Enabled ((unsigned int) 0x00000080) +#define UART_U0TER_TXEN_Disabled ((unsigned int) 0x00000000) + +#define UART_U0RS485CTRL_NMMEN_MASK ((unsigned int) 0x00000001) // Normal multi-drop mode +#define UART_U0RS485CTRL_NMMEN ((unsigned int) 0x00000001) +#define UART_U0RS485CTRL_RXDIS_MASK ((unsigned int) 0x00000002) // Receiver +#define UART_U0RS485CTRL_RXDIS ((unsigned int) 0x00000002) +#define UART_U0RS485CTRL_AADEN_MASK ((unsigned int) 0x00000004) // Auto-address detect +#define UART_U0RS485CTRL_AADEN ((unsigned int) 0x00000004) +#define UART_U0RS485CTRL_SEL_MASK ((unsigned int) 0x00000008) +#define UART_U0RS485CTRL_SEL_RTS ((unsigned int) 0x00000000) // Use RTS for direction control +#define UART_U0RS485CTRL_SEL_DTS ((unsigned int) 0x00000008) // Use DTS for direction control +#define UART_U0RS485CTRL_DCTRL_MASK ((unsigned int) 0x00000010) // Enable/Disable auto-direction control +#define UART_U0RS485CTRL_DCTRL_Disabled ((unsigned int) 0x00000000) +#define UART_U0RS485CTRL_DCTRL_Enabled ((unsigned int) 0x00000010) +#define UART_U0RS485CTRL_OINV_MASK ((unsigned int) 0x00000020) // Reverse polarity of direction control signal on RTS/DTR pin +#define UART_U0RS485CTRL_OINV_Normal ((unsigned int) 0x00000000) +#define UART_U0RS485CTRL_OINV_Inverted ((unsigned int) 0x00000020) + +#define UART_U0FIFOLVL_RXFIFOLVL_MASK ((unsigned int) 0x0000000F) +#define UART_U0FIFOLVL_RXFIFOLVL_Empty ((unsigned int) 0x00000000) +#define UART_U0FIFOLVL_RXFIFOLVL_Full ((unsigned int) 0x0000000F) +#define UART_U0FIFOLVL_TXFIFOLVL_MASK ((unsigned int) 0x00000F00) +#define UART_U0FIFOLVL_TXFIFOLVL_Empty ((unsigned int) 0x00000000) +#define UART_U0FIFOLVL_TXFIFOLVL_Full ((unsigned int) 0x00000F00) + +/*############################################################################## +## SSP - Synchronous Serial Port +##############################################################################*/ + +#define SSP_SSP0_BASE_ADDRESS (0x40040000) + +#define SSP_SSP0CR0 (*(pREG32 (0x40040000))) // Control register 0 +#define SSP_SSP0CR1 (*(pREG32 (0x40040004))) // Control register 1 +#define SSP_SSP0DR (*(pREG32 (0x40040008))) // Data register +#define SSP_SSP0SR (*(pREG32 (0x4004000C))) // Status register +#define SSP_SSP0CPSR (*(pREG32 (0x40040010))) // Clock prescale register +#define SSP_SSP0IMSC (*(pREG32 (0x40040014))) // Interrupt mask set/clear register +#define SSP_SSP0RIS (*(pREG32 (0x40040018))) // Raw interrupt status register +#define SSP_SSP0MIS (*(pREG32 (0x4004001C))) // Masked interrupt status register +#define SSP_SSP0ICR (*(pREG32 (0x40040020))) // SSPICR interrupt clear register + +/* SSP0CR0 (SSP0 Control Register 0) + This register controls the basic operation of the SSP controller. */ + +#define SSP_SSP0CR0_DSS_MASK ((unsigned int) 0x0000000F) // Data size select +#define SSP_SSP0CR0_DSS_4BIT ((unsigned int) 0x00000003) +#define SSP_SSP0CR0_DSS_5BIT ((unsigned int) 0x00000004) +#define SSP_SSP0CR0_DSS_6BIT ((unsigned int) 0x00000005) +#define SSP_SSP0CR0_DSS_7BIT ((unsigned int) 0x00000006) +#define SSP_SSP0CR0_DSS_8BIT ((unsigned int) 0x00000007) +#define SSP_SSP0CR0_DSS_9BIT ((unsigned int) 0x00000008) +#define SSP_SSP0CR0_DSS_10BIT ((unsigned int) 0x00000009) +#define SSP_SSP0CR0_DSS_11BIT ((unsigned int) 0x0000000A) +#define SSP_SSP0CR0_DSS_12BIT ((unsigned int) 0x0000000B) +#define SSP_SSP0CR0_DSS_13BIT ((unsigned int) 0x0000000C) +#define SSP_SSP0CR0_DSS_14BIT ((unsigned int) 0x0000000D) +#define SSP_SSP0CR0_DSS_15BIT ((unsigned int) 0x0000000E) +#define SSP_SSP0CR0_DSS_16BIT ((unsigned int) 0x0000000F) +#define SSP_SSP0CR0_FRF_MASK ((unsigned int) 0x00000030) // Frame format +#define SSP_SSP0CR0_FRF_SPI ((unsigned int) 0x00000000) +#define SSP_SSP0CR0_FRF_TI ((unsigned int) 0x00000010) +#define SSP_SSP0CR0_FRF_MWIRE ((unsigned int) 0x00000020) +#define SSP_SSP0CR0_CPOL_MASK ((unsigned int) 0x00000040) // Clock out polarity +#define SSP_SSP0CR0_CPOL_LOW ((unsigned int) 0x00000000) +#define SSP_SSP0CR0_CPOL_HIGH ((unsigned int) 0x00000040) +#define SSP_SSP0CR0_CPHA_MASK ((unsigned int) 0x00000080) // Clock out phase +#define SSP_SSP0CR0_CPHA_FIRST ((unsigned int) 0x00000000) +#define SSP_SSP0CR0_CPHA_SECOND ((unsigned int) 0x00000080) + +/* Serial Clock Rate. The number of prescaler-output clocks per + bit on the bus, minus one. Given that CPSDVSR is the + prescale divider, and the APB clock PCLK clocks the + prescaler, the bit frequency is PCLK / (CPSDVSR — [SCR+1]). */ + +#define SSP_SSP0CR0_SCR_MASK ((unsigned int) 0x0000FF00) // Serial clock rate +#define SSP_SSP0CR0_SCR_1 ((unsigned int) 0x00000100) +#define SSP_SSP0CR0_SCR_2 ((unsigned int) 0x00000200) +#define SSP_SSP0CR0_SCR_3 ((unsigned int) 0x00000300) +#define SSP_SSP0CR0_SCR_4 ((unsigned int) 0x00000400) +#define SSP_SSP0CR0_SCR_5 ((unsigned int) 0x00000500) +#define SSP_SSP0CR0_SCR_6 ((unsigned int) 0x00000600) +#define SSP_SSP0CR0_SCR_7 ((unsigned int) 0x00000700) +#define SSP_SSP0CR0_SCR_8 ((unsigned int) 0x00000800) +#define SSP_SSP0CR0_SCR_9 ((unsigned int) 0x00000900) +#define SSP_SSP0CR0_SCR_10 ((unsigned int) 0x00000A00) +#define SSP_SSP0CR0_SCR_11 ((unsigned int) 0x00000B00) +#define SSP_SSP0CR0_SCR_12 ((unsigned int) 0x00000C00) +#define SSP_SSP0CR0_SCR_13 ((unsigned int) 0x00000D00) +#define SSP_SSP0CR0_SCR_14 ((unsigned int) 0x00000E00) +#define SSP_SSP0CR0_SCR_15 ((unsigned int) 0x00000F00) +#define SSP_SSP0CR0_SCR_16 ((unsigned int) 0x00001000) + +/* SSP0CR1 (SSP0 Control Register 1) + This register controls certain aspects of the operation of the SSP controller. */ + +#define SSP_SSP0CR1_LBM_MASK ((unsigned int) 0x00000001) // Loop back mode +#define SSP_SSP0CR1_LBM_NORMAL ((unsigned int) 0x00000000) +#define SSP_SSP0CR1_LBM_INVERTED ((unsigned int) 0x00000001) // MISO/MOSI are reversed +#define SSP_SSP0CR1_SSE_MASK ((unsigned int) 0x00000002) // SSP enable +#define SSP_SSP0CR1_SSE_DISABLED ((unsigned int) 0x00000000) +#define SSP_SSP0CR1_SSE_ENABLED ((unsigned int) 0x00000002) +#define SSP_SSP0CR1_MS_MASK ((unsigned int) 0x00000004) // Master/Slave Mode +#define SSP_SSP0CR1_MS_MASTER ((unsigned int) 0x00000000) +#define SSP_SSP0CR1_MS_SLAVE ((unsigned int) 0x00000004) +#define SSP_SSP0CR1_SOD_MASK ((unsigned int) 0x00000008) // Slave output disable + +/* SSP0DR (SSP0 Data Register) + Software can write data to be transmitted to this register, and read data that has been + received. */ + +#define SSP_SSP0DR_MASK ((unsigned int) 0x0000FFFF) // Data + +/* SSP0SR (SSP0 Status Register) + This read-only register reflects the current status of the SSP controller. */ + +#define SSP_SSP0SR_TFE_MASK ((unsigned int) 0x00000001) // Transmit FIFO empty +#define SSP_SSP0SR_TFE_EMPTY ((unsigned int) 0x00000001) +#define SSP_SSP0SR_TFE_NOTEMPTY ((unsigned int) 0x00000000) +#define SSP_SSP0SR_TNF_MASK ((unsigned int) 0x00000002) // Transmit FIFO not full +#define SSP_SSP0SR_TNF_NOTFULL ((unsigned int) 0x00000002) +#define SSP_SSP0SR_TNF_FULL ((unsigned int) 0x00000000) +#define SSP_SSP0SR_RNE_MASK ((unsigned int) 0x00000004) // Receive FIFO not empty +#define SSP_SSP0SR_RNE_NOTEMPTY ((unsigned int) 0x00000004) +#define SSP_SSP0SR_RNE_EMPTY ((unsigned int) 0x00000000) +#define SSP_SSP0SR_RFF_MASK ((unsigned int) 0x00000008) // Receive FIFO full +#define SSP_SSP0SR_RFF_FULL ((unsigned int) 0x00000008) +#define SSP_SSP0SR_RFF_NOTFULL ((unsigned int) 0x00000000) +#define SSP_SSP0SR_BSY_MASK ((unsigned int) 0x00000010) // Busy Flag +#define SSP_SSP0SR_BSY_IDLE ((unsigned int) 0x00000000) +#define SSP_SSP0SR_BSY_BUSY ((unsigned int) 0x00000010) + +/* SSP0CPSR (SSP0 Clock Prescale Register) + This register controls the factor by which the Prescaler divides the SSP peripheral clock + SSP_PCLK to yield the prescaler clock that is, in turn, divided by the SCR factor in + SSP0CR0, to determine the bit clock. */ + +#define SSP_SSP0CPSR_CPSDVSR_MASK ((unsigned int) 0x000000FF) +#define SSP_SSP0CPSR_CPSDVSR_DIV2 ((unsigned int) 0x00000002) +#define SSP_SSP0CPSR_CPSDVSR_DIV4 ((unsigned int) 0x00000004) +#define SSP_SSP0CPSR_CPSDVSR_DIV10 ((unsigned int) 0x0000000A) +#define SSP_SSP0CPSR_CPSDVSR_DIV12 ((unsigned int) 0x0000000C) +#define SSP_SSP0CPSR_CPSDVSR_DIV16 ((unsigned int) 0x00000010) +#define SSP_SSP0CPSR_CPSDVSR_DIV20 ((unsigned int) 0x00000014) + +/* SSP0IMSC (SSP0 Interrupt Mask Set/Clear Register) + This register controls whether each of the four possible interrupt conditions in the SSP + controller are enabled. Note that ARM uses the word “masked” in the opposite sense from + classic computer terminology, in which “masked” meant “disabled”. ARM uses the word + “masked” to mean “enabled”. To avoid confusion we will not use the word “masked”. */ + +#define SSP_SSP0IMSC_RORIM_MASK ((unsigned int) 0x00000001) // Receive overrun interrupt +#define SSP_SSP0IMSC_RORIM_ENBL ((unsigned int) 0x00000001) +#define SSP_SSP0IMSC_RORIM_DSBL ((unsigned int) 0x00000000) +#define SSP_SSP0IMSC_RTIM_MASK ((unsigned int) 0x00000002) // Receive timeout interrupt +#define SSP_SSP0IMSC_RTIM_ENBL ((unsigned int) 0x00000002) +#define SSP_SSP0IMSC_RTIM_DSBL ((unsigned int) 0x00000000) +#define SSP_SSP0IMSC_RXIM_MASK ((unsigned int) 0x00000004) // Rx FIFO >= 1/2 full interrupt +#define SSP_SSP0IMSC_RXIM_ENBL ((unsigned int) 0x00000004) +#define SSP_SSP0IMSC_RXIM_DSBL ((unsigned int) 0x00000000) +#define SSP_SSP0IMSC_TXIM_MASK ((unsigned int) 0x00000008) // Tx FIFO >= 1/2 empty interrupt +#define SSP_SSP0IMSC_TXIM_ENBL ((unsigned int) 0x00000008) +#define SSP_SSP0IMSC_TXIM_DSBL ((unsigned int) 0x00000000) + +/* SSP0RIS (SSP0 Raw Interrupt Status Register) + This read-only register contains a 1 for each interrupt condition that is asserted, + regardless of whether or not the interrupt is enabled in the SSP0IMSC. */ + +#define SSP_SSP0RIS_RORRIS_MASK ((unsigned int) 0x00000001) // Frame received while Rx FIFO full +#define SSP_SSP0RIS_RORRIS_RCVD ((unsigned int) 0x00000001) +#define SSP_SSP0RIS_RTRIS_MASK ((unsigned int) 0x00000002) // Rx FIFO not empty no read within timeout +#define SSP_SSP0RIS_RTRIS_NOTEMPTY ((unsigned int) 0x00000002) +#define SSP_SSP0RIS_RXRIS_MASK ((unsigned int) 0x00000004) // Rx FIFO >= half full +#define SSP_SSP0RIS_RXRIS_HALFFULL ((unsigned int) 0x00000004) +#define SSP_SSP0RIS_TXRIS_MASK ((unsigned int) 0x00000008) // Tx FIF0 >= half-empty +#define SSP_SSP0RIS_TXRIS_HALFEMPTY ((unsigned int) 0x00000008) + +/* SSP0MIS (SSP0 Masked Interrupt Status Register) + This read-only register contains a 1 for each interrupt condition that is asserted and + enabled in the SSP0IMSC. When an SSP interrupt occurs, the interrupt service routine + should read this register to determine the cause(s) of the interrupt. */ + +#define SSP_SSP0MIS_RORMIS_MASK ((unsigned int) 0x00000001) // Frame received while Rx FIFO full +#define SSP_SSP0MIS_RORMIS_FRMRCVD ((unsigned int) 0x00000001) +#define SSP_SSP0MIS_RTMIS_MASK ((unsigned int) 0x00000002) // Rx FIFO not empty no read withing timeout +#define SSP_SSP0MIS_RTMIS_NOTEMPTY ((unsigned int) 0x00000002) +#define SSP_SSP0MIS_RXMIS_MASK ((unsigned int) 0x00000004) // Rx FIFO >= half full +#define SSP_SSP0MIS_RXMIS_HALFFULL ((unsigned int) 0x00000004) +#define SSP_SSP0MIS_TXMIS_MASK ((unsigned int) 0x00000008) // Tx FIFO >= half-empty +#define SSP_SSP0MIS_TXMIS_HALFEMPTY ((unsigned int) 0x00000008) + +/* SSP0ICR (SSP0 Interrupt Clear Register) + Software can write one or more one(s) to this write-only register, to clear the + corresponding interrupt condition(s) in the SSP controller. Note that the other two interrupt + conditions can be cleared by writing or reading the appropriate FIFO, or disabled by + clearing the corresponding bit in SSP0IMSC. */ + +#define SSP_SSP0ICR_RORIC_MASK ((unsigned int) 0x00000001) // Clears RORIC interrupt flag +#define SSP_SSP0ICR_RORIC_CLEAR ((unsigned int) 0x00000001) +#define SSP_SSP0ICR_RTIC_MASK ((unsigned int) 0x00000002) // Clear Rx FIFO not empty/no read flag +#define SSP_SSP0ICR_RTIC_CLEAR ((unsigned int) 0x00000002) + +/*############################################################################## +## I2C +##############################################################################*/ + +#define I2C_BASE_ADDRESS (0x40000000) + +#define I2C_I2CCONSET (*(pREG32 (0x40000000))) // I2C control set register +#define I2C_I2CSTAT (*(pREG32 (0x40000004))) // I2C status register +#define I2C_I2CDAT (*(pREG32 (0x40000008))) // I2C data register +#define I2C_I2CADR0 (*(pREG32 (0x4000000C))) // I2C slave address register +#define I2C_I2CSCLH (*(pREG32 (0x40000010))) // I2C SCL HIGH/LOW duty cycle register +#define I2C_I2CSCLL (*(pREG32 (0x40000014))) +#define I2C_I2CCONCLR (*(pREG32 (0x40000018))) // I2C control clear register +#define I2C_I2CMMCTRL (*(pREG32 (0x4000001C))) // I2C monitor control register +#define I2C_I2CADR1 (*(pREG32 (0x40000020))) // I2C slave address register 1 +#define I2C_I2CADR2 (*(pREG32 (0x40000024))) // I2C slave address register 2 +#define I2C_I2CADR3 (*(pREG32 (0x40000028))) // I2C slave address register 3 +#define I2C_I2CDATA_BUFFER (*(pREG32 (0x4000002C))) // I2C data buffer register +#define I2C_I2CMASK0 (*(pREG32 (0x40000030))) // I2C mask register 0 +#define I2C_I2CMASK1 (*(pREG32 (0x40000034))) // I2C mask register 1 +#define I2C_I2CMASK2 (*(pREG32 (0x40000038))) // I2C mask register 2 +#define I2C_I2CMASK3 (*(pREG32 (0x4000003C))) // I2C mask register 3 + +/* I2CCONSET (I2C Control Set register) + The I2CONSET registers control setting of bits in the I2CON register that controls + operation of the I2C interface. Writing a one to a bit of this register causes the + corresponding bit in the I2C control register to be set. Writing a zero has no effect. */ + +#define I2C_I2CCONSET_AA_MASK ((unsigned int) 0x00000004) +#define I2C_I2CCONSET_AA ((unsigned int) 0x00000004) // Asset acknowlegde flag +#define I2C_I2CCONSET_SI_MASK ((unsigned int) 0x00000008) +#define I2C_I2CCONSET_SI ((unsigned int) 0x00000008) // I2C interrupt flag +#define I2C_I2CCONSET_STO_MASK ((unsigned int) 0x00000010) +#define I2C_I2CCONSET_STO ((unsigned int) 0x00000010) // Stop flag +#define I2C_I2CCONSET_STA_MASK ((unsigned int) 0x00000020) +#define I2C_I2CCONSET_STA ((unsigned int) 0x00000020) // Start flag +#define I2C_I2CCONSET_I2EN_MASK ((unsigned int) 0x00000040) +#define I2C_I2CCONSET_I2EN ((unsigned int) 0x00000040) // I2C interface enable + +/* I2CSTAT (I2C Status register) + Each I2C Status register reflects the condition of the corresponding I2C interface. The I2C + Status register is Read-Only. */ + +#define I2C_I2CSTAT_Status_MASK ((unsigned int) 0x000000F8) // Status information + +/* I2CADR0 (I2C Slave Address register) + These registers are readable and writable and are only used when an I2C interface is set + to slave mode. */ + +#define I2C_I2CADR0_GC_MASK ((unsigned int) 0x00000001) +#define I2C_I2CADR0_GC ((unsigned int) 0x00000001) // General call enable bit +#define I2C_I2CADR0_Address_MASK ((unsigned int) 0x000000FE) // I2C device address for slave mode + +/* I2CCONCLR (I2C Control Clear register) + The I2CONCLR registers control clearing of bits in the I2CON register that controls + operation of the I2C interface. Writing a one to a bit of this register causes the + corresponding bit in the I2C control register to be cleared. Writing a zero has no effect. */ + +#define I2C_I2CCONCLR_AAC_MASK ((unsigned int) 0x00000004) // Assert acknowledge clear bit +#define I2C_I2CCONCLR_AAC ((unsigned int) 0x00000004) +#define I2C_I2CCONCLR_SIC_MASK ((unsigned int) 0x00000008) // I2C interrupt clear bit +#define I2C_I2CCONCLR_SIC ((unsigned int) 0x00000008) +#define I2C_I2CCONCLR_STAC_MASK ((unsigned int) 0x00000020) // Start flag clear bit +#define I2C_I2CCONCLR_STAC ((unsigned int) 0x00000020) +#define I2C_I2CCONCLR_I2ENC_MASK ((unsigned int) 0x00000040) // I2C interface disable bit +#define I2C_I2CCONCLR_I2ENC ((unsigned int) 0x00000040) + +/* I2CMMCTRL (I2C Monitor mode control register) + This register controls the Monitor mode which allows the I2C module to monitor traffic on + the I2C bus without actually participating in traffic or interfering with the I2C bus. */ + +#define I2C_I2CMMCTRL_MM_ENA_MASK ((unsigned int) 0x00000001) // Monitor mode enable +#define I2C_I2CMMCTRL_MM_ENA_ENABLED ((unsigned int) 0x00000001) +#define I2C_I2CMMCTRL_MM_ENA_DISABLED ((unsigned int) 0x00000000) +#define I2C_I2CMMCTRL_ENA_SCL_MASK ((unsigned int) 0x00000002) // SCL output enable +#define I2C_I2CMMCTRL_ENA_SCL_HOLDLOW ((unsigned int) 0x00000002) +#define I2C_I2CMMCTRL_ENA_SCL_FORCEHIGH ((unsigned int) 0x00000000) +#define I2C_I2CMMCTRL_MATCH_ALL_MASK ((unsigned int) 0x00000008) // Select interrupt register match +#define I2C_I2CMMCTRL_MATCH_ALL_NORMAL ((unsigned int) 0x00000000) +#define I2C_I2CMMCTRL_MATCH_ALL_ANYADDRESS ((unsigned int) 0x00000008) + +/* I2CADR1..3 (I2C Slave Address registers) + These registers are readable and writable and are only used when an I2C interface is set + to slave mode. In master mode, this register has no effect. The LSB of I2ADR is the + General Call bit. When this bit is set, the General Call address (0x00) is recognized. */ + +#define I2C_I2CADR1_GC_MASK ((unsigned int) 0x00000001) // General call enable bit +#define I2C_I2CADR1_GC ((unsigned int) 0x00000001) +#define I2C_I2CADR1_Address_MASK ((unsigned int) 0x000000FE) + +#define I2C_I2CADR2_GC_MASK ((unsigned int) 0x00000001) // General call enable bit +#define I2C_I2CADR2_GC ((unsigned int) 0x00000001) +#define I2C_I2CADR2_Address_MASK ((unsigned int) 0x000000FE) + +#define I2C_I2CADR3_GC_MASK ((unsigned int) 0x00000001) // General call enable bit +#define I2C_I2CADR3_GC ((unsigned int) 0x00000001) +#define I2C_I2CADR3_Address_MASK ((unsigned int) 0x000000FE) + +/* I2CMASK0..3 (I2C Mask registers) */ + +#define I2C_I2CMASK0_MASK_MASK ((unsigned int) 0x000000FE) + +#define I2C_I2CMASK1_MASK_MASK ((unsigned int) 0x000000FE) + +#define I2C_I2CMASK2_MASK_MASK ((unsigned int) 0x000000FE) + +#define I2C_I2CMASK3_MASK_MASK ((unsigned int) 0x000000FE) + +/*############################################################################## +## 16-Bit Timers (CT16B0/1) +##############################################################################*/ + +#define TMR_CT16B0_BASE_ADDRESS (0x4000C000) + +#define TMR_TMR16B0IR (*(pREG32 (0x4000C000))) // Interrupt register +#define TMR_TMR16B0TCR (*(pREG32 (0x4000C004))) // Timer control register +#define TMR_TMR16B0TC (*(pREG32 (0x4000C008))) // Timer counter +#define TMR_TMR16B0PR (*(pREG32 (0x4000C00C))) // Prescale register +#define TMR_TMR16B0PC (*(pREG32 (0x4000C010))) // Prescale counter register +#define TMR_TMR16B0MCR (*(pREG32 (0x4000C014))) // Match control register +#define TMR_TMR16B0MR0 (*(pREG32 (0x4000C018))) // Match register 0 +#define TMR_TMR16B0MR1 (*(pREG32 (0x4000C01C))) // Match register 1 +#define TMR_TMR16B0MR2 (*(pREG32 (0x4000C020))) // Match register 2 +#define TMR_TMR16B0MR3 (*(pREG32 (0x4000C024))) // Match register 3 +#define TMR_TMR16B0CCR (*(pREG32 (0x4000C028))) // Capture control register +#define TMR_TMR16B0CR0 (*(pREG32 (0x4000C02C))) // Capture register +#define TMR_TMR16B0EMR (*(pREG32 (0x4000C03C))) // External match register +#define TMR_TMR16B0CTCR (*(pREG32 (0x4000C070))) // Count control register +#define TMR_TMR16B0PWMC (*(pREG32 (0x4000C074))) // PWM control register + +#define TMR_TMR16B0IR_MR0_MASK ((unsigned int) 0x00000001) // Interrupt flag for match channel 0 +#define TMR_TMR16B0IR_MR0 ((unsigned int) 0x00000001) +#define TMR_TMR16B0IR_MR1_MASK ((unsigned int) 0x00000002) // Interrupt flag for match channel 1 +#define TMR_TMR16B0IR_MR1 ((unsigned int) 0x00000002) +#define TMR_TMR16B0IR_MR2_MASK ((unsigned int) 0x00000004) // Interrupt flag for match channel 2 +#define TMR_TMR16B0IR_MR2 ((unsigned int) 0x00000004) +#define TMR_TMR16B0IR_MR3_MASK ((unsigned int) 0x00000008) // Interrupt flag for match channel 3 +#define TMR_TMR16B0IR_MR3 ((unsigned int) 0x00000008) +#define TMR_TMR16B0IR_CR0_MASK ((unsigned int) 0x00000010) // Interrupt flag for capture channel 0 event +#define TMR_TMR16B0IR_CR0 ((unsigned int) 0x00000010) +#define TMR_TMR16B0IR_MASK_ALL ((unsigned int) 0x0000001F) + +#define TMR_TMR16B0TCR_COUNTERENABLE_MASK ((unsigned int) 0x00000001) // Counter enable +#define TMR_TMR16B0TCR_COUNTERENABLE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B0TCR_COUNTERENABLE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0TCR_COUNTERRESET_MASK ((unsigned int) 0x00000002) +#define TMR_TMR16B0TCR_COUNTERRESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B0TCR_COUNTERRESET_DISABLED ((unsigned int) 0x00000002) + +#define TMR_TMR16B0MCR_MR0_INT_MASK ((unsigned int) 0x00000001) // Interrupt on MRO +#define TMR_TMR16B0MCR_MR0_INT_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B0MCR_MR0_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR0_RESET_MASK ((unsigned int) 0x00000002) // Reset on MR0 +#define TMR_TMR16B0MCR_MR0_RESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B0MCR_MR0_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR0_STOP_MASK ((unsigned int) 0x00000004) // Stop on MR0 +#define TMR_TMR16B0MCR_MR0_STOP_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR16B0MCR_MR0_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR1_INT_MASK ((unsigned int) 0x00000008) // Interrupt on MR1 +#define TMR_TMR16B0MCR_MR1_INT_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR16B0MCR_MR1_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR1_RESET_MASK ((unsigned int) 0x00000010) // Reset on MR1 +#define TMR_TMR16B0MCR_MR1_RESET_ENABLED ((unsigned int) 0x00000010) +#define TMR_TMR16B0MCR_MR1_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR1_STOP_MASK ((unsigned int) 0x00000020) // Stop on MR1 +#define TMR_TMR16B0MCR_MR1_STOP_ENABLED ((unsigned int) 0x00000020) +#define TMR_TMR16B0MCR_MR1_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR2_INT_MASK ((unsigned int) 0x00000040) // Interrupt on MR2 +#define TMR_TMR16B0MCR_MR2_INT_ENABLED ((unsigned int) 0x00000040) +#define TMR_TMR16B0MCR_MR2_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR2_RESET_MASK ((unsigned int) 0x00000080) // Reset on MR2 +#define TMR_TMR16B0MCR_MR2_RESET_ENABLED ((unsigned int) 0x00000080) +#define TMR_TMR16B0MCR_MR2_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR2_STOP_MASK ((unsigned int) 0x00000100) // Stop on MR2 +#define TMR_TMR16B0MCR_MR2_STOP_ENABLED ((unsigned int) 0x00000100) +#define TMR_TMR16B0MCR_MR2_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR3_INT_MASK ((unsigned int) 0x00000200) // Interrupt on MR3 +#define TMR_TMR16B0MCR_MR3_INT_ENABLED ((unsigned int) 0x00000200) +#define TMR_TMR16B0MCR_MR3_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR3_RESET_MASK ((unsigned int) 0x00000400) // Reset on MR3 +#define TMR_TMR16B0MCR_MR3_RESET_ENABLED ((unsigned int) 0x00000400) +#define TMR_TMR16B0MCR_MR3_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0MCR_MR3_STOP_MASK ((unsigned int) 0x00000800) // Stop on MR3 +#define TMR_TMR16B0MCR_MR3_STOP_ENABLED ((unsigned int) 0x00000800) +#define TMR_TMR16B0MCR_MR3_STOP_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR16B0CCR_CAP0RE_MASK ((unsigned int) 0x00000001) // Capture on rising edge +#define TMR_TMR16B0CCR_CAP0RE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B0CCR_CAP0RE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0CCR_CAP0FE_MASK ((unsigned int) 0x00000002) // Capture on falling edge +#define TMR_TMR16B0CCR_CAP0FE_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B0CCR_CAP0FE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0CCR_CAP0I_MASK ((unsigned int) 0x00000004) // Interrupt on CAP0 event +#define TMR_TMR16B0CCR_CAP0I_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR16B0CCR_CAP0I_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR16B0EMR_EM0_MASK ((unsigned int) 0x00000001) // External match 0 +#define TMR_TMR16B0EMR_EM0 ((unsigned int) 0x00000001) +#define TMR_TMR16B0EMR_EMC0_MASK ((unsigned int) 0x00000030) +#define TMR_TMR16B0EMR_EMC0_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B0EMR_EMC0_LOW ((unsigned int) 0x00000010) +#define TMR_TMR16B0EMR_EMC0_HIGH ((unsigned int) 0x00000020) +#define TMR_TMR16B0EMR_EMC0_TOGGLE ((unsigned int) 0x00000030) +#define TMR_TMR16B0EMR_EM1_MASK ((unsigned int) 0x00000002) // External match 1 +#define TMR_TMR16B0EMR_EM1 ((unsigned int) 0x00000002) +#define TMR_TMR16B0EMR_EMC1_MASK ((unsigned int) 0x000000C0) +#define TMR_TMR16B0EMR_EMC1_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B0EMR_EMC1_LOW ((unsigned int) 0x00000040) +#define TMR_TMR16B0EMR_EMC1_HIGH ((unsigned int) 0x00000080) +#define TMR_TMR16B0EMR_EMC1_TOGGLE ((unsigned int) 0x000000C0) +#define TMR_TMR16B0EMR_EM2_MASK ((unsigned int) 0x00000004) // External match 2 +#define TMR_TMR16B0EMR_EM2 ((unsigned int) 0x00000004) +#define TMR_TMR16B0EMR_EMC2_MASK ((unsigned int) 0x00000300) +#define TMR_TMR16B0EMR_EMC2_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B0EMR_EMC2_LOW ((unsigned int) 0x00000100) +#define TMR_TMR16B0EMR_EMC2_HIGH ((unsigned int) 0x00000200) +#define TMR_TMR16B0EMR_EMC2_TOGGLE ((unsigned int) 0x00000300) +#define TMR_TMR16B0EMR_EM3_MASK ((unsigned int) 0x00000008) // External match 3 +#define TMR_TMR16B0EMR_EM3 ((unsigned int) 0x00000008) +#define TMR_TMR16B0EMR_EMC3_MASK ((unsigned int) 0x00000C00) +#define TMR_TMR16B0EMR_EMC3_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B0EMR_EMC3_LOW ((unsigned int) 0x00000400) +#define TMR_TMR16B0EMR_EMC3_HIGH ((unsigned int) 0x00000800) +#define TMR_TMR16B0EMR_EMC3_TOGGLE ((unsigned int) 0x00000C00) + +#define TMR_TMR16B0CTCR_CTMODE_MASK ((unsigned int) 0x00000003) // Counter/Timer mode +#define TMR_TMR16B0CTCR_CTMODE_TIMER ((unsigned int) 0x00000000) // Timer Mode: Every rising PCLK edge +#define TMR_TMR16B0CTCR_CTMODE_COUNTERRISING ((unsigned int) 0x00000001) // Counter: TC increments on rising edge of input +#define TMR_TMR16B0CTCR_CTMODE_COUNTERFALLING ((unsigned int) 0x00000002) // Counter: TC increments on falling edge of input +#define TMR_TMR16B0CTCR_CTMODE_COUNTERBOTH ((unsigned int) 0x00000003) // Counter: TC increments on both edges of input +#define TMR_TMR16B0CTCR_CINPUTSELECT_MASK ((unsigned int) 0x0000000C) +#define TMR_TMR16B0CTCR_CINPUTSELECT ((unsigned int) 0x00000000) // CINPUTSELECT must be set to 00 + +#define TMR_TMR16B0PWMC_PWM0_MASK ((unsigned int) 0x00000001) +#define TMR_TMR16B0PWMC_PWM0_ENABLED ((unsigned int) 0x00000001) // PWM mode is enabled for CT16Bn_MAT0 +#define TMR_TMR16B0PWMC_PWM0_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0PWMC_PWM1_MASK ((unsigned int) 0x00000002) +#define TMR_TMR16B0PWMC_PWM1_ENABLED ((unsigned int) 0x00000002) // PWM mode is enabled for CT16Bn_MAT1 +#define TMR_TMR16B0PWMC_PWM1_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0PWMC_PWM2_MASK ((unsigned int) 0x00000004) +#define TMR_TMR16B0PWMC_PWM2_ENABLED ((unsigned int) 0x00000004) // PWM mode is enabled for CT16Bn_MAT2 +#define TMR_TMR16B0PWMC_PWM2_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B0PWMC_PWM3_MASK ((unsigned int) 0x00000008) +#define TMR_TMR16B0PWMC_PWM3_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR16B0PWMC_PWM3_DISABLED ((unsigned int) 0x00000000) + +#define TMR_CT16B1_BASE_ADDRESS (0x40010000) + +#define TMR_TMR16B1IR (*(pREG32 (0x40010000))) // Interrupt register +#define TMR_TMR16B1TCR (*(pREG32 (0x40010004))) // Timer control register +#define TMR_TMR16B1TC (*(pREG32 (0x40010008))) // Timer counter +#define TMR_TMR16B1PR (*(pREG32 (0x4001000C))) // Prescale register +#define TMR_TMR16B1PC (*(pREG32 (0x40010010))) // Prescale counter register +#define TMR_TMR16B1MCR (*(pREG32 (0x40010014))) // Match control register +#define TMR_TMR16B1MR0 (*(pREG32 (0x40010018))) // Match register 0 +#define TMR_TMR16B1MR1 (*(pREG32 (0x4001001C))) // Match register 1 +#define TMR_TMR16B1MR2 (*(pREG32 (0x40010020))) // Match register 2 +#define TMR_TMR16B1MR3 (*(pREG32 (0x40010024))) // Match register 3 +#define TMR_TMR16B1CCR (*(pREG32 (0x40010028))) // Capture control register +#define TMR_TMR16B1CR0 (*(pREG32 (0x4001002C))) // Capture register +#define TMR_TMR16B1EMR (*(pREG32 (0x4001003C))) // External match register +#define TMR_TMR16B1CTCR (*(pREG32 (0x40010070))) // Count control register +#define TMR_TMR16B1PWMC (*(pREG32 (0x40010074))) // PWM control register + +#define TMR_TMR16B1IR_MR0_MASK ((unsigned int) 0x00000001) // Interrupt flag for match channel 0 +#define TMR_TMR16B1IR_MR0 ((unsigned int) 0x00000001) +#define TMR_TMR16B1IR_MR1_MASK ((unsigned int) 0x00000002) // Interrupt flag for match channel 1 +#define TMR_TMR16B1IR_MR1 ((unsigned int) 0x00000002) +#define TMR_TMR16B1IR_MR2_MASK ((unsigned int) 0x00000004) // Interrupt flag for match channel 2 +#define TMR_TMR16B1IR_MR2 ((unsigned int) 0x00000004) +#define TMR_TMR16B1IR_MR3_MASK ((unsigned int) 0x00000008) // Interrupt flag for match channel 3 +#define TMR_TMR16B1IR_MR3 ((unsigned int) 0x00000008) +#define TMR_TMR16B1IR_CR0_MASK ((unsigned int) 0x00000010) // Interrupt flag for capture channel 0 event +#define TMR_TMR16B1IR_CR0 ((unsigned int) 0x00000010) +#define TMR_TMR16B1IR_MASK_ALL ((unsigned int) 0x0000001F) + +#define TMR_TMR16B1TCR_COUNTERENABLE_MASK ((unsigned int) 0x00000001) // Counter enable +#define TMR_TMR16B1TCR_COUNTERENABLE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B1TCR_COUNTERENABLE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1TCR_COUNTERRESET_MASK ((unsigned int) 0x00000002) +#define TMR_TMR16B1TCR_COUNTERRESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B1TCR_COUNTERRESET_DISABLED ((unsigned int) 0x00000002) + +#define TMR_TMR16B1MCR_MR0_INT_MASK ((unsigned int) 0x00000001) // Interrupt on MRO +#define TMR_TMR16B1MCR_MR0_INT_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B1MCR_MR0_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR0_RESET_MASK ((unsigned int) 0x00000002) // Reset on MR0 +#define TMR_TMR16B1MCR_MR0_RESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B1MCR_MR0_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR0_STOP_MASK ((unsigned int) 0x00000004) // Stop on MR0 +#define TMR_TMR16B1MCR_MR0_STOP_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR16B1MCR_MR0_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR1_INT_MASK ((unsigned int) 0x00000008) // Interrupt on MR1 +#define TMR_TMR16B1MCR_MR1_INT_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR16B1MCR_MR1_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR1_RESET_MASK ((unsigned int) 0x00000010) // Reset on MR1 +#define TMR_TMR16B1MCR_MR1_RESET_ENABLED ((unsigned int) 0x00000010) +#define TMR_TMR16B1MCR_MR1_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR1_STOP_MASK ((unsigned int) 0x00000020) // Stop on MR1 +#define TMR_TMR16B1MCR_MR1_STOP_ENABLED ((unsigned int) 0x00000020) +#define TMR_TMR16B1MCR_MR1_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR2_INT_MASK ((unsigned int) 0x00000040) // Interrupt on MR2 +#define TMR_TMR16B1MCR_MR2_INT_ENABLED ((unsigned int) 0x00000040) +#define TMR_TMR16B1MCR_MR2_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR2_RESET_MASK ((unsigned int) 0x00000080) // Reset on MR2 +#define TMR_TMR16B1MCR_MR2_RESET_ENABLED ((unsigned int) 0x00000080) +#define TMR_TMR16B1MCR_MR2_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR2_STOP_MASK ((unsigned int) 0x00000100) // Stop on MR2 +#define TMR_TMR16B1MCR_MR2_STOP_ENABLED ((unsigned int) 0x00000100) +#define TMR_TMR16B1MCR_MR2_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR3_INT_MASK ((unsigned int) 0x00000200) // Interrupt on MR3 +#define TMR_TMR16B1MCR_MR3_INT_ENABLED ((unsigned int) 0x00000200) +#define TMR_TMR16B1MCR_MR3_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR3_RESET_MASK ((unsigned int) 0x00000400) // Reset on MR3 +#define TMR_TMR16B1MCR_MR3_RESET_ENABLED ((unsigned int) 0x00000400) +#define TMR_TMR16B1MCR_MR3_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1MCR_MR3_STOP_MASK ((unsigned int) 0x00000800) // Stop on MR3 +#define TMR_TMR16B1MCR_MR3_STOP_ENABLED ((unsigned int) 0x00000800) +#define TMR_TMR16B1MCR_MR3_STOP_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR16B1CCR_CAP0RE_MASK ((unsigned int) 0x00000001) // Capture on rising edge +#define TMR_TMR16B1CCR_CAP0RE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR16B1CCR_CAP0RE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1CCR_CAP0FE_MASK ((unsigned int) 0x00000002) // Capture on falling edge +#define TMR_TMR16B1CCR_CAP0FE_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR16B1CCR_CAP0FE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1CCR_CAP0I_MASK ((unsigned int) 0x00000004) // Interrupt on CAP0 event +#define TMR_TMR16B1CCR_CAP0I_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR16B1CCR_CAP0I_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR16B1EMR_EM0_MASK ((unsigned int) 0x00000001) // External match 0 +#define TMR_TMR16B1EMR_EM0 ((unsigned int) 0x00000001) +#define TMR_TMR16B1EMR_EMC0_MASK ((unsigned int) 0x00000030) +#define TMR_TMR16B1EMR_EMC0_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B1EMR_EMC0_LOW ((unsigned int) 0x00000010) +#define TMR_TMR16B1EMR_EMC0_HIGH ((unsigned int) 0x00000020) +#define TMR_TMR16B1EMR_EMC0_TOGGLE ((unsigned int) 0x00000030) +#define TMR_TMR16B1EMR_EM1_MASK ((unsigned int) 0x00000002) // External match 1 +#define TMR_TMR16B1EMR_EM1 ((unsigned int) 0x00000002) +#define TMR_TMR16B1EMR_EMC1_MASK ((unsigned int) 0x000000C0) +#define TMR_TMR16B1EMR_EMC1_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B1EMR_EMC1_LOW ((unsigned int) 0x00000040) +#define TMR_TMR16B1EMR_EMC1_HIGH ((unsigned int) 0x00000080) +#define TMR_TMR16B1EMR_EMC1_TOGGLE ((unsigned int) 0x000000C0) +#define TMR_TMR16B1EMR_EM2_MASK ((unsigned int) 0x00000004) // External match 2 +#define TMR_TMR16B1EMR_EM2 ((unsigned int) 0x00000004) +#define TMR_TMR16B1EMR_EMC2_MASK ((unsigned int) 0x00000300) +#define TMR_TMR16B1EMR_EMC2_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B1EMR_EMC2_LOW ((unsigned int) 0x00000100) +#define TMR_TMR16B1EMR_EMC2_HIGH ((unsigned int) 0x00000200) +#define TMR_TMR16B1EMR_EMC2_TOGGLE ((unsigned int) 0x00000300) +#define TMR_TMR16B1EMR_EM3_MASK ((unsigned int) 0x00000008) // External match 3 +#define TMR_TMR16B1EMR_EM3 ((unsigned int) 0x00000008) +#define TMR_TMR16B1EMR_EMC3_MASK ((unsigned int) 0x00000C00) +#define TMR_TMR16B1EMR_EMC3_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR16B1EMR_EMC3_LOW ((unsigned int) 0x00000400) +#define TMR_TMR16B1EMR_EMC3_HIGH ((unsigned int) 0x00000800) +#define TMR_TMR16B1EMR_EMC3_TOGGLE ((unsigned int) 0x00000C00) + +#define TMR_TMR16B1CTCR_CTMODE_MASK ((unsigned int) 0x00000003) // Counter/Timer mode +#define TMR_TMR16B1CTCR_CTMODE_TIMER ((unsigned int) 0x00000000) // Timer Mode: Every rising PCLK edge +#define TMR_TMR16B1CTCR_CTMODE_COUNTERRISING ((unsigned int) 0x00000001) // Counter: TC increments on rising edge of input +#define TMR_TMR16B1CTCR_CTMODE_COUNTERFALLING ((unsigned int) 0x00000002) // Counter: TC increments on falling edge of input +#define TMR_TMR16B1CTCR_CTMODE_COUNTERBOTH ((unsigned int) 0x00000003) // Counter: TC increments on both edges of input +#define TMR_TMR16B1CTCR_CINPUTSELECT_MASK ((unsigned int) 0x0000000C) +#define TMR_TMR16B1CTCR_CINPUTSELECT ((unsigned int) 0x00000000) // CINPUTSELECT must be set to 00 + +#define TMR_TMR16B1PWMC_PWM0_MASK ((unsigned int) 0x00000001) +#define TMR_TMR16B1PWMC_PWM0_ENABLED ((unsigned int) 0x00000001) // PWM mode is enabled for CT16Bn_MAT0 +#define TMR_TMR16B1PWMC_PWM0_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1PWMC_PWM1_MASK ((unsigned int) 0x00000002) +#define TMR_TMR16B1PWMC_PWM1_ENABLED ((unsigned int) 0x00000002) // PWM mode is enabled for CT16Bn_MAT1 +#define TMR_TMR16B1PWMC_PWM1_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1PWMC_PWM2_MASK ((unsigned int) 0x00000004) +#define TMR_TMR16B1PWMC_PWM2_ENABLED ((unsigned int) 0x00000004) // PWM mode is enabled for CT16Bn_MAT2 +#define TMR_TMR16B1PWMC_PWM2_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR16B1PWMC_PWM3_MASK ((unsigned int) 0x00000008) +#define TMR_TMR16B1PWMC_PWM3_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR16B1PWMC_PWM3_DISABLED ((unsigned int) 0x00000000) + +/*############################################################################## +## 32-Bit Timers (CT32B0/1) +##############################################################################*/ + +#define TMR_CT32B0_BASE_ADDRESS (0x40014000) + +#define TMR_TMR32B0IR (*(pREG32 (0x40014000))) // Interrupt register +#define TMR_TMR32B0TCR (*(pREG32 (0x40014004))) // Timer control register +#define TMR_TMR32B0TC (*(pREG32 (0x40014008))) // Timer counter +#define TMR_TMR32B0PR (*(pREG32 (0x4001400C))) // Prescale register +#define TMR_TMR32B0PC (*(pREG32 (0x40014010))) // Prescale counter register +#define TMR_TMR32B0MCR (*(pREG32 (0x40014014))) // Match control register +#define TMR_TMR32B0MR0 (*(pREG32 (0x40014018))) // Match register 0 +#define TMR_TMR32B0MR1 (*(pREG32 (0x4001401C))) // Match register 1 +#define TMR_TMR32B0MR2 (*(pREG32 (0x40014020))) // Match register 2 +#define TMR_TMR32B0MR3 (*(pREG32 (0x40014024))) // Match register 3 +#define TMR_TMR32B0CCR (*(pREG32 (0x40014028))) // Capture control register +#define TMR_TMR32B0CR0 (*(pREG32 (0x4001402C))) // Capture register +#define TMR_TMR32B0EMR (*(pREG32 (0x4001403C))) // External match register +#define TMR_TMR32B0CTCR (*(pREG32 (0x40014070))) // Count control register +#define TMR_TMR32B0PWMC (*(pREG32 (0x40014074))) // PWM control register + +#define TMR_TMR32B0IR_MR0_MASK ((unsigned int) 0x00000001) // Interrupt flag for match channel 0 +#define TMR_TMR32B0IR_MR0 ((unsigned int) 0x00000001) +#define TMR_TMR32B0IR_MR1_MASK ((unsigned int) 0x00000002) // Interrupt flag for match channel 1 +#define TMR_TMR32B0IR_MR1 ((unsigned int) 0x00000002) +#define TMR_TMR32B0IR_MR2_MASK ((unsigned int) 0x00000004) // Interrupt flag for match channel 2 +#define TMR_TMR32B0IR_MR2 ((unsigned int) 0x00000004) +#define TMR_TMR32B0IR_MR3_MASK ((unsigned int) 0x00000008) // Interrupt flag for match channel 3 +#define TMR_TMR32B0IR_MR3 ((unsigned int) 0x00000008) +#define TMR_TMR32B0IR_CR0_MASK ((unsigned int) 0x00000010) // Interrupt flag for capture channel 0 event +#define TMR_TMR32B0IR_CR0 ((unsigned int) 0x00000010) +#define TMR_TMR32B0IR_MASK_ALL ((unsigned int) 0x0000001F) + +#define TMR_TMR32B0TCR_COUNTERENABLE_MASK ((unsigned int) 0x00000001) // Counter enable +#define TMR_TMR32B0TCR_COUNTERENABLE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B0TCR_COUNTERENABLE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0TCR_COUNTERRESET_MASK ((unsigned int) 0x00000002) +#define TMR_TMR32B0TCR_COUNTERRESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B0TCR_COUNTERRESET_DISABLED ((unsigned int) 0x00000002) + +#define TMR_TMR32B0MCR_MR0_INT_MASK ((unsigned int) 0x00000001) // Interrupt on MRO +#define TMR_TMR32B0MCR_MR0_INT_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B0MCR_MR0_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR0_RESET_MASK ((unsigned int) 0x00000002) // Reset on MR0 +#define TMR_TMR32B0MCR_MR0_RESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B0MCR_MR0_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR0_STOP_MASK ((unsigned int) 0x00000004) // Stop on MR0 +#define TMR_TMR32B0MCR_MR0_STOP_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR32B0MCR_MR0_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR1_INT_MASK ((unsigned int) 0x00000008) // Interrupt on MR1 +#define TMR_TMR32B0MCR_MR1_INT_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR32B0MCR_MR1_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR1_RESET_MASK ((unsigned int) 0x00000010) // Reset on MR1 +#define TMR_TMR32B0MCR_MR1_RESET_ENABLED ((unsigned int) 0x00000010) +#define TMR_TMR32B0MCR_MR1_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR1_STOP_MASK ((unsigned int) 0x00000020) // Stop on MR1 +#define TMR_TMR32B0MCR_MR1_STOP_ENABLED ((unsigned int) 0x00000020) +#define TMR_TMR32B0MCR_MR1_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR2_INT_MASK ((unsigned int) 0x00000040) // Interrupt on MR2 +#define TMR_TMR32B0MCR_MR2_INT_ENABLED ((unsigned int) 0x00000040) +#define TMR_TMR32B0MCR_MR2_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR2_RESET_MASK ((unsigned int) 0x00000080) // Reset on MR2 +#define TMR_TMR32B0MCR_MR2_RESET_ENABLED ((unsigned int) 0x00000080) +#define TMR_TMR32B0MCR_MR2_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR2_STOP_MASK ((unsigned int) 0x00000100) // Stop on MR2 +#define TMR_TMR32B0MCR_MR2_STOP_ENABLED ((unsigned int) 0x00000100) +#define TMR_TMR32B0MCR_MR2_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR3_INT_MASK ((unsigned int) 0x00000200) // Interrupt on MR3 +#define TMR_TMR32B0MCR_MR3_INT_ENABLED ((unsigned int) 0x00000200) +#define TMR_TMR32B0MCR_MR3_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR3_RESET_MASK ((unsigned int) 0x00000400) // Reset on MR3 +#define TMR_TMR32B0MCR_MR3_RESET_ENABLED ((unsigned int) 0x00000400) +#define TMR_TMR32B0MCR_MR3_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0MCR_MR3_STOP_MASK ((unsigned int) 0x00000800) // Stop on MR3 +#define TMR_TMR32B0MCR_MR3_STOP_ENABLED ((unsigned int) 0x00000800) +#define TMR_TMR32B0MCR_MR3_STOP_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR32B0CCR_CAP0RE_MASK ((unsigned int) 0x00000001) // Capture on rising edge +#define TMR_TMR32B0CCR_CAP0RE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B0CCR_CAP0RE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0CCR_CAP0FE_MASK ((unsigned int) 0x00000002) // Capture on falling edge +#define TMR_TMR32B0CCR_CAP0FE_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B0CCR_CAP0FE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0CCR_CAP0I_MASK ((unsigned int) 0x00000004) // Interrupt on CAP0 event +#define TMR_TMR32B0CCR_CAP0I_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR32B0CCR_CAP0I_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR32B0EMR_EM0_MASK ((unsigned int) 0x00000001) // External match 0 +#define TMR_TMR32B0EMR_EM0 ((unsigned int) 0x00000001) +#define TMR_TMR32B0EMR_EMC0_MASK ((unsigned int) 0x00000030) +#define TMR_TMR32B0EMR_EMC0_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B0EMR_EMC0_LOW ((unsigned int) 0x00000010) +#define TMR_TMR32B0EMR_EMC0_HIGH ((unsigned int) 0x00000020) +#define TMR_TMR32B0EMR_EMC0_TOGGLE ((unsigned int) 0x00000030) +#define TMR_TMR32B0EMR_EM1_MASK ((unsigned int) 0x00000002) // External match 1 +#define TMR_TMR32B0EMR_EM1 ((unsigned int) 0x00000002) +#define TMR_TMR32B0EMR_EMC1_MASK ((unsigned int) 0x000000C0) +#define TMR_TMR32B0EMR_EMC1_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B0EMR_EMC1_LOW ((unsigned int) 0x00000040) +#define TMR_TMR32B0EMR_EMC1_HIGH ((unsigned int) 0x00000080) +#define TMR_TMR32B0EMR_EMC1_TOGGLE ((unsigned int) 0x000000C0) +#define TMR_TMR32B0EMR_EM2_MASK ((unsigned int) 0x00000004) // External match 2 +#define TMR_TMR32B0EMR_EM2 ((unsigned int) 0x00000004) +#define TMR_TMR32B0EMR_EMC2_MASK ((unsigned int) 0x00000300) +#define TMR_TMR32B0EMR_EMC2_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B0EMR_EMC2_LOW ((unsigned int) 0x00000100) +#define TMR_TMR32B0EMR_EMC2_HIGH ((unsigned int) 0x00000200) +#define TMR_TMR32B0EMR_EMC2_TOGGLE ((unsigned int) 0x00000300) +#define TMR_TMR32B0EMR_EM3_MASK ((unsigned int) 0x00000008) // External match 3 +#define TMR_TMR32B0EMR_EM3 ((unsigned int) 0x00000008) +#define TMR_TMR32B0EMR_EMC3_MASK ((unsigned int) 0x00000C00) +#define TMR_TMR32B0EMR_EMC3_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B0EMR_EMC3_LOW ((unsigned int) 0x00000400) +#define TMR_TMR32B0EMR_EMC3_HIGH ((unsigned int) 0x00000800) +#define TMR_TMR32B0EMR_EMC3_TOGGLE ((unsigned int) 0x00000C00) + +#define TMR_TMR32B0CTCR_CTMODE_MASK ((unsigned int) 0x00000003) // Counter/Timer mode +#define TMR_TMR32B0CTCR_CTMODE_TIMER ((unsigned int) 0x00000000) // Timer Mode: Every rising PCLK edge +#define TMR_TMR32B0CTCR_CTMODE_COUNTERRISING ((unsigned int) 0x00000001) // Counter: TC increments on rising edge of input +#define TMR_TMR32B0CTCR_CTMODE_COUNTERFALLING ((unsigned int) 0x00000002) // Counter: TC increments on falling edge of input +#define TMR_TMR32B0CTCR_CTMODE_COUNTERBOTH ((unsigned int) 0x00000003) // Counter: TC increments on both edges of input +#define TMR_TMR32B0CTCR_CINPUTSELECT_MASK ((unsigned int) 0x0000000C) +#define TMR_TMR32B0CTCR_CINPUTSELECT ((unsigned int) 0x00000000) // CINPUTSELECT must be set to 00 + +#define TMR_TMR32B0PWMC_PWM0_MASK ((unsigned int) 0x00000001) +#define TMR_TMR32B0PWMC_PWM0_ENABLED ((unsigned int) 0x00000001) // PWM mode is enabled for CT32Bn_MAT0 +#define TMR_TMR32B0PWMC_PWM0_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0PWMC_PWM1_MASK ((unsigned int) 0x00000002) +#define TMR_TMR32B0PWMC_PWM1_ENABLED ((unsigned int) 0x00000002) // PWM mode is enabled for CT32Bn_MAT1 +#define TMR_TMR32B0PWMC_PWM1_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0PWMC_PWM2_MASK ((unsigned int) 0x00000004) +#define TMR_TMR32B0PWMC_PWM2_ENABLED ((unsigned int) 0x00000004) // PWM mode is enabled for CT32Bn_MAT2 +#define TMR_TMR32B0PWMC_PWM2_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B0PWMC_PWM3_MASK ((unsigned int) 0x00000008) +#define TMR_TMR32B0PWMC_PWM3_ENABLED ((unsigned int) 0x00000008) // PWM mode is enabled for CT32Bn_MAT3 +#define TMR_TMR32B0PWMC_PWM3_DISABLED ((unsigned int) 0x00000000) + +#define TMR_CT32B1_BASE_ADDRESS (0x40018000) + +#define TMR_TMR32B1IR (*(pREG32 (0x40018000))) // Interrupt register +#define TMR_TMR32B1TCR (*(pREG32 (0x40018004))) // Timer control register +#define TMR_TMR32B1TC (*(pREG32 (0x40018008))) // Timer counter +#define TMR_TMR32B1PR (*(pREG32 (0x4001800C))) // Prescale register +#define TMR_TMR32B1PC (*(pREG32 (0x40018010))) // Prescale counter register +#define TMR_TMR32B1MCR (*(pREG32 (0x40018014))) // Match control register +#define TMR_TMR32B1MR0 (*(pREG32 (0x40018018))) // Match register 0 +#define TMR_TMR32B1MR1 (*(pREG32 (0x4001801C))) // Match register 1 +#define TMR_TMR32B1MR2 (*(pREG32 (0x40018020))) // Match register 2 +#define TMR_TMR32B1MR3 (*(pREG32 (0x40018024))) // Match register 3 +#define TMR_TMR32B1CCR (*(pREG32 (0x40018028))) // Capture control register +#define TMR_TMR32B1CR0 (*(pREG32 (0x4001802C))) // Capture register +#define TMR_TMR32B1EMR (*(pREG32 (0x4001803C))) // External match register +#define TMR_TMR32B1CTCR (*(pREG32 (0x40018070))) // Count control register +#define TMR_TMR32B1PWMC (*(pREG32 (0x40018074))) // PWM control register + +#define TMR_TMR32B1IR_MR0_MASK ((unsigned int) 0x00000001) // Interrupt flag for match channel 0 +#define TMR_TMR32B1IR_MR0 ((unsigned int) 0x00000001) +#define TMR_TMR32B1IR_MR1_MASK ((unsigned int) 0x00000002) // Interrupt flag for match channel 1 +#define TMR_TMR32B1IR_MR1 ((unsigned int) 0x00000002) +#define TMR_TMR32B1IR_MR2_MASK ((unsigned int) 0x00000004) // Interrupt flag for match channel 2 +#define TMR_TMR32B1IR_MR2 ((unsigned int) 0x00000004) +#define TMR_TMR32B1IR_MR3_MASK ((unsigned int) 0x00000008) // Interrupt flag for match channel 3 +#define TMR_TMR32B1IR_MR3 ((unsigned int) 0x00000008) +#define TMR_TMR32B1IR_CR0_MASK ((unsigned int) 0x00000010) // Interrupt flag for capture channel 0 event +#define TMR_TMR32B1IR_CR0 ((unsigned int) 0x00000010) +#define TMR_TMR32B1IR_MASK_ALL ((unsigned int) 0x0000001F) + +#define TMR_TMR32B1TCR_COUNTERENABLE_MASK ((unsigned int) 0x00000001) // Counter enable +#define TMR_TMR32B1TCR_COUNTERENABLE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B1TCR_COUNTERENABLE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1TCR_COUNTERRESET_MASK ((unsigned int) 0x00000002) +#define TMR_TMR32B1TCR_COUNTERRESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B1TCR_COUNTERRESET_DISABLED ((unsigned int) 0x00000002) + +#define TMR_TMR32B1MCR_MR0_INT_MASK ((unsigned int) 0x00000001) // Interrupt on MRO +#define TMR_TMR32B1MCR_MR0_INT_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B1MCR_MR0_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR0_RESET_MASK ((unsigned int) 0x00000002) // Reset on MR0 +#define TMR_TMR32B1MCR_MR0_RESET_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B1MCR_MR0_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR0_STOP_MASK ((unsigned int) 0x00000004) // Stop on MR0 +#define TMR_TMR32B1MCR_MR0_STOP_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR32B1MCR_MR0_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR1_INT_MASK ((unsigned int) 0x00000008) // Interrupt on MR1 +#define TMR_TMR32B1MCR_MR1_INT_ENABLED ((unsigned int) 0x00000008) +#define TMR_TMR32B1MCR_MR1_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR1_RESET_MASK ((unsigned int) 0x00000010) // Reset on MR1 +#define TMR_TMR32B1MCR_MR1_RESET_ENABLED ((unsigned int) 0x00000010) +#define TMR_TMR32B1MCR_MR1_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR1_STOP_MASK ((unsigned int) 0x00000020) // Stop on MR1 +#define TMR_TMR32B1MCR_MR1_STOP_ENABLED ((unsigned int) 0x00000020) +#define TMR_TMR32B1MCR_MR1_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR2_INT_MASK ((unsigned int) 0x00000040) // Interrupt on MR2 +#define TMR_TMR32B1MCR_MR2_INT_ENABLED ((unsigned int) 0x00000040) +#define TMR_TMR32B1MCR_MR2_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR2_RESET_MASK ((unsigned int) 0x00000080) // Reset on MR2 +#define TMR_TMR32B1MCR_MR2_RESET_ENABLED ((unsigned int) 0x00000080) +#define TMR_TMR32B1MCR_MR2_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR2_STOP_MASK ((unsigned int) 0x00000100) // Stop on MR2 +#define TMR_TMR32B1MCR_MR2_STOP_ENABLED ((unsigned int) 0x00000100) +#define TMR_TMR32B1MCR_MR2_STOP_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR3_INT_MASK ((unsigned int) 0x00000200) // Interrupt on MR3 +#define TMR_TMR32B1MCR_MR3_INT_ENABLED ((unsigned int) 0x00000200) +#define TMR_TMR32B1MCR_MR3_INT_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR3_RESET_MASK ((unsigned int) 0x00000400) // Reset on MR3 +#define TMR_TMR32B1MCR_MR3_RESET_ENABLED ((unsigned int) 0x00000400) +#define TMR_TMR32B1MCR_MR3_RESET_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1MCR_MR3_STOP_MASK ((unsigned int) 0x00000800) // Stop on MR3 +#define TMR_TMR32B1MCR_MR3_STOP_ENABLED ((unsigned int) 0x00000800) +#define TMR_TMR32B1MCR_MR3_STOP_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR32B1CCR_CAP0RE_MASK ((unsigned int) 0x00000001) // Capture on rising edge +#define TMR_TMR32B1CCR_CAP0RE_ENABLED ((unsigned int) 0x00000001) +#define TMR_TMR32B1CCR_CAP0RE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1CCR_CAP0FE_MASK ((unsigned int) 0x00000002) // Capture on falling edge +#define TMR_TMR32B1CCR_CAP0FE_ENABLED ((unsigned int) 0x00000002) +#define TMR_TMR32B1CCR_CAP0FE_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1CCR_CAP0I_MASK ((unsigned int) 0x00000004) // Interrupt on CAP0 event +#define TMR_TMR32B1CCR_CAP0I_ENABLED ((unsigned int) 0x00000004) +#define TMR_TMR32B1CCR_CAP0I_DISABLED ((unsigned int) 0x00000000) + +#define TMR_TMR32B1EMR_EM0_MASK ((unsigned int) 0x00000001) // External match 0 +#define TMR_TMR32B1EMR_EM0 ((unsigned int) 0x00000001) +#define TMR_TMR32B1EMR_EMC0_MASK ((unsigned int) 0x00000030) +#define TMR_TMR32B1EMR_EMC0_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B1EMR_EMC0_LOW ((unsigned int) 0x00000010) +#define TMR_TMR32B1EMR_EMC0_HIGH ((unsigned int) 0x00000020) +#define TMR_TMR32B1EMR_EMC0_TOGGLE ((unsigned int) 0x00000030) +#define TMR_TMR32B1EMR_EM1_MASK ((unsigned int) 0x00000002) // External match 1 +#define TMR_TMR32B1EMR_EM1 ((unsigned int) 0x00000002) +#define TMR_TMR32B1EMR_EMC1_MASK ((unsigned int) 0x000000C0) +#define TMR_TMR32B1EMR_EMC1_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B1EMR_EMC1_LOW ((unsigned int) 0x00000040) +#define TMR_TMR32B1EMR_EMC1_HIGH ((unsigned int) 0x00000080) +#define TMR_TMR32B1EMR_EMC1_TOGGLE ((unsigned int) 0x000000C0) +#define TMR_TMR32B1EMR_EM2_MASK ((unsigned int) 0x00000004) // External match 2 +#define TMR_TMR32B1EMR_EM2 ((unsigned int) 0x00000004) +#define TMR_TMR32B1EMR_EMC2_MASK ((unsigned int) 0x00000300) +#define TMR_TMR32B1EMR_EMC2_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B1EMR_EMC2_LOW ((unsigned int) 0x00000100) +#define TMR_TMR32B1EMR_EMC2_HIGH ((unsigned int) 0x00000200) +#define TMR_TMR32B1EMR_EMC2_TOGGLE ((unsigned int) 0x00000300) +#define TMR_TMR32B1EMR_EM3_MASK ((unsigned int) 0x00000008) // External match 3 +#define TMR_TMR32B1EMR_EM3 ((unsigned int) 0x00000008) +#define TMR_TMR32B1EMR_EMC3_MASK ((unsigned int) 0x00000C00) +#define TMR_TMR32B1EMR_EMC3_DONOTHING ((unsigned int) 0x00000000) +#define TMR_TMR32B1EMR_EMC3_LOW ((unsigned int) 0x00000400) +#define TMR_TMR32B1EMR_EMC3_HIGH ((unsigned int) 0x00000800) +#define TMR_TMR32B1EMR_EMC3_TOGGLE ((unsigned int) 0x00000C00) + +#define TMR_TMR32B1CTCR_CTMODE_MASK ((unsigned int) 0x00000003) // Counter/Timer mode +#define TMR_TMR32B1CTCR_CTMODE_TIMER ((unsigned int) 0x00000000) // Timer Mode: Every rising PCLK edge +#define TMR_TMR32B1CTCR_CTMODE_COUNTERRISING ((unsigned int) 0x00000001) // Counter: TC increments on rising edge of input +#define TMR_TMR32B1CTCR_CTMODE_COUNTERFALLING ((unsigned int) 0x00000002) // Counter: TC increments on falling edge of input +#define TMR_TMR32B1CTCR_CTMODE_COUNTERBOTH ((unsigned int) 0x00000003) // Counter: TC increments on both edges of input +#define TMR_TMR32B1CTCR_CINPUTSELECT_MASK ((unsigned int) 0x0000000C) +#define TMR_TMR32B1CTCR_CINPUTSELECT ((unsigned int) 0x00000000) // CINPUTSELECT must be set to 00 + +#define TMR_TMR32B1PWMC_PWM0_MASK ((unsigned int) 0x00000001) +#define TMR_TMR32B1PWMC_PWM0_ENABLED ((unsigned int) 0x00000001) // PWM mode is enabled for CT32Bn_MAT0 +#define TMR_TMR32B1PWMC_PWM0_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1PWMC_PWM1_MASK ((unsigned int) 0x00000002) +#define TMR_TMR32B1PWMC_PWM1_ENABLED ((unsigned int) 0x00000002) // PWM mode is enabled for CT32Bn_MAT1 +#define TMR_TMR32B1PWMC_PWM1_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1PWMC_PWM2_MASK ((unsigned int) 0x00000004) +#define TMR_TMR32B1PWMC_PWM2_ENABLED ((unsigned int) 0x00000004) // PWM mode is enabled for CT32Bn_MAT2 +#define TMR_TMR32B1PWMC_PWM2_DISABLED ((unsigned int) 0x00000000) +#define TMR_TMR32B1PWMC_PWM3_MASK ((unsigned int) 0x00000008) +#define TMR_TMR32B1PWMC_PWM3_ENABLED ((unsigned int) 0x00000008) // PWM mode is enabled for CT32Bn_MAT3 +#define TMR_TMR32B1PWMC_PWM3_DISABLED ((unsigned int) 0x00000000) + +/*############################################################################## +## System Tick Timer +##############################################################################*/ + +#define SYSTICK_BASE_ADDRESS (0xE000E000) + +#define SYSTICK_STCTRL (*(pREG32 (0xE000E010))) // System tick control +#define SYSTICK_STRELOAD (*(pREG32 (0xE000E014))) // System timer reload +#define SYSTICK_STCURR (*(pREG32 (0xE000E018))) // System timer current +#define SYSTICK_STCALIB (*(pREG32 (0xE000E01C))) // System timer calibration + +/* STCTRL (System Timer Control and status register) + The STCTRL register contains control information for the System Tick Timer, and provides + a status flag. */ + +#define SYSTICK_STCTRL_ENABLE (0x00000001) // System tick counter enable +#define SYSTICK_STCTRL_TICKINT (0x00000002) // System tick interrupt enable +#define SYSTICK_STCTRL_CLKSOURCE (0x00000004) // NOTE: This isn't documented but is based on NXP examples +#define SYSTICK_STCTRL_COUNTFLAG (0x00010000) // System tick counter flag + +/* STRELOAD (System Timer Reload value register) + The STRELOAD register is set to the value that will be loaded into the System Tick Timer + whenever it counts down to zero. This register is loaded by software as part of timer + initialization. The STCALIB register may be read and used as the value for STRELOAD if + the CPU or external clock is running at the frequency intended for use with the STCALIB + value. */ + +#define SYSTICK_STRELOAD_MASK (0x00FFFFFF) + +/* STCURR (System Timer Current value register) + The STCURR register returns the current count from the System Tick counter when it is + read by software. */ + +#define SYSTICK_STCURR_MASK (0x00FFFFFF) + +/* STCALIB (System Timer Calibration value register) */ + +#define SYSTICK_STCALIB_TENMS_MASK (0x00FFFFFF) +#define SYSTICK_STCALIB_SKEW_MASK (0x40000000) +#define SYSTICK_STCALIB_NOREF_MASK (0x80000000) + +/*############################################################################## +## ADC +##############################################################################*/ + +#define ADC_AD0_BASE_ADDRESS (0x4001C000) + +#define ADC_AD0CR (*(pREG32 (0x4001C000))) // ADC Control Register +#define ADC_AD0GDR ((unsigned int) 0x4001C004) // ADC Global Data Register +#define ADC_AD0INTEN ((unsigned int) 0x4001C00C) // ADC Interrupt Enable Register +#define ADC_AD0DR0 ((unsigned int) 0x4001C010) // ADC Data Register 0 +#define ADC_AD0DR1 ((unsigned int) 0x4001C014) // ADC Data Register 1 +#define ADC_AD0DR2 ((unsigned int) 0x4001C018) // ADC Data Register 2 +#define ADC_AD0DR3 ((unsigned int) 0x4001C01C) // ADC Data Register 3 +#define ADC_AD0DR4 ((unsigned int) 0x4001C020) // ADC Data Register 4 +#define ADC_AD0DR5 ((unsigned int) 0x4001C024) // ADC Data Register 5 +#define ADC_AD0DR6 ((unsigned int) 0x4001C028) // ADC Data Register 6 +#define ADC_AD0DR7 ((unsigned int) 0x4001C02C) // ADC Data Register 7 +#define ADC_AD0STAT ((unsigned int) 0x4001C030) // ADC Status Register + +#define ADC_AD0CR_SEL_MASK (0x000000FF) +#define ADC_AD0CR_SEL_AD0 (0x00000001) +#define ADC_AD0CR_SEL_AD1 (0x00000002) +#define ADC_AD0CR_SEL_AD2 (0x00000004) +#define ADC_AD0CR_SEL_AD3 (0x00000008) +#define ADC_AD0CR_SEL_AD4 (0x00000010) +#define ADC_AD0CR_SEL_AD5 (0x00000020) +#define ADC_AD0CR_SEL_AD6 (0x00000040) +#define ADC_AD0CR_SEL_AD7 (0x00000080) +#define ADC_AD0CR_CLKDIV_MASK (0x0000FF00) +#define ADC_AD0CR_BURST_MASK (0x00010000) +#define ADC_AD0CR_BURST_SWMODE (0x00000000) +#define ADC_AD0CR_BURST_HWSCANMODE (0x00010000) +#define ADC_AD0CR_CLKS_MASK (0x000E0000) +#define ADC_AD0CR_CLKS_10BITS (0x00000000) +#define ADC_AD0CR_CLKS_9BITS (0x00020000) +#define ADC_AD0CR_CLKS_8BITS (0x00040000) +#define ADC_AD0CR_CLKS_7BITS (0x00060000) +#define ADC_AD0CR_CLKS_6BITS (0x00080000) +#define ADC_AD0CR_CLKS_5BITS (0x000A0000) +#define ADC_AD0CR_CLKS_4BITS (0x000C0000) +#define ADC_AD0CR_CLKS_3BITS (0x000E0000) +#define ADC_AD0CR_START_MASK (0x07000000) +#define ADC_AD0CR_START_NOSTART (0x00000000) +#define ADC_AD0CR_START_STARTNOW (0x01000000) +#define ADC_AD0CR_EDGE_MASK (0x08000000) +#define ADC_AD0CR_EDGE_FALLING (0x08000000) +#define ADC_AD0CR_EDGE_RISING (0x00000000) + +/* AD9GDR (A/D Global Data Register) + The A/D Global Data Register contains the result of the most recent A/D conversion. This + includes the data, DONE, and Overrun flags, and the number of the A/D channel to which + the data relates. */ + +#define ADC_AD0GDR_RESULT_MASK (0x0000FFC0) +#define ADC_AD0GDR_CHN_MASK (0x07000000) // Channel from which the results were converted +#define ADC_AD0GDR_OVERUN_MASK (0x40000000) +#define ADC_AD0GDR_OVERUN (0x40000000) +#define ADC_AD0GDR_DONE_MASK (0x80000000) +#define ADC_AD0GDR_DONE (0x80000000) + +/* AD0STAT (A/D Status Register) + The A/D Status register allows checking the status of all A/D channels simultaneously. + The DONE and OVERRUN flags appearing in the ADDRn register for each A/D channel + are mirrored in ADSTAT. The interrupt flag (the logical OR of all DONE flags) is also found + in ADSTAT. */ + +#define ADC_AD0STAT_DONE0_MASK (0x00000001) +#define ADC_AD0STAT_DONE0 (0x00000001) +#define ADC_AD0STAT_DONE1_MASK (0x00000002) +#define ADC_AD0STAT_DONE1 (0x00000002) +#define ADC_AD0STAT_DONE2_MASK (0x00000004) +#define ADC_AD0STAT_DONE2 (0x00000004) +#define ADC_AD0STAT_DONE3_MASK (0x00000008) +#define ADC_AD0STAT_DONE3 (0x00000008) +#define ADC_AD0STAT_DONE4_MASK (0x00000010) +#define ADC_AD0STAT_DONE4 (0x00000010) +#define ADC_AD0STAT_DONE5_MASK (0x00000020) +#define ADC_AD0STAT_DONE5 (0x00000020) +#define ADC_AD0STAT_DONE6_MASK (0x00000040) +#define ADC_AD0STAT_DONE6 (0x00000040) +#define ADC_AD0STAT_DONE7_MASK (0x00000080) +#define ADC_AD0STAT_DONE7 (0x00000080) +#define ADC_AD0STAT_OVERRUN0_MASK (0x00000100) +#define ADC_AD0STAT_OVERRUN0 (0x00000100) +#define ADC_AD0STAT_OVERRUN1_MASK (0x00000200) +#define ADC_AD0STAT_OVERRUN1 (0x00000200) +#define ADC_AD0STAT_OVERRUN2_MASK (0x00000400) +#define ADC_AD0STAT_OVERRUN2 (0x00000400) +#define ADC_AD0STAT_OVERRUN3_MASK (0x00000800) +#define ADC_AD0STAT_OVERRUN3 (0x00000800) +#define ADC_AD0STAT_OVERRUN4_MASK (0x00001000) +#define ADC_AD0STAT_OVERRUN4 (0x00001000) +#define ADC_AD0STAT_OVERRUN5_MASK (0x00002000) +#define ADC_AD0STAT_OVERRUN5 (0x00002000) +#define ADC_AD0STAT_OVERRUN6_MASK (0x00004000) +#define ADC_AD0STAT_OVERRUN6 (0x00004000) +#define ADC_AD0STAT_OVERRUN7_MASK (0x00008000) +#define ADC_AD0STAT_OVERRUN7 (0x00008000) +#define ADC_AD0STAT_ADINT_MASK (0x00010000) +#define ADC_AD0STAT_ADINT (0x00010000) + +/* ADINTEN0 (A/D Interrupt Enable Register) + This register allows control over which A/D channels generate an interrupt when a + conversion is complete. For example, it may be desirable to use some A/D channels to + monitor sensors by continuously performing conversions on them. The most recent + results are read by the application program whenever they are needed. In this case, an + interrupt is not desirable at the end of each conversion for some A/D channels. */ + +#define ADC_AD0INTEN_ADINTEN0_MASK (0x00000001) +#define ADC_AD0INTEN_ADINTEN0 (0x00000001) +#define ADC_AD0INTEN_ADINTEN1_MASK (0x00000002) +#define ADC_AD0INTEN_ADINTEN1 (0x00000002) +#define ADC_AD0INTEN_ADINTEN2_MASK (0x00000004) +#define ADC_AD0INTEN_ADINTEN2 (0x00000004) +#define ADC_AD0INTEN_ADINTEN3_MASK (0x00000008) +#define ADC_AD0INTEN_ADINTEN3 (0x00000008) +#define ADC_AD0INTEN_ADINTEN4_MASK (0x00000010) +#define ADC_AD0INTEN_ADINTEN4 (0x00000010) +#define ADC_AD0INTEN_ADINTEN5_MASK (0x00000020) +#define ADC_AD0INTEN_ADINTEN5 (0x00000020) +#define ADC_AD0INTEN_ADINTEN6_MASK (0x00000040) +#define ADC_AD0INTEN_ADINTEN6 (0x00000040) +#define ADC_AD0INTEN_ADINTEN7_MASK (0x00000080) +#define ADC_AD0INTEN_ADINTEN7 (0x00000080) +#define ADC_AD0INTEN_ADGINTEN_MASK (0x00000100) +#define ADC_AD0INTEN_ADGINTEN_ENABLE (0x00000100) +#define ADC_AD0INTEN_ADGINTEN_DISABLE (0x00000000) + +/* AD0DR0..7 (A/D Data Registers) + The A/D Data Register hold the result when an A/D conversion is complete, and also + include the flags that indicate when a conversion has been completed and when a + conversion overrun has occurred. */ + +#define ADC_DR_V_MASK (0x0000FFC0) +#define ADC_DR_OVERRUN_MASK (0x40000000) +#define ADC_DR_OVERRUN (0x40000000) +#define ADC_DR_DONE_MASK (0x80000000) +#define ADC_DR_DONE (0x80000000) + +/*############################################################################## +## WDT - Watchdog Timer +##############################################################################*/ + +#define WDT_BASE_ADDRESS (0x40004000) + +#define WDT_WDMOD (*(pREG32 (0x40004000))) // Watchdog mode register +#define WDT_WDTC (*(pREG32 (0x40004004))) // Watchdog timer constant register +#define WDT_WDFEED (*(pREG32 (0x40004008))) // Watchdog feed sequence register +#define WDT_WDTV (*(pREG32 (0x4000400C))) // Watchdog timer value register + +/* WDMOD (Watchdog Mode register) + The WDMOD register controls the operation of the Watchdog through the combination of + WDEN and RESET bits. Note that a watchdog feed must be performed before any + changes to the WDMOD register take effect. */ + +#define WDT_WDMOD_WDEN_DISABLED (0x00000000) // Watchdog enable bit +#define WDT_WDMOD_WDEN_ENABLED (0x00000001) +#define WDT_WDMOD_WDEN_MASK (0x00000001) +#define WDT_WDMOD_WDRESET_DISABLED (0x00000000) // Watchdog reset enable bit +#define WDT_WDMOD_WDRESET_ENABLED (0x00000002) +#define WDT_WDMOD_WDRESET_MASK (0x00000002) +#define WDT_WDMOD_WDTOF (0x00000004) // Watchdog time-out interrupt flag +#define WDT_WDMOD_WDTOF_MASK (0x00000004) // Set when the watchdog times out +#define WDT_WDMOD_WDINT (0x00000008) // Watchdog timer interrupt flag +#define WDT_WDMOD_WDINT_MASK (0x00000008) + +/* WDFEED (Watchdog Feed register) + Writing 0xAA followed by 0x55 to this register will reload the Watchdog timer with the + WDTC value. This operation will also start the Watchdog if it is enabled via the WDMOD + register. Setting the WDEN bit in the WDMOD register is not sufficient to enable the + Watchdog. A valid feed sequence must be completed after setting WDEN before the + Watchdog is capable of generating a reset. Until then, the Watchdog will ignore feed + errors. After writing 0xAA to WDFEED, access to any Watchdog register other than writing + 0x55 to WDFEED causes an immediate reset/interrupt when the Watchdog is enabled. + The reset will be generated during the second PCLK following an incorrect access to a + Watchdog register during a feed sequence. + Interrupts should be disabled during the feed sequence. An abort condition will occur if an + interrupt happens during the feed sequence. */ + +#define WDT_WDFEED_FEED1 (0x000000AA) +#define WDT_WDFEED_FEED2 (0x00000055) + +/*############################################################################## +## Misc. Inline Functions +##############################################################################*/ + +/**************************************************************************/ +/*! + @brief Reverses the bit order of a 32-bit value + + Allows single-cycle reversing of 32-bit values (ASM RBIT) + + @param[in] value + The 32-bit value to reverse + @returns The reversed value +*/ +/**************************************************************************/ +static inline uint32_t RBIT(uint32_t value) { uint32_t result=0; __asm volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); return(result); } + +/**************************************************************************/ +/*! + @brief Causes a system reset and enters the USB Bootloader + + Resets the system using the AIRCR register, and waits in a loop until + reset occurs. The resistor/divider on the LPC1343 Reference Design + Base Board [1] causes the AIRCR reset to enter the bootloader rather + than executing the existing firmware. If you wish to reset and execute + the existing firmware, you need to use the watchdog timer to reset + (see "wdt/wdt.c"). + + [1] http://www.microbuilder.eu/Projects/LPC1343ReferenceDesign.aspx +*/ +/**************************************************************************/ +static inline void __resetBootloader() { __disable_irq(); SCB_AIRCR = SCB_AIRCR_VECTKEY_VALUE | SCB_AIRCR_SYSRESETREQ; while(1); } + +#endif \ No newline at end of file diff --git a/lpc1xxx/LPC11xx_handlers.c b/lpc1xxx/LPC11xx_handlers.c new file mode 100644 index 0000000..2f713e2 --- /dev/null +++ b/lpc1xxx/LPC11xx_handlers.c @@ -0,0 +1,170 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// The GCC compiler defines the current architecture derived from the -mcpu argument. +// When target cpu is the cortex-m0, it automatically defines __ARM_ARCH_6M__ +#ifndef __ARM_ARCH_6M__ + #error "The target ARM cpu must be Cortex-M0 compatible (-mcpu=cortex-m0)" +#endif + +// Declare a weak alias macro as described in the GCC manual[1][2] +#define WEAK_ALIAS(f) __attribute__ ((weak, alias (#f))); +#define SECTION(s) __attribute__ ((section(s))) + +/****************************************************************************** + * Forward undefined IRQ handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + *****************************************************************************/ + +void irq_undefined() { + // Do nothing when occured interrupt is not defined, just keep looping + while(1); +} + +void CAN_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void SSP1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void I2C_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER16_0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER16_1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER32_0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER32_1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void SSP0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void UART_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void USB_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void USB_FIQHandler(void) WEAK_ALIAS(irq_undefined); +void ADC_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void WDT_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void BOD_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void FMC_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT3_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT2_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void WAKEUP_IRQHandler(void) WEAK_ALIAS(irq_undefined); + +/***************************************************************************** + * Forward undefined fault handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + ****************************************************************************/ + +void fault_undefined() { + // Do nothing when occured interrupt is not defined, just keep looping + while(1); +} + +void NMI_Handler(void) WEAK_ALIAS(fault_undefined); +void HardFault_Handler(void) WEAK_ALIAS(fault_undefined); +void MemManage_Handler(void) WEAK_ALIAS(fault_undefined); +void BusFault_Handler(void) WEAK_ALIAS(fault_undefined); +void UsageFault_Handler(void) WEAK_ALIAS(fault_undefined); +void SVCall_Handler(void) WEAK_ALIAS(fault_undefined); +void DebugMon_Handler(void) WEAK_ALIAS(fault_undefined); +void PendSV_Handler(void) WEAK_ALIAS(fault_undefined); +void SysTick_Handler(void) WEAK_ALIAS(fault_undefined); + +/****************************************************************************** + * Forward undefined IRQ handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + *****************************************************************************/ + +// Prototype the entry values, which are handled by the linker script +extern void* stack_entry; +extern void boot_entry(void); + +// Defined irq vectors using simple c code following the description in a white +// paper from ARM[3] and code example from Simonsson Fun Technologies[4]. +// These vectors are placed at the memory location defined in the linker script +const void *vectors[] SECTION(".irq_vectors") = +{ + // Stack and program reset entry point + &stack_entry, // The initial stack pointer + boot_entry, // The reset handler + + // Various fault handlers + NMI_Handler, // The NMI handler + HardFault_Handler, // The hard fault handler + MemManage_Handler, // MemManage_Handler + BusFault_Handler, // BusFault_Handler + UsageFault_Handler, // UsageFault_Handler + 0, // Reserved + 0, // Reserved + 0, // Reserved + 0, // Reserved + SVCall_Handler, // SVCall handler + DebugMon_Handler, // DebugMon_Handler + 0, // Reserved + PendSV_Handler, // The PendSV handler + SysTick_Handler, // The SysTick handler + + // Wakeup I/O pins handlers + WAKEUP_IRQHandler, // PIO0_0 Wakeup + WAKEUP_IRQHandler, // PIO0_1 Wakeup + WAKEUP_IRQHandler, // PIO0_2 Wakeup + WAKEUP_IRQHandler, // PIO0_3 Wakeup + WAKEUP_IRQHandler, // PIO0_4 Wakeup + WAKEUP_IRQHandler, // PIO0_5 Wakeup + WAKEUP_IRQHandler, // PIO0_6 Wakeup + WAKEUP_IRQHandler, // PIO0_7 Wakeup + WAKEUP_IRQHandler, // PIO0_8 Wakeup + WAKEUP_IRQHandler, // PIO0_9 Wakeup + WAKEUP_IRQHandler, // PIO0_10 Wakeup + WAKEUP_IRQHandler, // PIO0_11 Wakeup + WAKEUP_IRQHandler, // PIO1_0 Wakeup + + // Specific peripheral irq handlers + CAN_IRQHandler, // CAN + SSP1_IRQHandler, // SSP1 + I2C_IRQHandler, // I2C0 + TIMER16_0_IRQHandler, // CT16B0 (16-bit Timer 0) + TIMER16_1_IRQHandler, // CT16B1 (16-bit Timer 1) + TIMER32_0_IRQHandler, // CT32B0 (32-bit Timer 0) + TIMER32_1_IRQHandler, // CT32B1 (32-bit Timer 1) + SSP0_IRQHandler, // SSP0 + UART_IRQHandler, // UART0 + USB_IRQHandler, // USB IRQ + USB_FIQHandler, // USB FIQ + ADC_IRQHandler, // ADC (A/D Converter) + WDT_IRQHandler, // WDT (Watchdog Timer) + BOD_IRQHandler, // BOD (Brownout Detect) + FMC_IRQHandler, // Flash (IP2111 Flash Memory Controller) + PIOINT3_IRQHandler, // PIO INT3 + PIOINT2_IRQHandler, // PIO INT2 + PIOINT1_IRQHandler, // PIO INT1 + PIOINT0_IRQHandler, // PIO INT0 +}; + +/****************************************************************************** + * References + * [1] http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + * [2] http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html + * [3] http://www.arm.com/files/pdf/Cortex-M3_programming_for_ARM7_developers.pdf + * [4] http://fun-tech.se/stm32/OlimexBlinky/mini.php + *****************************************************************************/ + diff --git a/lpc1xxx/LPC13xx_handlers.c b/lpc1xxx/LPC13xx_handlers.c new file mode 100644 index 0000000..9524de6 --- /dev/null +++ b/lpc1xxx/LPC13xx_handlers.c @@ -0,0 +1,193 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// The GCC compiler defines the current architecture derived from the -mcpu argument. +// When target cpu is the cortex-m3, it automatically defines __ARM_ARCH_7M__ +#ifndef __ARM_ARCH_7M__ + #error "The target ARM cpu must be Cortex-M3 compatible (-mcpu=cortex-m3)" +#endif + +// Declare a weak alias macro as described in the GCC manual[1][2] +#define WEAK_ALIAS(f) __attribute__ ((weak, alias (#f))); +#define SECTION(s) __attribute__ ((section(s))) + +/****************************************************************************** + * Forward undefined IRQ handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + *****************************************************************************/ + +void irq_undefined() { + // Do nothing when occured interrupt is not defined, just keep looping + while(1); +} + +void I2C_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER16_0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER16_1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER32_0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void TIMER32_1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void SSP_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void UART_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void USB_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void USB_FIQHandler(void) WEAK_ALIAS(irq_undefined); +void ADC_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void WDT_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void BOD_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void FMC_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT3_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT2_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT1_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void PIOINT0_IRQHandler(void) WEAK_ALIAS(irq_undefined); +void WAKEUP_IRQHandler(void) WEAK_ALIAS(irq_undefined); + +/***************************************************************************** + * Forward undefined fault handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + ****************************************************************************/ + +void fault_undefined() { + // Do nothing when occured interrupt is not defined, just keep looping + while(1); +} + +void NMI_Handler(void) WEAK_ALIAS(fault_undefined); +void HardFault_Handler(void) WEAK_ALIAS(fault_undefined); +void MemManage_Handler(void) WEAK_ALIAS(fault_undefined); +void BusFault_Handler(void) WEAK_ALIAS(fault_undefined); +void UsageFault_Handler(void) WEAK_ALIAS(fault_undefined); +void SVCall_Handler(void) WEAK_ALIAS(fault_undefined); +void DebugMon_Handler(void) WEAK_ALIAS(fault_undefined); +void PendSV_Handler(void) WEAK_ALIAS(fault_undefined); +void SysTick_Handler(void) WEAK_ALIAS(fault_undefined); + +/****************************************************************************** + * Forward undefined IRQ handlers to an infinite loop function. The Handlers + * are weakly aliased which means that (re)definitions will overide these. + *****************************************************************************/ + +// Prototype the entry values, which are handled by the linker script +extern void* stack_entry; +extern void boot_entry(void); + +// Defined irq vectors using simple c code following the description in a white +// paper from ARM[3] and code example from Simonsson Fun Technologies[4]. +// These vectors are placed at the memory location defined in the linker script +const void *vectors[] SECTION(".irq_vectors") = +{ + // Stack and program reset entry point + &stack_entry, // The initial stack pointer + boot_entry, // The reset handler + + // Various fault handlers + NMI_Handler, // The NMI handler + HardFault_Handler, // The hard fault handler + MemManage_Handler, // The MPU fault handler + BusFault_Handler, // The bus fault handler + UsageFault_Handler, // The usage fault handler + 0, // Reserved + 0, // Reserved + 0, // Reserved + 0, // Reserved + SVCall_Handler, // SVCall handler + DebugMon_Handler, // Debug monitor handler + 0, // Reserved + PendSV_Handler, // The PendSV handler + SysTick_Handler, // The SysTick handler + + // Wakeup I/O pins handlers + WAKEUP_IRQHandler, // PIO0_0 Wakeup + WAKEUP_IRQHandler, // PIO0_1 Wakeup + WAKEUP_IRQHandler, // PIO0_2 Wakeup + WAKEUP_IRQHandler, // PIO0_3 Wakeup + WAKEUP_IRQHandler, // PIO0_4 Wakeup + WAKEUP_IRQHandler, // PIO0_5 Wakeup + WAKEUP_IRQHandler, // PIO0_6 Wakeup + WAKEUP_IRQHandler, // PIO0_7 Wakeup + WAKEUP_IRQHandler, // PIO0_8 Wakeup + WAKEUP_IRQHandler, // PIO0_9 Wakeup + WAKEUP_IRQHandler, // PIO0_10 Wakeup + WAKEUP_IRQHandler, // PIO0_11 Wakeup + WAKEUP_IRQHandler, // PIO1_0 Wakeup + WAKEUP_IRQHandler, // PIO1_1 Wakeup + WAKEUP_IRQHandler, // PIO1_2 Wakeup + WAKEUP_IRQHandler, // PIO1_3 Wakeup + WAKEUP_IRQHandler, // PIO1_4 Wakeup + WAKEUP_IRQHandler, // PIO1_5 Wakeup + WAKEUP_IRQHandler, // PIO1_6 Wakeup + WAKEUP_IRQHandler, // PIO1_7 Wakeup + WAKEUP_IRQHandler, // PIO1_8 Wakeup + WAKEUP_IRQHandler, // PIO1_9 Wakeup + WAKEUP_IRQHandler, // PIO1_10 Wakeup + WAKEUP_IRQHandler, // PIO1_11 Wakeup + WAKEUP_IRQHandler, // PIO2_0 Wakeup + WAKEUP_IRQHandler, // PIO2_1 Wakeup + WAKEUP_IRQHandler, // PIO2_2 Wakeup + WAKEUP_IRQHandler, // PIO2_3 Wakeup + WAKEUP_IRQHandler, // PIO2_4 Wakeup + WAKEUP_IRQHandler, // PIO2_5 Wakeup + WAKEUP_IRQHandler, // PIO2_6 Wakeup + WAKEUP_IRQHandler, // PIO2_7 Wakeup + WAKEUP_IRQHandler, // PIO2_8 Wakeup + WAKEUP_IRQHandler, // PIO2_9 Wakeup + WAKEUP_IRQHandler, // PIO2_10 Wakeup + WAKEUP_IRQHandler, // PIO2_11 Wakeup + WAKEUP_IRQHandler, // PIO3_0 Wakeup + WAKEUP_IRQHandler, // PIO3_1 Wakeup + WAKEUP_IRQHandler, // PIO3_2 Wakeup + WAKEUP_IRQHandler, // PIO3_3 Wakeup + + // Specific peripheral irq handlers + I2C_IRQHandler, // I2C0 + TIMER16_0_IRQHandler, // CT16B0 (16-bit Timer 0) + TIMER16_1_IRQHandler, // CT16B1 (16-bit Timer 1) + TIMER32_0_IRQHandler, // CT32B0 (32-bit Timer 0) + TIMER32_1_IRQHandler, // CT32B1 (32-bit Timer 1) + SSP_IRQHandler, // SSP0 + UART_IRQHandler, // UART0 + USB_IRQHandler, // USB IRQ + USB_FIQHandler, // USB FIQ + ADC_IRQHandler, // ADC (A/D Converter) + WDT_IRQHandler, // WDT (Watchdog Timer) + BOD_IRQHandler, // BOD (Brownout Detect) + FMC_IRQHandler, // Flash (IP2111 Flash Memory Controller) + PIOINT3_IRQHandler, // PIO INT3 + PIOINT2_IRQHandler, // PIO INT2 + PIOINT1_IRQHandler, // PIO INT1 + PIOINT0_IRQHandler, // PIO INT0 +}; + +/****************************************************************************** + * References + * [1] http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + * [2] http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html + * [3] http://www.arm.com/files/pdf/Cortex-M3_programming_for_ARM7_developers.pdf + * [4] http://fun-tech.se/stm32/OlimexBlinky/mini.php + *****************************************************************************/ + diff --git a/lpc1xxx/LPC1xxx_startup.c b/lpc1xxx/LPC1xxx_startup.c new file mode 100644 index 0000000..db54ec3 --- /dev/null +++ b/lpc1xxx/LPC1xxx_startup.c @@ -0,0 +1,65 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// These are defined and created by the linker, locating them in memory +extern unsigned char _etext; +extern unsigned char _data; +extern unsigned char _edata; +extern unsigned char _bss; +extern unsigned char _ebss; + +// Prototype the required startup functions +extern void main(void); + +// The entry point of the application, prepare segments, +// initialize the cpu and execute main() +void boot_entry(void) +{ + register unsigned char *src, *dst; + + // Get physical data address and copy it to sram + src = &_etext; + dst = &_data; + while(dst < &_edata) { + *dst++ = *src++; + } + + // Clear the bss segment + dst = &_bss; + while(dst < &_ebss) { + *dst++ = 0; + } + + // Execute the code at the program entry point + main(); + + // Do nothing when returned from main, just keep looping + while(1); +} diff --git a/lpc1xxx/linkscript.ld b/lpc1xxx/linkscript.ld new file mode 100644 index 0000000..74134f1 --- /dev/null +++ b/lpc1xxx/linkscript.ld @@ -0,0 +1,77 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2010, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +sram_top = ORIGIN(sram) + LENGTH(sram); +ENTRY(boot_entry) + +SECTIONS +{ + .text : + { + KEEP(*(.irq_vectors)) + *(.text*) + *(.rodata*) + } > flash + + /* + * More information about Special Section Indexes is available in the + * free "ELF for the ARM Architecture" document from ARM Limited + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf + * + */ + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > flash + __exidx_start = .; + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > flash + __exidx_end = .; + + _etext = .; + + .data : AT (__exidx_end) + { + _data = .; + *(vtable) + *(.data*) + _edata = .; + } > sram + + /* zero initialized data */ + .bss : + { + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } > sram + + end = .; + + /* For GDB compatibility we decrease the top with 16 bytes */ + stack_entry = sram_top - 16; +} diff --git a/lpcrc b/lpcrc new file mode 100644 index 0000000000000000000000000000000000000000..a6c1aa4c7198bc4784d905bbe1814033684eec7b GIT binary patch literal 7384 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 hAs5qdNPBf6KHrq1jBTvZ*p82Npi + +#include "projectconfig.h" +#include "sysinit.h" + +#include "drivers/sensors/pn532/pn532.h" +#include "drivers/sensors/pn532/pn532_drvr.h" + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. + + Note: CFG_INTERFACE is normally enabled by default. If you wish to + enable the blinking LED code in main, you will need to open + projectconfig.h, comment out "#define CFG_INTERFACE" and + rebuild the project. +*/ +/**************************************************************************/ +int main (void) +{ + #ifdef CFG_INTERFACE + //#error "CFG_INTERFACE must be disabled in projectconfig.h for this demo" + #endif + #if !defined CFG_PRINTF_USBCDC + #error "CFG_PRINTF_USBCDC must be enabled in projectconfig.h for this demo" + #endif + + // Configure cpu and mandatory peripherals + systemInit(); + + // Wait 5 second for someone to open the USB connection for printf + systickDelay(5000); + + // Initialise the PN532 + pn532Init(); + + byte_t response[256]; + size_t responseLen; + pn532_error_t error; + + // Setup command to initialise a single ISO14443A target at 106kbps + byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS }; + + while (1) + { + printf("%s", CFG_PRINTF_NEWLINE); + printf("Wait for an ISO14443A card (Mifare Classic, etc.)%s", CFG_PRINTF_NEWLINE); + + // Send the command + error = pn532Write(abtCommand, sizeof(abtCommand)); + + // Wait until we get a response or an unexpected error message + do + { + error = pn532Read(response, &responseLen); + systickDelay(25); + } + #ifdef PN532_UART + while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + #endif + #ifdef PN532_SPI + while ((error == PN532_ERROR_RESPONSEBUFFEREMPTY) || (error = PN532_ERROR_SPIREADYSTATUSTIMEOUT)); + #endif + + // Print the card details if possible + if (!error) + { + /* Response for ISO14443A 106KBPS (Mifare Classic, etc.) + See UM0701-02 section 7.3.5 for more information + + byte Description + ------------- ------------------------------------------ + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID + + SENS_RES SEL_RES Manufacturer/Card Type NFCID Len + -------- ------- ----------------------- --------- + 00 04 08 NXP Mifare Classic 1K 4 bytes */ + + printf("%s", CFG_PRINTF_NEWLINE); + printf("%-12s: %d %s", "Tags Found", response[7], CFG_PRINTF_NEWLINE); + printf("%-12s: %02X %02X %s", "SENS_RES", response[9], response[10], CFG_PRINTF_NEWLINE); + printf("%-12s: %02X %s", "SEL_RES", response[11], CFG_PRINTF_NEWLINE); + printf("%-12s: ", "NFCID"); + size_t pos; + for (pos=0; pos < response[12]; pos++) + { + printf("%02x ", response[13 + pos]); + } + printf(CFG_PRINTF_NEWLINE); + } + else + { + // Oops .... something bad happened. Check 'error' + printf("Ooops! Error %02X %s", error, CFG_PRINTF_NEWLINE); + } + + // Wait at least one second before trying again + systickDelay(1000); + } +} diff --git a/project/cmd_tbl.h b/project/cmd_tbl.h new file mode 100644 index 0000000..e516834 --- /dev/null +++ b/project/cmd_tbl.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/*! + @file cmd_tbl.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __CMD_TBL_H__ +#define __CMD_TBL_H__ + +#define CMD_COUNT (sizeof(cmd_tbl)/sizeof(cmd_t)) + +#include + +#ifdef CFG_INTERFACE_UART +#include "core/uart/uart.h" +#endif + +// Function prototypes for the command table +void cmd_help(uint8_t argc, char **argv); // handled by core/cmd/cmd.c +void cmd_sysinfo(uint8_t argc, char **argv); + +#ifdef CFG_TFTLCD +void cmd_button(uint8_t argc, char **argv); +void cmd_circle(uint8_t argc, char **argv); +void cmd_clear(uint8_t argc, char **argv); +void cmd_line(uint8_t argc, char **argv); +void cmd_rectangle(uint8_t argc, char **argv); +void cmd_pixel(uint8_t argc, char **argv); +void cmd_progress(uint8_t argc, char **argv); +void cmd_getpixel(uint8_t argc, char **argv); +void cmd_gettext(uint8_t argc, char **argv); +void cmd_calibrate(uint8_t argc, char **argv); +void cmd_orientation(uint8_t argc, char **argv); +void cmd_text(uint8_t argc, char **argv); +void cmd_textw(uint8_t argc, char **argv); +void cmd_tsthreshhold(uint8_t argc, char **argv); +void cmd_tswait(uint8_t argc, char **argv); +#ifdef CFG_SDCARD +void cmd_bmp(uint8_t argc, char **argv); +#endif +#endif + +#ifdef CFG_CHIBI +void cmd_chibi_addr(uint8_t argc, char **argv); +void cmd_chibi_tx(uint8_t argc, char **argv); +#endif + +#ifdef CFG_I2CEEPROM +void cmd_i2ceeprom_read(uint8_t argc, char **argv); +void cmd_i2ceeprom_write(uint8_t argc, char **argv); +void cmd_uart(uint8_t argc, char **argv); +#endif + +#ifdef CFG_LM75B +void cmd_lm75b_gettemp(uint8_t argc, char **argv); +#endif + +#ifdef CFG_SDCARD +void cmd_sd_dir(uint8_t argc, char **argv); +#endif + +#define CMD_NOPARAMS "This command has no parameters" + +/**************************************************************************/ +/*! + Command list for the command-line interpreter and the name of the + corresponding method that handles the command. + + Note that a trailing ',' is required on the last entry, which will + cause a NULL entry to be appended to the end of the table. +*/ +/**************************************************************************/ +cmd_t cmd_tbl[] = +{ + // command name, min args, max args, hidden, function name, command description, syntax + { "?", 0, 0, 0, cmd_help , "Help" , CMD_NOPARAMS }, + { "V", 0, 0, 0, cmd_sysinfo , "System Info" , CMD_NOPARAMS }, + + #ifdef CFG_I2CEEPROM + { "e", 1, 1, 0, cmd_i2ceeprom_read , "EEPROM Read" , "'e '" }, + { "w", 2, 2, 0, cmd_i2ceeprom_write , "EEPROM Write" , "'w '" }, + { "U", 0, 1, 0, cmd_uart , "UART baud rate" , "'U []'" }, + #endif + + #ifdef CFG_TFTLCD + { "b", 7, 99, 0, cmd_button , "Button" , "'b []'" }, + #ifdef CFG_SDCARD + { "B", 3, 3, 0, cmd_bmp , "Bitmap (SD Card)" , "'B '" }, + #endif + { "c", 4, 6, 0, cmd_circle , "Circle" , "'c [ ]'" }, + { "C", 0, 0, 0, cmd_calibrate , "Calibrate Touch Screen" , CMD_NOPARAMS }, + { "F", 0, 1, 0, cmd_clear , "Fill" , "'F []'" }, + { "l", 5, 7, 0, cmd_line , "Line" , "'l [ ]'" }, + { "o", 0, 1, 0, cmd_orientation , "LCD Orientation" , "'o [<0|1>]'" }, + { "p", 3, 3, 0, cmd_pixel , "Draw Pixel" , "'p '" }, + { "P", 9, 9, 0, cmd_progress , "Progress Bar" , "'P <%> '" }, + { "r", 5, 7, 0, cmd_rectangle , "Rectangle" , "'r [ ]'" }, + { "R", 2, 2, 0, cmd_getpixel , "Read Pixel" , "'R '" }, + { "s", 2, 99, 0, cmd_textw , "Text Width" , "'s '" }, + { "t", 5, 99, 0, cmd_text , "Text" , "'t '" }, + { "T", 0, 0, 0, cmd_gettext , "Text Dialogue" , CMD_NOPARAMS }, + { "W", 0, 1, 0, cmd_tswait , "Wait for Touch" , "'W []'" }, + { "x", 0, 1, 0, cmd_tsthreshhold , "Touch Threshold" , "'x [<0..254>]'" }, + #endif + + #ifdef CFG_CHIBI + { "A", 0, 1, 0, cmd_chibi_addr , "Get/Set Node Address" , "'A [<0x0..0xFFFE>]'" }, + { "S", 2, 99, 0, cmd_chibi_tx , "Send Message" , "'S '" }, + #endif + + #ifdef CFG_LM75B + { "m", 0, 0, 0, cmd_lm75b_gettemp , "Temp (C)" , CMD_NOPARAMS }, + #endif + + #ifdef CFG_SDCARD + { "d", 0, 1, 0, cmd_sd_dir , "Dir (SD Card)" , "'d []'" }, + #endif +}; + +#endif \ No newline at end of file diff --git a/project/commands.c b/project/commands.c new file mode 100644 index 0000000..91bce4e --- /dev/null +++ b/project/commands.c @@ -0,0 +1,120 @@ +/**************************************************************************/ +/*! + @file commands.c + @author K. Townsend (microBuilder.eu) + + @brief Common helper-functions for all commands in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include +#include // memset +#include // isdigit, isspace, etc. + +#include "core/cmd/cmd.h" +#include "commands.h" + +/**************************************************************************/ +/*! + @brief Attempts to convert the supplied decimal or hexadecimal + string to the matching 32-bit value. All hexadecimal values + must be preceded by either '0x' or '0X' to be properly parsed. + + @param[in] s + Input string + @param[out] result + Signed 32-bit integer to hold the conversion results + + @section Example + + @code + char *hex = "0xABCD"; + char *dec = "1234"; + + // Convert supplied values to integers + int32_t hexValue, decValue; + getNumber (hex, &hexValue); + getNumber (dec, &decValue); + + @endcode +*/ +/**************************************************************************/ +int getNumber (char *s, int32_t *result) +{ + int32_t value; + uint32_t mustBeHex = FALSE; + uint32_t sgn = 1; + const unsigned char hexToDec [] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15}; + + if (!s) + return 0; + + // Check if this is a hexadecimal value + if ((strlen (s) > 2) && (!strncmp (s, "0x", 2) || !strncmp (s, "0X", 2))) + { + mustBeHex = TRUE; + s += 2; + } + + // Check for negative sign + if (!mustBeHex && *s && (*s == '-')) + { + sgn = -1; + s++; + } + + // Try to convert value + for (value = 0; *s; s++) + { + if (mustBeHex && isxdigit ((uint8_t)*s)) + value = (value << 4) | hexToDec [toupper((uint8_t)*s) - '0']; + else if (isdigit ((uint8_t)*s)) + value = (value * 10) + ((uint8_t)*s - '0'); + else + { + printf ("Malformed number. Must be decimal number, or hex value preceeded by '0x'%s", CFG_PRINTF_NEWLINE); + return 0; + } + } + + // Set number to negative value if required + if (!mustBeHex) + value *= sgn; + + *result = value; + + return 1; +} + + + diff --git a/project/commands.h b/project/commands.h new file mode 100644 index 0000000..dd3acd1 --- /dev/null +++ b/project/commands.h @@ -0,0 +1,44 @@ +/**************************************************************************/ +/*! + @file commands.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef __COMMANDS_H__ +#define __COMMANDS_H__ + +#include "projectconfig.h" + +// Method Prototypes +int getNumber (char *s, int32_t *result); + +#endif \ No newline at end of file diff --git a/project/commands/cmd_chibi_addr.c b/project/commands/cmd_chibi_addr.c new file mode 100644 index 0000000..d195262 --- /dev/null +++ b/project/commands/cmd_chibi_addr.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/*! + @file cmd_chibi_addr.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_chibi_addr in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_CHIBI + #include "drivers/chibi/chb.h" + #include "drivers/chibi/chb_drvr.h" + +/**************************************************************************/ +/*! + Gets or sets the 16-bit sensor node address. This value can be + anything between 1-65534 (0x0001-0xFFFE), and in decimal or + hexadecimal notation. All hexadecimal values must be preceded by + '0x' or '0X' to be properly interpreted (ex. 0x009F). +*/ +/**************************************************************************/ +void cmd_chibi_addr(uint8_t argc, char **argv) +{ + if (argc > 0) + { + // Try to convert supplied value to an integer + int32_t addr; + getNumber (argv[0], &addr); + + // Check for invalid values (getNumber may complain about this as well) + if (addr <= 0 || addr > 0xFFFF) + { + printf("Invalid Address: 1-65534 or 0x0001-0xFFFE required.%s", CFG_PRINTF_NEWLINE); + return; + } + if (addr == 0xFFFF) + { + printf("Invalid Address: 0xFFFF reserved for broadcast.%s", CFG_PRINTF_NEWLINE); + return; + } + + // Write address to EEPROM and update peripheral control block + chb_set_short_addr((uint16_t)addr); + chb_pcb_t *pcb = chb_get_pcb(); + printf("Address set to: 0x%04X%s", pcb->src_addr, CFG_PRINTF_NEWLINE); + } + else + { + // Display the current address + chb_pcb_t *pcb = chb_get_pcb(); + printf("0x%04X%s", pcb->src_addr, CFG_PRINTF_NEWLINE); + } +} + +#endif diff --git a/project/commands/cmd_chibi_tx.c b/project/commands/cmd_chibi_tx.c new file mode 100644 index 0000000..eb2e43d --- /dev/null +++ b/project/commands/cmd_chibi_tx.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/*! + @file cmd_chibi_tx.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_chibi_tx in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_CHIBI + #include "drivers/chibi/chb.h" + #include "drivers/chibi/chb_drvr.h" + +/**************************************************************************/ +/*! + Sends text or data via Chibi + +*/ +/**************************************************************************/ +void cmd_chibi_tx(uint8_t argc, char **argv) +{ + uint8_t i, len, *data_ptr, data[50]; + uint16_t addr; + + // Try to convert supplied address to an integer + int32_t addr32; + getNumber (argv[0], &addr32); + + // Check for invalid values (getNumber may complain about this as well) + if (addr32 <= 0 || addr32 > 0xFFFF) + { + printf("Invalid Address: 1-65534 or 0x0001-0xFFFE required.%s", CFG_PRINTF_NEWLINE); + return; + } + + // Address seems to be OK + addr = (uint16_t)addr32; + + // Get message contents + data_ptr = data; + for (i=0; i + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_I2CEEPROM + #include "drivers/eeprom/eeprom.h" + +/**************************************************************************/ +/*! + Reads a single byte at the supplied EEPROM address +*/ +/**************************************************************************/ +void cmd_i2ceeprom_read(uint8_t argc, char **argv) +{ + uint16_t addr; + uint8_t value; + + // Try to convert supplied address to an integer + int32_t addr32; + getNumber (argv[0], &addr32); + + // Check for invalid values (getNumber may complain about this as well) + if (addr32 < 0 || eepromCheckAddress(addr32)) + { + printf("Address out of range %s", CFG_PRINTF_NEWLINE); + return; + } + + // Address seems to be OK + addr = (uint16_t)addr32; + value = eepromReadU8(addr); + + printf("0x%02X%s", value, CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/cmd_i2ceeprom_write.c b/project/commands/cmd_i2ceeprom_write.c new file mode 100644 index 0000000..0eec71e --- /dev/null +++ b/project/commands/cmd_i2ceeprom_write.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/*! + @file cmd_i2ceeprom_write.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_i2ceeprom_write in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_I2CEEPROM + #include "drivers/eeprom/eeprom.h" + +/**************************************************************************/ +/*! + Writes a single byte at the supplied EEPROM address +*/ +/**************************************************************************/ +void cmd_i2ceeprom_write(uint8_t argc, char **argv) +{ + uint16_t addr; + uint8_t val; + + // Try to convert supplied address to an integer + int32_t addr32; + getNumber (argv[0], &addr32); + + // Check for invalid values (getNumber may complain about this as well) + if (addr32 < 0 || eepromCheckAddress(addr32)) + { + printf("Address out of range %s", CFG_PRINTF_NEWLINE); + return; + } + + // Address seems to be OK + addr = (uint16_t)addr32; + + // Make sure this isn't in the reserved system config space + if (addr <= CFG_EEPROM_RESERVED) + { + printf("ERROR: Reserved address (0x%04X-0x%04X)%s", 0, CFG_EEPROM_RESERVED, CFG_PRINTF_NEWLINE); + return; + } + + // Try to convert supplied data to an integer + int32_t val32; + getNumber (argv[1], &val32); + + // Check for invalid values (getNumber may complain about this as well) + if (val32 < 0 || val32 > 0xFF) + { + printf("Invalid Data: 0-255 or 0x00-0xFF required.%s", CFG_PRINTF_NEWLINE); + return; + } + + // Data seems to be OK + val = (uint8_t)val32; + + // Write data at supplied address + eepromWriteU8(addr, val); + + // Write successful + printf("0x%02X written at 0x%04X%s", val, addr, CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/cmd_lm75b_gettemp.c b/project/commands/cmd_lm75b_gettemp.c new file mode 100644 index 0000000..e0a113a --- /dev/null +++ b/project/commands/cmd_lm75b_gettemp.c @@ -0,0 +1,67 @@ +/**************************************************************************/ +/*! + @file cmd_lm75b_gettemp.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_lm75b_gettemp in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_LM75B + #include "drivers/sensors/lm75b/lm75b.h" + +/**************************************************************************/ +/*! + Gets the current temperature in degrees celsius from the LM75B +*/ +/**************************************************************************/ +void cmd_lm75b_gettemp(uint8_t argc, char **argv) +{ + int32_t temp = 0; + + // Get the current temperature (in 0.125°C units) + lm75bGetTemperature(&temp); + + // Multiply value by 125 for fixed-point math (0.125°C per unit) + temp *= 125; + + // Use modulus operator to display decimal value + printf("%d.%d C%s", temp / 1000, temp % 1000, CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/cmd_sd_dir.c b/project/commands/cmd_sd_dir.c new file mode 100644 index 0000000..49fe6a5 --- /dev/null +++ b/project/commands/cmd_sd_dir.c @@ -0,0 +1,144 @@ +/**************************************************************************/ +/*! + @file cmd_sd_dir.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_sd_dir in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_SDCARD + #include "core/timer32/timer32.h" + #include "core/ssp/ssp.h" + #include "drivers/fatfs/diskio.h" + #include "drivers/fatfs/ff.h" + + static FILINFO Finfo; + static FATFS Fatfs[1]; + +/**************************************************************************/ +/*! + sd 'dir' command handler + + This demonstrates how to initialise the SD card, read the contents + of a directory (including checking if an entry is a folder or a file), + and checking the amount of freespace on available on the disk. +*/ +/**************************************************************************/ +void cmd_sd_dir(uint8_t argc, char **argv) +{ + char* path; + + // Display root folder by default + path = argc > 0 ? argv[0] : "/"; + + // Initialise SD Card + DSTATUS stat; + stat = disk_initialize(0); + if (stat & STA_NOINIT) + { + printf("SD init failed%s", CFG_PRINTF_NEWLINE); + } + if (stat & STA_NODISK) + { + printf("No SD card%s", CFG_PRINTF_NEWLINE); + } + if (stat == 0) + { + BYTE res; + DIR dir; + + // Try to mount drive + res = f_mount(0, &Fatfs[0]); + if (res != FR_OK) + { + printf("Failed to mount partition%s" , CFG_PRINTF_NEWLINE); + } + if (res == FR_OK) + { + res = f_opendir(&dir, path); + if (res) + { + printf("Failed to open '%s' %s", path, CFG_PRINTF_NEWLINE); + return; + } + + // Display directory name + printf("%s Contents of %s %s%s", CFG_PRINTF_NEWLINE, path, CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + printf(" %-25s %12s %s", "Filename", "Size", CFG_PRINTF_NEWLINE); + printf(" %-25s %12s %s", "--------", "----", CFG_PRINTF_NEWLINE); + + // Read directory contents + int folderBytes = 0; + for(;;) + { + res = f_readdir(&dir, &Finfo); + if ((res != FR_OK) || !Finfo.fname[0]) break; + #if _USE_LFN == 0 + if (Finfo.fattrib & AM_DIR) + printf(" %-25s %s", (char *)&Finfo.fname[0], CFG_PRINTF_NEWLINE); + else + printf(" %-25s %12d Bytes %s", (char *)&Finfo.fname[0], (int)(Finfo.fsize), CFG_PRINTF_NEWLINE); + folderBytes += Finfo.fsize; + #else + // ToDo + #endif + } + + // Display folder size + printf("%s", CFG_PRINTF_NEWLINE); + printf(" %-25s %12d KB %s", "Folder Size: ", (int)(folderBytes / 1024), CFG_PRINTF_NEWLINE); + + // Get free disk space (only available if FATFS was compiled with _FS_MINIMIZE set to 0) + #if _FS_MINIMIZE == 0 && _FS_READONLY == 0 + FATFS *fs = &Fatfs[0]; + DWORD clust; + + // Get free clusters + res = f_getfree("0:", &clust, &fs); + + // Display total and free space + printf(" %-25s %12d KB %s", "Disk Size: ", (int)((DWORD)(fs->max_clust - 2) * fs->csize / 2), CFG_PRINTF_NEWLINE); + printf(" %-25s %12d KB %s", "Space Available: ", (int)(clust * fs->csize / 2), CFG_PRINTF_NEWLINE); + #endif + } + } +} + +#endif diff --git a/project/commands/cmd_sysinfo.c b/project/commands/cmd_sysinfo.c new file mode 100644 index 0000000..36b66e9 --- /dev/null +++ b/project/commands/cmd_sysinfo.c @@ -0,0 +1,126 @@ +/**************************************************************************/ +/*! + @file cmd_sysinfo.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_sysinfo in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "core/systick/systick.h" +#include "core/iap/iap.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_CHIBI + #include "drivers/chibi/chb.h" + #include "drivers/chibi/chb_drvr.h" +#endif + +#ifdef CFG_PRINTF_UART + #include "core/uart/uart.h" +#endif + +#ifdef CFG_LM75B + #include "drivers/sensors/lm75b/lm75b.h" +#endif + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" +#endif + +#ifdef CFG_SDCARD + #include "core/gpio/gpio.h" +#endif + +/**************************************************************************/ +/*! + 'sysinfo' command handler +*/ +/**************************************************************************/ +void cmd_sysinfo(uint8_t argc, char **argv) +{ + IAP_return_t iap_return; + + printf("%-25s : %d.%d MHz %s", "System Clock", CFG_CPU_CCLK / 1000000, CFG_CPU_CCLK % 1000000, CFG_PRINTF_NEWLINE); + printf("%-25s : v%d.%d.%d %s", "Firmware", CFG_FIRMWARE_VERSION_MAJOR, CFG_FIRMWARE_VERSION_MINOR, CFG_FIRMWARE_VERSION_REVISION, CFG_PRINTF_NEWLINE); + + // 128-bit MCU Serial Number + iap_return = iapReadSerialNumber(); + if(iap_return.ReturnCode == 0) + { + printf("%-25s : %08X %08X %08X %08X %s", "Serial Number", iap_return.Result[0],iap_return.Result[1],iap_return.Result[2],iap_return.Result[3], CFG_PRINTF_NEWLINE); + } + + // CLI and buffer Settings + #ifdef CFG_INTERFACE + printf("%-25s : %d bytes %s", "Max CLI Command", CFG_INTERFACE_MAXMSGSIZE, CFG_PRINTF_NEWLINE); + #endif + + #ifdef CFG_PRINTF_UART + uart_pcb_t *pcb = uartGetPCB(); + printf("%-25s : %d %s", "UART Baud Rate", pcb->baudrate, CFG_PRINTF_NEWLINE); + #endif + + // TFT LCD Settings (if CFG_TFTLCD enabled) + #ifdef CFG_TFTLCD + printf("%-25s : %d %s", "LCD Width", (int)lcdGetWidth(), CFG_PRINTF_NEWLINE); + printf("%-25s : %d %s", "LCD Height", (int)lcdGetHeight(), CFG_PRINTF_NEWLINE); + #endif + + // Wireless Settings (if CFG_CHIBI enabled) + #ifdef CFG_CHIBI + chb_pcb_t *pcb = chb_get_pcb(); + printf("%-25s : %s %s", "Wireless", "AT86RF212", CFG_PRINTF_NEWLINE); + printf("%-25s : 0x%04X %s", "802.15.4 PAN ID", CFG_CHIBI_PANID, CFG_PRINTF_NEWLINE); + printf("%-25s : 0x%04X %s", "802.15.4 Node Address", pcb->src_addr, CFG_PRINTF_NEWLINE); + printf("%-25s : %d %s", "802.15.4 Channel", CFG_CHIBI_CHANNEL, CFG_PRINTF_NEWLINE); + #endif + + // System Uptime (based on systick timer) + printf("%-25s : %u s %s", "System Uptime", (unsigned int)systickGetSecondsActive(), CFG_PRINTF_NEWLINE); + + // System Temperature (if LM75B Present) + #ifdef CFG_LM75B + int32_t temp = 0; + lm75bGetTemperature(&temp); + temp *= 125; + printf("%-25s : %d.%d C %s", "Temperature", temp / 1000, temp % 1000, CFG_PRINTF_NEWLINE); + #endif + + #ifdef CFG_SDCARD + printf("%-25s : %s %s", "SD Card Present", gpioGetValue(CFG_SDCARD_CDPORT, CFG_SDCARD_CDPIN) ? "True" : "False", CFG_PRINTF_NEWLINE); + #endif +} diff --git a/project/commands/cmd_uart.c b/project/commands/cmd_uart.c new file mode 100644 index 0000000..8e634f4 --- /dev/null +++ b/project/commands/cmd_uart.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/*! + @file cmd_uart.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_uart in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_I2CEEPROM + #include "drivers/eeprom/eeprom.h" + #include "core/uart/uart.h" + +/**************************************************************************/ +/*! + Gets or sets the UART speed from EEPROM. +*/ +/**************************************************************************/ +void cmd_uart(uint8_t argc, char **argv) +{ + if (argc > 0) + { + // Try to convert supplied value to an integer + int32_t speed; + getNumber (argv[0], &speed); + + // Check for invalid values (getNumber may complain about this as well) + if (speed < 9600 || speed > 115200) + { + printf("Invalid baud rate: 9600-115200 required.%s", CFG_PRINTF_NEWLINE); + return; + } + + // Write baud rate to EEPROM and reinitialise UART if using it + printf("Setting UART to: %d%s", (int)speed, CFG_PRINTF_NEWLINE); + eepromWriteU32(CFG_EEPROM_UART_SPEED, speed); + #ifdef CFG_PRINTF_UART + uartInit(speed); + #endif + } + else + { + // Display the current baud rate + #ifdef CFG_PRINTF_UART + uart_pcb_t *pcb = uartGetPCB(); + printf("%d%s", pcb->baudrate, CFG_PRINTF_NEWLINE); + #else + printf("UART not initialised (using USBCDC)%s", CFG_PRINTF_NEWLINE); + #endif + } +} + +#endif diff --git a/project/commands/drawing/cmd_bmp.c b/project/commands/drawing/cmd_bmp.c new file mode 100644 index 0000000..4ae5423 --- /dev/null +++ b/project/commands/drawing/cmd_bmp.c @@ -0,0 +1,99 @@ +/**************************************************************************/ +/*! + @file cmd_bmp.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_bmp in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#if defined CFG_TFTLCD && defined CFG_SDCARD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a bitmap image on the LCD. +*/ +/**************************************************************************/ +void cmd_bmp(uint8_t argc, char **argv) +{ + int32_t x, y; + char* filename; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + filename = argv[2]; + + // Render image + bmp_error_t error; + error = drawBitmapImage(x, y, filename); + + switch (error) + { + case BMP_ERROR_SDINITFAIL: + printf("SD Init Failed%s", CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_FILENOTFOUND: + printf("File Not Found: '%s'%s", filename, CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_UNABLETOCREATEFILE: + printf("Unable to create file: '%s'%s", filename, CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_NOTABITMAP: + printf("Not a Bitmap: '%s'%s", filename, CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_INVALIDBITDEPTH: + printf("Not a 24-Bit Image%s", CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_INVALIDDIMENSIONS: + printf("Image Exceeds %d x %d Pixels%s", lcdGetWidth(), lcdGetHeight(), CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_COMPRESSEDDATA: + printf("Compressed Images Unsupported%s", CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_PREMATUREEOF: + printf("Premature EOF%s", CFG_PRINTF_NEWLINE); + break; + case BMP_ERROR_NONE: + break; + } +} + +#endif diff --git a/project/commands/drawing/cmd_button.c b/project/commands/drawing/cmd_button.c new file mode 100644 index 0000000..98d376f --- /dev/null +++ b/project/commands/drawing/cmd_button.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/*! + @file cmd_buton.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_button in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/tft/touchscreen.h" + #include "drivers/lcd/tft/fonts/dejavusans9.h" + +/**************************************************************************/ +/*! + Displays a button on the LCD with or without text. + + Ex.: "btn 10 10 220 35 1 This is a test" will display a 220x35 pixel + button in the 'pressed' state (arg 5 = 1) starting at pixel 10, 10 with + 'This is a test' rendered on the button. +*/ +/**************************************************************************/ +void cmd_button(uint8_t argc, char **argv) +{ + int32_t x, y, w, h, border, fill, font; + + // ToDo: Validate data! + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + getNumber (argv[2], &w); + getNumber (argv[3], &h); + getNumber (argv[4], &border); + getNumber (argv[5], &fill); + getNumber (argv[6], &font); + + if (argc == 7) + { + // Render the button with no text + drawButton(x, y, w, h, &dejaVuSans9ptFontInfo, 7, (uint16_t)border, (uint16_t)fill, (uint16_t)font, NULL); + } + else + { + // Get text contents + uint8_t i, len, *data_ptr, data[50]; + data_ptr = data; + for (i = 0; i < argc - 7; i++) + { + len = strlen(argv[i + 7]); + strcpy((char *)data_ptr, (char *)argv[i + 7]); + data_ptr += len; + *data_ptr++ = ' '; + } + *data_ptr++ = '\0'; + + // Render the button with text + drawButton(x, y, w, h, &dejaVuSans9ptFontInfo, 7, (uint16_t)border, (uint16_t)fill, (uint16_t)font, (char *)&data); + } +} + +#endif diff --git a/project/commands/drawing/cmd_calibrate.c b/project/commands/drawing/cmd_calibrate.c new file mode 100644 index 0000000..93316b7 --- /dev/null +++ b/project/commands/drawing/cmd_calibrate.c @@ -0,0 +1,63 @@ +/**************************************************************************/ +/*! + @file cmd_calibrate.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_calibrate in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#if defined CFG_TFTLCD + #include "drivers/lcd/tft/touchscreen.h" + +/**************************************************************************/ +/*! + Starts the touch-screen calibration process. +*/ +/**************************************************************************/ +void cmd_calibrate(uint8_t argc, char **argv) +{ + printf("Starting touch-screen calibration%s", CFG_PRINTF_NEWLINE); + + // Run through the calibration process + tsCalibrate(); + + printf("Calibration complete%s", CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/drawing/cmd_circle.c b/project/commands/drawing/cmd_circle.c new file mode 100644 index 0000000..9eb1227 --- /dev/null +++ b/project/commands/drawing/cmd_circle.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/*! + @file cmd_circle.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_circle in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a circle on the LCD. +*/ +/**************************************************************************/ +void cmd_circle(uint8_t argc, char **argv) +{ + int32_t x, y, r, c, filled, border; + filled = 0; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + getNumber (argv[2], &r); + getNumber (argv[3], &c); + if (argc >= 5) + { + getNumber (argv[4], &filled); + } + if (argc == 6) + { + getNumber (argv[5], &border); + if (border < 0 || border > 0xFFFF) + { + printf("Invalid Border Color%s", CFG_PRINTF_NEWLINE); + return; + } + } + + // ToDo: Validate data! + if (c < 0 || c > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + if (r < 1) + { + printf("Invalid Radius%s", CFG_PRINTF_NEWLINE); + return; + } + + if (filled) + drawCircleFilled(x, y, r, (uint16_t)c); + else + drawCircle(x, y, r, (uint16_t)c); + + // Draw border if requested + if (argc == 6) + { + drawCircle(x, y, r, (uint16_t)border); + } +} + +#endif diff --git a/project/commands/drawing/cmd_clear.c b/project/commands/drawing/cmd_clear.c new file mode 100644 index 0000000..50ef287 --- /dev/null +++ b/project/commands/drawing/cmd_clear.c @@ -0,0 +1,74 @@ +/**************************************************************************/ +/*! + @file cmd_clear.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_clear in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Fills the LCD with the supplied RGB565 color +*/ +/**************************************************************************/ +void cmd_clear(uint8_t argc, char **argv) +{ + int32_t col = 0; + if (argc > 0) + { + // Try to convert supplied value to an integer + getNumber (argv[0], &col); + + // Check for invalid values (getNumber may complain about this as well) + if (col < 0 || col > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + } + + // Fill the screen + drawFill((uint16_t)col); +} + +#endif diff --git a/project/commands/drawing/cmd_gettext.c b/project/commands/drawing/cmd_gettext.c new file mode 100644 index 0000000..8f41386 --- /dev/null +++ b/project/commands/drawing/cmd_gettext.c @@ -0,0 +1,64 @@ +/**************************************************************************/ +/*! + @file cmd_gettext.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_gettext in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#if defined CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/tft/touchscreen.h" + #include "drivers/lcd/tft/dialogues/alphanumeric.h" + +/**************************************************************************/ +/*! + Displays an alpha-numeric input dialogue on the TFT LCD screen. +*/ +/**************************************************************************/ +void cmd_gettext(uint8_t argc, char **argv) +{ + // Print results from an alpha-numeric dialogue + char* results = alphaShowDialogue(); + drawFill(COLOR_BLACK); + printf("%s%s", results, CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/drawing/cmd_line.c b/project/commands/drawing/cmd_line.c new file mode 100644 index 0000000..2f9ed53 --- /dev/null +++ b/project/commands/drawing/cmd_line.c @@ -0,0 +1,85 @@ +/**************************************************************************/ +/*! + @file cmd_line.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_line in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a line on the LCD. +*/ +/**************************************************************************/ +void cmd_line(uint8_t argc, char **argv) +{ + int32_t x1, y1, x2, y2, c, empty, solid; + + // Convert supplied parameters + getNumber (argv[0], &x1); + getNumber (argv[1], &y1); + getNumber (argv[2], &x2); + getNumber (argv[3], &y2); + getNumber (argv[4], &c); + if (argc > 5) + { + getNumber (argv[5], &empty); + getNumber (argv[6], &solid); + } + else + { + empty = 0; + solid = 1; + } + + // ToDo: Validate data! + if (c < 0 || c > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + + drawLineDotted(x1, y1, x2, y2, empty, solid, (uint16_t)c); +} + +#endif diff --git a/project/commands/drawing/cmd_orientation.c b/project/commands/drawing/cmd_orientation.c new file mode 100644 index 0000000..07ac9b3 --- /dev/null +++ b/project/commands/drawing/cmd_orientation.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/*! + @file cmd_orientation.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_orientation in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Changes the LCD orientation +*/ +/**************************************************************************/ +void cmd_orientation(uint8_t argc, char **argv) +{ + int32_t value; + + if (argc == 0) + { + printf("%d%s", lcdGetOrientation(), CFG_PRINTF_NEWLINE); + return; + } + + // Convert supplied parameters + getNumber (argv[0], &value); + + switch (value) + { + case 0: + lcdSetOrientation(LCD_ORIENTATION_PORTRAIT); + break; + case 1: + lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE); + break; + default: + printf("Invalid value: Enter 0 or 1%s", CFG_PRINTF_NEWLINE); + return; + } + + return; +} + +#endif diff --git a/project/commands/drawing/cmd_pixel.c b/project/commands/drawing/cmd_pixel.c new file mode 100644 index 0000000..9aeffb8 --- /dev/null +++ b/project/commands/drawing/cmd_pixel.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/*! + @file cmd_pixel.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_pixel in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a single pixel on the LCD. +*/ +/**************************************************************************/ +void cmd_pixel(uint8_t argc, char **argv) +{ + int32_t x, y, c; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + getNumber (argv[2], &c); + + // ToDo: Validate data! + if (c < 0 || c > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + + drawPixel(x, y, (uint16_t)c); +} + +/**************************************************************************/ +/*! + Gets a single pixel from the LCD. +*/ +/**************************************************************************/ +void cmd_getpixel(uint8_t argc, char **argv) +{ + int32_t x, y; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + + uint16_t value = lcdGetPixel(x, y); + + // Output the results + printf("%d%s", value, CFG_PRINTF_NEWLINE); +} + +#endif diff --git a/project/commands/drawing/cmd_progress.c b/project/commands/drawing/cmd_progress.c new file mode 100644 index 0000000..40c1b27 --- /dev/null +++ b/project/commands/drawing/cmd_progress.c @@ -0,0 +1,85 @@ +/**************************************************************************/ +/*! + @file cmd_progress.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_progress in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a progress bar on the LCD. +*/ +/**************************************************************************/ +void cmd_progress(uint8_t argc, char **argv) +{ + int32_t x, y, w, h, percent, border, borderfill, progressborder, progressfill; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + getNumber (argv[2], &w); + getNumber (argv[3], &h); + getNumber (argv[4], &percent); + getNumber (argv[5], &border); + getNumber (argv[6], &borderfill); + getNumber (argv[7], &progressborder); + getNumber (argv[8], &progressfill); + + // ToDo: Validate data! + if (border < 0 || border > 0xFFFF || borderfill < 0 || borderfill > 0xFFFF || progressborder < 0 || progressborder > 0xFFFF || progressfill < 0 || progressfill > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + if (percent < 0 || percent > 100) + { + printf("Invalid Percentage%s", CFG_PRINTF_NEWLINE); + return; + } + + // Draw the progress bar (always use rounded corners for simplicity sake) + drawProgressBar(x, y, w, h, DRAW_ROUNDEDCORNERS_ALL, DRAW_ROUNDEDCORNERS_ALL, border, borderfill, progressborder, progressfill, percent); +} + +#endif diff --git a/project/commands/drawing/cmd_rectangle.c b/project/commands/drawing/cmd_rectangle.c new file mode 100644 index 0000000..581d864 --- /dev/null +++ b/project/commands/drawing/cmd_rectangle.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/*! + @file cmd_rectangle.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_rectangle in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + +/**************************************************************************/ +/*! + Displays a rectangle on the LCD. +*/ +/**************************************************************************/ +void cmd_rectangle(uint8_t argc, char **argv) +{ + int32_t x1, y1, x2, y2, c, filled, border; + filled = 0; + + // Convert supplied parameters + getNumber (argv[0], &x1); + getNumber (argv[1], &y1); + getNumber (argv[2], &x2); + getNumber (argv[3], &y2); + getNumber (argv[4], &c); + if (argc >= 6) + { + getNumber (argv[5], &filled); + } + if (argc == 7) + { + getNumber (argv[6], &border); + if (border < 0 || border > 0xFFFF) + { + printf("Invalid Border Color%s", CFG_PRINTF_NEWLINE); + return; + } + } + + // ToDo: Validate data! + if (c < 0 || c > 0xFFFF) + { + printf("Invalid Color%s", CFG_PRINTF_NEWLINE); + return; + } + + if (filled) + drawRectangleFilled(x1, y1, x2, y2, (uint16_t)c); + else + drawRectangle(x1, y1, x2, y2, (uint16_t)c); + + if (argc == 7) + { + drawRectangle(x1, y1, x2, y2, (uint16_t)border); + } +} + +#endif diff --git a/project/commands/drawing/cmd_text.c b/project/commands/drawing/cmd_text.c new file mode 100644 index 0000000..ac3bc7c --- /dev/null +++ b/project/commands/drawing/cmd_text.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/*! + @file cmd_text.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_text in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/tft/fonts/dejavusans9.h" + +/**************************************************************************/ +/*! + Displays the supplied text on the LCD. +*/ +/**************************************************************************/ +void cmd_text(uint8_t argc, char **argv) +{ + int32_t x, y, color; + int32_t font; + uint8_t i, len; + char *data_ptr, data[80]; + + // Convert supplied parameters + getNumber (argv[0], &x); + getNumber (argv[1], &y); + getNumber (argv[2], &color); + getNumber (argv[3], &font); + + // Get message contents + data_ptr = data; + for (i=0; i +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/drawing.h" + #include "drivers/lcd/tft/fonts/dejavusans9.h" + +/**************************************************************************/ +/*! + Returns the width of the supplied text in pixels. +*/ +/**************************************************************************/ +void cmd_textw(uint8_t argc, char **argv) +{ + int32_t font; + uint8_t i, len; + char *data_ptr, data[80]; + + // Convert supplied parameters + getNumber (argv[0], &font); + + // Get message contents + data_ptr = data; + for (i=0; i +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/eeprom/eeprom.h" + #include "drivers/lcd/tft/touchscreen.h" + +/**************************************************************************/ +/*! + Gets or sets the touch screen 'touch event' threshhold +*/ +/**************************************************************************/ +void cmd_tsthreshhold(uint8_t argc, char **argv) +{ + int32_t input; + uint8_t value; + + if (argc == 0) + { + // Display default threshold (from projectconfig.h) + value = tsGetThreshhold(); + printf("%u%s", value, CFG_PRINTF_NEWLINE); + return; + } + + // Convert supplied parameters + getNumber (argv[0], &input); + if ((input < 0) || (input > 254)) + { + printf("Invalid value: Enter 0..254%s", CFG_PRINTF_NEWLINE); + return; + } + + // Store value in EEPROM and update the TS + tsSetThreshhold((uint8_t)input); + value = tsGetThreshhold(); + printf("Set to %u%s", (uint8_t)value, CFG_PRINTF_NEWLINE); + return; +} + +#endif diff --git a/project/commands/drawing/cmd_tswait.c b/project/commands/drawing/cmd_tswait.c new file mode 100644 index 0000000..6a75183 --- /dev/null +++ b/project/commands/drawing/cmd_tswait.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/*! + @file cmd_tswait.c + @author K. Townsend (microBuilder.eu) + + @brief Code to execute for cmd_tswait in the 'core/cmd' + command-line interpretter. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include + +#include "projectconfig.h" +#include "core/cmd/cmd.h" +#include "project/commands.h" // Generic helper functions + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/touchscreen.h" + +/**************************************************************************/ +/*! + Waits for a touch screen event and returns the co-ordinates +*/ +/**************************************************************************/ +void cmd_tswait(uint8_t argc, char **argv) +{ + tsTouchData_t data; + int32_t delay; + int32_t error = 0; + + if (argc == 1) + { + getNumber (argv[0], &delay); + } + else + { + delay = 0; + } + + // Validate delay + if (delay < 0) + { + printf("Invalid timeout%s", CFG_PRINTF_NEWLINE); + return; + } + + // Blocking delay until a valid touch event occurs + error = tsWaitForEvent(&data, delay > 0 ? (uint32_t)delay : 0); + + if (error == TS_ERROR_NONE) + { + // A valid touch event occurred ... parse data + printf("%d, %d%s",(int)data.xlcd, (int)data.ylcd, CFG_PRINTF_NEWLINE); + } + else + { + // Display error code + printf("%d %s", (int)error, CFG_PRINTF_NEWLINE); + } + + return; +} + +#endif diff --git a/project/readme.txt b/project/readme.txt new file mode 100644 index 0000000..c2ff73d --- /dev/null +++ b/project/readme.txt @@ -0,0 +1,32 @@ +Project Folder +============================================================================== +All project-specific files should be stored in the /project folder to try to +keep the generic HW-level code and drivers seperate from the business logic +of your individual project. This makes it easier to reuse your code in other +projects, and also update the drivers and HW-level code if newer version of +these common-files become available. + +FOLDERS +============================================================================== +commands/ Code to implements specific commands for the + command-line interface. Requires CFG_INTERFACE + to be enabled in projectconfig.h. Generally, + each command will be stored in a seperate file + (ex.: "command/cmd_hello.c"), though this isn't + a strict requirement and you may wish to store + multiple related commands in one .c file, such + as 'cmds_graphics.c' etc. + +documentation/ Project-specific documentation is stored here + +FILES +============================================================================== +cmd_tbl.h Contains the master command list for the + command-line interface if CFG_INTERFACE is + enabled in projectconfig.h. All commands + must be present in this list to be properly + handled by the CLI. + +commands.c Common helper functions for the command-line + interface. + diff --git a/projectconfig.h b/projectconfig.h new file mode 100644 index 0000000..489064d --- /dev/null +++ b/projectconfig.h @@ -0,0 +1,884 @@ +/**************************************************************************/ +/*! + @file projectconfig.h + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _PROJECTCONFIG_H_ +#define _PROJECTCONFIG_H_ + +#include "lpc134x.h" +#include "sysdefs.h" + +/*========================================================================= + BOARD SELECTION + + Because several boards use this code library with sometimes slightly + different pin configuration, you will need to specify which board you + are using by enabling one of the following definitions. The code base + will then try to configure itself accordingly for that board. + -----------------------------------------------------------------------*/ + // #define CFG_BRD_LPC1343_REFDESIGN + #define CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_BRD_LPC1343_802154USBSTICK +/*=========================================================================*/ + + +/************************************************************************** + PIN USAGE + ----------------------------------------------------------------------- + This table tries to give an indication of which GPIO pins and + peripherals are used by the available drivers and SW examples. Only + dedicated GPIO pins available on the LPC1343 Reference Board are shown + below. Any unused peripheral blocks like I2C, SSP, ADC, etc., can + also be used as GPIO if they are available. + + PORT 1 PORT 2 PORT 3 + ========= ================= ======= + 8 9 10 11 1 2 3 4 5 6 7 8 9 0 1 2 3 + + SDCARD . . . . . . . . . . . . . X . . . + PWM . X . . . . . . . . . . . . . . . + STEPPER . . . . . . . . . . . . . X X X X + CHIBI X X X . . . . . . . . . . . . . . + ILI9325/8 X X X X X X X X X X X X X . . . X + ST7565 X X X X X X X X X X X X X . . . X + ST7735 . . . . X X X X X X . . . . . . . + SSD1306 . . . . X X X . X X . . . . . . . + MCP121 . . . . . . . . . . . . . . X . . + + TIMERS SSP ADC UART + ====================== === ======= ==== + 16B0 16B1 32B0 32B1 0 0 1 2 3 0 + + SDCARD . . . . X . . . . . + PWM . X . . . . . . . . + PMU [1] . . X . . . . . . . + USB . . . X . . . . . . + STEPPER . . X . . . . . . . + CHIBI x . . . X . . . . . + ILI9325/8 . . . . . X X X X . + ST7565 . . . . . X X X X . + ST7535 . . . . . . . . . . + SSD1306 . . . . . . . . . . + INTERFACE . . . . . . . . . X[2] + + [1] PMU uses 32-bit Timer 0 for SW wakeup from deep-sleep. This timer + can safely be used by other peripherals, but may need to be + reconfigured when you wakeup from deep-sleep. + [2] INTERFACE can be configured to use either USBCDC or UART + + **************************************************************************/ + + +/*========================================================================= + FIRMWARE VERSION SETTINGS + -----------------------------------------------------------------------*/ + #define CFG_FIRMWARE_VERSION_MAJOR (0) + #define CFG_FIRMWARE_VERSION_MINOR (9) + #define CFG_FIRMWARE_VERSION_REVISION (2) +/*=========================================================================*/ + + +/*========================================================================= + CORE CPU SETTINGS + ----------------------------------------------------------------------- + + CFG_CPU_CCLK Value is for reference only. 'core/cpu/cpu.c' must + be modified to change the clock speed, but the value + should be indicated here since CFG_CPU_CCLK is used by + other peripherals to determine timing. + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_CPU_CCLK (72000000) // 1 tick = 13.88nS + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_CPU_CCLK (72000000) // 1 tick = 13.88nS + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_CPU_CCLK (72000000) // 1 tick = 13.88nS + #endif +/*=========================================================================*/ + + +/*========================================================================= + SYSTICK TIMER + ----------------------------------------------------------------------- + + CFG_SYSTICK_DELAY_IN_MS The number of milliseconds between each tick + of the systick timer. + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_SYSTICK_DELAY_IN_MS (1) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_SYSTICK_DELAY_IN_MS (1) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_SYSTICK_DELAY_IN_MS (1) + #endif +/*=========================================================================*/ + + +/*========================================================================= + UART + ----------------------------------------------------------------------- + + CFG_UART_BAUDRATE The default UART speed. This value is used + when initialising UART, and should be a + standard value like 57600, 9600, etc. + NOTE: This value may be overridden if + another value is stored in EEPROM! + CFG_UART_BUFSIZE The length in bytes of the UART RX FIFO. This + will determine the maximum number of received + characters to store in memory. + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_UART_BAUDRATE (115200) + #define CFG_UART_BUFSIZE (512) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_UART_BAUDRATE (115200) + #define CFG_UART_BUFSIZE (512) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_UART_BAUDRATE (115200) + #define CFG_UART_BUFSIZE (512) + #endif +/*=========================================================================*/ + + +/*========================================================================= + SSP + ----------------------------------------------------------------------- + + CFG_SSP0_SCKPIN_2_11 Indicates which pin should be used for SCK0 + CFG_SSP0_SCKPIN_0_6 + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_SSP0_SCKPIN_2_11 + // #define CFG_SSP0_SCKPIN_0_6 + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_SSP0_SCKPIN_2_11 + // #define CFG_SSP0_SCKPIN_0_6 + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_SSP0_SCKPIN_2_11 + #define CFG_SSP0_SCKPIN_0_6 + #endif +/*=========================================================================*/ + + +/*========================================================================= + ON-BOARD LED + ----------------------------------------------------------------------- + + CFG_LED_PORT The port for the on board LED + CFG_LED_PIN The pin for the on board LED + CFG_LED_ON The pin state to turn the LED on (0 = low, 1 = high) + CFG_LED_OFF The pin state to turn the LED off (0 = low, 1 = high) + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_LED_PORT (2) + #define CFG_LED_PIN (10) + #define CFG_LED_ON (0) + #define CFG_LED_OFF (1) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_LED_PORT (2) + #define CFG_LED_PIN (10) + #define CFG_LED_ON (0) + #define CFG_LED_OFF (1) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_LED_PORT (3) + #define CFG_LED_PIN (2) + #define CFG_LED_ON (0) + #define CFG_LED_OFF (1) + #endif +/*=========================================================================*/ + + +/*========================================================================= + MICRO-SD CARD + ----------------------------------------------------------------------- + + CFG_SDCARD If this field is defined SD Card and FAT32 + file system support will be included + CFG_SDCARD_READONLY If this is set to 1, all commands to + write to the SD card will be removed + saving some flash space. + CFG_SDCARD_CDPORT The card detect port number + CFG_SDCARD_CDPIN The card detect pin number + + NOTE: All config settings for FAT32 are defined + in ffconf.h + + BENCHMARK: With SPI set to 6.0MHz, FATFS can read + ~300KB/s (w/512 byte read buffer) + + PIN LAYOUT: The pin layout that is used by this driver + can be seen in the following schematic: + /tools/schematics/Breakout_TFTLCD_ILI9325_v1.3 + + DEPENDENCIES: SDCARD requires the use of SSP0. + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_SDCARD + #define CFG_SDCARD_READONLY (1) // Must be 0 or 1 + #define CFG_SDCARD_CDPORT (3) + #define CFG_SDCARD_CDPIN (0) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_SDCARD + #define CFG_SDCARD_READONLY (1) // Must be 0 or 1 + #define CFG_SDCARD_CDPORT (3) + #define CFG_SDCARD_CDPIN (0) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_SDCARD + #define CFG_SDCARD_READONLY (1) // Must be 0 or 1 + #define CFG_SDCARD_CDPORT (3) + #define CFG_SDCARD_CDPIN (0) + #endif +/*=========================================================================*/ + + +/*========================================================================= + USB + ----------------------------------------------------------------------- + + CFG_USBHID If this field is defined USB HID support will + be included. Currently uses ROM-based USB HID + CFG_USBCDC If this field is defined USB CDC support will + be included, with the USB Serial Port speed + set to 115200 BPS by default + CFG_USBCDC_BAUDRATE The default TX/RX speed. This value is used + when initialising USBCDC, and should be a + standard value like 57600, 9600, etc. + CFG_USBCDC_INITTIMEOUT The maximum delay in milliseconds to wait for + USB to connect. Must be a multiple of 10! + CFG_USBCDC_BUFFERSIZE Size of the buffer (in bytes) that stores + printf data until it can be sent out in + 64 byte frames. The buffer is required since + only one frame per ms can be sent using USB + CDC (see 'puts' in systeminit.c). + + -----------------------------------------------------------------------*/ + #define CFG_USB_VID (0x239A) + #define CFG_USB_PID (0x1002) + + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_USBHID + #define CFG_USBCDC + #define CFG_USBCDC_BAUDRATE (115200) + #define CFG_USBCDC_INITTIMEOUT (5000) + #define CFG_USBCDC_BUFFERSIZE (256) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_USBHID + #define CFG_USBCDC + #define CFG_USBCDC_BAUDRATE (115200) + #define CFG_USBCDC_INITTIMEOUT (5000) + #define CFG_USBCDC_BUFFERSIZE (256) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_USBHID + #define CFG_USBCDC + #define CFG_USBCDC_BAUDRATE (115200) + #define CFG_USBCDC_INITTIMEOUT (5000) + #define CFG_USBCDC_BUFFERSIZE (256) + #endif +/*=========================================================================*/ + + +/*========================================================================= + PRINTF REDIRECTION + ----------------------------------------------------------------------- + + CFG_PRINTF_UART Will cause all printf statements to be + redirected to UART + CFG_PRINTF_USBCDC Will cause all printf statements to be + redirect to USB Serial + CFG_PRINTF_NEWLINE This should be either "\r\n" for Windows or + "\n" for *nix + + Note: If no printf redirection definitions are present, all printf + output will be ignored, though this will also save ~350 bytes flash. + + NOTE: PRINTF Support = ~350 bytes Flash (-Os) + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_PRINTF_UART + #define CFG_PRINTF_USBCDC + #define CFG_PRINTF_NEWLINE "\r\n" + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_PRINTF_UART + #define CFG_PRINTF_USBCDC + #define CFG_PRINTF_NEWLINE "\r\n" + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_PRINTF_UART + #define CFG_PRINTF_USBCDC + #define CFG_PRINTF_NEWLINE "\r\n" + #endif +/*=========================================================================*/ + + +/*========================================================================= + COMMAND LINE INTERFACE + ----------------------------------------------------------------------- + + CFG_INTERFACE If this field is defined the UART or USBCDC + based command-line interface will be included + CFG_INTERFACE_MAXMSGSIZE The maximum number of bytes to accept for an + incoming command + CFG_INTERFACE_PROMPT The command prompt to display at the start + of every new data entry line + CFG_INTERFACE_SILENTMODE If this is set to 1 only text generated in + response to commands will be send to the + output buffer. The command prompt will not + be displayed and incoming text will not be + echoed back to the output buffer (allowing + you to see the text you have input). This + is normally only desirable in a situation + where another MCU is communicating with + the LPC1343. + CFG_INTERFACE_ENABLEIRQ If this is set to 1 the IRQ pin will be + set high when a command starts executing + and will go low when the command has + finished executing or the LCD is not busy. + This allows another device to know when a + new command can safely be sent. + CFG_INTERFACE_IRQPORT The gpio port for the IRQ/busy pin + CFG_INTERFACE_IRQPIN The gpio pin number for the IRQ/busy pin + + NOTE: The command-line interface will use either + USB-CDC or UART depending on whether + CFG_PRINTF_UART or CFG_PRINTF_USBCDC are + selected. + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_INTERFACE + #define CFG_INTERFACE_MAXMSGSIZE (256) + #define CFG_INTERFACE_PROMPT "LPC1343 >> " + #define CFG_INTERFACE_SILENTMODE (0) + #define CFG_INTERFACE_ENABLEIRQ (0) + #define CFG_INTERFACE_IRQPORT (2) + #define CFG_INTERFACE_IRQPIN (0) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_INTERFACE + #define CFG_INTERFACE_MAXMSGSIZE (256) + #define CFG_INTERFACE_PROMPT "LCD >> " + #define CFG_INTERFACE_SILENTMODE (0) + #define CFG_INTERFACE_ENABLEIRQ (1) + #define CFG_INTERFACE_IRQPORT (2) + #define CFG_INTERFACE_IRQPIN (0) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_INTERFACE + #define CFG_INTERFACE_MAXMSGSIZE (256) + #define CFG_INTERFACE_PROMPT "CMD >> " + #define CFG_INTERFACE_SILENTMODE (0) + #define CFG_INTERFACE_ENABLEIRQ (0) + #define CFG_INTERFACE_IRQPORT (2) + #define CFG_INTERFACE_IRQPIN (0) + #endif +/*=========================================================================*/ + + +/*========================================================================= + PWM SETTINGS + ----------------------------------------------------------------------- + + CFG_PWM If this is defined, a basic PWM driver + will be included using 16-bit Timer 1 and + Pin 1.9 (MAT0) for the PWM output. In + order to allow for a fixed number of + pulses to be generated, some PWM-specific + code is required in the 16-Bit Timer 1 + ISR. See "core/timer16/timer16.c" for + more information. + CFG_PWM_DEFAULT_PULSEWIDTH The default pulse width in ticks + CFG_PWM_DEFAULT_DUTYCYCLE The default duty cycle in percent + + DEPENDENCIES: PWM output requires the use of 16-bit + timer 1 and pin 1.9 (CT16B1_MAT0). + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_PWM + #define CFG_PWM_DEFAULT_PULSEWIDTH (CFG_CPU_CCLK / 1000) + #define CFG_PWM_DEFAULT_DUTYCYCLE (50) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_PWM + #define CFG_PWM_DEFAULT_PULSEWIDTH (CFG_CPU_CCLK / 1000) + #define CFG_PWM_DEFAULT_DUTYCYCLE (50) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_PWM + #define CFG_PWM_DEFAULT_PULSEWIDTH (CFG_CPU_CCLK / 1000) + #define CFG_PWM_DEFAULT_DUTYCYCLE (50) + #endif +/*=========================================================================*/ + + +/*========================================================================= + STEPPER MOTOR SETTINGS + ----------------------------------------------------------------------- + + CFG_STEPPER If this is defined, a simple bi-polar + stepper motor will be included for common + H-bridge chips like the L293D or SN754410N + + DEPENDENCIES: STEPPER requires the use of pins 3.0-3 and + 32-bit Timer 0. + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_STEPPER + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_STEPPER + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_STEPPER + #endif +/*=========================================================================*/ + + +/*========================================================================= + EEPROM + ----------------------------------------------------------------------- + + CFG_I2CEEPROM If defined, drivers for the onboard EEPROM + will be included during build + CFG_I2CEEPROM_SIZE The number of bytes available on the EEPROM + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + #define CFG_I2CEEPROM + #define CFG_I2CEEPROM_SIZE (3072) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_I2CEEPROM + #define CFG_I2CEEPROM_SIZE (3072) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_I2CEEPROM + #define CFG_I2CEEPROM_SIZE (3072) + #endif +/*=========================================================================*/ + + +/*========================================================================= + EEPROM MEMORY MAP + ----------------------------------------------------------------------- + EEPROM is used to persist certain user modifiable values to make + sure that these changes remain in effect after a reset or hard + power-down. The addresses in EEPROM for these various system + settings/values are defined below. The first 256 bytes of EEPROM + are reserved for this (0x0000..0x00FF). + + CFG_EEPROM_RESERVED The last byte of reserved EEPROM memory + + EEPROM Address (0x0000..0x00FF) + =============================== + 0 1 2 3 4 5 6 7 8 9 A B C D E F + 000x x x x x x x x x . x x . . . . . Chibi + 001x . . . . . . . . . . . . . . . . + 002x x x x x . . . . . . . . . . . . UART + 003x x x x x x x x x x x x x x x x x Touch Screen Calibration + 004x x x x x x x x x x x x x x x . . Touch Screen Calibration + 005x . . . . . . . . . . . . . . . . + 006x . . . . . . . . . . . . . . . . + 007x . . . . . . . . . . . . . . . . + 008x . . . . . . . . . . . . . . . . + 009x . . . . . . . . . . . . . . . . + 00Ax . . . . . . . . . . . . . . . . + 00Bx . . . . . . . . . . . . . . . . + 00Cx . . . . . . . . . . . . . . . . + 00Dx . . . . . . . . . . . . . . . . + 00Ex . . . . . . . . . . . . . . . . + 00Fx . . . . . . . . . . . . . . . . + + -----------------------------------------------------------------------*/ + #define CFG_EEPROM_RESERVED (0x00FF) // Protect first 256 bytes of memory + #define CFG_EEPROM_CHIBI_IEEEADDR (uint16_t)(0x0000) // 8 + #define CFG_EEPROM_CHIBI_SHORTADDR (uint16_t)(0x0009) // 2 + #define CFG_EEPROM_TOUCHSCREEN_CALIBRATED (uint16_t)(0x0030) // 1 + #define CFG_EEPROM_TOUCHSCREEN_CAL_AN (uint16_t)(0x0031) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_BN (uint16_t)(0x0035) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_CN (uint16_t)(0x0039) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_DN (uint16_t)(0x003D) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_EN (uint16_t)(0x0041) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_FN (uint16_t)(0x0045) // 4 + #define CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER (uint16_t)(0x0049) // 4 + #define CFG_EEPROM_TOUCHSCREEN_THRESHHOLD (uint16_t)(0x004D) // 1 + #define CFG_EEPROM_UART_SPEED (uint16_t)(0x0020) // 4 +/*=========================================================================*/ + + +/*========================================================================= + LM75B TEMPERATURE SENSOR + ----------------------------------------------------------------------- + + CFG_LM75B If defined, drivers for an optional LM75B + temperature sensor will be included during + build (requires external HW) + + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_LM75B + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_LM75B + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_LM75B + #endif +/*=========================================================================*/ + + +/*========================================================================= + CHIBI WIRELESS STACK + ----------------------------------------------------------------------- + + CFG_CHIBI If defined, the CHIBI wireless stack will be + included during build. Requires external HW. + CFG_CHIBI_MODE The mode to use when receiving and transmitting + wireless data. See chb_drvr.h for possible values + CFG_CHIBI_POWER The power level to use when transmitting. See + chb_drvr.h for possible values + CFG_CHIBI_CHANNEL 802.15.4 Channel (0 = 868MHz, 1-10 = 915MHz) + CFG_CHIBI_PANID 16-bit PAN Identifier (ex.0x1234) + CFG_CHIBI_PROMISCUOUS Set to 1 to enabled promiscuous mode or + 0 to disable it. If promiscuous mode is + enabled be sure to set CFG_CHIBI_BUFFERSIZE + to an appropriately large value (ex. 1024) + CFG_CHIBI_BUFFERSIZE The size of the message buffer in bytes + + DEPENDENCIES: Chibi requires the use of SSP0, 16-bit timer + 0 and pins 3.1, 3.2, 3.3. It also requires + the presence of CFG_I2CEEPROM. + + NOTE: These settings are not relevant to all boards! + 'tools/schematics/AT86RF212LPC1114_v1.6.pdf' + show how 'CHIBI' is meant to be connected + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_CHIBI + #define CFG_CHIBI_MODE (0) // OQPSK_868MHZ + #define CFG_CHIBI_POWER (0xE9) // CHB_PWR_EU2_3DBM + #define CFG_CHIBI_CHANNEL (0) // 868-868.6 MHz + #define CFG_CHIBI_PANID (0x1234) + #define CFG_CHIBI_PROMISCUOUS (0) + #define CFG_CHIBI_BUFFERSIZE (128) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_CHIBI + #define CFG_CHIBI_MODE (0) // OQPSK_868MHZ + #define CFG_CHIBI_POWER (0xE9) // CHB_PWR_EU2_3DBM + #define CFG_CHIBI_CHANNEL (0) // 868-868.6 MHz + #define CFG_CHIBI_PANID (0x1234) + #define CFG_CHIBI_PROMISCUOUS (0) + #define CFG_CHIBI_BUFFERSIZE (128) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + #define CFG_CHIBI + #define CFG_CHIBI_MODE (0) // OQPSK_868MHZ + #define CFG_CHIBI_POWER (0xE9) // CHB_PWR_EU2_3DBM + #define CFG_CHIBI_CHANNEL (0) // 868-868.6 MHz + #define CFG_CHIBI_PANID (0x1234) + #define CFG_CHIBI_PROMISCUOUS (0) + #define CFG_CHIBI_BUFFERSIZE (1024) + #endif +/*=========================================================================*/ + + +/*========================================================================= + TFT LCD + ----------------------------------------------------------------------- + + CFG_TFTLCD If defined, this will cause drivers for + a pre-determined LCD screen to be included + during build. Only one LCD driver can be + included during the build process (for ex. + 'drivers/lcd/hw/ILI9325.c') + CFG_TFTLCD_INCLUDESMALLFONTS If set to 1, smallfont support will be + included for 3x6, 5x8, 7x8 and 8x8 fonts. + This should only be enabled if these small + fonts are required since there is already + support for larger fonts generated with + Dot Factory + http://www.pavius.net/downloads/tools/53-the-dot-factory + CFG_TFTLCD_TS_DEFAULTTHRESHOLD Default minimum threshold to trigger a + touch event with the touch screen (and exit + from 'tsWaitForEvent' in touchscreen.c). + Should be an 8-bit value somewhere between + 8 and 75 in normal circumstances. This is + the default value and may be overriden by + a value stored in EEPROM. + CFG_TFTLCD_TS_KEYPADDELAY The delay in milliseconds between key + presses in dialogue boxes + + PIN LAYOUT: The pin layout that is used by this driver + can be seen in the following schematic: + /tools/schematics/Breakout_TFTLCD_ILI9325_v1.3 + + DEPENDENCIES: TFTLCD requires the use of pins 1.8, 1.9, + 1.10, 1.11, 3.3 and 2.1-9. + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_TFTLCD + #define CFG_TFTLCD_INCLUDESMALLFONTS (0) + #define CFG_TFTLCD_TS_DEFAULTTHRESHOLD (50) + #define CFG_TFTLCD_TS_KEYPADDELAY (100) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + #define CFG_TFTLCD + #define CFG_TFTLCD_INCLUDESMALLFONTS (0) + #define CFG_TFTLCD_TS_DEFAULTTHRESHOLD (50) + #define CFG_TFTLCD_TS_KEYPADDELAY (100) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_TFTLCD + #define CFG_TFTLCD_INCLUDESMALLFONTS (0) + #define CFG_TFTLCD_TS_DEFAULTTHRESHOLD (50) + #define CFG_TFTLCD_TS_KEYPADDELAY (100) + #endif +/*=========================================================================*/ + + +/*========================================================================= + 128x64 Graphic LCDs + ----------------------------------------------------------------------- + + CFG_ST7565 If defined, this will cause drivers for + the 128x64 pixel ST7565 LCD to be included + CFG_SSD1306 If defined, this will cause drivers for + the 128x64 pixel SSD1306 OLED display to be + included + + Note: LPC1114 @ 36MHz and the ST7565 with the + backlight enabled consumes ~35mA + + DEPENDENCIES: ST7565 requires the use of pins 2.1-6. + DEPENDENCIES: SSD1306 requires the use of pins 2.1-6. + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_ST7565 + // #define CFG_SSD1306 + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_ST7565 + // #define CFG_SSD1306 + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_ST7565 + // #define CFG_SSD1306 + #endif +/*=========================================================================*/ + + +/*========================================================================= + RSA Encryption + ----------------------------------------------------------------------- + + CFG_RSA If defined, support for basic RSA + encryption will be included. + CFG_RSA_BITS Indicates the number of bits used for + RSA encryption keys. To keep code size + reasonable, RSA encryption is currently + limited to using 64-bit or 32-bit numbers, + with 64-bit providing higher security, and + 32-bit providing smaller encrypted text + size. + + NOTE: Please note that Printf can not be + used to display 64-bit values (%lld)! + -----------------------------------------------------------------------*/ + #ifdef CFG_BRD_LPC1343_REFDESIGN + // #define CFG_RSA + #define CFG_RSA_BITS (32) + #endif + + #ifdef CFG_BRD_LPC1343_TFTLCDSTANDALONE + // #define CFG_RSA + #define CFG_RSA_BITS (32) + #endif + + #ifdef CFG_BRD_LPC1343_802154USBSTICK + // #define CFG_RSA + #define CFG_RSA_BITS (32) + #endif +/*=========================================================================*/ + + + + +/*========================================================================= + CONFIG FILE VALIDATION + ------------------------------------------------------------------------- + Basic error checking to make sure that incompatible defines are not + enabled at the same time, etc. + + =========================================================================*/ + +#if !defined CFG_BRD_LPC1343_REFDESIGN && !defined CFG_BRD_LPC1343_TFTLCDSTANDALONE && !defined CFG_BRD_LPC1343_802154USBSTICK + #error "You must defined a target board (CFG_BRD_LPC1343_REFDESIGN or CFG_BRD_LPC1343_TFTLCDSTANDALONE or CFG_BRD_LPC1343_802154USBSTICK)" +#endif +#if (defined CFG_BRD_LPC1343_REFDESIGN && defined CFG_BRD_LPC1343_TFTLCDSTANDALONE) || (defined CFG_BRD_LPC1343_TFTLCDSTANDALONE && defined CFG_BRD_LPC1343_802154USBSTICK) || (defined CFG_BRD_LPC1343_REFDESIGN && defined CFG_BRD_LPC1343_802154USBSTICK) + #error "Only one target board can be defined at a time" +#endif + +#if defined CFG_PRINTF_USBCDC && defined CFG_PRINTF_UART + #error "CFG_PRINTF_UART or CFG_PRINTF_USBCDC cannot both be defined at once" +#endif + +#if defined CFG_PRINTF_USBCDC && !defined CFG_USBCDC + #error "CFG_PRINTF_CDC requires CFG_USBCDC to be defined as well" +#endif + +#if defined CFG_USBCDC && defined CFG_USBHID + #error "Only one USB class can be defined at a time (CFG_USBCDC or CFG_USBHID)" +#endif + +#if defined CFG_SSP0_SCKPIN_2_11 && defined CFG_SSP0_SCKPIN_0_6 + #error "Only one SCK pin can be defined at a time for SSP0" +#endif + +#if !defined CFG_SSP0_SCKPIN_2_11 && !defined CFG_SSP0_SCKPIN_0_6 + #error "An SCK pin must be selected for SSP0 (CFG_SSP0_SCKPIN_2_11 or CFG_SSP0_SCKPIN_0_6)" +#endif + +#ifdef CFG_INTERFACE + #if !defined CFG_PRINTF_UART && !defined CFG_PRINTF_USBCDC + #error "CFG_PRINTF_UART or CFG_PRINTF_USBCDC must be defined for for CFG_INTERFACE Input/Output" + #endif + #if defined CFG_PRINTF_USBCDC && CFG_INTERFACE_SILENTMODE == 1 + #error "CFG_INTERFACE_SILENTMODE typically isn't enabled with CFG_PRINTF_USBCDC" + #endif +#endif + +#ifdef CFG_CHIBI + #if !defined CFG_I2CEEPROM + #error "CFG_CHIBI requires CFG_I2CEEPROM to store and retrieve addresses" + #endif + #ifdef CFG_SDCARD + #error "CFG_CHIBI and CFG_SDCARD can not be defined at the same time. Only one SPI block is available on the LPC1343." + #endif + #ifdef CFG_TFTLCD + #error "CFG_CHIBI and CFG_TFTLCD can not be defined at the same time since they both use pins 1.8, 1.9 and 1.10." + #endif + #ifdef CFG_PWM + #error "CFG_CHIBI and CFG_PWM can not be defined at the same time since they both use pin 1.9." + #endif + #if CFG_CHIBI_PROMISCUOUS != 0 && CFG_CHIBI_PROMISCUOUS != 1 + #error "CFG_CHIBI_PROMISCUOUS must be equal to either 1 or 0" + #endif +#endif + +#ifdef CFG_TFTLCD + #ifdef CFG_ST7565 + #error "CFG_TFTLCD and CFG_ST7565 can not be defined at the same time." + #endif + #ifdef CFG_SSD1306 + #error "CFG_TFTLCD and CFG_SSD1306 can not be defined at the same time." + #endif + #ifdef CFG_PWM + #error "CFG_TFTLCD and CFG_PWM can not be defined at the same time since they both use pin 1.9." + #endif + #if !defined CFG_I2CEEPROM + #error "CFG_TFTLCD requires CFG_I2CEEPROM to store and retrieve configuration settings" + #endif +#endif + +#ifdef CFG_SDCARD + #ifdef CFG_STEPPER + #error "CFG_SDCARD and CFG_STEPPER can not be defined at the same time since they both use pin 3.0." + #endif +#endif + +#ifdef CFG_ST7565 + #ifdef CFG_SSD1306 + #error "CFG_ST7565 and CFG_SSD1306 can not be defined at the same time" + #endif +#endif + +#ifdef CFG_RSA + #if CFG_RSA_BITS != 64 && CFG_RSA_BITS != 32 + #error "CFG_RSA_BITS must be equal to either 32 or 64." + #endif +#endif + +#endif diff --git a/sysdefs.h b/sysdefs.h new file mode 100644 index 0000000..bf79c8e --- /dev/null +++ b/sysdefs.h @@ -0,0 +1,65 @@ +/**************************************************************************/ +/*! + @file sysdefs.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _SYSDEFS_H_ +#define _SYSDEFS_H_ + +#include +#include +#include + +// Stay compatible with ugly "windows" style +#define BOOL bool +#define TRUE true +#define FALSE false + +typedef volatile uint8_t REG8; +typedef volatile uint16_t REG16; +typedef volatile uint32_t REG32; +typedef unsigned char byte_t; + +#define pREG8 (REG8 *) +#define pREG16 (REG16 *) +#define pREG32 (REG32 *) + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#endif + diff --git a/sysinit.c b/sysinit.c new file mode 100644 index 0000000..60b4d3b --- /dev/null +++ b/sysinit.c @@ -0,0 +1,278 @@ +/**************************************************************************/ +/*! + @file sysinit.c + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include +#include +#include + +#include "sysinit.h" + +#include "core/cpu/cpu.h" +#include "core/pmu/pmu.h" +#include "core/adc/adc.h" + +#ifdef CFG_PRINTF_UART + #include "core/uart/uart.h" +#endif + +#ifdef CFG_INTERFACE + #include "core/cmd/cmd.h" +#endif + +#ifdef CFG_CHIBI + #include "drivers/chibi/chb.h" +#endif + +#ifdef CFG_USBHID + #include "core/usbhid-rom/usbhid.h" +#endif + +#ifdef CFG_USBCDC + volatile unsigned int lastTick; + #include "core/usbcdc/usb.h" + #include "core/usbcdc/usbcore.h" + #include "core/usbcdc/usbhw.h" + #include "core/usbcdc/cdcuser.h" + #include "core/usbcdc/cdc_buf.h" +#endif + +#ifdef CFG_ST7565 + #include "drivers/lcd/bitmap/st7565/st7565.h" + #include "drivers/lcd/smallfonts.h" +#endif + +#ifdef CFG_SSD1306 + #include "drivers/lcd/bitmap/ssd1306/ssd1306.h" + #include "drivers/lcd/smallfonts.h" +#endif + +#ifdef CFG_TFTLCD + #include "drivers/lcd/tft/lcd.h" + #include "drivers/lcd/tft/touchscreen.h" + #include "drivers/lcd/tft/drawing.h" +#endif + +#ifdef CFG_I2CEEPROM + #include "drivers/eeprom/mcp24aa/mcp24aa.h" + #include "drivers/eeprom/eeprom.h" +#endif + +#ifdef CFG_PWM + #include "core/pwm/pwm.h" +#endif + +#ifdef CFG_SDCARD + #include "core/ssp/ssp.h" + #include "drivers/fatfs/diskio.h" + #include "drivers/fatfs/ff.h" + + DWORD get_fattime () + { + // ToDo! + return 0; + } +#endif + +/**************************************************************************/ +/*! + Configures the core system clock and sets up any mandatory + peripherals like the systick timer, UART for printf, etc. + + This function should set the HW to the default state you wish to be + in coming out of reset/startup, such as disabling or enabling LEDs, + setting specific pin states, etc. +*/ +/**************************************************************************/ +void systemInit() +{ + cpuInit(); // Configure the CPU + systickInit(CFG_SYSTICK_DELAY_IN_MS); // Start systick timer + gpioInit(); // Enable GPIO + pmuInit(); // Configure power management + adcInit(); // Config adc pins to save power + + // Set LED pin as output and turn LED off + gpioSetDir(CFG_LED_PORT, CFG_LED_PIN, 1); + gpioSetValue(CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + + // Initialise EEPROM + #ifdef CFG_I2CEEPROM + mcp24aaInit(); + #endif + + // Initialise UART with the default baud rate + #ifdef CFG_PRINTF_UART + uint32_t uart = eepromReadU32(CFG_EEPROM_UART_SPEED); + if ((uart == 0xFFFFFFFF) || (uart > 115200)) + { + uartInit(CFG_UART_BAUDRATE); // Use default baud rate + } + else + { + uartInit(uart); // Use baud rate from EEPROM + } + #endif + + // Initialise PWM (requires 16-bit Timer 1 and P1.9) + #ifdef CFG_PWM + pwmInit(); + #endif + + // Initialise USB HID + #ifdef CFG_USBHID + usbHIDInit(); + #endif + + // Initialise USB CDC + #ifdef CFG_USBCDC + lastTick = systickGetTicks(); // Used to control output/printf timing + CDC_Init(); // Initialise VCOM + USB_Init(); // USB Initialization + USB_Connect(TRUE); // USB Connect + // Wait until USB is configured or timeout occurs + uint32_t usbTimeout = 0; + while ( usbTimeout < CFG_USBCDC_INITTIMEOUT / 10 ) + { + if (USB_Configuration) break; + systickDelay(10); // Wait 10ms + usbTimeout++; + } + #endif + + // Printf can now be used with UART or USBCDC + + // Initialise the ST7565 128x64 pixel display + #ifdef CFG_ST7565 + st7565Init(); + st7565ClearScreen(); // Clear the screen + st7565Backlight(1); // Enable the backlight + #endif + + // Initialise the SSD1306 OLED display + #ifdef CFG_SSD1306 + ssd1306Init(SSD1306_SWITCHCAPVCC); + ssd1306ClearScreen(); // Clear the screen + #endif + + // Initialise TFT LCD Display + #ifdef CFG_TFTLCD + lcdInit(); + #endif + + // Initialise Chibi + // Warning: CFG_CHIBI must be disabled if no antenna is connected, + // otherwise the SW will halt during initialisation + #ifdef CFG_CHIBI + // Write addresses to EEPROM for the first time if necessary + // uint16_t addr_short = 0x0025; + // uint64_t addr_ieee = 0x0000000000000025; + // mcp24aaWriteBuffer(CFG_EEPROM_CHIBI_SHORTADDR, (uint8_t *)&addr_short, 2); + // mcp24aaWriteBuffer(CFG_EEPROM_CHIBI_IEEEADDR, (uint8_t *)&addr_ieee, 8); + chb_init(); + // chb_pcb_t *pcb = chb_get_pcb(); + // printf("%-40s : 0x%04X%s", "Chibi Initialised", pcb->src_addr, CFG_PRINTF_NEWLINE); + #endif + + // Start the command line interface + #ifdef CFG_INTERFACE + cmdInit(); + #endif +} + +/**************************************************************************/ +/*! + @brief Sends a single byte to a pre-determined peripheral (UART, etc.). + + @param[in] byte + Byte value to send +*/ +/**************************************************************************/ +void __putchar(const char c) +{ + #ifdef CFG_PRINTF_UART + // Send output to UART + uartSendByte(c); + #endif +} + +/**************************************************************************/ +/*! + @brief Sends a string to a pre-determined end point (UART, etc.). + + @param[in] str + Text to send + + @note This function is only called when using the GCC-compiler + in Codelite or running the Makefile manually. This function + will not be called when using the C library in Crossworks for + ARM. +*/ +/**************************************************************************/ +int puts(const char * str) +{ + // There must be at least 1ms between USB frames (of up to 64 bytes) + // This buffers all data and writes it out from the buffer one frame + // and one millisecond at a time + #ifdef CFG_PRINTF_USBCDC + if (USB_Configuration) + { + while(*str) + cdcBufferWrite(*str++); + // Check if we can flush the buffer now or if we need to wait + unsigned int currentTick = systickGetTicks(); + if (currentTick != lastTick) + { + uint8_t frame[64]; + uint32_t bytesRead = 0; + while (cdcBufferDataPending()) + { + // Read up to 64 bytes as long as possible + bytesRead = cdcBufferReadLen(frame, 64); + USB_WriteEP (CDC_DEP_IN, frame, bytesRead); + systickDelay(1); + } + lastTick = currentTick; + } + } + #else + // Handle output character by character in __putchar + while(*str) __putchar(*str++); + #endif + + return 0; +} diff --git a/sysinit.h b/sysinit.h new file mode 100644 index 0000000..f7beabd --- /dev/null +++ b/sysinit.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/*! + @file sysinit.h + @author K. Townsend (microBuilder.eu) + @date 22 March 2010 + @version 0.10 + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef __SYSINIT_H__ +#define __SYSINIT_H__ + +#include "projectconfig.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +// Function prototypes +void systemInit(); + +#endif \ No newline at end of file diff --git a/tools/colors_h.png b/tools/colors_h.png new file mode 100644 index 0000000000000000000000000000000000000000..4212ae6bb067c1dfc99f43d35da9b20d30df69f5 GIT binary patch literal 25564 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

`Dvu45{TwXnm{ZMkRv8)QapFfYNc_M~J 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 JN}pJK_&+;m5di=I literal 0 HcmV?d00001 diff --git a/tools/dotfactory/DotFactorySettings_FixedWidth.png b/tools/dotfactory/DotFactorySettings_FixedWidth.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd97de5cbafe7d17f0847432792cd078dde6a3d GIT binary patch literal 25428 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 + + + true + true + true + C + # + RotateNinety + false + false + Fixed + Tighest + AtColumn + MsbFirst + Hex + 0x + true + DisplayInBits + DontDisplay + DisplayInBytes + true + 5 + VariableWidth + + + true + true + true + C + # + RotateNinety + false + false + Fixed + Fixed + AtColumn + MsbFirst + Hex + 0x + true + DisplayInBits + DontDisplay + DisplayInBytes + true + 8 + FixedWidth + + \ No newline at end of file diff --git a/tools/dotfactory/TheDotFactory-src-0.0.9.7z b/tools/dotfactory/TheDotFactory-src-0.0.9.7z new file mode 100644 index 0000000000000000000000000000000000000000..f2ee5a9be34a80df7ef0a9fc786f391ca2d108e3 GIT binary patch literal 46783 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 HQ{evsMPN*E literal 0 HcmV?d00001 diff --git a/tools/dotfactory/readme.txt b/tools/dotfactory/readme.txt new file mode 100644 index 0000000..b7891ae --- /dev/null +++ b/tools/dotfactory/readme.txt @@ -0,0 +1,14 @@ +The Dot Factory is a GPL license program that can be used to +convert TTF fonts for .c files. The latest Windows binary +and source files can be found at: + +http://www.pavius.net/downloads/tools/53-the-dot-factory + +The source and binary files are included here purely for +convenience sake. + +The following fonts are used in the LPC1343 +Code Base: + +DejaVu - http://dejavu-fonts.org/wiki/Main_Page +Bitsream Vera - http://www.gnome.org/fonts/ \ No newline at end of file diff --git a/tools/examples/basics/blinky/main.c b/tools/examples/basics/blinky/main.c new file mode 100644 index 0000000..7a6484d --- /dev/null +++ b/tools/examples/basics/blinky/main.c @@ -0,0 +1,72 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +/**************************************************************************/ +/*! + Cause the LED to blink once per second using a blocking delay +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + while (1) + { + // Wait one second + systickDelay(1000); + // Toggle the LED + if (gpioGetValue(CFG_LED_PORT, CFG_LED_PIN) == CFG_LED_OFF) + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + } + else + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + } + } + + return 0; +} diff --git a/tools/examples/basics/blinky/readme.txt b/tools/examples/basics/blinky/readme.txt new file mode 100644 index 0000000..84b078f --- /dev/null +++ b/tools/examples/basics/blinky/readme.txt @@ -0,0 +1 @@ +Causes the LED to blink once per second \ No newline at end of file diff --git a/tools/examples/basics/blinky_nonblocking/main.c b/tools/examples/basics/blinky_nonblocking/main.c new file mode 100644 index 0000000..0de837d --- /dev/null +++ b/tools/examples/basics/blinky_nonblocking/main.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +#ifdef CFG_INTERFACE + #include "core/cmd/cmd.h" +#endif + +/**************************************************************************/ +/*! + Causes the LED to flash every second using a non-blocking delay, + and constantly checks if any incoming characters have arrived in + the UART buffer for the CLI + + projectconfig.h settings: + -------------------------------------------------- + CFG_INTERFACE -> Enabled + CFG_PRINTF_USBCDC -> Enabled if USB is used for the CLI + CFG_PRINTF_UART -> Enabled if UART is used for the CLI +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + uint32_t currentSecond, lastSecond; + currentSecond = lastSecond = 0; + + // Toggle LED once per second and constantly check CLI input + while (1) + { + // Get the number of seconds the CPU has been active + // If the value has changed we've advanced at least 1 second + currentSecond = systickGetSecondsActive(); + if (currentSecond != lastSecond) + { + lastSecond = currentSecond; + // Toggle the LED + if (gpioGetValue(CFG_LED_PORT, CFG_LED_PIN) == CFG_LED_OFF) + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + } + else + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + } + } + + // Poll for CLI input if CFG_INTERFACE is enabled in projectconfig.h + #ifdef CFG_INTERFACE + cmdPoll(); + #endif + } + + return 0; +} diff --git a/tools/examples/basics/blinky_nonblocking/readme.txt b/tools/examples/basics/blinky_nonblocking/readme.txt new file mode 100644 index 0000000..18a74d8 --- /dev/null +++ b/tools/examples/basics/blinky_nonblocking/readme.txt @@ -0,0 +1,5 @@ +Causes the LED to blink once per second using a non-blocking delay, as well +as constantly checking the UART or USB CDC buffer for incoming data to be +passed to the CLI. You can connect to the CLI using UART or USB, send and +receive commands, and the LED should continue blinking (as long as a command +is not using the MCU). \ No newline at end of file diff --git a/tools/examples/basics/pwm_piezobuzzer/main.c b/tools/examples/basics/pwm_piezobuzzer/main.c new file mode 100644 index 0000000..d65ad07 --- /dev/null +++ b/tools/examples/basics/pwm_piezobuzzer/main.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +#ifdef CFG_INTERFACE + #include "core/cmd/cmd.h" +#endif + +#ifdef CFG_PWM + #include "core/pwm/pwm.h" +#endif + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + #ifndef CFG_PWM + #ERROR "CFG_PWM must be enabled in projectconfig.h for this example." + #endif + + // Configure cpu and mandatory peripherals + // PWM is initialised in systemInit and defaults to using P1.9 + systemInit(); + + // Set duty cycle to 50% for square wave output + pwmSetDutyCycle(50); + + // Frequency can be set anywhere from 2khz and 10khz (4khz is loudest) + // Note: Since this is a 16-bit timer, make sure the delay is not + // greater than 0xFFFF (65535), though anything 2khz and higher + // is within an acceptable range + // The piezo buzzer used for this example is the PS1240, available at + // http://www.adafruit.com/index.php?main_page=product_info&cPath=35&products_id=160 + pwmSetFrequencyInTicks(CFG_CPU_CCLK / 2000); + + uint32_t currentSecond, lastSecond; + currentSecond = lastSecond = 0; + + while (1) + { + // Beep the piezo buzzer very briefly once per second, toggling the LED at the same time + currentSecond = systickGetSecondsActive(); + // Make sure that at least one second has passed + if (currentSecond != lastSecond) + { + // Update the second tracker + lastSecond = currentSecond; + // Set the LED state and buzzer loudness depending on the current LED state + if (gpioGetValue(CFG_LED_PORT, CFG_LED_PIN) == CFG_LED_OFF) + { + pwmSetFrequencyInTicks(CFG_CPU_CCLK / 4000); // 4khz (louder) + pwmStartFixed(200); // 2x as long as @ 2khz since it's 2x faster + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); // Turn the LED on + } + else + { + pwmSetFrequencyInTicks(CFG_CPU_CCLK / 2000); // 2khz (softer) + pwmStartFixed(100); + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); // Turn the LED off + } + } + } + + return 0; +} diff --git a/tools/examples/basics/pwm_piezobuzzer/readme.txt b/tools/examples/basics/pwm_piezobuzzer/readme.txt new file mode 100644 index 0000000..3a39b81 --- /dev/null +++ b/tools/examples/basics/pwm_piezobuzzer/readme.txt @@ -0,0 +1,12 @@ +This example uses a 16-bit timer for PWM, and makes a common +piezo buzzer beep briefly once per second at an alternating +frequency. + +The piezo-buzzer used in this example is the PS1240, available +from Adafruit Industries at: + +http://www.adafruit.com/index.php?main_page=product_info&cPath=35&products_id=160 + +The buzzer should have one pin (either is fine) connected to P1.9 +on the LPC1343, and the other pin connected to GND. CFG_PWM must also +be enabled in projectconfig.h. \ No newline at end of file diff --git a/tools/examples/chibi/receive/main.c b/tools/examples/chibi/receive/main.c new file mode 100644 index 0000000..3daafcf --- /dev/null +++ b/tools/examples/chibi/receive/main.c @@ -0,0 +1,117 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "drivers/chibi/chb.h" +#include "drivers/chibi/chb_drvr.h" + +static chb_rx_data_t rx_data; + +/**************************************************************************/ +/*! + Converts the ED (Energy Detection) value to dBm using the following + formula: dBm = RSSI_BASE_VAL + 1.03 * ED + + For more information see section 6.5 of the AT86RF212 datasheet +*/ +/**************************************************************************/ +int edToDBM(uint32_t ed) +{ + #if CFG_CHIBI_MODE == 0 || CFG_CHIBI_MODE == 1 || CFG_CHIBI_MODE == 2 + // Calculate for OQPSK (RSSI Base Value = -100) + int dbm = (103 * ed - 10000); + #else + // Calculate for BPSK (RSSI Base Value = -98) + int dbm = (103 * ed - 9800); + #endif + + return dbm / 100; +} + +/**************************************************************************/ +/*! + Constantly checks for incoming messages, and displays them using + printf when they arrive. This program will display messages sent + to the global broadcast address (0xFFFF) or messages addressed to + this node using it's unique 16-bit ID. + + projectconfig.h settings: + -------------------------------------------------- + CFG_CHIBI -> Enabled + CFG_CHIBI_PROMISCUOUS -> 0 + CFG_CHIBI_BUFFERSIZE -> 128 +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + // Make sure that projectconfig.h is properly configured for this example + #if !defined CFG_CHIBI + #error "CFG_CHIBI must be enabled in projectconfig.h for this example" + #endif + #if CFG_CHIBI_PROMISCUOUS != 0 + #error "CFG_CHIBI_PROMISCUOUS must be set to 0 in projectconfig.h for this example" + #endif + + // Get a reference to the Chibi peripheral control block + chb_pcb_t *pcb = chb_get_pcb(); + + while(1) + { + // Check for incoming messages + while (pcb->data_rcv) + { + // Enable LED to indicate message reception + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + // get the length of the data + rx_data.len = chb_read(&rx_data); + // make sure the length is nonzero + if (rx_data.len) + { + int dbm = edToDBM(pcb->ed); + printf("Message received from node %02X: %s, len=%d, dBm=%d.%s", rx_data.src_addr, rx_data.data, rx_data.len, dbm, CFG_PRINTF_NEWLINE); + } + // Disable LED + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + } + } + + return 0; +} diff --git a/tools/examples/chibi/receive/readme.txt b/tools/examples/chibi/receive/readme.txt new file mode 100644 index 0000000..d0afae6 --- /dev/null +++ b/tools/examples/chibi/receive/readme.txt @@ -0,0 +1,3 @@ +Looks for any incoming messages using the global broadcast address +(0xFFFF) of this node's addresses and displays the message content +using printf (redirect to UART by default). \ No newline at end of file diff --git a/tools/examples/chibi/sniffer_wsbridge/main.c b/tools/examples/chibi/sniffer_wsbridge/main.c new file mode 100644 index 0000000..0e3bcdc --- /dev/null +++ b/tools/examples/chibi/sniffer_wsbridge/main.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" + +#if defined CFG_CHIBI + #include + #include + #include "drivers/chibi/chb.h" + #include "drivers/chibi/chb_drvr.h" + #include "core/uart/uart.h" + static chb_rx_data_t rx_data; +#endif + +#ifdef CFG_PRINTF_USBCDC + volatile unsigned int lastTick; + #include "core/usbcdc/usb.h" + #include "core/usbcdc/usbcore.h" + #include "core/usbcdc/usbhw.h" + #include "core/usbcdc/cdcuser.h" + #include "core/usbcdc/cdc_buf.h" +#endif + +/**************************************************************************/ +/*! + Use Chibi as a wireless sniffer and write all captured frames + to UART or USB CDC for wsbridge to handle (see "tools/wsbridge") + + projectconfig.h settings: + -------------------------------------------------- + CFG_CHIBI -> Enabled + CFG_CHIBI_PROMISCUOUS -> 1 + CFG_CHIBI_BUFFERSIZE -> 1024 +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + // Check if projectconfig.h is properly configured for this example + #if !defined CFG_CHIBI + #error "CFG_CHIBI must be enabled in projectconfig.h for this example" + #endif + #if CFG_CHIBI_PROMISCUOUS == 0 + #error "CFG_CHIBI_PROMISCUOUS must set to 1 in projectconfig.h for this example" + #endif + #if defined CFG_INTERFACE + #error "CFG_INTERFACE must be disabled in projectconfig.h for this example" + #endif + + #if defined CFG_CHIBI && CFG_CHIBI_PROMISCUOUS != 0 + // Get a reference to the Chibi peripheral control block + chb_pcb_t *pcb = chb_get_pcb(); + + // Wait for incoming frames and transmit the raw data over uart + while(1) + { + // Check for incoming messages + while (pcb->data_rcv) + { + // get the length of the data + rx_data.len = chb_read(&rx_data); + // make sure the length is nonzero + if (rx_data.len) + { + // Enable LED to indicate message reception + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + + + // Send raw data the to PC for processing using wsbridge + uint8_t i; + for (i=0; i + #include + #include "drivers/chibi/chb.h" + #include "drivers/chibi/chb_drvr.h" +#endif + +/**************************************************************************/ +/*! + Broadcast a basic message every 250 milliseconds + + projectconfig.h settings: + -------------------------------------------------- + CFG_CHIBI -> Enabled + CFG_CHIBI_PROMISCUOUS -> 0 + CFG_CHIBI_BUFFERSIZE -> 128 +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + // Make sure that projectconfig.h is properly configured for this example + #if !defined CFG_CHIBI + #error "CFG_CHIBI must be enabled in projectconfig.h for this example" + #endif + #if CFG_CHIBI_PROMISCUOUS != 0 + #error "CFG_CHIBI_PROMISCUOUS must be set to 0 in projectconfig.h for this example" + #endif + + #ifdef CFG_CHIBI + uint32_t counter = 0; + chb_pcb_t *pcb = chb_get_pcb(); + + while(1) + { + // Enable LED + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + // Create and send the message + char text[10]; + counter++; + itoa(counter, text, 10); + chb_write(0xFFFF, text, strlen(text) + 1); + // Disable LED + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + systickDelay(250); + } + #endif + + return 0; +} diff --git a/tools/examples/chibi/transmit/readme.txt b/tools/examples/chibi/transmit/readme.txt new file mode 100644 index 0000000..66c2496 --- /dev/null +++ b/tools/examples/chibi/transmit/readme.txt @@ -0,0 +1,2 @@ +Broadcasts a basic messages every 250 milliseconds that will be +received by every node within hearing distance. \ No newline at end of file diff --git a/tools/examples/default/main.c b/tools/examples/default/main.c new file mode 100644 index 0000000..8b75435 --- /dev/null +++ b/tools/examples/default/main.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +#ifdef CFG_INTERFACE + #include "core/cmd/cmd.h" +#endif + +/**************************************************************************/ +/*! + Approximates a 1 millisecond delay using "nop". This is less + accurate than a dedicated timer, but is useful in certain situations. + + The number of ticks to delay depends on the optimisation level set + when compiling (-O). Depending on the compiler settings, one of the + two defined values for 'delay' should be used. +*/ +/**************************************************************************/ +void delayms(uint32_t ms) +{ + uint32_t delay = ms * ((CFG_CPU_CCLK / 100) / 45); // Release Mode (-Os) + // uint32_t delay = ms * ((CFG_CPU_CCLK / 100) / 120); // Debug Mode (No optimisations) + + while (delay > 0) + { + __asm volatile ("nop"); + delay--; + } +} + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + uint32_t currentSecond, lastSecond; + currentSecond = lastSecond = 0; + + while (1) + { + // Toggle LED once per second ... rollover = 136 years :) + currentSecond = systickGetSecondsActive(); + if (currentSecond != lastSecond) + { + lastSecond = currentSecond; + if (gpioGetValue(CFG_LED_PORT, CFG_LED_PIN) == CFG_LED_OFF) + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + } + else + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + } + } + + // Poll for CLI input if CFG_INTERFACE is enabled in projectconfig.h + #ifdef CFG_INTERFACE + cmdPoll(); + #endif + } + + return 0; +} diff --git a/tools/examples/default/readme.txt b/tools/examples/default/readme.txt new file mode 100644 index 0000000..dd4bb08 --- /dev/null +++ b/tools/examples/default/readme.txt @@ -0,0 +1,3 @@ +This is the default main.c file used in the code base and will cause +the test LED to blinks once per second and process any incoming data +for the CLI if CFG_INTERFACE is enabled in projectconfig.h \ No newline at end of file diff --git a/tools/examples/lcd/tft/basic_ui/basic_ui_screenshot.png b/tools/examples/lcd/tft/basic_ui/basic_ui_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..36f52c88df5d6915c32c99817eddd7fbcb653fb6 GIT binary patch literal 3947 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 + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/adc/adc.h" +#include "core/systick/systick.h" + +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/bmp.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/touchscreen.h" +#include "drivers/lcd/tft/fonts/dejavusans9.h" +#include "drivers/lcd/tft/fonts/dejavusansbold9.h" +#include "drivers/lcd/tft/fonts/dejavusansmono8.h" + +#include "drivers/lcd/icons16.h" + +// Color scheme +#define FONT_REGULAR &dejaVuSans9ptFontInfo +#define FONT_BOLD &dejaVuSansBold9ptFontInfo +#define COL_BACKGROUND COLOR_GRAY_80 +#define COL_TEXT COLOR_WHITE + +#define COL_BUTTON COLOR_GRAY_50 +#define COL_BUTTONBORDER COLOR_GRAY_50 +#define COL_BUTTONTEXT COLOR_WHITE +#define COL_BUTTONACTIVE COLOR_THEME_DEFAULT_BASE +#define COL_BUTTONACTIVEBORDER COLOR_THEME_DEFAULT_DARKER +#define COL_BUTTONACTIVETEXT COLOR_BLACK + +#define COL_MENU COLOR_GRAY_30 +#define COL_MENULIGHTER COLOR_GRAY_50 +#define COL_MENUTEXT COLOR_WHITE +#define COL_MENUACTIVE COLOR_THEME_DEFAULT_DARKER +#define COL_MENUACTIVELIGHTER COLOR_THEME_DEFAULT_BASE +#define COL_MENUACTIVETEXT COLOR_BLACK + +/**************************************************************************/ +/*! + Draws a single entry in the menu (adjusting the display depending + on whether the item is currently active or not) +*/ +/**************************************************************************/ +void renderMenuItem(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t headerWidth, bool active, char* headerText, char* bodyText) +{ + drawRectangleRounded(x, y, x+headerWidth, y+height, active ? COL_MENUACTIVE : COL_MENU, 10, DRAW_ROUNDEDCORNERS_NONE); + drawRectangleRounded(x+headerWidth, y, x+width, y+height, active ? COL_MENUACTIVELIGHTER : COL_MENULIGHTER, 10, DRAW_ROUNDEDCORNERS_NONE); + drawString(x+10, y+height/2, active ? COL_MENUACTIVETEXT : COL_MENUTEXT, FONT_BOLD, headerText); + drawString(x+headerWidth+25, y+height/2, active ? COL_MENUACTIVETEXT: COL_MENUTEXT, FONT_REGULAR, bodyText); + + if (active) + { + drawArrow(x+headerWidth+5, y+height/2, 7, DRAW_DIRECTION_LEFT, COL_MENUACTIVETEXT); + drawArrow(x+width-5, y+height/2, 7, DRAW_DIRECTION_RIGHT, COL_MENUACTIVETEXT); + } +} + +/**************************************************************************/ +/*! + Renders the 16x16 icons found in "/drivers/lcd/icons16.h" +*/ +/**************************************************************************/ +void renderIcons(void) +{ + // Cross/Failed + drawRectangleRounded(10, 190, 30, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(12, 192, COLOR_RED, icons16_failed); + drawRectangleRounded(10, 220, 30, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(12, 222, COLOR_RED, icons16_failed); + drawIcon16(12, 222, COLOR_WHITE, icons16_failed_interior); + drawRectangleRounded(10, 250, 30, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(12, 252, COLOR_WHITE, icons16_failed_interior); + + // Alert + drawRectangleRounded(40, 190, 60, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(42, 192, COLOR_YELLOW, icons16_alert); + drawRectangleRounded(40, 220, 60, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(42, 222, COLOR_YELLOW, icons16_alert); + drawIcon16(42, 222, COLOR_WHITE, icons16_alert_interior); + drawRectangleRounded(40, 250, 60, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(42, 252, COLOR_WHITE, icons16_alert_interior); + + // Checkmark/Passed + drawRectangleRounded(70, 190, 90, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(72, 192, COLOR_GREEN, icons16_passed); + drawRectangleRounded(70, 220, 90, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(72, 222, COLOR_GREEN, icons16_passed); + drawIcon16(72, 222, COLOR_WHITE, icons16_passed_interior); + drawRectangleRounded(70, 250, 90, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(72, 252, COLOR_WHITE, icons16_passed_interior); + + // Info + drawRectangleRounded(100, 190, 120, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(102, 192, COLOR_BLUE, icons16_info); + drawRectangleRounded(100, 220, 120, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(102, 222, COLOR_BLUE, icons16_info); + drawIcon16(102, 222, COLOR_WHITE, icons16_info_interior); + drawRectangleRounded(100, 250, 120, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(102, 252, COLOR_WHITE, icons16_info_interior); + + // Tools/Config + drawRectangleRounded(130, 190, 150, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(132, 192, COLOR_GREEN, icons16_tools); + + // Pointer + drawRectangleRounded(160, 190, 180, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(162, 192, COLOR_MAGENTA, icons16_pointer); + drawRectangleRounded(160, 220, 180, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(162, 222, COLOR_MAGENTA, icons16_pointer); + drawIcon16(162, 222, COLOR_WHITE, icons16_pointer_dot); + drawRectangleRounded(160, 250, 180, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(162, 252, COLOR_WHITE, icons16_pointer_dot); + + // Tag + drawRectangleRounded(190, 190, 210, 210, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(192, 192, COLOR_CYAN, icons16_tag); + drawRectangleRounded(190, 220, 210, 240, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(192, 222, COLOR_CYAN, icons16_tag); + drawIcon16(192, 222, COLOR_WHITE, icons16_tag_dot); + drawRectangleRounded(190, 250, 210, 270, COL_BUTTON, 5, DRAW_ROUNDEDCORNERS_ALL); + drawIcon16(192, 252, COLOR_WHITE, icons16_tag_dot); +} + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + #if !defined CFG_TFTLCD + #error "CFG_TFTLCD must be enabled in projectconfig.h for this test" + #endif + + // Configure cpu and mandatory peripherals + systemInit(); + + // Background + drawFill(COL_BACKGROUND); + + // Top/bottom action bars + drawRectangleFilled(0, 0, 239, 19, COL_MENU); + drawRectangleFilled(0, 280, 239, 319, COL_MENU); + + // Menu + drawRectangleRounded(6, 30, 232, 160, COL_MENU, 10, DRAW_ROUNDEDCORNERS_ALL); + drawString(20, 45, COL_MENUTEXT, FONT_BOLD, "SYSTEM SETTINGS"); + renderMenuItem(8, 65, 222, 23, 90, false, "LANGUAGE", "English"); + renderMenuItem(8, 90, 222, 23, 90, false, "TIMEZONE", "GMT+1"); + renderMenuItem(8, 115, 222, 23, 90, true, "SLEEP", "5 Minutes"); + + // Progress bar + drawProgressBar (70, 165, 160, 15, DRAW_ROUNDEDCORNERS_ALL, DRAW_ROUNDEDCORNERS_ALL, COL_MENU, COL_MENULIGHTER, COL_MENUACTIVE, COL_MENUACTIVELIGHTER, 72 ); + + // Render some icons + renderIcons(); + + // Action bar buttons + drawButton(5, 285, 75, 29, FONT_BOLD, 7, COL_BUTTONBORDER, COL_BUTTON, COL_BUTTONTEXT, "CANCEL"); + drawButton(160, 285, 75, 29, FONT_BOLD, 7, COL_BUTTONBORDER, COL_BUTTON, COL_BUTTONTEXT, "SAVE"); + + tsTouchData_t touch; + tsTouchError_t error; + + // Draw pixels whenever a touch screen event is detected + while (1) + { + // Wait for a valid touch event + error = tsWaitForEvent(&touch, 0); + if (!error) + { + drawPixel(touch.xlcd, touch.ylcd, COLOR_WHITE); + } + } + + return 0; +} diff --git a/tools/examples/lcd/tft/drawing_basic/main.c b/tools/examples/lcd/tft/drawing_basic/main.c new file mode 100644 index 0000000..f3468ab --- /dev/null +++ b/tools/examples/lcd/tft/drawing_basic/main.c @@ -0,0 +1,182 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include +#include +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/systick/systick.h" + +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/touchscreen.h" +#include "drivers/lcd/tft/dialogues/alphanumeric.h" + +#include "drivers/lcd/tft/fonts/dejavusans9.h" +#include "drivers/lcd/tft/fonts/dejavusansbold9.h" +#include "drivers/lcd/tft/fonts/dejavusansmono8.h" + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + #if !defined CFG_TFTLCD + #error "CFG_TFTLCD must be enabled in projectconfig.h for this test" + #endif + #if defined CFG_INTERFACE + #error "CFG_INTERFACE must be disabled in projectconfig.h for this test (to save space)" + #endif + + // Clear the screen + // --------------------------------------------------------------------- + drawFill(COLOR_WHITE); + + // Render some text using DejaVu Sans 9 and Sans Mono 8 + // --------------------------------------------------------------------- + drawString(5, 10, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "DejaVu Sans 9 Bold"); + drawString(5, 30, COLOR_BLACK, &dejaVuSans9ptFontInfo, "DejaVu Sans 9"); + drawString(5, 50, COLOR_BLACK, &dejaVuSansMono8ptFontInfo, "DejaVu Sans Mono 8"); + + // Change the LCD orientation to render text horizontally + // --------------------------------------------------------------------- + lcdProperties_t lcdProperties = lcdGetProperties(); + // Check if the screen orientation can be changed + if (lcdProperties.orientation) + { + // Change the orientation + lcdSetOrientation(lcdGetOrientation() == LCD_ORIENTATION_PORTRAIT ? + LCD_ORIENTATION_LANDSCAPE : LCD_ORIENTATION_PORTRAIT); + // Render some text in the new orientation + drawString(5, 10, COLOR_BLACK, &dejaVuSans9ptFontInfo, "DejaVu Sans 9 (Rotated)"); + // Change the orientation back + lcdSetOrientation(lcdGetOrientation() == LCD_ORIENTATION_PORTRAIT ? + LCD_ORIENTATION_LANDSCAPE : LCD_ORIENTATION_PORTRAIT); + } + + // Draw some primitive shapes + // --------------------------------------------------------------------- + drawLine(5, 65, 200, 65, COLOR_RED); + drawLine(5, 67, 200, 67, COLOR_GREEN); + drawLine(5, 69, 200, 69, COLOR_BLUE); + drawCircleFilled(30, 105, 25, COLOR_BLACK); + drawCircleFilled(30, 105, 23, drawRGB24toRGB565(0x33, 0x00, 0x00)); + drawCircleFilled(30, 105, 19, drawRGB24toRGB565(0x66, 0x00, 0x00)); + drawCircleFilled(30, 105, 15, drawRGB24toRGB565(0x99, 0x00, 0x00)); + drawCircleFilled(30, 105, 11, drawRGB24toRGB565(0xCC, 0x00, 0x00)); + drawCircleFilled(30, 105, 7, drawRGB24toRGB565(0xFF, 0x00, 0x00)); + drawRectangleFilled( 80, 80, 180, 125, COLOR_DARKERGRAY); + drawRectangleFilled( 85, 85, 175, 120, COLOR_DARKGRAY); + drawRectangleFilled( 90, 90, 170, 115, COLOR_MEDIUMGRAY); + drawRectangleFilled( 95, 95, 165, 110, COLOR_LIGHTGRAY); + drawRectangleFilled(100, 100, 160, 105, COLOR_PALEGRAY); + + // Draw some compound shapes + // --------------------------------------------------------------------- + drawProgressBar(70, 140, 75, 12, COLOR_BLACK, COLOR_MEDIUMGRAY, 78); + drawString(5, 144, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "Progress"); + drawString(155, 144, COLOR_BLACK, &dejaVuSans9ptFontInfo, "78%"); + drawRectangleFilled(0, 175, 239, 210, COLOR_DARKERGRAY); + drawButton(20, 180, 200, 25, &dejaVuSans9ptFontInfo, 7, "Click For Text Entry", false); + + // Wait for a valid touch event + // --------------------------------------------------------------------- + tsTouchData_t data; + tsTouchError_t error = -1; + bool success = false; + while(!success) + { + // Wait forever for a valid touch event to occur (ignoring faulty readings) + while (error) + { + // Only exit this loop when '0' is returned + error = tsWaitForEvent(&data, 0); + // printf("Error: %d X: %u Y: %u \r\n", error, data.x, data.y); + } + + // Reset error to an error state in case we got a valid reading, but it's not + // within the expected range + error = -1; + + // Check if the captured touch event is within the specified X/Y range + if (data.x > 20 && data.x < 220 && data.y > 180 && data.y < 205) + { + // Wait a few milliseconds then display the text input dialogue + systickDelay(100); + char* results = alphaShowDialogue(); + // At this point, results contains the text from the dialogue ... + // clear the screen and show the results + drawFill(COLOR_WHITE); + drawString(10, 10, COLOR_BLACK, &dejaVuSans9ptFontInfo, "You Entered:"); + drawString(10, 30, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, results); + drawString(10, 155, COLOR_BLACK, &dejaVuSans9ptFontInfo, "Thanks ... starting blinky!"); + // Setting success to true allow the code to move in to blinky + success = true; + } + } + + uint32_t currentSecond, lastSecond; + currentSecond = lastSecond = 0; + + while (1) + { + // Toggle LED once per second ... rollover = 136 years :) + currentSecond = systickGetSecondsActive(); + if (currentSecond != lastSecond) + { + lastSecond = currentSecond; + if (gpioGetValue(CFG_LED_PORT, CFG_LED_PIN) == CFG_LED_OFF) + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_ON); + } + else + { + gpioSetValue (CFG_LED_PORT, CFG_LED_PIN, CFG_LED_OFF); + } + } + } + + return 0; +} diff --git a/tools/examples/lcd/tft/oscilloscope/main.c b/tools/examples/lcd/tft/oscilloscope/main.c new file mode 100644 index 0000000..8c0f37b --- /dev/null +++ b/tools/examples/lcd/tft/oscilloscope/main.c @@ -0,0 +1,273 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/adc/adc.h" +#include "core/systick/systick.h" + +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/touchscreen.h" +#include "drivers/lcd/tft/fonts/dejavusans9.h" +#include "drivers/lcd/tft/fonts/dejavusansbold9.h" + +static uint8_t adcBuffer[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static uint8_t digBuffer[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +bool adcEnabled = true; +bool digEnabled = false; + +/**************************************************************************/ +/*! + Renders the frame around the data grid +*/ +/**************************************************************************/ +void renderLCDFrame(void) +{ + // Clear the screen + drawFill(COLOR_DARKGRAY); + + // Render V references + drawString(245, 27, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "3.5V"); + drawString(244, 26, COLOR_WHITE, &dejaVuSansBold9ptFontInfo, "3.5V"); + drawString(245, 195, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "0.0V"); + drawString(244, 194, COLOR_WHITE, &dejaVuSansBold9ptFontInfo, "0.0V"); + + // Div settings + drawString( 10, 10, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "~100ms/Div"); + drawString( 9, 9, COLOR_WHITE, &dejaVuSansBold9ptFontInfo, "~100ms/Div"); + drawString( 95, 10, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "500mV/Div"); + drawString( 94, 9, COLOR_WHITE, &dejaVuSansBold9ptFontInfo, "500mV/Div"); + + // Clear the ADC output level just in case + drawRectangleFilled(175, 5, 250, 18, COLOR_DARKGRAY); + + // Render the channel text + drawString( 25, 220, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "P1.4 (Analog)"); + drawString( 24, 219, adcEnabled ? COLOR_YELLOW : COLOR_MEDIUMGRAY, &dejaVuSansBold9ptFontInfo, "P1.4 (Analog)"); + drawString(135, 220, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "P2.0 (Digital)"); + drawString(134, 219, digEnabled ? COLOR_GREEN : COLOR_MEDIUMGRAY, &dejaVuSansBold9ptFontInfo, "P2.0 (Digital)"); + + // ADC Warning + drawString(245, 80, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, "Warning:"); + drawString(244, 79, COLOR_WHITE, &dejaVuSansBold9ptFontInfo, "Warning:"); + drawString(244, 95, COLOR_WHITE, &dejaVuSans9ptFontInfo, "ADC input"); + drawString(244, 110, COLOR_WHITE, &dejaVuSans9ptFontInfo, "is not 5.0V"); + drawString(244, 125, COLOR_WHITE, &dejaVuSans9ptFontInfo, "tolerant!"); +} + +/**************************************************************************/ +/*! + Converts the supplied 8-bit value to an approximate pixel height in + the data grid +*/ +/**************************************************************************/ +uint16_t adcValToPixel(uint8_t value) +{ + // Since the chart is 175 pixels high and 3.3V is at + // approximately 165 pixels height, we need to + // divide the 8 bit ADC readings by 1.545 (255/165) + // using fixed point math, and then substract + // the number from the bottom of the frame + uint16_t pixel = 0; + pixel = 200 - (value * 1000 / 1545); + return pixel; +} + +/**************************************************************************/ +/*! + Redraws the grid and renders the data points on the LCD +*/ +/**************************************************************************/ +void renderLCDGrid(void) +{ + if ((!adcEnabled) && (!digEnabled)) + { + return; + } + + // Redraw the grid + drawRectangle(9, 24, 236, 201, COLOR_LIGHTGRAY); + drawRectangleFilled(10, 25, 235, 200, COLOR_BLACK); + + // Horizontal lines + drawLine(10, 50, 235, 50, COLOR_DARKERGRAY); + drawLine(10, 75, 235, 75, COLOR_DARKERGRAY); + drawLine(10, 100, 235, 100, COLOR_DARKERGRAY); + drawLine(10, 125, 235, 125, COLOR_DARKERGRAY); + drawLine(10, 150, 235, 150, COLOR_DARKERGRAY); + drawLine(10, 175, 235, 175, COLOR_DARKERGRAY); + + // Vertical lines + drawLine( 35, 25, 35, 200, COLOR_DARKERGRAY); + drawLine( 60, 25, 60, 200, COLOR_DARKERGRAY); + drawLine( 85, 25, 85, 200, COLOR_DARKERGRAY); + drawLine(110, 25, 110, 200, COLOR_DARKERGRAY); + drawLine(135, 25, 135, 200, COLOR_DARKERGRAY); + drawLine(160, 25, 160, 200, COLOR_DARKERGRAY); + drawLine(185, 25, 185, 200, COLOR_DARKERGRAY); + drawLine(210, 25, 210, 200, COLOR_DARKERGRAY); + + // Render the individual data points + uint32_t counter; + for (counter = 0; counter < 9; counter++) + { + // Draw historical data + uint32_t arrayPosition = 9 - counter; + + // Draw analog data points (Yellow) + if (adcEnabled) + { + drawLine(10 + counter * 25, adcValToPixel(adcBuffer[arrayPosition]), 10 + (counter + 1) * 25, adcValToPixel(adcBuffer[arrayPosition - 1]), COLOR_YELLOW); + } + + // Draw digital data points (Green) + if (digEnabled) + { + drawLine(10 + counter * 25, adcValToPixel(digBuffer[arrayPosition]), 10 + (counter + 1) * 25, adcValToPixel(digBuffer[arrayPosition - 1]), COLOR_GREEN); + } + } + + // Render ADC value in text if present + if (adcEnabled) + { + char text[10]; + // Assuming 3.3V supply and 8-bit ADC values, 1 unit = 12.89mV (3300/256) + sprintf(text, "%u.%u V", ((adcBuffer[0] * 1289) / 100) / 1000, ((adcBuffer[0] * 1294) / 100) % 1000); + // Clear the previous text + drawRectangleFilled(175, 5, 250, 18, COLOR_DARKGRAY); + // Render the latest value in mV + drawString(180, 10, COLOR_BLACK, &dejaVuSansBold9ptFontInfo, text); + drawString(179, 9, COLOR_YELLOW, &dejaVuSansBold9ptFontInfo, text); + } +} + +/**************************************************************************/ +/*! + Shifts the buffer contents right one byte, and inserts the latest + value at the beginning. +*/ +/**************************************************************************/ +void addToBuffer(uint8_t *buffer, uint8_t value) +{ + uint32_t counter; + for (counter = 9; counter > 0; counter--) + { + buffer[counter] = buffer[counter - 1]; + } + + // Append the latest value + buffer[0] = value; +} + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + #if !defined CFG_TFTLCD + #error "CFG_TFTLCD must be enabled in projectconfig.h for this test" + #endif + #if defined CFG_INTERFACE + #error "CFG_INTERFACE must be disabled in projectconfig.h for this test (to save space)" + #endif + + // Configure cpu and mandatory peripherals + systemInit(); + + /* Set P2.0 to GPIO input (just in case) */ + gpioSetDir(2, 0, 0); + + /* Set P1.4/AD5 to analog input (only AD0..3 are configured by adcInit) */ + IOCON_PIO1_4 &= ~(IOCON_PIO1_4_ADMODE_MASK | + IOCON_PIO1_4_FUNC_MASK | + IOCON_PIO1_4_MODE_MASK); + IOCON_PIO1_4 |= (IOCON_PIO1_4_FUNC_AD5 & + IOCON_PIO1_4_ADMODE_ANALOG); + + // Rotate the screen and render the area around the data grid + lcdSetOrientation(LCD_ORIENTATION_LANDSCAPE); + renderLCDFrame(); + + tsTouchData_t touch; + + // Start reading + while (1) + { + // Wait up to 5ms for a touch event + tsTouchError_t error = tsWaitForEvent(&touch, 5); + if (!error) + { + if (touch.x > 25 && touch.x < 100 && touch.y > 210) + { + // Analog switch selected + adcEnabled = adcEnabled ? false : true; + } + if (touch.x > 125 && touch.x < 200 && touch.y > 210) + { + // Digital switch selected + digEnabled = digEnabled ? false : true; + } + // Refresh the frame + renderLCDFrame(); + } + + // Read pins + if (adcEnabled) + addToBuffer(adcBuffer, adcRead(5) / 4); // 10-bit value converted to 8-bits + if (digEnabled) + addToBuffer(digBuffer, gpioGetValue(2, 0) ? 0xFF : 0x00); + + // Update the LCD is required + renderLCDGrid(); + + // Note, this will actually mean the timing is ~100mS + the amount of + // time it took to get the readings and update the LCD + // A timer interrupt could be used to get much more accurate results, + // filling the buffer inside the IRQ and rendering the screen updates + // every x milliseconds + systickDelay(100); + } + + return 0; +} diff --git a/tools/examples/lcd/tft/oscilloscope/readme.txt b/tools/examples/lcd/tft/oscilloscope/readme.txt new file mode 100644 index 0000000..e50a9d2 --- /dev/null +++ b/tools/examples/lcd/tft/oscilloscope/readme.txt @@ -0,0 +1,38 @@ +OVERVIEW +============================================================ +This examples simulates a very crude oscilloscope, and +reads the values on an analog input pin (P1.4/Wakeup, which +can be configured as AD5) as well as a digital pin (P2.0). +The digital pin will simply be displayed as 'High' (3.3V) +or 'Low' (0V/GND). + +The last 10 readings are rendered in a data grid on the LCD, +with new readings added approximately every 100ms. + +This sample demonstrates the following features +============================================================ + +- Rotating the LCD orientation +- Rendering text with different colors and fonts +- Using the touch screen to enable or disable a feature + +WARNING +============================================================ +Please note that the ADC pins on the LPC1343 are NOT 5.0V +tolerant. Supply more than 3.3V to an ADC pin will +permanently damage the MCU. Digital I/O pins are 5.0V +tolerant and can safely test 5.0V logic. + +Pin Usage +============================================================ +This examples is based on the LPC1343 TFT LCD Stand-Alone +board, which has a very limited number of pins broken out. +P1.4 can be configure as GPIO, but also as WAKEUP and AD5. + +This pin is also available on the LPC1343 Reference Design +Base Base and is marked as WAKEUP on the PCB, but it has +a 10K pullup on it to allow the pin to function as a WAKEUP +source. + +For details on the differences between these boards, see +the schematics available in the ~/tools/schematics folder. \ No newline at end of file diff --git a/tools/examples/lcd/tft/touchscreen/main.c b/tools/examples/lcd/tft/touchscreen/main.c new file mode 100644 index 0000000..2c75e78 --- /dev/null +++ b/tools/examples/lcd/tft/touchscreen/main.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/gpio/gpio.h" +#include "core/adc/adc.h" +#include "core/systick/systick.h" + +#include "drivers/lcd/tft/lcd.h" +#include "drivers/lcd/tft/drawing.h" +#include "drivers/lcd/tft/touchscreen.h" +#include "drivers/lcd/tft/fonts/dejavusans9.h" +#include "drivers/lcd/tft/fonts/dejavusansbold9.h" + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + #if !defined CFG_TFTLCD + #error "CFG_TFTLCD must be enabled in projectconfig.h for this test" + #endif + + // Configure cpu and mandatory peripherals + systemInit(); + + tsTouchData_t touch; + tsTouchError_t error; + + // Start reading + while (1) + { + // Wait for a valid touch event + error = tsWaitForEvent(&touch, 0); + if (!error) + { + drawCircleFilled(touch.xlcd, touch.ylcd, 2, COLOR_WHITE); + } + } + + return 0; +} diff --git a/tools/examples/lcd/tft/touchscreen/readme.txt b/tools/examples/lcd/tft/touchscreen/readme.txt new file mode 100644 index 0000000..8860bc6 --- /dev/null +++ b/tools/examples/lcd/tft/touchscreen/readme.txt @@ -0,0 +1,11 @@ +OVERVIEW +============================================================ +This example continually samples the touch screen and +whenever a valid reading occurs draws a pixel at the +corresponding X/Y position. + +This sample demonstrates the following features +============================================================ + +- Waiting for a valid TS reading (ignoring X/Y mismatches) +- Getting the X/Y position from TS events diff --git a/tools/examples/readme.txt b/tools/examples/readme.txt new file mode 100644 index 0000000..d1685f3 --- /dev/null +++ b/tools/examples/readme.txt @@ -0,0 +1,4 @@ +This folder contains various examples showing how to use the LPC1343 to +accomplish certain tasks or how to use it with external devices, such as +communicating with the PC using USB HID, etc. + diff --git a/tools/examples/sensors/pn532/ISO14443A_ID/main.c b/tools/examples/sensors/pn532/ISO14443A_ID/main.c new file mode 100644 index 0000000..fb6066e --- /dev/null +++ b/tools/examples/sensors/pn532/ISO14443A_ID/main.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2010, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "drivers/sensors/pn532/pn532.h" +#include "drivers/sensors/pn532/pn532_drvr.h" + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. + + Note: CFG_INTERFACE is normally enabled by default. If you wish to + enable the blinking LED code in main, you will need to open + projectconfig.h, comment out "#define CFG_INTERFACE" and + rebuild the project. +*/ +/**************************************************************************/ +int main (void) +{ + #ifdef CFG_INTERFACE + #error "CFG_INTERFACE must be disabled in projectconfig.h for this demo" + #endif + #if !defined CFG_PRINTF_USBCDC + #error "CFG_PRINTF_USBCDC must be enabled in projectconfig.h for this demo" + #endif + + // Configure cpu and mandatory peripherals + systemInit(); + + // Wait 5 second for someone to open the USB connection for printf + systickDelay(5000); + + // Initialise the PN532 + pn532Init(); + + byte_t response[256]; + size_t responseLen; + pn532_error_t error; + + // Setup command to initialise a single ISO14443A target at 106kbps + byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS }; + + while (1) + { + printf("%s", CFG_PRINTF_NEWLINE); + printf("Wait for an ISO14443A card (Mifare Classic, etc.)%s", CFG_PRINTF_NEWLINE); + + // Send the command + error = pn532Write(abtCommand, sizeof(abtCommand)); + + // Wait until we get a response or an unexpected error message + do + { + error = pn532Read(response, &responseLen); + systickDelay(25); + } + #ifdef PN532_UART + while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + #endif + #ifdef PN532_SPI + while ((error == PN532_ERROR_RESPONSEBUFFEREMPTY) || (error = PN532_ERROR_SPIREADYSTATUSTIMEOUT)); + #endif + + // Print the card details if possible + if (!error) + { + /* Response for ISO14443A 106KBPS (Mifare Classic, etc.) + See UM0701-02 section 7.3.5 for more information + + byte Description + ------------- ------------------------------------------ + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID + + SENS_RES SEL_RES Manufacturer/Card Type NFCID Len + -------- ------- ----------------------- --------- + 00 04 08 NXP Mifare Classic 1K 4 bytes */ + + printf("%s", CFG_PRINTF_NEWLINE); + printf("%-12s: %d %s", "Tags Found", response[7], CFG_PRINTF_NEWLINE); + printf("%-12s: %02X %02X %s", "SENS_RES", response[9], response[10], CFG_PRINTF_NEWLINE); + printf("%-12s: %02X %s", "SEL_RES", response[11], CFG_PRINTF_NEWLINE); + printf("%-12s: ", "NFCID"); + size_t pos; + for (pos=0; pos < response[12]; pos++) + { + printf("%02x ", response[13 + pos]); + } + printf(CFG_PRINTF_NEWLINE); + } + else + { + // Oops .... something bad happened. Check 'error' + printf("Ooops! Error %02X %s", error, CFG_PRINTF_NEWLINE); + } + + // Wait at least one second before trying again + systickDelay(1000); + } +} diff --git a/tools/examples/sensors/pn532/ISO14443A_ID/readme.txt b/tools/examples/sensors/pn532/ISO14443A_ID/readme.txt new file mode 100644 index 0000000..020a1fd --- /dev/null +++ b/tools/examples/sensors/pn532/ISO14443A_ID/readme.txt @@ -0,0 +1,30 @@ +OVERVIEW +============================================================ +This example will wait for an ISO14443A card to enter +the RF field, and then try to determine the specific card +model (SENS_RES and SEL_RES) as well as the card's NFCID. + +All information will be sent to USBCDC by default, with the +PN532 breakout board connected via UART. + +HOW TO USE THIS EXAMPLE +============================================================ +1.) Connect the PN532 NFC Breakout Board to UART on the + LPC1343 as follows: + + PN532 LPC1343 + ----- ------- + GND GND + TXD RXD + RXD TXD + 3.3V 3.3V + +2.) Configure your terminal software to open the USB COM + port at 115K. + +3.) When the application starts, there is a 5 second delay + (to allow you time to connect via USB CDC), after which + point the device will wait for a single ISO14443A card + (Mifare Classic, etc.) and try to read it's ID. Once a + card is found, the reader will start looking again for a + card after a 1 second delay. diff --git a/tools/examples/sensors/tsl2561/main.c b/tools/examples/sensors/tsl2561/main.c new file mode 100644 index 0000000..5f2fda3 --- /dev/null +++ b/tools/examples/sensors/tsl2561/main.c @@ -0,0 +1,70 @@ +/**************************************************************************/ +/*! + @file main.c + @author K. Townsend (microBuilder.eu) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2011, microBuilder SARL + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include + +#include "projectconfig.h" +#include "sysinit.h" + +#include "core/systick/systick.h" +#include "drivers/sensors/tsl2561/tsl2561.h" + +/**************************************************************************/ +/*! + Main program entry point. After reset, normal code execution will + begin here. +*/ +/**************************************************************************/ +int main(void) +{ + // Configure cpu and mandatory peripherals + systemInit(); + + uint16_t lumMixed, lumInfrared; + + while (1) + { + // Read mixed and infrared luminosity from tsl2561 + tsl2561GetLuminosity(&lumMixed, &lumInfrared); + + // Display the readings + printf("Luminosity: \r\n Broadband (Visible + IR) - %d \r\n Infrared - %d \r\n", lumMixed, lumInfrared); + + // Wait 1 second between reading + systickDelay(1000); + } + + return 0; +} diff --git a/tools/examples/sensors/tsl2561/readme.txt b/tools/examples/sensors/tsl2561/readme.txt new file mode 100644 index 0000000..7c922bd --- /dev/null +++ b/tools/examples/sensors/tsl2561/readme.txt @@ -0,0 +1,2 @@ +Reads the mixed (visible + UV) and UV only luminosity levels +using the TSL2561 digital light sensor. \ No newline at end of file diff --git a/tools/lpcrc/Makefile b/tools/lpcrc/Makefile new file mode 100644 index 0000000..6957146 --- /dev/null +++ b/tools/lpcrc/Makefile @@ -0,0 +1,12 @@ +CC = gcc +LD = gcc +LDFLAGS = -Wall -O4 -std=c99 +EXES = lpcrc + +all: $(EXES) + +% : %.c + $(LD) $(LDFLAGS) -o $@ $< + +clean: + rm -f $(EXES) diff --git a/tools/lpcrc/bin/lpcrc-linux b/tools/lpcrc/bin/lpcrc-linux new file mode 100644 index 0000000000000000000000000000000000000000..a6c1aa4c7198bc4784d905bbe1814033684eec7b GIT binary patch literal 7384 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 hAs5qdNPBf6KHrq1jBTvZ*p82Npi +#include + +#define BLOCK_COUNT 7 +#define BLOCK_LENGTH 4 +#define BLOCK_TOTAL (BLOCK_COUNT*BLOCK_LENGTH) + +typedef unsigned char byte_t; + +int main(int argc, char *argv[]) +{ + FILE* pf; + byte_t buf[BLOCK_TOTAL]; + uint32_t crc = 0; + uint32_t block; + + // Check for required arguments + if (argc < 2) + { + printf("syntax: lpcrc \n"); + return 1; + } + + // Try to open the supplied firmware + if ((pf = fopen(argv[1],"rb+")) == NULL) + { + printf("error: could not open file [%s] with write access\n",argv[1]); + return 1; + } + + // Read out the data blocks used for crc calculation + if (fread(buf,1,BLOCK_TOTAL,pf) != BLOCK_TOTAL) + { + printf("error: could not read required bytes\n"); + fclose(pf); + return 1; + } + + // Compute the crc value + for (block=0; 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%0AQ6TRDov zl82rgbh`l=WH@pD2dSDc%rUDuzW9b9Mq#=yQK z2}WN7xYKqNmYEWwGER6ytTJr;jDn94kMBUrh5Jfs)NT9A_X>|h7l{@1A(2SKZw5&F z;#)Mzf>IBR-+uZSyj8W;BFE)4Sd#@fmN*DFzYMITFwPuyu(*DmCyUXRS`&Mj*9Xzv zLff8JX`^c>8akF2&xu@Br(5hkXFJN5equ|*RB*{(?8ncKH6AMW`2!$mBqN->Q{v~g z(_0Y0!>cEfzU6NjS+gS8>DJLuUa}%Sy|CWRIQo44!SFWM+aW2N;qwdriozYxZSe7GIKq6J#rMeVbGSYR_DKEd4>nWbuZzy8k_s5hy6LhFR z4L#*q$#VXBn4Bh^H%QthbltMmbY~f>S`I+3a5y+Oc6@DP;Iq-#cn%gZ5xN|=>dl*R zFS#QbQ+4NO(SATr8{1*HW*0uGp4%nxwW6mZc9{KWxC1XTX0AdS9mBus|+c7l_ zEpWb>woeSA;XXQ9#a%bX7>BX9R}Bj7Ioga;`J&eZM$`GCvFPn^ZgToDIfs^fZTN2K zYWj|A`~15S$B(250GS&lBay-5PLa*`?=Ra``G7<3OE2Iu^yt;g%r*9?6RO(BU|Bqk zGS(*$$yQzWUHaPr>jXyE?_`1Trxrzc;(9z0s1vVbG&izI3d}u0Ujh8EYy%XH>=COY zjiL(hnnAZ&3>K+xD@_7>b8L%R)(%M?DrTGlm_aAQp9u#rvE8DJ!-FmPs9z=>Jk+7J zf(@G=z|NR&3?FKms#N$DBeLp%eI(#3F+@$r$OPfpd_p2sTzTpE@vc;nTXf8HM<44L z-s^E~s4M9bC+1ev62GgZ+Ut=M4~+WX$4YtecMrqCJ5*9j52se`<>yvrKC%Y`<< zC3pwV1roeBEp)?0l8h4y3Md3f6i%J4*K+{~^#Ji7=dnBb!i&t7BIzj1Ltf&D=&PZ~sV6%v&E$VUY}G`-uIfb}DA|qNcaGazdtKqgym4jmws~B>z>i zs(d$$=^SJl2OL9jo`}iqCpTuzsr(t|bLCc})x!q_!oq18Ok4^$oB3FQK|lp9a2aK^ z2el{w_KG}|imL39>!hATng~@zWyz;~*qb`F$ZI8-y6o)bzoljO?g&vC_Mw=a4BnGd zCQi4=q1)Fo8QiUCYZ~}M8+QcKm`X-iK8jZMCo)kH&Cpb_3hgSX@EWacXYjY-4m8IV znPGmr0@j$%!a{of7z8+fc{?e%QT|g;vE@LCq}mVeQsR+5k=#nPVG$ppk#jCNlO4Rd zivxQ(ng5kBf#oFEeSA?lX*v087Ryjm(``DuSN>|0R-}OWy)}6foz1Y6*;PBechi*! z;~EA2vw87#C(P$6YdCG~&Du3XL~U>er}$LiFU#(4{;vFCnR4xS!yQ|Jy?bt@*rsyi zu8m~kcuWJj<(Mux(?G~mc=co7BKcLvH&u$8#TOf;aS5GS7d|h~iJ~QKTlmJ5`8h#X zVXRaK>K@~Bs;#1z6L?nFwh?O7rh++ddd{b)d5A0+N&2H0aD!naU2|^+rbnL~`>3?5 z!-Yh;=dk=FDTG4q@eT-OYf$U}_D zJ&g=&J^JrOD{uO4pCu7va!@M|5CZWdcY1MuSKH3gp~zJ-xcg0cb6mku?# z<@aEIv|=PDrq4z2gjh}F!a?k8tlAfL>$({9Z4TQNvvj?=Ae>sA2d5GOu{)4~=o7a@ zv*IZy!PM08`#5*ouj-kWwW49hmX5h?w-BGhsdj(t7bdpGAF#jut}d|g$wAq^RIlOY z$3H~a6IMTYN#te5$`XERSR7@0^~x=}WSjc!(N`^_pQ}PR7sTW2Pr%yk>=|qFxB@nl zsZzd4A-=`L1(;K0$M)ccW4t`BMHsVUz0;6kZn-#Gg6Od$hiUO|NcGV8aM5}t6z79u z9?Tx{x7FO2YnB!TO?JP%W`b8IY~-@;FCnHHkXhLBoo zzZ$j9ISFI97*;@O_aa~Ms=3%o32kknZKRvaKE%QgJjdM>>>`48FR$S3KEB4tb`#m9 z_=}SgmiKZ|yl+lta6F|!?zT8EX(3jK>hW8S`@Ex2?!BX#!>}kx;-@};n3H8xgp zD#Vq^25_@FCwVJrVvWlm)Vbor#M4ib^~N3(5)v;gaxGNW=s}rug**i%P9ZvOfoY5< z2>_QYtieSKKw5#%3}M@}2`X1di{I;%OIDuKCebxs!wlJ8x&a&QT~PZTNj@}S6!-(q zKk;q|qb0>e@(eOaT*}&XUyXWopZP{J;_qGMS8olYy>I=Z+R~> zr*BN(0ipz$I^0;c*~sIEdmP(^Y01hF+XME*h&%JnjnoLiKsL;?xzkQlc!tvo2)@oj z94FarMR-0@j&fu^A3sGSj>)Ik(Q136jLz=p=-5)zJX16zvnBTIBiN(vA-3_lk@-WT zar@QPqQW{;f&UTntvqOCE_WRBXzZJ(W=?T5?w{V=aZj)tBR+A?F@Z18Lo!)59^&$s z*Y|yx9xNFKh>Tn8-NN#I#oJN~lo$2a&PDf$8}rj(4~>7G_&L?d0zznOtbSadfYvW1ik2|f;Usxrke6wUj4L%mkKZ+|JgCLcHL^kIo z(9to{LqSgI9BvC!*t`;yaM`|v@~s*FLb0=SRg!DgthLGepY)9a!Sk>YkeZvicW!4> z@lU9lq|kaFq)*^#eT`o51xW`xE2PP3%g0|y;K&8c5=?@_9|1t93fJKwq=1R-8(LL-|@z`S3kVY-<$M&E@ z+s*y&rp-JJB{Tb4Z=MKv?~-JSBdcKjcfDy*Oot;Z-eQwNA1jEH%ncc6$cs+^dJ(V? zBW0L}%5CL1;4z5-B3-Z7M1I}#2`8+(>U8L`M`JiG)4rv469f{nc!5O6Ujm68p!Zb@ ztB!@dAPYcVK*SMOqhNfU2AbeeF^%FWq`}R;Gdr*2WajrUGdPg7L6rSBo<06BcbW4o z0s#PuciAGH5Y>i{{9sQRoU?XnCm(eAf_)7}g#|wdu&d*0Qgi!ryuce77;t4u{*QSSfKo$zAK~NeGAv|&cz&s z^o_NJoKmEbM+4(6VW58)fwBw>cZmd+xPR!q^R0RHSQ#9)k* z7y+j?5QqZ*n$S-o0+T8*{kmNY%d zP0U)Pic>}0KqM}s4xLul+MUf@Zm1oPjJYKQGh$A(t6?)A=n}C=_}S4a?l3k^CV8>{ zr)JLV3r=v?FzBwoPqij}qjl<`RJVu0=x05xf5n?}_H%Ar^U2O0`b_nfFI1eOvN)&ZsmQQlp9F(%B>gwTl5aa?ZA#*%kF=ESA(=|CK*RM}uWmaRW`@|;B7H`a zg%8oizxhI`O8PdAQ<3zgOeC&``VIetXC?qrW#BRN01lMH;F#xk*I@Q$L3NiIy^o5g zb?3SL?0WyIq0t!o6V={;xzD5TxHJVy3w+{LW5xOJw~r)tyEX6@O=Z8PHnasjP7d|+ z%gFY6b}uqu0>RPG=u-J_&53W!g~ktCOhz;ckUz}f3*Gi{Qg7+J;~(MRl?!Td^SA_O zt)au?2yDSdp;&OEaI|EhH#o{h&9U3fYe8VOiH*Lc!bn)Oau~d58xAeN#&6Ern01L6 zoS>&5kF=t*(OurS*1#ZI@pM5$o^Lh-$no+L%DLU;>r$MzS_;5z;t6_+eX5)tK||`N zz9ImQpQtI9Ol1Drko}V!mSx*|w7XJxzMXXItdI&Q_2yv9$6VG zp&vTGwfoh%lF60}7>0J@NYJn)&Q;)qmS8R$v2&)u4YfGI3Q_1r zFB{|ET}(ODs=DXMW^R8z!b6D0yV?7{eX*JEmI`qs_#?zh*K^)9DCWS(bFbdCn$fAa zqCK`U#joo;c&=4;~JT8M_u~eYzY0oe1o4P&x`=uMMi!798IBNu@ zfUhH~zQv`QTB?rH-E=#$FBrJy13e!Mtg5*gp4u#REAyxud1S;TSWKadYAAJ={(Y|Ab=+Ypp{C-F!3MuRs! z)zG%eU|+FHdxHR~>dtAJYC!_{Y5bV~X6;zIULTiNP(yC}y?;@6_VkKNrnjL?+>G~z_a zh|qZbuF!2WZ)E{n^Cz-s77b3{Tl*S^n*8ydmJ=00^vu5}y@A<12QibU$3{_U+md1W z+4s^s6RlqhSPlbqsEeTGXRS^*d(;=KAeRkDH8EBOQ%x|FxxX+pNYKtQ<#Gg1kwBBj zZ=o(oV}?Tu!y9lh)z&vGEbhD863c7@g6p+N)BA>Ht6vN)1zZe3-u%XPZuB?6Ob(FW|JFSbEc7VLB+ez z=DR=}xHAIAUuEIJxeW0jQT{{|N6d#D%e!_oh#I_X4rb&%g??aG?nsv~^pby`tcbEB zt%#_E-}pQ*8`y2gs=wlydE}z${k?22%`>AM&`;j> zlARCcjjP9H8Zh$DIImCWR`|oxaygDvY+7guW#5PMgmj=unW6duPbw58P&M|FO)u~_ z$`hEKi-mg5S{60CE~NkjJ!rrTaAPpoFz=ymah@WNw&l3oU}|+#l{O)mx}2dHp|THC zdt=Hl2Q;!LCp0n~*Fz`Xaa-9Iokp0>;gwRL{A^Mq_w>sr=bnsv%ciVl$CHk`40mtD z1qiXCyzt-w)}%b63tvUM`O%#CfPUwwActl{dCxsKNMRhhXb}ZP$4YwzuN)V<<#b`f zgQ^!Fv{~qbo^x85k=G`gey-Nkx8kpOa8|JU7T&eVFg3`*r_gG`R8HJ{#vj#3_Nw{r zMXHS0TEev}2^iGGX!0^jkMrllax)|KM=o>fZ>DT*S*$M90a0iSsAN;@D0 zUPA*?eb*F)!|K9yxIa+9o}or{Hr=_?G8N`)|sLr2DP%;>MfIHVf zM)f7B#>vWy>os}oRv5oqY0crmF{NdPd2Mn=*#svv9>Du#>2v=piV=3;6?2ePr7-K1W5N&wL?Vrfh3W| z)48)}&mk~U$UMQLbU-}mj^-Izxifa6@u1*zfT1}p*cG}=dG#aaNoG} zVB|T5O(&dsb!Ql?4LCtajs41dUO0p%5ZfnzCF^t0D5$xN0?T%N)}Tci(lsGIpR%U# z(V9_>C)u@Y5-eK>*Fr{;!XnsvTW>H!+0%1LLpR5GE61C}7pPPlY8baGBUTf^kJ6^AFRI}=A zH-33V?}XLibWpwbS@Z&HMc{TPG@MZAy)e0SGVtPyzkzgA8FK~<8Zc^lUQg0tyN;cBUSAC{P!N*)d= z?gkDV$sUNxk}R|B-8YDsJpoeA#2$domD^bd)k5+JFn^Z*yo%$k@TXe?X*gM$(fjYVe`~&4APB3P zeiAAe3J{n8nD4PSg~!wO=o2Qg$wOU|h_C*o&W1e^qbU$80n-X!mjd$Th2;I+(D3I?i?=!{>WFx-_-7yd> zUi{v0rHa^9$7TLiB?BR0gef0*)Ju5nJEW+}m?=d4L z#GKAg{5(kKXA0)eq5gG0K;E$N5G0bL#o?ZXliNSKY-hMY|ANM8NCN(Z_5{u&`$>N{ z25!@ei9!Ec4ru#4PQ?Ye^3RHLA4qN{ny-Nc6lP#GEN*UJRs6eI9<=clC$>GNa61t! zC5Xhylfhr7-3sv)``qfnC-&3O`~;Bb53BL2Jp3(@}0;{J}; z%Bq$oQEifeA)tOim+uCGJO6G6EGnuezW+d`>;cL^)#^`WG zLoO!`s2dL?FD!PNnn&FghTpmm+lt1#2$3+G2}7%j&6bzZa%BLewGdQE$1gUK_})w9Hl9RHv- zg^gD*yb_u^hcFdh-~w}%Rk_6ffd_6>sxQQb4O_+MNa4ky(?tbZbku3^7T;(GumcOY zM&XQ3NA?Fo;HBPGQtuv3);Ld_DMuI2zD69lW)l z=%^Lx$9G$ze$#4FwqO5P6JZ1vP3P~<9Zu1{3aA!>PAmrT3$GJX`b#GUJ<3fn|2Uev zJ)5`ljhChtYMC;x6w}uCYyj9YBOEzG5&Lv+YiB2mxhK<^TlI>3BJTMYW`Uoc2wmT< zaNqH$ATDa`NCOADy+!1FSbQJEW;40!wa0}XD$@K1g~;CCSO*)~Ga5v-nj*XLnrS&M zE5ffS0h-F)jXmQRwT0_eUPn)X(|<023ILAg!J>c+DY|(k*ljBigkcaCX>B6czg8H zXPWzdjK&S2#Rf))3>c`UD@HzU%Y?^Gv)_6(X|7~lA4D4U+MoOOK-W@_3dkEi%$HVT z0)1;GgF1hnM||_D`8w0rYCymZMw)%jFJ1`qJX4?udcoSOe5j8=RlWICFCXA;vGSz& z^24^*2jq!*ZLn`pvBlEI=ipO@2laTcHO_26Q!rO*>+?9%Jor98$tO(p82)12djT>`(@mx^7Iv7_4fK}VR_;Gh=JyIn@G3XEf4>P*a zEm&Liqfu_Z8^r79f{|u_Zu^XS-$2^M(09zXGz}w!G8KHHK*LKUlHr~Q|6wLs^t}~d zp#&V=Toh%+R*}Kp`nur>nsN`n`V$Cj1L2A4gu)-3+g@HbtPTCu|9ES;FS`%ZX1q7E zSaqpvNK>Ic4!CdWFq_Bh1Ul`M!I-XvZ4c*VtNrZB%N2(|?Qf#9i+^?)RtZtK^$A9f zU^vi5ZNA%fj?pc)6(4*@2=00bzAU5UdWPZ;OWz<1ma0A)OV>*+O|#rKckTn-bmw4T zZj7zr-r8cN9~uI@$`bg&k+U~a!J4Df)|Y;4gC(ud<@a(r<=Zm)-E+Zw1ws%x6MXJ8 zXiZ)Vl0Jir`GdF@}L9PfX78ZA7l}VKZ{wI?J}i)?uTt~RcNax==$r%jfC-DI$etL9I+j8 z!S1c3imuN{#PNb39K1sE1ofzszA(ANrIhxKZ-tN817=iJcSOQdrBcjL2UE|@clr6{ zh^hC?i6l4C*fN(DA3?{91jpj_F7oV@xbvr~=Yo!Zy?!wd{&~D!Vs*XJ*REE$IC=4& zww^vr=!6ZH>>V=(#x~v?&JxqKi$BMES$kWL65e!ami3a@Y3nqe12aL!7TuRLtc%<1 zBJ|A}{q6;ikC<)#mLN=1u-Bs@l_aS>#GVB=xP=Dc7Kd-j!6(o8Ct$;4RT+{5cNQ<4 z)g98sR*t?kL#+&fI;g$%21Om#q|BNQ3pi`&awG|Ok4T!rZD#=fS@(xg{_Dm5)wpg} zmf@NKB}wNI`_xB6eUi&V*$>bW)!T$2KUMwktzgOSE1xrN6og5`a;}DY>41@V`K!uc z{qrO7z0(^EgxC0DF{H&ta}`gO!cN7z@N6jmnMr@{*e_(;AIGaJVmWg=gSBK4Zr3=^x6S};Do#+Z^<+;}#9x33e zA~4w91vk2a!Isqzdias?<7d2;3P0fP|K)u5VrQC{utuKoXiw~x6JA|eW9`HSa}-$%Uvt$s9ESygrf z#-oW{q(2Dk{|qbqlXqD52AhLIm%4uUXYXWw8|+@4BfILraEsZ+WjY5ARXcaPh}jCR zQD2jIYSX=U%$KpA9lmO!c1b(JH>R8X6K%sfB7ACTBwmm9O(wmy1MAfAm@?_2_cKZJ z{=pl|+uhrPStbY>mEE%74ha^Lb%zS}?|0LhA9H&qGkDp}47)chepq-OrFl<+Gw3GX zU{3Yx(l;+vq1WOz)eYEawk3YEp|IWEL&|od`jgt0(zhp0dAkKDVNb{GxU~1*+>1ZI zUr{m7=(+8QHmWOd)uJTW*Z8N;-c@JMDKp*XdZl$1aj{U;_u<2UNY>2cI9JsE{6L%J zrroohbr-DonujQrJtn9&bf!wdih!TIp49V?%wA8GpGm^*gx8y^VCM;&k7j zr1?V8mP+#CLSa?g-j~9~8_Y9-T$@c9gvWd=`9p3^gfwRT+(q4J%F|!GoGVv0T7UNj zD~Bgb3wNVVAf^~XRU?0SDus+KIExDwdOhb*g8#Pnz$tCPB3o_x+pCF5*z2gTkuFmY zCIU;Zr8d8y*w&Bz?Ak})jYnulv`kCN^FlT3PL@>knK&-SPJPlRyK>rLcyBQI(!Sq< zq?71r?N!6Z!4KcVCJkQj*3r*)|HMx|sV@_oD-)3m=%+_AKUUW8FX%tM%5Ha#m$RsF zx3%`3{YsL1_3{{*=a^9J=|0pWv$$+#xAz~o_!5&h#iuS*?e6T56@_KZi(R^c!`2R2 ziAuu~wwRD2rb7eH7lTc_-P|IzbsGLU7ayPJ$i&GHS~1V?L1Vtwo>7&bxWtg zuBG*Muh6Uf#MX$us-o^odZXLZo4R7{eHtqA03%|3#R#3b$ooxOqufV#HB;}yi}K%d za1!gwnL;x(ultp1R9;Y6M%kvcNlb5?@NM_>#LgZC4~&_SmV*=e(o-b**wSN;H3x4( zEyhnt!*J;3W1_NX>cb_`)OZGnH6stsx8%^el7B5y+>vqxxa`r+Z5?J;fXCR$1RhlMjaq_j-Sh6HUS zd>lF=qTy?Gr6h9_*0H_C<3-B0a`1A(q)4*x=_*-BY7L!{t{As*LriV*+UFxDj5Q0( z^7I$1byWsR_>52n60`ka(I z4AnQnR=jq9Tx6{fM$(#Y;M1gWF|7GkGpQne+?U9MBasP_BP6QNwM9ddnmpHR_l@eJ zN?1(Vv8}`CnovWpw>*)C275<4I7Um>x6DZuc_a!8wSFk-w=RSd{5{hrt<)ZrAokK+ zl7Z^?LhI@X7ut*W~XgrA~z;2y~6SVwc>EzDZ4ZkD0iH z_bP@TX3NWwGP22S)1~$#FMRc5=6-N8ux69oV18cI7Jey$6Hn0kH4Zq(N%2p3 z4C+4JeAPoase6FlAUjaei1LZeLvAko3h-sC3a}IZ2jv-1&Qb`{m!5$iDTiF3nygp z)Ph9&gl2K&p8Ke~W9w)X5k*)=Sw^-8ul>0ycL`xliDNH%4Ho{)_}edjsYBh$r9JL*v15l< z>NWFkOehh1S-0ToSMt3^-yMSkc#35xw;$0R)2y#xZ%``yIAKVDFJSX@0_XkjdWgzJ zJX+p>>vs?9AC`D=>(J&uU>~=aC`;wzqXzYt`v{c9zsgDdGBwLZx{bZRSeN7W^8>F{ z{GGi{I(doC4s3!5y(UbmuO0!?vf8SO4zY}+PC}+z9at(gQM#a$JbvJp{5rZI_m^Oc z6(o1P{pkQYz>c>k2E$-S4R6WGYWxk0LVB6q3-^jrBb0tD%#aOzMjV$a# z@1POx(W=6!O%zqU?(pmc6HU<@$L9_P#@F{eo<&B)A7v-kFuPdaIC%kYG%%J|a>uW^ z{|t$UH-0vR-sc|GX!om#qA|Z2D=y~N_G#kx^!gcwoi?oVm{8*Z*~ekA?ZaeyvM4Yq zsopj{cfu@Hj;60X)^gPN^V*8P?fbUAn4C2S%E!GXuISXn{3-pYGt8081SblcJ2%7p zXlya~MtfNC@1k;R6Yfo3UxdkoMW0ws2WgQ3p`&C>>w}uG&*$Av3L}$hF`>oBIZ7f3 zal!B9^$UFL0|l%PYaW+Qf6@IzLr!c?KDUk*mb!cJc7-U*-+kFFw6RQD<;M)-KUkT(L1^7i7!7Atjydx(+o!o_ z&3pCHm-N}V1`Yp!R<@5{H2l%6Y$ELv{v65|q5q^GOgR`fA@AY&j!BA>VXEP27xKDR0Z;y-i?XkqAiMU-D0?%DFMOOO z@|X*s>9~e|cZ})FuO^R!6CP~Ca7CO_xp z32~;^*c2k?O$qEggB0 zPIGAdaJvv-tSUq!X~}=@caMuKA44uPQdsGTiyof58t_)kaM_4AS+)QJ+P!6{^}AcmBULZ< z*RQI)cMtvQsuZ|0)x_^X->t!Emqa z0FDH6ziVha#EWnPgVVwQAmoH+C)vMLKYzEuknOtsvOF(%gvvU_SE`(s$0djua)l7e zA3IW_PrQAidi%1F&OI55<0iTZm&{HrJj|1kUnc#)PI>%!@%VGBp|;4-QZAS!3*$tT z91hrjgkR;rR&86nC$-PA$A5n9Xij}Xf+sWl8qF(k04XKC9fif Jx^CqEzW~~BrpN#Q literal 0 HcmV?d00001 diff --git a/tools/schematics/LPC1343_BaseBoard_v1.6.png b/tools/schematics/LPC1343_BaseBoard_v1.6.png new file mode 100644 index 0000000000000000000000000000000000000000..cace69a5118265f70c326eb6f739a39a2ed37375 GIT binary patch literal 86252 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�gO7-LNEL&v(rwlM}vjvkLHf^>Kz8 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 MHTA36mrZ^D5AsBYX#fBK literal 0 HcmV?d00001 diff --git a/tools/schematics/LPC1343_StandAloneLCD_v1.3.png b/tools/schematics/LPC1343_StandAloneLCD_v1.3.png new file mode 100644 index 0000000000000000000000000000000000000000..b820f3f19f5af37d791a9e6a8e7378f5c5137a7d GIT binary patch literal 85012 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

5ntrhdc;Is~ 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 literal 0 HcmV?d00001 diff --git a/tools/testfirmware/blinky.bin b/tools/testfirmware/blinky.bin new file mode 100644 index 0000000000000000000000000000000000000000..bacaf86f07a6b25d415b029e9b2eff02f6ff7840 GIT binary patch literal 6412 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 literal 0 HcmV?d00001 diff --git a/tools/testfirmware/uartcli.bin b/tools/testfirmware/uartcli.bin new file mode 100644 index 0000000000000000000000000000000000000000..263ef63d6aea75ba293619585e526ab8c5c35f83 GIT binary patch literal 9240 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 RWw@tczk7h=oVbGk{09*i=`H{O literal 0 HcmV?d00001 diff --git a/tools/testfirmware/usbcli.bin b/tools/testfirmware/usbcli.bin new file mode 100644 index 0000000000000000000000000000000000000000..652047ca4b51b65a9539ffe72e00bdf1c80e4248 GIT binary patch literal 13026 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 literal 0 HcmV?d00001 diff --git a/tools/wsbridge/v0.50/Exe/Linux/wsbridge b/tools/wsbridge/v0.50/Exe/Linux/wsbridge new file mode 100644 index 0000000000000000000000000000000000000000..d77c29756a5d639702bc3858fa37dfc9b50e8055 GIT binary patch literal 13509 zcmeHOe{fXCecwBs;A}7wKP_ww5BspeH75Zw1&nP#IxHLvgda9GwjZavce*R5yX)OM z46WS=pNWx$3U!l=Cv{V>spFZX4wEu687Hx@T(BpdT4b0cO*$!UadJe|RXZxCvBUNA z*?lWLJ-c=KXJ@pqZ};>4e)qfI{l49O_uhW>g~0_t>RCd zBgDjS1%k|*(BMTd*)REi6bASPc+CC4%;d8V<`%SRLEp^Hz|5NW`SuW_#LRpH8*br{fFLsZ4QCxR4DmRy>#{`S)$uhMKaII%|;Gb{aB$L;m0`8&~bj z-~YWgcPy;AaJ&89)!jFqf(yh9WbK~`sIx8UOw@4=GId;wJPCOUGW(@}+1Etm&r0+k zQ&1K1O5cx1zRpEsl{Qaz(b$$W+9>bWEyB<;3vN0%>#sidFTG%mN=X-@y)Sw2N)P_J z2Wx+jY#=|(ij%1C2O6Lx{*DKiJoruzKIFj*JUHsX-}c}aJ-Ee#n>={02RC@|0pOj- z13}*J$zrCs0p&LjX^gvVnr-dbj9IXwdD}DvreloLaH%lYZ3H!*%9zE16&Hy_x>!gG zYfs7+IV+#f=0zdZmWifCu4osa1{z|!%vf@lnTVz&HI|I#MJ}Jp*a`5_CX+SO*;v$0 zWi!H#6)Zby+j)_Ox3YItEtSGl zt`cWamvo8!;%Aa8#31&KahSmLb2TS0%JEPXV2Lh+EV0lgvc!$3WeNFdEV0n)SYpA< zVu^)hu*BjFvBct>&k_sx4whJy4J=_%BTFpYWh}9{SF(hGt5{;8H?zbfZefWVtO>Whj{QR2=nOKWC!tiiRt0w0pfm%>FMNQ;$GlI|N406Kb0;FZrZvzIrzum z2rRtMKbTx~Fc2)yfxe-`qmpxfI_{1`jVRuGG>P(!I(UEIz%*P>edI3<_?G{qFuS`4 zeTY)8?`4WqrtK$NiZB03zI?ix3Vfxv13Mn;>$Yzd-QT^9^3Y$pRCjb1WcT;lQ%;hG z$~9*KA<`=S@Ae&*GJCor!OqAr>nJC4XJlwm@0sRMfX|Dr$g$2qsO-n+IwL1aPo1a~ zXPi8KC=l%0cC2%2C_v_y!5ny$E&R1YbVp8-IQW}Nr7P0gap7ubq!*Ra`8RdtJ*VhFJ($cK z30h5-1`m~<8mbgSoE4SFu!~pB-FY5%Z(6bdjj1a?3cH~t4Db$U8xk= z@)BF_cD4L9>K;RM1ADftb#0pnKfzr=?XGcX10OrhmYsD{lY4u2WRTP&XZIFl4E-doOK{_j{Vcn?uZmfV29~KH8Ru{xzHIItm%oI>x^7tQR?+~ zM#>{Ykj8!A@xE(R-xx)b_P#KRB<$}@a#?fd5ncIVEs3r=;ulX#gDrz0PVf%PNe)kkpGX(y)yM?DjLjj%sM zTSw$WAI$i0MdU*M8^3icIh2~$rz?xAUxHbQ_=Y1{*Af)yd zy6A;2xGE6h3@rcZztnVeXmWM^Xf%yKO3QD7rf%JZ!O|t8{2Fdn>8YT7v}fLzf7 zw}bux=~IT)O@q2}E2c15*Zv7uAENny@31rhYJ&#sr}~cLO8rCNW80cmdNMRk{Lzn3 zH;3kki}z#XH9ggT$@|vRi#>Nz>58xBnWxeA;w;I7SWQm{mArI}6Rrx{*_uuk>> zV-~rLdG(6hn9u071Ea!9z}2z3rss#HU-_2z7jIAoiPP1uz&anD4a>?u{Seb{r{md$ z=n5L01I?k?=6_y#d)AJ} z&Byw>W`+7>fBgM+l<%>au=dn9SW6r(4FoFvT~(aJLnZmpAC{F*4E)x66xNhqgf}0? z?>cOD@;7e5%BP}flRvOm7~3+@*0g2VS%cV^R|sD#BK(%PG#DROtjsK8@W`@ z5}K9GWUQEEqM?g_Z4-XGvkj%gVi>9}ja)WwgSth&Kp3%T&MxMmI+}?aacg%fW)+M= zvS=Ie>=T)=*dpzejf_mzmIhk!(e#+I1Z3l(Xi8d?fbCDD@>T(_A-fDm0Iwcl3}0;I zAV;|D*^!_N_?D73@Zw_S6VaHp0`9n0)Z6X3`r#>Ory8&J%^!d@9~%87ny`y5OF$ zXiBH7jIA|dWYDQRu^!#tZOm&}ydC~!fg3ZH#yl|QfiVw^d0@-~V;&guz?cWdJTT^g zF%OJ+;Qy}&@D7iUv^XZ*j$iG0oj64qU0$L~i$8=v)(+Xq$w$@_CE*g#D#_*zrjYWx;El?QeXJ|xU zsMBqX8G3G(SY76#@QYSlyOE2?o-QhUWEv>iG ziF$cx8r~x6TZ^f5TwUY!RvS+y7cLGjY6vf?j}?kMq&HS?F&2jxHwa%xr|f|@E8ts# ztM4gXm>L-;fxH8Czb}|&;U90X2#lxMC>~?s8~6Jx{MGlOs0vi$Lxu1MtDhk;IDR{e zKsCDw1ja*n4{FuxaPiH2me=Yko&b;IcPinVENaJ1_D%6s1#b+_3r^+S^G){6n>2Mo z)ubCIkyDF#=nshr6Yc>C{W(?KqJXMGd!wkDv|_?t6V^?=ccLHjb=<9D9jDtv$bPYy zN_`(qx(-(v9|F(%KEX|F)EWU2Z%c!=) zKpuWOc8thUkq8&|w%gHGly+W~NnHcq%I8EFpSi5?>NV>Y+R-)#wPlK7IhKW~xCj#^ zqd1Tc$M>JzQ99vH!#7=TWQLxN-k}SFf9~e+uDO`0syRRJZeWp*h-p- zJU(ZMaLmr;3+O-pjyF8@}B;dmm49+iNxhBgOEFhI`?xX$}u-1Os_kPOpd9W3gozO{JP)J?e-ycNgGl6n^ew+Z{b3-S}GI5K;mMZ(@ zzW-Gia|dX;U$$r7h0JFK>wI3k1Gxs&-TeZJKR~_)nR0yIT!9?z(fR=8bEkw-%klZd z2OXbVl;g8d%N<0Wwvx{0xW!Fiq90CbiZE%fpQ%0tB`vYa)&(qvdt^VT5cK6OqSt+taXsD>&G2A z-a8qmHb?o3GE7?Tb=0Ykay;;#_CLsd-zCR`{aKLHI=~vP_n}T7Q=g1~gIquAyuVCr zLpS{`vhJ6$E`y( zAw~-oILUBI&T;C~bA)oYI6M~QF@NBcC}ip|guwsdq^HDhK%u-&M+_n6X@FAmu0uuF z$^IKdh<{_nNt!p)BX?phetBpilv?gKkKELog?Rc?gW<`=G*{!D_!#Sj}Gjd$vl>AhmxfK1ZSUDTVp#8uJYM zhr--x)xM+fM2We-D9oJ-{=hz>Fn$stalcR)n*kE{1%>H3#AsptE6ls1*15t{B<6Zn zSUsV*ZWZPaMIqOx!s^#Q*P+6U@>K5u3iH>$5L{OZ*GbIvqwq|L^`%Ni1Zu?l!>8s* zsv5NelFwgUBdJQv9s#IT2`qY7suITV{3bad{=Fg%ab$=&V(xn+`4DwE7p3-x4^QlR zVC@ed-f|e@xytdsACaq@fn~%KN*`hzqbL~{#hF8#MR~*}@9qB&!LN1oe*jqDFCSLX zSAgC9zXZJVkg}Kkp9I#4<9QQ+VM40^I&lV=VGA1n9dHM*fs*$9808UQE;Qov#5io` z${_wFN*>y4%zcb!{#;7!0>-@KoAeG(4=>-9ecQPz*SLBEW25|dG!MkQzLex>m$9$w=vlZqn=^w+GbP6WS~U9f?)_|YcU~WseDXU@^N;K z_-I+A5l)TQY=lZDQfbRnQPZwa5Fx`jq)JnUepo89T8}ar&pQM)5DylJoItmGZL44+ zcASRb)f!dXi5Eu|ubk$6>o=}hz24locI}qPR&(p>HR~gEG?{o=%t!Z{Rwh20AU_TW zv-#l-s~=d`B>1wa;=ds_I=Xz-Djf#iAmhi~*a;lRcNtT@@H#3~FuPlDB<9>r^~B4Y z_wP10TWzTV-rJkf(Lw?5v}Ph_CZ9mT_i04O^HYFZg><=_@QGMVBn-Q=<-$6t0mJO+OSlNKvF;Y11O8 zgH-Bo-tHZbrtBo>e?j-SvorJN&CHwkX7;`1#DgEBC=toHZ{8$&8Be|j1blZ;Lvwoj zAEfDv$#3p>SsMT5j@bp%Q!B1B?`maL*KFJI)uN%gRa-S}b!2izEjuM6-`1Ae8LFNd zB^sBa^n<_LJW;oHogz|;lpyK^2M@mf;t+s}djK~B6^}^ZH!$HpKdFWre7>SI@ByL? z;&1gCl7R>vQ`Uo0X*EZZu*8tH_6DiP_IIg(Z zw+O{(X&2E4(?k+z+-&U^y0-Ky^f`3IUxp%2o1Hf#{IwO-gk$!13E}D427_L$Fc~W**9GSPgP+JMa#rUCF5|kGYj|$aaN#A>P&2 z`xBDH!hV)^eTA~HgbDf=x}Bsx#yk3>^c*;l&Cro7Fj`@)t%*^vuwa6WI}%4rFs>c2 z@nIBDC~yjkR`(@f~@JeQGi#W>I+YDVX}d8d+hNyB>H$xGo4WmaYr+m6s0V+*NCX zB=y$ga3@@^ul}Qde&goN-pSBSEHC-`)l?33S80hWH=;p?w2+d+Y~rtXC%9qWWa`|` zP=5mUge6+x2NLABcN6hE9Yr9ZGeawELBvbwZi)A8B$eZ~+Hm1Qs`s`arh*BQ6zJK~ zAEB$z1K|v*-}>4YK9kziaz|@tTW5!?@(ZFPrhX0W9SW`R(o>vWJe2~EJHOJv6HR#1 z$u;;Hxv4`Y)=Y$|=vwD4a-VIi^&I9CmXL06k0hin7q*9%sZ#>k0)Ho&B`)Dcq}-%6 z+DtUt4o$zCwZN7{cOMRHZRwZkHQ2$T&(PYmTH&<1&>?4Jwf4-jbm^hijGPG22L*e3 z>e54Pkxds;krol}#zN+)N+gwd4q6rBayX88;?M?bn%l2mb?)K_zO%FIbyB^KUQ+BE zaz{K9@B7?(solXCf0ec@1!MfTFpEWX3W^WH9!{`>#f`N6v(>rba=a@nV!NMKdD&e5 z2xoDP6S7mJX=jbc5G>l=+akF==(Brh=DkCbc*_Luti}EL{`{f-!}|~OM8?PhJPmC< zD@0$!a}&bTGvm9aJ@2u^H&6h6iVDHO9RC4taSqyzlEg$QMFBWDN$A*dAF4LHavj*woWz_A?5@RI`&JQX^IO@TtgfGK*K zR)BwrK2K@-HoXd%mtF;DVu^J%FGrt=D)d(ZN;3P91x(Wcne840R1iDnKMa^6OJ?6bF0*f+6s=zut^X?E zTY!88qYCC^19j1MjGd_{8j!z;P6oQ}ubmcfirRrcB;XumGxS@u6Yv$tX6QQY0sJ2I z1AZU!8Tw}de?$j?|CkN~{#5jeOYZ@`MPmL==>#dt=cRT!SNmP*FyL1Kr&RXtD%~Xw z!W)$*4Agt2Q<&4&dIG8_ox#%Eurr{1X&Tg@hSUe7bD;L19CN?lkRHYyZ$Qyv>eG-+ zQbkZtZhAq|L5&A(o6<3E?xO#SK0&`vN%={@4gojI zAEvKTUj8UrUyyzia9GyqJ2WkSn%*Qs{w!?<|3&a01AjBs0C#}%61@fZW$<5?|A-{% z4fz$ozmZ=9=lk;C3Mde*^p-u>0Qv{*Qp~7qC_O521gDfJtcPILgSM z1b!QFx6&Pm-3DY^0z1ulz;ST$)I!^&JaR7wxJAGo0ec1i0CFf#?-88)1e~Fh;OJBV zyiAV+dh{gV`{<+6H?YS36DNm^B-iSz8Pg{&Y=O7}G%gA2ldq`A?|?0lE0DEvL2g8Y z-p3}KQq?j}&=s%fnx%OoZ!8%!6`Xe(OgcF;L1j;OT+1xd%(CYjWtuErGIXCt?P{5Z z&YqkZdvKJd28Zt(oh_Uko~fhbqi1Mjbm;7rgCePP=gXT=jC|qGJ%y9jJZ1(L%ET>6)b%SeM7Di`6;!1Cxb$6mrcfER-+=!I*Q)YbeBmbj$G! z%Im)4(xPTnjY5HDW(TKd3&Vp`vuCFp0k4x<;EG*BWH2m4rz!)})~y1M+;Po$fz07J zYX~fu&HS`6XJI-`$EHEgGs;D4dDiqTW6;MNs#ryk>g*B2(_OQ|qU#%n9ed84ue#bh zHJ2-zy}Z63R$Qs7`|BHqoysztUbscmtgDrbvgTgCMX##mSKTJ(>!YmNJj?kp8*9g@ z@H#Vfth`~Te9rXDV)J-URn5|R&F2i)yXDAFVmYmA49$+1+Pv*}SXds77{%)Rytc+v zz{5rW*$8jm<}946K)KuOEf^xi(}txj3CvsdbvhU&_d`?Bv`oJ#9~-vLw=a_K@K-(l1R1Fnq5L9NGp}r zrWquSS3jKoS7f%5Y|QL34!+Grd`dg^*|>)3|1CnL+-zWRiw` zn~FN(fbvnBaU$s&rUlRrC zV9hG57~m}eu3D>0snBDg)y5k_r8(h?3YvSFu;K%Az+dg@;Y~!tqtNVz(hh7J&|u=8 zha~&HetdbfOrVV=j^n1mTgQRz1^Nss32jxp!*u9GC4?tccxKTud(m+PHbpf;)HGn1 z5vh0ctO9w~z0Q}h^XPA4wDmF6Bca0^8$T3G(aMwx%mG|)9X{25Z8CKKRstMaTMTQh z;<`#U?~CxwL6Qf}(>)GzI%o^jMf~!(>qEJC3pc1|SWF*J3Qyo#TO3w6dfWT3;oL*} zq4NQH9LMOXFgU6RriXq-*k>KA!GaGwv7+yxsW9v;b&N(EUY`Jc8B)u520?lh6UKps zMJ&j}AAVlh`h|&)=Xc)u%#Z)xr^N3)cJbWigV#S4QIaw?K@?FEGVdv52_T|GBq^%2 zx8cs<-WXN3#v)2As$^OtN-CnHvFVn!rWI^ty4yEnyPw1@#nC&JNR^))2w>GP$B_6`ZR$8q_6Gd*hv@zVf1~?(g5%PuT0z&cla{V*fpNAMU$HKh)oM z@Ib%OryaU`e_yG8?r_mCj?C@Xb(|d~itoeG5kDN>KnT4+aQ?)#jZZMMkguu zgzVuwN-F>VVm(jAsa3^qKm6Dy#yKKJNRo6a>Jaed6HHg>Vc zvlGjMm5OCzmkEFKTBVZR8))#|s^^c{bI#A{&;G3F2irI{Re7Tu(%`tpqgD8c6RK-2 zVn04_ct5As19hz`u2nrKd3u#S9yb;ZOSKpt&uZS7z35yv+^kwP2X&lI5XL#p@{Dj$ zLdo7+cvE+M?;78a?QH}B0>}2&X9$EyiC;c|LWIwy^m^0#=$CJh|39L4iopK@Xzh7| literal 0 HcmV?d00001 diff --git a/tools/wsbridge/v0.50/Linux/Makefile b/tools/wsbridge/v0.50/Linux/Makefile new file mode 100644 index 0000000..62afba0 --- /dev/null +++ b/tools/wsbridge/v0.50/Linux/Makefile @@ -0,0 +1,17 @@ + +CC = gcc +CFLAGS = -c +SOURCES = main.c +OBJECTS = $(SOURCES:.c=.o) +EXE = wsbridge + +all: $(SOURCES) $(EXE) + +$(EXE): $(OBJECTS) + $(CC) $(OBJECTS) -o $@ + +%.o:%.c + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm *.o diff --git a/tools/wsbridge/v0.50/Linux/main.c b/tools/wsbridge/v0.50/Linux/main.c new file mode 100644 index 0000000..5672614 --- /dev/null +++ b/tools/wsbridge/v0.50/Linux/main.c @@ -0,0 +1,419 @@ +/******************************************************************* + Copyright (C) 2009 FreakLabs + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Originally written by Christopher Wang aka Akiba. + Please post support questions to the FreakLabs forum. + +*******************************************************************/ +/*! + FreakLabs Freakduino/Wireshark Bridge + + This program allows data from the Freakduino to be piped into wireshark. + When the sniffer firmware is loaded into the Freakduino, then the Freakduino + will be in promiscuous mode and will just dump any frames it sees. This + program takes the frame dump and sends it into Wireshark for analysis. The + global header is already set up to inform wireshark that the link layer for + all frames will be in IEEE 802.15.4 format. After that, it is up to the user + to choose any higher layer protocols to decode above 802.15.4 via the + wireshark "enable protocols" menu. +*/ +/**************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORTBUFSIZE 32 +#define BUFSIZE 1024 +#define PACKET_FCS 2 +#define DEBUG 1 +#define PIPENAME "/tmp/wireshark" +#define BAUDRATE B115200 + +enum FSM +{ + START_CAPTURE, + PACKET_CAPTURE +}; + +static int FD_pipe = -1; +static int FD_com = -1; +static uint8_t port_buf[PORTBUFSIZE]; +static uint8_t circ_buf[BUFSIZE]; +static uint16_t rd_idx = 0; +static uint16_t wr_idx = 0; +static uint8_t len; +static uint8_t state = START_CAPTURE; +static uint8_t file_write = 0; + +/**************************************************************************/ +/*! + Open the serial port that we'll be communicating with the Freakduino (sniffer) + through. +*/ +/**************************************************************************/ +static int serial_open(char *portname) +{ + int FD_com; // file descriptor for the serial port + struct termios term; + + FD_com = open(portname, O_RDONLY | O_NOCTTY | O_NDELAY); + + if(FD_com == -1) // if open is unsucessful + { + printf("serial_open: Unable to open %s.\n", portname); + } + else + { + // set speed of port + cfsetspeed(&term, BAUDRATE); + + // set to 8-bits, no parity, 1 stop bit + term.c_cflag &= ~PARENB; + term.c_cflag &= ~CSTOPB; + term.c_cflag &= ~CSIZE; + term.c_cflag |= CS8; + + term.c_cflag |= (CLOCAL | CREAD); + tcsetattr(FD_com, TCSANOW, &term); + } + return(FD_com); +} + +/**************************************************************************/ +/*! + Create the named pipe that we will be communicating with wireshark through. +*/ +/**************************************************************************/ +static void named_pipe_create(char *name) +{ + int rv = 0; + rv = mkfifo(name, 0666); + if ((rv == -1) && (errno != EEXIST)) + { + perror("Error creating named pipe"); + exit(1); + } + + FD_pipe = open(name, O_WRONLY); + + if (FD_pipe == -1) + { + perror("Error connecting to named pipe"); + exit(1); + } +} + +/**************************************************************************/ +/*! + Write data to the pipe +*/ +/**************************************************************************/ +size_t data_write(const void *ptr, size_t size) +{ + ssize_t bytes = 0; + if (FD_pipe != -1) + { + bytes = write(FD_pipe, ptr, size); + } +} + +/**************************************************************************/ +/*! + Write the global header to wireshark. This is only done once at the + beginning of the capture. +*/ +/**************************************************************************/ +static void write_global_hdr() +{ + uint32_t magic_number = 0xa1b2c3d4; /* magic number */ + uint16_t version_major = 2; /* major version number */ + uint16_t version_minor = 4; /* minor version number */ + int32_t thiszone = 0; /* GMT to local correction */ + uint32_t sigfigs = 0; /* accuracy of timestamps */ + uint32_t snaplen = 65535; /* max length of captured packets, in octets */ + uint32_t network = 195; /* data link type (DLT) - IEEE 802.15.4 */ + + data_write(&magic_number, sizeof(magic_number)); + data_write(&version_major, sizeof(version_major)); + data_write(&version_minor, sizeof(version_minor)); + data_write(&thiszone, sizeof(thiszone)); + data_write(&sigfigs, sizeof(sigfigs)); + data_write(&snaplen, sizeof(snaplen)); + data_write(&network, sizeof(network)); +} + +/**************************************************************************/ +/*! + Write the frame header into wireshark. This is required for the libpcap + format and informs wireshark that a new frame is coming. +*/ +/**************************************************************************/ +static void write_frame_hdr(uint8_t len) +{ + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ + struct timeval tv; + + gettimeofday(&tv, NULL); + ts_sec = tv.tv_sec; + ts_usec = tv.tv_usec; + incl_len = len; + orig_len = len + PACKET_FCS; + + data_write(&ts_sec, sizeof(ts_sec)); + data_write(&ts_usec, sizeof(ts_usec)); + data_write(&incl_len, sizeof(incl_len)); + data_write(&orig_len, sizeof(orig_len)); +} + +/**************************************************************************/ +/*! + Write one frame into wireshark (via the pipe). +*/ +/**************************************************************************/ +static void write_frame(uint8_t frame_len) +{ + uint8_t i; + + // actual frame length for wireshark should not include FCS + frame_len -= PACKET_FCS; + + // write header to inform WS that new frame has arrived + write_frame_hdr(frame_len); + + // bump rd_idx. we don't want to write the length byte + rd_idx = (rd_idx + 1) % BUFSIZE; + + // write frame into wireshark + for (i=0; i wr_idx) + { + // read index is greater than write. we must have wrapped around + return (BUFSIZE - (rd_idx - wr_idx)); + } + else + { + return (wr_idx - rd_idx); + } +} + +/**************************************************************************/ +/*! + Deal with any received signals. This includes ctrl-C to stop the program. +*/ +/**************************************************************************/ +static void sig_int(int signo) +{ + (void) signo; + if (FD_pipe != -1) + { + printf("\nClosing pipe.\n"); + close(FD_pipe); + } + + if (FD_com != -1) + { + printf("\nClosing serial port.\n"); + close(FD_com); + } + + printf("\nSignal captured and devices shut down.\n"); + + exit(0); +} + +/**************************************************************************/ +/*! + Init the signals we'll be checking for. +*/ +/**************************************************************************/ +static void signal_init(void) +{ + signal(SIGINT, sig_int); + signal(SIGHUP, sig_int); + signal(SIGTERM, sig_int); +} + +/**************************************************************************/ +/*! + Here's the meat of the code. +*/ +/**************************************************************************/ +int main(int argc, char *argv[]) +{ + int nbytes; + uint8_t i; + + // capture any signals that will terminate program + signal_init(); + + // make sure the COM port is specified + if (argc == 2) + { + // open the COM port + if ((FD_com = serial_open(argv[1])) == -1) + { + printf("Serial port not opened.\n"); + return 0; + } + else + { + printf("Serial port connected. Waiting for wireshark connection.\n"); + printf("Open wireshark and connect to local interface: %s\n", PIPENAME); + } + } + else + { + printf("Usage: wsbridge .\n"); + return 0; + } + + + // create and open pipe for wireshark + named_pipe_create(PIPENAME); + + // wait for wireshark to connect to pipe. Once wireshark + // connects, then the global header will be written to it. + if (FD_pipe != -1) + { + write_global_hdr(); + printf("Client connected to pipe.\n"); + } + + for (;;) + { + uint16_t bytes_in_buf; + uint8_t frame_len, byte_ctr; + + // wait for data to come in on the serial port + if ((nbytes = read(FD_com, port_buf, PORTBUFSIZE)) > 0) + { + // write data to circular buffer. loop through all received bytes + for (i=0; i frame_len) + { + // if more than one frame is available, then write one frame to + // wireshark and then see if any more are available. + write_frame(frame_len); + } + else if (bytes_in_buf == frame_len) + { + // only one frame is available. write to wireshark and then quit + // the loop + write_frame(frame_len); + file_write = 0; + } + else + { + // less than one frame is available. quit the loop and collect more + // bytes. we normally should not get here. + file_write = 0; + } + } + } + } +} + diff --git a/tools/wsbridge/v0.50/Win/wsbridge.sln b/tools/wsbridge/v0.50/Win/wsbridge.sln new file mode 100644 index 0000000..43470d2 --- /dev/null +++ b/tools/wsbridge/v0.50/Win/wsbridge.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "wsbridge", "wsbridge\wsbridge.csproj", "{9CAFA529-8FF4-42D4-B6A6-54992B3BB40F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9CAFA529-8FF4-42D4-B6A6-54992B3BB40F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9CAFA529-8FF4-42D4-B6A6-54992B3BB40F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CAFA529-8FF4-42D4-B6A6-54992B3BB40F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9CAFA529-8FF4-42D4-B6A6-54992B3BB40F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/wsbridge/v0.50/Win/wsbridge.suo b/tools/wsbridge/v0.50/Win/wsbridge.suo new file mode 100644 index 0000000000000000000000000000000000000000..00ead54bb87f5f3773654b8fd1aef5ab1f460aa1 GIT binary patch literal 12800 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 literal 0 HcmV?d00001 diff --git a/tools/wsbridge/v0.50/Win/wsbridge/Program.cs b/tools/wsbridge/v0.50/Win/wsbridge/Program.cs new file mode 100644 index 0000000..98e7c49 --- /dev/null +++ b/tools/wsbridge/v0.50/Win/wsbridge/Program.cs @@ -0,0 +1,305 @@ +using System; +using System.IO; +using System.IO.Ports; +using System.IO.Pipes; + +namespace wsbridge +{ + class Program + { + static NamedPipeServerStream wspipe; + static BinaryWriter ws; + static SerialPort p; + const uint BUFSIZE = 1024; + static byte[] b = new byte[BUFSIZE]; + static uint wr_idx = 0; + static uint rd_idx = 0; + static byte len; + static FSM state; + static bool file_write = false; + static long start_time_in_ticks; + static byte byte_ctr; + static String port; + + enum FSM + { + START_CAPTURE, + PACKET_CAPTURE + } + + const int PACKET_FCS = 2; + const int PACKET_LEN = 1; + const bool DEBUG_PRINT = true; + + static void Main(string[] args) + { + // Commane line options + if (args.Length == 0) + { + // generate list of active serial ports + string[] names = SerialPort.GetPortNames(); + Console.WriteLine("Serial ports:"); + foreach (string name in names) Console.WriteLine(name); + Console.Write("Choose one:"); + port = Console.ReadLine(); + } + else if (args.Length == 1) + { + port = args[0]; + } + else + { + Console.WriteLine("Usage: wsbridge "); + Console.WriteLine("or leave portname blank for a list of ports."); + Environment.Exit(0); + } + + // Open serial port + try + { + p = new SerialPort(port, 115200, Parity.None, 8, StopBits.One); + p.Open(); + } + catch (Exception e) + { + // ooops, serial port can't be opened. throw exception, print message, and exit + Console.WriteLine("Error opening serial port. Msg = " + e.Message); + Environment.Exit(0); + } + Console.WriteLine("Serial port opened successfully."); + + // create pipe + try + { + wspipe = new NamedPipeServerStream("wireshark", PipeDirection.Out); + } + catch (Exception e) + { + Console.WriteLine("Error opening pipe. Msg = " + e.Message); + p.Close(); + Environment.Exit(0); + } + + // wait for wireshark to connect to pipe + Console.WriteLine("Waiting for connection to wireshark."); + Console.WriteLine("Open wireshark and connect to interface: Local:\\\\.\\pipe\\wireshark"); + wspipe.WaitForConnection(); + Console.WriteLine("Client connected."); + + // connect binary writer to pipe to write binary data into it + ws = new BinaryWriter(wspipe); + + // add serial data capture + p.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived); + state = FSM.START_CAPTURE; + + // keep track of time started. this will be used for timestamps + start_time_in_ticks = DateTime.Now.Ticks; + + // generate header to identify the packet capture + write_global_hdr(); + + // run forever + while (true) + { + } + } + + // serial port handler. this gets executed whenever data is available on the serial port + static void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) + { + uint frame_len, bytes_in_buf; + + // loop until serial port buffer is empty + while (p.BytesToRead != 0) + { + switch (state) + { + case FSM.START_CAPTURE: + // starting a new frame. the first byte will indicate the length + len = (byte)p.ReadByte(); + b[wr_idx] = len; + byte_ctr = 0; + + if (DEBUG_PRINT) + { + Console.WriteLine(); + Console.Write(String.Format("{0,2:X}", b[wr_idx]) + ' '); + } + + // increment the buffer index and wrap back to 0 if it reaches the max size + // (standard circular buffer behavior) + wr_idx = (wr_idx + 1) % BUFSIZE; + state = FSM.PACKET_CAPTURE; + break; + + case FSM.PACKET_CAPTURE: + // capture bytes until the total length of the frame + b[wr_idx] = (byte)p.ReadByte(); + + if (DEBUG_PRINT) + { + Console.Write(String.Format("{0,2:X}", b[wr_idx]) + ' '); + } + + wr_idx = (wr_idx + 1) % BUFSIZE; + byte_ctr++; + + // we've captured all bytes in frame. tell the next section we're ready to write + // the frame into wireshark + if (byte_ctr == (len - 1)) + { + state = FSM.START_CAPTURE; + file_write = true; + } + break; + } + } + + // at least one frame has been captured. write it into wireshark + while (file_write == true) + { + frame_len = b[rd_idx]; + bytes_in_buf = calc_bytes_in_buf(); + + // compare the frame lengto the number of bytes in the buffer. if the bytes in buffer are + // greater than the frame length, then we have more than one frame. loop through until + // all complete frames have been written to wireshark + if (bytes_in_buf > frame_len) + { + // more than one frame in buffer. don't indicate we're finished yet in case we have + // other frames we can send out. + write_frame(frame_len); + } + else if (bytes_in_buf == frame_len) + { + // only one frame in buffer. indicate we're finished after we write this frame out. + write_frame(frame_len); + file_write = false; + } + else + { + // what?! no frames in buffer? we shouldn't reach here. + file_write = false; + } + } + } + + // return the number of bytes in the buffer + static uint calc_bytes_in_buf() + { + if (rd_idx > wr_idx) + { + // since we're using a circular buffer, its possible for the write index to be less + // than the read index if a wraparound occurred. handle this case here. + return (BUFSIZE - (rd_idx - wr_idx)); + } + else + { + // normal way to calculate bytes in the buffer + return (wr_idx - rd_idx); + } + } + + // this is the global header that starts any packet capture file. this will tell wireshark what + // kind of protocol it is (indicated by the DLT) as well as other information like endianness, etc. + static void write_global_hdr() + { + uint magic_num = 0xa1b2c3d4; // used for endianness + short version_major = 2; // version + short version_minor = 4; // version + int thiszone = 0; // zone (unused) + uint sigfigs = 0; // significant figures (unused) + uint snaplen = 65535; // snapshot length (max value) + uint network = 195; // Data Link Type (DLT): indicates link layer protocol + + try + { + // write to wireshark pipe + ws.Write(magic_num); + ws.Write(version_major); + ws.Write(version_minor); + ws.Write(thiszone); + ws.Write(sigfigs); + ws.Write(snaplen); + ws.Write(network); + } + catch + { + Console.WriteLine("Pipe has been closed."); + close(); + } + } + + // this writes a frame header into wireshark in libpcap format. the format is simple and just + // requires a timestamp and length + static void write_frm_hdr(long sec, long usec, uint incl_len, uint orig_len) + { + try + { + // write to wireshark + ws.Write((uint)sec); + ws.Write((uint)usec); + ws.Write(incl_len); + ws.Write(orig_len); + } + catch + { + Console.WriteLine("Pipe has been closed."); + close(); + } + } + + // this writes a frame into wireshark. it calculates the timestamp and length and uses that + // for the frame header. it then writes captured bytes into wireshark + static void write_frame(uint frame_len) + { + uint incl_len, orig_len; + long sec, usec; + + // generating timestamp. its kind of cheesy but there isn't a unix timestamp mechanism in win. + // just counting ticks from when program was started. each tick is 100 nsec. + long diff_in_ticks = DateTime.Now.Ticks - start_time_in_ticks; // get difference in ticks + sec = diff_in_ticks / TimeSpan.TicksPerSecond; // get seconds + diff_in_ticks -= (sec * TimeSpan.TicksPerSecond); // subtract off seconds from total + usec = diff_in_ticks / 10; // get usec + + // calculate frame length. we won't be feeding frame checksum (FCS) into wireshark. + incl_len = (uint)frame_len - PACKET_FCS; + orig_len = frame_len; + + // increment over the length byte. we won't feed that into wireshark either. it doesn't seem to like it. + rd_idx = (rd_idx + 1) % BUFSIZE; + + // write frame header first + write_frm_hdr(sec, usec, incl_len, orig_len); + + // loop through entire length and write data into wireshark + for (int i = 0; i < incl_len; i++) + { + try + { + ws.Write(b[rd_idx]); + } + catch + { + Console.WriteLine("Pipe has been closed."); + close(); + } + rd_idx = (rd_idx + 1) % BUFSIZE; + } + rd_idx = (rd_idx + 1) % BUFSIZE; + } + + // Received some type of termination. Close everything and wrap up. + static void close() + { + p.Close(); + wspipe.Close(); + ws.Close(); + Console.WriteLine("Press key to quit."); + Console.ReadLine(); + Environment.Exit(0); + } + } +} diff --git a/tools/wsbridge/v0.50/Win/wsbridge/Properties/AssemblyInfo.cs b/tools/wsbridge/v0.50/Win/wsbridge/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cca8ed9 --- /dev/null +++ b/tools/wsbridge/v0.50/Win/wsbridge/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("pcap")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("pcap")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("75eb0967-9c50-430e-a562-d0f7bee8f2cc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj b/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj new file mode 100644 index 0000000..be4b179 --- /dev/null +++ b/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj @@ -0,0 +1,96 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {9CAFA529-8FF4-42D4-B6A6-54992B3BB40F} + Exe + Properties + wsbridge + wsbridge + v3.5 + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj.user b/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj.user new file mode 100644 index 0000000..191b576 --- /dev/null +++ b/tools/wsbridge/v0.50/Win/wsbridge/wsbridge.csproj.user @@ -0,0 +1,15 @@ + + + publish\ + + + + + + + + + en-US + false + + \ No newline at end of file -- 2.20.1