├── .dir-locals.el ├── .gitattributes ├── .gitignore ├── BUILDING.md ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── cli ├── CMakeLists.txt ├── arg_reader.h ├── cli.cpp ├── cli_info.rc.in ├── exception_with_exit_code.h └── exit_codes.h ├── default.nix ├── drivers ├── pavr2-bootloader.inf ├── pavr2-serial.inf ├── pavr2.inf └── pololu.cat ├── gui ├── CMakeLists.txt ├── gui_info.rc.in ├── main.cpp ├── main_controller.cpp ├── main_controller.h ├── main_model.cpp ├── main_model.h ├── main_view.cpp ├── main_view.h └── qt │ ├── app_icns.qrc │ ├── app_ico.qrc │ ├── frequency_validator.cpp │ ├── frequency_validator.h │ ├── icon_resources.qrc │ ├── main_window.cpp │ ├── main_window.h │ ├── voltage_spin_box.cpp │ └── voltage_spin_box.h ├── images ├── .gitignore └── README.md ├── include ├── pavr2_protocol.h ├── pavrpgm_config.h.in ├── programmer.h └── programmer_frequency_tables.h ├── lib ├── CMakeLists.txt ├── isp_freq_table.cpp └── programmer.cpp ├── nix ├── build.nix ├── build_installer.rb ├── builder.sh ├── license_builder.rb ├── linux_installer_builder.rb ├── macos_installer_builder.rb └── windows_installer_builder.rb └── udev-rules └── 99-pololu.rules /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ( 2 | (c-default-style . "BSD") 3 | (c-mode . ((c-basic-offset . 4) (tab-width . 4) (indent-tabs-mode . nil))) 4 | (nil . ((fill-column . 80))) 5 | ) 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.inf -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build* 3 | /result* 4 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building from source 2 | 3 | If you want to build this software from its source code, you can follow these 4 | instructions. 5 | 6 | 7 | ## Building from source on Linux with nixcrpkgs 8 | 9 | This software can be cross-compiled on a Linux machine using 10 | [nixcrpkgs](https://github.com/pololu/nixcrpkgs), a collection of tools for 11 | cross-compiling. This is how we build our official installers. 12 | 13 | To get started, you should first install [Nix, the purely functional 14 | package manager](http://nixos.org/nix/), on a Linux machine by following the 15 | instructions on the Nix website. 16 | 17 | Next, run the comamnds below to download the latest version of 18 | [nixcrpkgs](https://github.com/pololu/nixcrpkgs), a collection of tools for 19 | cross-compiling, and add nixcrpkgs to your `NIX_PATH` environment variable. You 20 | will need to have [git](https://git-scm.com/) installed. (If you don't want to 21 | install git, you can download nixcrpkgs as a ZIP file from GitHub instead.) 22 | 23 | git clone https://github.com/pololu/nixcrpkgs 24 | export NIX_PATH=$NIX_PATH:nixcrpkgs=$(pwd)/nixcrpkgs 25 | 26 | Now run these commands to download this software and build it: 27 | 28 | git clone https://github.com/pololu/pololu-usb-avr-programmer-v2 pavr2 29 | cd pavr2 30 | nix-build -A linux-x86 31 | 32 | The name `linux-x86` in the command above is the name of an attribute in the set 33 | defined in `default.nix`, which specifies that we want to build the software 34 | for a Linux machine running on an i686 processor. You can specify `win32` 35 | instead to get a version for Windows, and you can look in `default.nix` to see 36 | the names of other supported platforms. 37 | 38 | Note: Building for macOS requires you to get a tarball of the macOS SDK 39 | and put it in the right place in nixcrpkgs. See the `README.md` file in 40 | nixcrpkgs for details. 41 | 42 | The first time you build the software with this method, because it involves 43 | building a GCC cross-compiler and Qt from source, it will take a lot of time, 44 | memory, and disk space. Subsequent builds will usually be faster because the 45 | the compiled Qt and GCC versions can be reused if you have not changed the 46 | recipes for building them. 47 | 48 | Once the build is completed, there should be a symbolic link in the current 49 | directory named `result` that points to the compiled software. 50 | 51 | 52 | ## Building from source on Linux (for Linux) 53 | 54 | Another way to build for Linux is to use the development tools provided by your 55 | Linux distribution. Note that if you use this method, the executables you build 56 | will most likely depend on a plethora of shared libraries from your Linux 57 | distribution. 58 | 59 | You will need to install git, gcc, make, CMake, libudev, 60 | and Qt 5. Most Linux distributions come with a package manager that you can use 61 | to install these dependencies. 62 | 63 | On Ubuntu, Raspbian, and other Debian-based distributions, you can install the 64 | dependencies by running: 65 | 66 | sudo apt-get install build-essential git cmake libudev-dev qtbase5-dev 67 | 68 | On Arch Linux, libudev should already be installed, and you can install the 69 | other dependencies by running: 70 | 71 | pacman -Sy base-devel git cmake qt5-base 72 | 73 | For other Linux distributions, consult the documentation of your distribution 74 | for information about how to install these dependencies. 75 | 76 | This software depends on the Pololu USB Library version 1.x.x (libusbp-1). Run 77 | the commands below to download, compile, and install the latest version of that 78 | library on your computer. 79 | 80 | git clone https://github.com/pololu/libusbp -b v1-latest 81 | cd libusbp 82 | mkdir build 83 | cd build 84 | cmake .. 85 | make 86 | sudo make install 87 | cd ../.. 88 | 89 | You can test to see if libusbp-1 was installed correctly by running 90 | `pkg-config libusbp-1 --cflags`, 91 | which should output something like 92 | `-I/usr/local/include/libusbp-1`. 93 | If it says that libusbp-1 was not found in the pkg-config search path, 94 | retry the instructions above. 95 | 96 | Run these commands to download, build, and install this software: 97 | 98 | git clone https://github.com/pololu/pololu-usb-avr-programmer-v2 pavr2 99 | cd pavr2 100 | mkdir build 101 | cd build 102 | cmake .. 103 | make 104 | sudo make install 105 | cd ../.. 106 | 107 | You will need to install a udev rule to give non-root users permission to access 108 | Pololu USB devices. Run this command: 109 | 110 | sudo cp udev-rules/99-pololu.rules /etc/udev/rules.d/ 111 | 112 | You should now be able to run the command-line utility by running `pavr2cmd` in 113 | your shell, and you should be able to start the graphical configuration utility 114 | by running `pavr2gui`. 115 | 116 | If you get an error about libusbp failing to load (for example, 117 | "cannot open shared object file: No such file or directory"), then 118 | run `sudo ldconfig` and try again. If that does not work, it is likely that 119 | your system does not search for libraries in `/usr/local/lib` 120 | by default. To fix this issue for your current shell session, run: 121 | 122 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 123 | 124 | A more permanent solution, which will affect all users of the computer, is to 125 | run: 126 | 127 | sudo sh -c 'echo /usr/local/lib > /etc/ld.so.conf.d/local.conf' 128 | sudo ldconfig 129 | 130 | 131 | ## Building from source on Windows with MSYS2 132 | 133 | Another way to build this software for Windows is to use 134 | [MSYS2](http://msys2.github.io/). Note that if you use this method, the 135 | executables you build will depend on a plethora of DLLs from the MSYS2 136 | environment. 137 | 138 | If you have not done so already, follow the instructions on the MSYS2 website to 139 | download, install, and update your MSYS2 environment. In particular, be sure to 140 | update your installed packages. 141 | 142 | Next, start a shell by selecting "MSYS2 MinGW 32-bit" from your Start menu or 143 | by running `mingw32.exe`. This is the right environment to use if you want to 144 | build 32-bit software that works on 32-bit or 64-bit Windows. (If you want to 145 | build 64-bit software that only works on 64-bit versions of Windows, select 146 | "MSYS2 MinGW 64-bit" or `mingw64.exe`.) 147 | 148 | Run this command to install the required development tools: 149 | 150 | pacman -S base-devel git $MINGW_PACKAGE_PREFIX-{toolchain,cmake,qt5} 151 | 152 | If pacman prompts you to enter a selection of packages to install, just press 153 | enter to install all of the packages. 154 | 155 | This software depends on the Pololu USB Library version 1.x.x (libusbp-1). Run 156 | the commands below to download, compile, and install the latest version of that 157 | library into your MSYS2 environment. 158 | 159 | git clone https://github.com/pololu/libusbp -b v1-latest 160 | cd libusbp 161 | mkdir build 162 | cd build 163 | MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX 164 | make install DESTDIR=/ 165 | cd ../.. 166 | 167 | You can test to see if libusbp-1 was installed correctly by running 168 | `pkg-config libusbp-1 --cflags`, 169 | which should output something like 170 | `-IC:/msys64/mingw32/include/libusbp-1`. 171 | If it says that libusbp-1 was not found in the pkg-config search path, 172 | retry the instructions above. 173 | 174 | Run these commands to build this software and install it: 175 | 176 | git clone https://github.com/pololu/pololu-usb-avr-programmer-v2 pavr2 177 | cd pavr2 178 | mkdir build 179 | cd build 180 | MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX 181 | make install DESTDIR=/ 182 | cd ../.. 183 | 184 | You should now be able to run the command-line utility by running `pavr2cmd` in 185 | your shell, and you should be able to start the graphical configuration utility 186 | by running `pavr2gui`. 187 | 188 | 189 | ## Building from source on macOS with Homebrew 190 | 191 | First, install [Homebrew](http://brew.sh/). 192 | 193 | Then use brew to install the dependencies: 194 | 195 | brew install pkg-config cmake qt5 196 | 197 | This software depends on the Pololu USB Library version 1.x.x (libusbp-1). Run 198 | the commands below to download, compile, and install the latest version of that 199 | library on your computer. 200 | 201 | git clone https://github.com/pololu/libusbp -b v1-latest 202 | cd libusbp 203 | mkdir build 204 | cd build 205 | cmake .. 206 | make 207 | sudo make install 208 | cd ../.. 209 | 210 | You can test to see if libusbp-1 was installed correctly by running 211 | `pkg-config libusbp-1 --cflags`, 212 | which should output something like 213 | `-I/usr/local/include/libusbp-1`. 214 | If it says that libusbp-1 was not found in the pkg-config search path, 215 | retry the instructions above. 216 | 217 | Run these commands to build this software and install it: 218 | 219 | git clone https://github.com/pololu/pololu-usb-avr-programmer-v2 pavr2 220 | cd pavr2 221 | mkdir build 222 | cd build 223 | cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix qt5) 224 | make 225 | sudo make install 226 | cd ../.. 227 | 228 | You should now be able to run the command-line utility by running `pavr2cmd` in 229 | your shell, and you should be able to start the graphical configuration utility 230 | by running `pavr2gui`. 231 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.10.0) 2 | 3 | project (pavr2) 4 | 5 | set(ENABLE_GUI TRUE CACHE BOOL 6 | "True if you want to build the GUI, which depends on Qt 5.") 7 | 8 | if (EXISTS "${CMAKE_SOURCE_DIR}/images/app.icns") 9 | set (POLOLU_BUILD TRUE) 10 | endif () 11 | if (EXISTS "${CMAKE_SOURCE_DIR}/images/app.ico") 12 | set (POLOLU_BUILD TRUE) 13 | endif () 14 | 15 | set (CLI_NAME "pavr2cmd") 16 | set (GUI_NAME "pavr2gui") 17 | set (DOCUMENTATION_URL "https://www.pololu.com/docs/0J67") 18 | 19 | set (SOFTWARE_VERSION_MAJOR 1) 20 | set (SOFTWARE_VERSION_MINOR 1) 21 | set (SOFTWARE_VERSION_PATCH 1) 22 | set (SOFTWARE_VERSION ${SOFTWARE_VERSION_MAJOR}.${SOFTWARE_VERSION_MINOR}.${SOFTWARE_VERSION_PATCH}) 23 | 24 | string(TIMESTAMP YEAR "%Y") 25 | 26 | find_package(PkgConfig) 27 | 28 | if (NOT CMAKE_BUILD_TYPE) 29 | set (CMAKE_BUILD_TYPE "Release" CACHE STRING 30 | "Options are Debug Release RelWithDebInfo MinSizeRel" FORCE) 31 | endif () 32 | 33 | # Our C++ code uses features from the C++11 standard. 34 | macro(use_cxx11) 35 | if (CMAKE_VERSION VERSION_LESS "3.1") 36 | if (CMAKE_C_COMPILER_ID STREQUAL "GNU") 37 | set (CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}") 38 | endif () 39 | else () 40 | set (CMAKE_CXX_STANDARD 11) 41 | endif () 42 | endmacro(use_cxx11) 43 | 44 | # Put libraries and executables in the top level of the build directory 45 | # so that the executables can find the libraries and it is easy to run 46 | # everything. 47 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 48 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 49 | 50 | # Warn about everything. 51 | set (CMAKE_C_FLAGS "-Wall -Wextra ${CMAKE_C_FLAGS}") 52 | set (CMAKE_CXX_FLAGS "-Wall -Wextra ${CMAKE_CXX_FLAGS}") 53 | 54 | if (WIN32) 55 | 56 | # Enable correct behavior for the return value of vsnprintf. 57 | add_definitions (-D__USE_MINGW_ANSI_STDIO=1) 58 | 59 | # Enable functions only available in Windows Vista and later. 60 | add_definitions (-D_WIN32_WINNT=0x0600 -DNTDDI_VERSION=0x06000000) 61 | 62 | endif () 63 | 64 | # Detect Linux. 65 | if (CMAKE_SYSTEM_NAME STREQUAL "Linux") 66 | set (LINUX 1) 67 | endif () 68 | 69 | # Find libusbp and use it. 70 | pkg_check_modules(LIBUSBP REQUIRED libusbp-1) 71 | string (REPLACE ";" " " LIBUSBP_CFLAGS "${LIBUSBP_CFLAGS}") 72 | string (REPLACE ";" " " LIBUSBP_LDFLAGS "${LIBUSBP_LDFLAGS}") 73 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBUSBP_CFLAGS}") 74 | 75 | include_directories ( 76 | "${CMAKE_SOURCE_DIR}/include" 77 | "${CMAKE_BINARY_DIR}/include" 78 | ) 79 | 80 | configure_file ( 81 | "include/pavrpgm_config.h.in" 82 | "include/pavrpgm_config.h" 83 | ) 84 | 85 | file(WRITE "${CMAKE_BINARY_DIR}/version.txt" "${SOFTWARE_VERSION}") 86 | 87 | add_subdirectory (lib) 88 | add_subdirectory (cli) 89 | 90 | if (ENABLE_GUI) 91 | add_subdirectory (gui) 92 | endif () 93 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 Pololu Corporation. For more information, see 2 | 3 | http://www.pololu.com/ 4 | http://forum.pololu.com/ 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pololu USB AVR Programmer v2 software 2 | 3 | Version: 1.1.0
4 | Release date: 2018-03-23
5 | [www.pololu.com](https://www.pololu.com/) 6 | 7 | This repository contains the source code of the configuration software that 8 | supports the [Pololu USB AVR Programmer v2](https://www.pololu.com/product/3170) 9 | and [Pololu USB AVR Programmer v2.1](https://www.pololu.com/product/3172). 10 | There are two programs: the Pololu USB AVR Programmer v2 Command-line Utility 11 | (pavr2cmd) and the Pololu USB AVR Programmer v2 Configuration Utility (pavr2gui). 12 | 13 | This software only supports the Pololu USB AVR Programmer *v2* and *v2.1*, 14 | which are *blue-colored*. If you have an older Pololu programmer, 15 | refer to the product page and user's guide of that programmer for software 16 | resources. 17 | 18 | Note that the *v2* in the name of this repository refers to the versions of the 19 | hardware products, which is different from the version number of this software. 20 | 21 | This software is only used for configuring the programmer and reading 22 | information from it. To actually program an AVR, you will need AVR programming 23 | software such as AVRDUDE or Atmel Studio. 24 | 25 | ## Installation 26 | 27 | Installers for this software are available for download from the 28 | [Pololu USB AVR Programmer v2 User's Guide][guide]. 29 | 30 | See [BUILDING.md](BUILDING.md) for information about how to compile 31 | the software from source. 32 | 33 | ## Version history 34 | 35 | * 1.1.1 (2024-12-23): 36 | * No changes to this software, but having this new version number helps 37 | us release an updated version that works on the Raspberry Pi 5. 38 | * 1.1.0 (2018-03-23): 39 | * Added support for the Pololu USB AVR Programmer v2.1 40 | * Changed the installers to be built with nixcrpkgs. 41 | * GUI: Center the window at startup. 42 | * 1.0.2 (2016-05-05): Fixed a problem with the macOS release that prevented 43 | it from finding libusbp at run-time. 44 | * 1.0.1 (2016-03-20): 45 | * Fixed the Windows installer so that it sends the appropriate message to 46 | notify other applications that the PATH has changed. 47 | * Changed the Windows installer "repair" option so that it reinstalls 48 | the driver files when it is run. 49 | * 1.0.0 (2016-03-02): Original release. 50 | 51 | [guide]: http://www.pololu.com/docs/0J67 52 | -------------------------------------------------------------------------------- /cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | use_cxx11() 2 | 3 | configure_file (cli_info.rc.in cli_info.rc) 4 | 5 | add_executable (cli cli.cpp ${CMAKE_CURRENT_BINARY_DIR}/cli_info.rc) 6 | 7 | set_target_properties (cli PROPERTIES 8 | OUTPUT_NAME ${CLI_NAME} 9 | ) 10 | 11 | include_directories ( 12 | "${CMAKE_SOURCE_DIR}/include" 13 | ) 14 | 15 | target_link_libraries (cli lib) 16 | 17 | install(TARGETS cli DESTINATION bin) 18 | -------------------------------------------------------------------------------- /cli/arg_reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ArgReader 4 | { 5 | public: 6 | ArgReader(int argc, char ** argv) : argc(argc), argv(argv), index(0) 7 | { 8 | } 9 | 10 | ArgReader() : argc(0), argv(NULL), index(0) 11 | { 12 | } 13 | 14 | // Advance and return the current argument, or NULL if we have reached the 15 | // end. The first call of this returns argv[1] because argv[0] is just the 16 | // program name. 17 | const char * next() 18 | { 19 | if (index < argc) 20 | { 21 | index++; 22 | return argv[index]; 23 | } 24 | else 25 | { 26 | return NULL; 27 | } 28 | } 29 | 30 | // Return the argument before the current one, or NULL. 31 | const char * last() const 32 | { 33 | if (index > 0) 34 | { 35 | return argv[index - 1]; 36 | } 37 | else 38 | { 39 | return NULL; 40 | } 41 | } 42 | 43 | private: 44 | int argc; 45 | char ** argv; 46 | int index; 47 | }; 48 | -------------------------------------------------------------------------------- /cli/cli.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include "arg_reader.h" 11 | #include "exit_codes.h" 12 | #include "exception_with_exit_code.h" 13 | #include "pavr2_protocol.h" 14 | 15 | // [all-settings] 16 | static const char help[] = 17 | CLI_NAME ": Pololu USB AVR Programmer v2 Command-line Utility\n" 18 | "Version " SOFTWARE_VERSION_STRING "\n" 19 | "Usage: " CLI_NAME " OPTIONS\n" 20 | "\n" 21 | "General options:\n" 22 | " -s, --status Show programmer settings and info.\n" 23 | " -d SERIALNUMBER Specifies the serial number of the programmer.\n" 24 | " --list List programmers connected to computer.\n" 25 | " --prog-port Print the name of the programming serial port.\n" 26 | " --ttl-port Print the name of the TTL serial port.\n" 27 | " -h, --help Show this help screen.\n" 28 | "\n" 29 | "Options for changing settings:\n" 30 | " --regulator-mode MODE Sets programmer's operating voltage.\n" 31 | " auto - choose 3.3 V or 5 V based on target VCC\n" 32 | " 3v3 - always 3.3 V\n" 33 | " 5v - switch to 5 V soon after power up\n" 34 | " --vcc-output OPTION Choose whether the programmer supplies power\n" 35 | " to target VCC. Options: enabled, disabled.\n" 36 | " --vcc-output-ind OPTION Sets how to indicate that VCC is an output.\n" 37 | " blinking - yellow LED(s) blink at 8 Hz (default)\n" 38 | " steady - yellow LED(s) are on constantly\n" 39 | " --freq NUM Sets the ISP frequency (in units of kHz).\n" 40 | " Must be typed exactly as shown below.\n" 41 | " Suggested values: 3000, 2400, 2000, 1714\n" 42 | " 1500, 1200, 1000, 750, 444, 114 (default),\n" 43 | " 28.3, 14.0, 6.98, 3.47, 1.744, 1.465.\n" 44 | " --max-freq NUM Sets the max ISP frequency (in units of kHz).\n" 45 | " Must be typed exactly as shown below.\n" 46 | " Suggested values: 3000, 2400, 2000,\n" 47 | " 1714 (default), 1500, 1200, 1000, 750.\n" 48 | " --line-a FUNC Set the function of line A. Valid FUNCs are:\n" 49 | " none, cd, dsr, dtr, rts, dtr-reset.\n" 50 | " --line-b FUNC Set the function of line B. Valid FUNCs are:\n" 51 | " none, cd, dsr, dtr, rts, clock, dtr-reset.\n" 52 | " --vcc-vdd-max-range MV Sets the maximum allowed range for VCC and VDD\n" 53 | " while programming.\n" 54 | " --vcc-3v3-min MV Minimum allowed VCC while programming at 3.3 V.\n" 55 | " --vcc-3v3-max MV Maximum allowed VCC while programming at 3.3 V.\n" 56 | " --vcc-5v-min MV Minimum allowed VCC while programming at 5 V.\n" 57 | " --vcc-5v-max MV Maximum allowed VCC while programming at 5 V.\n" 58 | " --sw-minor HEXNUM Set STK500 software version minor (in hex)\n" 59 | " --sw-major HEXNUM Set STK500 software version major (in hex)\n" 60 | " --hw HEXNUM Set STK500 software hardware version (in hex)\n" 61 | " --restore-defaults Restore factory settings\n" 62 | "\n" 63 | "This utility only supports the Pololu USB AVR Programmer v2\n" 64 | "(blue-colored, labeled \"pgm04a\").\n" 65 | "\n" 66 | "MV should be a voltage in millivolts. For example, \"3400\" means 3.4 V.\n" 67 | "\n" 68 | "For more help, see: " DOCUMENTATION_URL "\n" 69 | "\n"; 70 | 71 | // Note: The arguments that are entered as a number by the user are all 72 | // uint32_t. If we made them be their proper types, that would mean adding more 73 | // information to the CLI code that could instead be in the library, and it 74 | // means a typical user would see a wider variety of error messages. 75 | // [all-settings] 76 | struct Arguments 77 | { 78 | bool showStatus = false; 79 | 80 | bool serialNumberSpecified = false; 81 | std::string serialNumber; 82 | 83 | bool showList = false; 84 | 85 | bool regulatorModeSpecified = false; 86 | uint8_t regulatorMode; 87 | 88 | bool vccOutputEnabledSpecified = false; 89 | bool vccOutputEnabled; 90 | 91 | bool vccOutputIndicatorSpecified = false; 92 | uint8_t vccOutputIndicator; 93 | 94 | bool frequencySpecified = false; 95 | std::string frequencyName; 96 | 97 | bool maxFrequencySpecified = false; 98 | std::string maxFrequencyName; 99 | 100 | bool lineAFunctionSpecified = false; 101 | uint8_t lineAFunction; 102 | 103 | bool lineBFunctionSpecified = false; 104 | uint8_t lineBFunction; 105 | 106 | bool softwareVersionMinorSpecified = false; 107 | uint32_t softwareVersionMinor; 108 | 109 | bool softwareVersionMajorSpecified = false; 110 | uint32_t softwareVersionMajor; 111 | 112 | bool hardwareVersionSpecified = false; 113 | uint32_t hardwareVersion; 114 | 115 | bool vccVddMaxRangeSpecified = false; 116 | uint32_t vccVddMaxRange; 117 | 118 | bool vcc3v3MinSpecified = false; 119 | uint32_t vcc3v3Min; 120 | 121 | bool vcc3v3MaxSpecified = false; 122 | uint32_t vcc3v3Max; 123 | 124 | bool vcc5vMinSpecified = false; 125 | uint32_t vcc5vMin; 126 | 127 | bool vcc5vMaxSpecified = false; 128 | uint32_t vcc5vMax; 129 | 130 | bool restoreDefaults = false; 131 | 132 | bool printProgrammingPort = false; 133 | 134 | bool printTtlPort = false; 135 | 136 | bool showHelp = false; 137 | 138 | bool digitalRead = false; 139 | 140 | // [all-settings] 141 | bool settingsSpecified() const 142 | { 143 | return regulatorModeSpecified || 144 | vccOutputEnabledSpecified || 145 | vccOutputIndicatorSpecified || 146 | frequencySpecified || 147 | maxFrequencySpecified || 148 | lineAFunctionSpecified || 149 | lineBFunctionSpecified || 150 | softwareVersionMinorSpecified || 151 | softwareVersionMajorSpecified || 152 | hardwareVersionSpecified || 153 | vccVddMaxRangeSpecified || 154 | vcc3v3MinSpecified || 155 | vcc3v3MaxSpecified || 156 | vcc5vMinSpecified || 157 | vcc5vMaxSpecified || 158 | restoreDefaults; 159 | } 160 | 161 | bool actionSpecified() const 162 | { 163 | return showStatus || 164 | showList || 165 | settingsSpecified() || 166 | printProgrammingPort || 167 | printTtlPort || 168 | showHelp || 169 | digitalRead; 170 | } 171 | }; 172 | 173 | /* Contains the logic for finding programmers. */ 174 | class ProgrammerSelector 175 | { 176 | public: 177 | void specifySerialNumber(const std::string & serialNumber) 178 | { 179 | assert(!listInitialized); 180 | this->serialNumber = serialNumber; 181 | this->serialNumberSpecified = true; 182 | } 183 | 184 | std::vector listProgrammers() 185 | { 186 | if (listInitialized) { return list; } 187 | 188 | list.clear(); 189 | for (const ProgrammerInstance & instance : programmerGetList()) 190 | { 191 | if (serialNumberSpecified && 192 | instance.getSerialNumber() != serialNumber) 193 | { 194 | continue; 195 | } 196 | 197 | list.push_back(instance); 198 | } 199 | listInitialized = true; 200 | return list; 201 | } 202 | 203 | ProgrammerInstance selectProgrammer() 204 | { 205 | if (programmer) { return programmer; } 206 | 207 | auto list = listProgrammers(); 208 | if (list.size() == 0) 209 | { 210 | throw deviceNotFoundError(); 211 | } 212 | 213 | if (list.size() > 1) 214 | { 215 | throw deviceMultipleFoundError(); 216 | } 217 | 218 | programmer = list[0]; 219 | return programmer; 220 | } 221 | 222 | private: 223 | 224 | std::string deviceNotFoundMessage() const 225 | { 226 | std::string r = "No programmer was found"; 227 | 228 | if (serialNumberSpecified) 229 | { 230 | r += std::string(" with serial number '") + 231 | serialNumber + "'"; 232 | } 233 | 234 | r += "."; 235 | return r; 236 | } 237 | 238 | ExceptionWithExitCode deviceNotFoundError() const 239 | { 240 | return ExceptionWithExitCode(PAVRPGM_ERROR_DEVICE_NOT_FOUND, 241 | deviceNotFoundMessage()); 242 | } 243 | 244 | ExceptionWithExitCode deviceMultipleFoundError() const 245 | { 246 | return ExceptionWithExitCode(PAVRPGM_ERROR_DEVICE_MULTIPLE_FOUND, 247 | "There are multiple qualifying programmers connected to this computer.\n" 248 | "Use the -d option to specify which device you want to use,\n" 249 | "or disconnect the others."); 250 | } 251 | 252 | bool serialNumberSpecified = false; 253 | std::string serialNumber; 254 | 255 | bool listInitialized = false; 256 | std::vector list; 257 | 258 | ProgrammerInstance programmer; 259 | }; 260 | 261 | // Converts a string to an unsigned long, returning true if there is an error. 262 | static bool niceStrToULong(std::string string, unsigned long & out, int base = 10) 263 | { 264 | out = 0; 265 | size_t pos; 266 | 267 | try 268 | { 269 | out = stoul(string, &pos, base); 270 | return (pos != string.size()); 271 | } 272 | catch(const std::invalid_argument & e) { } 273 | catch(const std::out_of_range & e) { } 274 | return true; 275 | } 276 | 277 | void parseArgUInt32(ArgReader & argReader, uint32_t & out, int base = 10) 278 | { 279 | const char * valueCStr = argReader.next(); 280 | if (valueCStr == NULL) 281 | { 282 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 283 | "Expected a number after '" + std::string(argReader.last()) + "'."); 284 | } 285 | 286 | unsigned long value; 287 | if (niceStrToULong(valueCStr, value, base)) 288 | { 289 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 290 | "The number after '" + std::string(argReader.last()) + "' is invalid."); 291 | } 292 | 293 | if (value > std::numeric_limits::max()) 294 | { 295 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 296 | "The number after '" + std::string(argReader.last()) + "' is too large."); 297 | } 298 | 299 | out = value; 300 | } 301 | 302 | void parseArgLineFunction(ArgReader & argReader, uint8_t & out) 303 | { 304 | const char * valueCStr = argReader.next(); 305 | if (valueCStr == NULL) 306 | { 307 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 308 | "Expected a line function name after '" + 309 | std::string(argReader.last()) + "'."); 310 | } 311 | 312 | std::string value = valueCStr; 313 | 314 | if (value == "none" || value == "NONE") 315 | { 316 | out = PAVR2_LINE_IS_NOTHING; 317 | } 318 | else if (value == "cd" || value == "CD") 319 | { 320 | out = PAVR2_LINE_IS_CD; 321 | } 322 | else if (value == "dsr" || value == "DSR") 323 | { 324 | out = PAVR2_LINE_IS_DSR; 325 | } 326 | else if (value == "dtr" || value == "DTR") 327 | { 328 | out = PAVR2_LINE_IS_DTR; 329 | } 330 | else if (value == "rts" || value == "RTS") 331 | { 332 | out = PAVR2_LINE_IS_RTS; 333 | } 334 | else if (value == "clock" || value == "CLOCK") 335 | { 336 | out = PAVR2_LINE_IS_CLOCK; 337 | } 338 | else if (value == "dtr-reset" || value == "DTR-RESET" || value == "DTR-reset") 339 | { 340 | out = PAVR2_LINE_IS_DTR_RESET; 341 | } 342 | else 343 | { 344 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 345 | "The line function name after '" + 346 | std::string(argReader.last()) + "' is invalid."); 347 | } 348 | } 349 | 350 | static void parseArgSerialNumber(ArgReader & argReader, Arguments & args) 351 | { 352 | const char * valueCStr = argReader.next(); 353 | if (valueCStr == NULL) 354 | { 355 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 356 | "Expected a serial number after '" 357 | + std::string(argReader.last()) + "'."); 358 | } 359 | if (valueCStr[0] == 0) 360 | { 361 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 362 | "An empty serial number was specified."); 363 | } 364 | 365 | args.serialNumberSpecified = true; 366 | args.serialNumber = valueCStr; 367 | } 368 | 369 | static void parseArgString(ArgReader & argReader, std::string & str) 370 | { 371 | const char * valueCStr = argReader.next(); 372 | if (valueCStr == NULL) 373 | { 374 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 375 | "Expected an argument after '" + 376 | std::string(argReader.last()) + "'."); 377 | } 378 | str = valueCStr; 379 | } 380 | 381 | static void parseArgRegulatorMode(ArgReader & argReader, Arguments & args) 382 | { 383 | const char * valueCStr = argReader.next(); 384 | std::string value = valueCStr == NULL ? "" : valueCStr; 385 | 386 | if (value == "auto") 387 | { 388 | args.regulatorMode = PAVR2_REGULATOR_MODE_AUTO; 389 | args.regulatorModeSpecified = true; 390 | } 391 | else if (value == "5v" || value == "5V") 392 | { 393 | args.regulatorMode = PAVR2_REGULATOR_MODE_5V; 394 | args.regulatorModeSpecified = true; 395 | } 396 | else if (value == "3v3" || value == "3V3") 397 | { 398 | args.regulatorMode = PAVR2_REGULATOR_MODE_3V3; 399 | args.regulatorModeSpecified = true; 400 | } 401 | else 402 | { 403 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 404 | "Expected 'auto', '5v', or '3v3' after '" 405 | + std::string(argReader.last()) + "'."); 406 | } 407 | } 408 | 409 | static void parseArgVccOutput(ArgReader & argReader, Arguments & args) 410 | { 411 | const char * valueCStr = argReader.next(); 412 | std::string value = valueCStr == NULL ? "" : valueCStr; 413 | 414 | if (value == "disabled" || value == "Disabled") 415 | { 416 | args.vccOutputEnabled = false; 417 | args.vccOutputEnabledSpecified = true; 418 | } 419 | else if (value == "enabled" || value == "Enabled") 420 | { 421 | args.vccOutputEnabled = true; 422 | args.vccOutputEnabledSpecified = true; 423 | } 424 | else 425 | { 426 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 427 | "Expected 'disabled' or 'enabled' after '" 428 | + std::string(argReader.last()) + "'."); 429 | } 430 | } 431 | 432 | static void parseArgVccOutputIndicator(ArgReader & argReader, Arguments & args) 433 | { 434 | const char * valueCStr = argReader.next(); 435 | std::string value = valueCStr == NULL ? "" : valueCStr; 436 | 437 | if (value == "blinking" || value == "Blinking") 438 | { 439 | args.vccOutputIndicator = PAVR2_VCC_OUTPUT_INDICATOR_BLINKING; 440 | args.vccOutputIndicatorSpecified = true; 441 | } 442 | else if (value == "steady" || value == "Steady") 443 | { 444 | args.vccOutputIndicator = PAVR2_VCC_OUTPUT_INDICATOR_STEADY; 445 | args.vccOutputIndicatorSpecified = true; 446 | } 447 | else 448 | { 449 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 450 | "Expected 'blinking' or 'steady' after '" 451 | + std::string(argReader.last()) + "'."); 452 | } 453 | } 454 | 455 | // [all-settings] 456 | static Arguments parseArgs(int argc, char ** argv) 457 | { 458 | ArgReader argReader(argc, argv); 459 | Arguments args; 460 | 461 | while (1) 462 | { 463 | const char * argCStr = argReader.next(); 464 | if (argCStr == NULL) 465 | { 466 | break; // Done reading arguments. 467 | } 468 | 469 | std::string arg = argCStr; 470 | 471 | if (arg == "-d") 472 | { 473 | parseArgSerialNumber(argReader, args); 474 | } 475 | else if (arg == "-s" || arg == "--status") 476 | { 477 | args.showStatus = true; 478 | } 479 | else if (arg == "--list") 480 | { 481 | args.showList = true; 482 | } 483 | else if (arg == "--regulator-mode") 484 | { 485 | parseArgRegulatorMode(argReader, args); 486 | } 487 | else if (arg == "--vcc-output") 488 | { 489 | parseArgVccOutput(argReader, args); 490 | } 491 | else if (arg == "--vcc-output-ind" || arg == "--vcc-output-indicator") 492 | { 493 | parseArgVccOutputIndicator(argReader, args); 494 | } 495 | else if (arg == "--freq") 496 | { 497 | parseArgString(argReader, args.frequencyName); 498 | args.frequencySpecified = true; 499 | } 500 | else if (arg == "--max-freq") 501 | { 502 | parseArgString(argReader, args.maxFrequencyName); 503 | args.maxFrequencySpecified = true; 504 | } 505 | else if (arg == "--line-a") 506 | { 507 | parseArgLineFunction(argReader, args.lineAFunction); 508 | args.lineAFunctionSpecified = true; 509 | } 510 | else if (arg == "--line-b") 511 | { 512 | parseArgLineFunction(argReader, args.lineBFunction); 513 | args.lineBFunctionSpecified = true; 514 | } 515 | else if (arg == "--sw-minor") 516 | { 517 | parseArgUInt32(argReader, args.softwareVersionMinor, 16); 518 | args.softwareVersionMinorSpecified = true; 519 | } 520 | else if (arg == "--sw-major") 521 | { 522 | parseArgUInt32(argReader, args.softwareVersionMajor, 16); 523 | args.softwareVersionMajorSpecified = true; 524 | } 525 | else if (arg == "--hw") 526 | { 527 | parseArgUInt32(argReader, args.hardwareVersion, 16); 528 | args.hardwareVersionSpecified = true; 529 | } 530 | else if (arg == "--vcc-vdd-max-range") 531 | { 532 | parseArgUInt32(argReader, args.vccVddMaxRange); 533 | args.vccVddMaxRangeSpecified = true; 534 | } 535 | else if (arg == "--vcc-3v3-min") 536 | { 537 | parseArgUInt32(argReader, args.vcc3v3Min); 538 | args.vcc3v3MinSpecified = true; 539 | } 540 | else if (arg == "--vcc-3v3-max") 541 | { 542 | parseArgUInt32(argReader, args.vcc3v3Max); 543 | args.vcc3v3MaxSpecified = true; 544 | } 545 | else if (arg == "--vcc-5v-min") 546 | { 547 | parseArgUInt32(argReader, args.vcc5vMin); 548 | args.vcc5vMinSpecified = true; 549 | } 550 | else if (arg == "--vcc-5v-max") 551 | { 552 | parseArgUInt32(argReader, args.vcc5vMax); 553 | args.vcc5vMaxSpecified = true; 554 | } 555 | else if (arg == "--restore-defaults") 556 | { 557 | args.restoreDefaults = true; 558 | } 559 | else if (arg == "--prog-port") 560 | { 561 | args.printProgrammingPort = true; 562 | } 563 | else if (arg == "--ttl-port") 564 | { 565 | args.printTtlPort = true; 566 | } 567 | else if (arg == "-h" || arg == "--help" || 568 | arg == "--h" || arg == "-help" || arg == "/help" || arg == "/h") 569 | { 570 | args.showHelp = true; 571 | } 572 | else if (arg == "-r" || arg == "--digital-read") 573 | { 574 | args.digitalRead = true; 575 | } 576 | else 577 | { 578 | throw ExceptionWithExitCode(PAVRPGM_ERROR_BAD_ARGS, 579 | std::string("Unknown option: '") + arg + "'."); 580 | } 581 | } 582 | return args; 583 | } 584 | 585 | static void adjustArguments(Arguments & args) 586 | { 587 | if (!args.actionSpecified()) 588 | { 589 | // The user did not explicitly specify an action on the command-line. 590 | // Show the help screen. 591 | args.showHelp = true; 592 | } 593 | } 594 | 595 | static void printProgrammerList(ProgrammerSelector & selector) 596 | { 597 | for (const ProgrammerInstance & instance : selector.listProgrammers()) 598 | { 599 | std::cout << std::left << std::setfill(' '); 600 | std::cout << std::setw(17) << instance.getSerialNumber() + "," << " "; 601 | std::cout << std::setw(45) << instance.getName(); 602 | std::cout << std::endl; 603 | } 604 | } 605 | 606 | // [all-settings] 607 | static void printProgrammerStatus(ProgrammerSelector & selector) 608 | { 609 | ProgrammerInstance instance = selector.selectProgrammer(); 610 | ProgrammerHandle handle(instance); 611 | std::string firmwareVersion = handle.getFirmwareVersionString(); 612 | ProgrammerSettings settings = handle.getSettings(); 613 | ProgrammerVariables variables = handle.getVariables(); 614 | 615 | // The output here is compatible with YAML so that people can more easily 616 | // write scripts that use it. 617 | 618 | int leftColumnWidth = 41; 619 | auto leftColumn = std::setw(leftColumnWidth); 620 | 621 | std::string frequency = Programmer::getFrequencyName( 622 | settings.sckDuration, settings.ispFastestPeriod); 623 | std::string maxFrequency = Programmer::getMaxFrequencyName( 624 | settings.ispFastestPeriod); 625 | 626 | std::cout << std::left << std::setfill(' '); 627 | 628 | std::cout << leftColumn << "Name: " 629 | << handle.getInstance().getName() << std::endl; 630 | 631 | std::cout << leftColumn << "Serial number: " 632 | << handle.getInstance().getSerialNumber() << std::endl; 633 | 634 | std::cout << leftColumn << "Firmware version: " 635 | << firmwareVersion << std::endl; 636 | 637 | std::cout << leftColumn << "Programming port: " 638 | << handle.getInstance().tryGetProgrammingPortName() << std::endl; 639 | 640 | std::cout << leftColumn << "TTL port: " 641 | << handle.getInstance().tryGetTtlPortName() << std::endl; 642 | 643 | std::cout << std::endl; 644 | 645 | std::cout << "Settings:" << std::endl; 646 | 647 | std::cout << leftColumn << " ISP frequency (kHz): " 648 | << frequency << std::endl; 649 | 650 | std::cout << leftColumn << " Max ISP frequency (kHz): " 651 | << maxFrequency << std::endl; 652 | 653 | std::cout << leftColumn << " Regulator mode: " 654 | << Programmer::convertRegulatorModeToString(settings.regulatorMode) 655 | << std::endl; 656 | 657 | std::cout << leftColumn << " VCC output: " 658 | << (settings.vccOutputEnabled ? "Enabled" : "Disabled") << std::endl; 659 | 660 | std::cout << leftColumn << " VCC output indicator: " 661 | << (settings.vccOutputIndicator ? "Steady" : "Blinking") << std::endl; 662 | 663 | std::cout << leftColumn << " Line A function: " 664 | << Programmer::convertLineFunctionToString(settings.lineAFunction) 665 | << std::endl; 666 | 667 | std::cout << leftColumn << " Line B function: " 668 | << Programmer::convertLineFunctionToString(settings.lineBFunction) 669 | << std::endl; 670 | 671 | std::cout << leftColumn << " VCC/VDD maximum range (mV): " 672 | << settings.vccVddMaxRange << std::endl; 673 | 674 | std::cout << leftColumn << " VCC 3.3 V minimum (mV): " 675 | << settings.vcc3v3Min << std::endl; 676 | 677 | std::cout << leftColumn << " VCC 3.3 V maximum (mV): " 678 | << settings.vcc3v3Max << std::endl; 679 | 680 | std::cout << leftColumn << " VCC 5 V minimum (mV): " 681 | << settings.vcc5vMin << std::endl; 682 | 683 | std::cout << leftColumn << " VCC 5 V maximum (mV): " 684 | << settings.vcc5vMax << std::endl; 685 | 686 | std::cout << std::hex << std::uppercase; 687 | 688 | std::cout << leftColumn << " STK500 hardware version: " 689 | << settings.hardwareVersion << std::endl; 690 | 691 | std::cout << leftColumn << " STK500 software version: " 692 | << settings.softwareVersionMajor << "." 693 | << settings.softwareVersionMinor << std::endl; 694 | 695 | std::cout << std::dec; 696 | 697 | std::cout << std::endl; 698 | 699 | if (variables.hasResultsFromLastProgramming) 700 | { 701 | std::cout << "Results from last programming:" << std::endl; 702 | std::cout << leftColumn << " Programming error: " 703 | << Programmer::convertProgrammingErrorToShortString(variables.programmingError) 704 | << std::endl; 705 | std::cout << leftColumn << " Target VCC measured minimum (mV): " 706 | << variables.targetVccMeasuredMinMv 707 | << std::endl; 708 | std::cout << leftColumn << " Target VCC measured maximum (mV): " 709 | << variables.targetVccMeasuredMaxMv 710 | << std::endl; 711 | std::cout << leftColumn << " Programmer VDD measured minimum (mV): " 712 | << variables.programmerVddMeasuredMinMv 713 | << std::endl; 714 | std::cout << leftColumn << " Programmer VDD measured maximum (mV): " 715 | << variables.programmerVddMeasuredMaxMv 716 | << std::endl; 717 | } 718 | else 719 | { 720 | std::cout << leftColumn << "Results from last programming: " 721 | << "N/A" << std::endl; 722 | } 723 | 724 | std::cout << std::endl; 725 | 726 | std::cout << "Current status:" << std::endl; 727 | std::cout << leftColumn << " Target VCC (mV): " 728 | << variables.targetVccMv 729 | << std::endl; 730 | std::cout << leftColumn << " Programmer VDD (mV): " 731 | << variables.programmerVddMv 732 | << std::endl; 733 | std::cout << leftColumn << " VDD regulator set point: " 734 | << Programmer::convertRegulatorLevelToString(variables.regulatorLevel) 735 | << std::endl; 736 | 737 | std::cout << leftColumn << " Last device reset: " 738 | << Programmer::convertDeviceResetToString(variables.lastDeviceReset) 739 | << std::endl; 740 | } 741 | 742 | // Print the name of the programming serial port (e.g. "COM 4"). 743 | void printProgrammingPort(ProgrammerSelector & selector) 744 | { 745 | ProgrammerInstance instance = selector.selectProgrammer(); 746 | std::string programmingPortName = instance.getProgrammingPortName(); 747 | std::cout << programmingPortName << std::endl; 748 | } 749 | 750 | void printTtlPort(ProgrammerSelector & selector) 751 | { 752 | ProgrammerInstance instance = selector.selectProgrammer(); 753 | std::string programmingPortName = instance.getTtlPortName(); 754 | std::cout << programmingPortName << std::endl; 755 | } 756 | 757 | static void printDigitalReadings(ProgrammerSelector & selector) 758 | { 759 | ProgrammerHandle handle(selector.selectProgrammer()); 760 | ProgrammerDigitalReadings readings = handle.digitalRead(); 761 | 762 | std::cout << "PORTA: " << std::bitset<8>(readings.portA) << std::endl; 763 | std::cout << "PORTB: " << std::bitset<8>(readings.portB) << std::endl; 764 | std::cout << "PORTC: " << std::bitset<8>(readings.portC) << std::endl; 765 | } 766 | 767 | // [all-settings] 768 | static void applySettings(ProgrammerSelector & selector, const Arguments args) 769 | { 770 | assert(args.settingsSpecified()); 771 | 772 | ProgrammerHandle handle(selector.selectProgrammer()); 773 | 774 | if (args.restoreDefaults) 775 | { 776 | handle.restoreDefaults(); 777 | } 778 | 779 | ProgrammerSettings settings = handle.getSettings(); 780 | 781 | if (args.maxFrequencySpecified) 782 | { 783 | Programmer::setMaxFrequency(settings, args.maxFrequencyName); 784 | } 785 | 786 | if (args.frequencySpecified) 787 | { 788 | Programmer::setFrequency(settings, args.frequencyName); 789 | } 790 | 791 | if (args.regulatorModeSpecified) 792 | { 793 | settings.regulatorMode = args.regulatorMode; 794 | } 795 | 796 | if (args.vccOutputEnabledSpecified) 797 | { 798 | settings.vccOutputEnabled = args.vccOutputEnabled; 799 | } 800 | 801 | if (args.vccOutputIndicatorSpecified) 802 | { 803 | settings.vccOutputIndicator = args.vccOutputIndicator; 804 | } 805 | 806 | if (args.lineAFunctionSpecified) 807 | { 808 | settings.lineAFunction = args.lineAFunction; 809 | } 810 | 811 | if (args.lineBFunctionSpecified) 812 | { 813 | settings.lineBFunction = args.lineBFunction; 814 | } 815 | 816 | if (args.softwareVersionMajorSpecified) 817 | { 818 | settings.softwareVersionMajor = args.softwareVersionMajor; 819 | } 820 | 821 | if (args.softwareVersionMinorSpecified) 822 | { 823 | settings.softwareVersionMinor = args.softwareVersionMinor; 824 | } 825 | 826 | if (args.hardwareVersionSpecified) 827 | { 828 | settings.hardwareVersion = args.hardwareVersion; 829 | } 830 | 831 | if (args.vccVddMaxRangeSpecified) 832 | { 833 | settings.vccVddMaxRange = args.vccVddMaxRange; 834 | } 835 | 836 | if (args.vcc3v3MinSpecified) 837 | { 838 | settings.vcc3v3Min = args.vcc3v3Min; 839 | } 840 | 841 | if (args.vcc3v3MaxSpecified) 842 | { 843 | settings.vcc3v3Max = args.vcc3v3Max; 844 | } 845 | 846 | if (args.vcc5vMinSpecified) 847 | { 848 | settings.vcc5vMin = args.vcc5vMin; 849 | } 850 | 851 | if (args.vcc5vMaxSpecified) 852 | { 853 | settings.vcc5vMax = args.vcc5vMax; 854 | } 855 | 856 | handle.applySettings(settings); 857 | } 858 | 859 | static void run(int argc, char ** argv) 860 | { 861 | Arguments args = parseArgs(argc, argv); 862 | adjustArguments(args); 863 | 864 | if (args.showHelp) 865 | { 866 | std::cout << help; 867 | return; 868 | } 869 | 870 | ProgrammerSelector selector; 871 | if (args.serialNumberSpecified) 872 | { 873 | selector.specifySerialNumber(args.serialNumber); 874 | } 875 | 876 | if (args.showList) 877 | { 878 | printProgrammerList(selector); 879 | return; 880 | } 881 | 882 | if (args.settingsSpecified()) 883 | { 884 | applySettings(selector, args); 885 | } 886 | 887 | if (args.showStatus) 888 | { 889 | printProgrammerStatus(selector); 890 | } 891 | 892 | if (args.printProgrammingPort) 893 | { 894 | printProgrammingPort(selector); 895 | } 896 | 897 | if (args.printTtlPort) 898 | { 899 | printTtlPort(selector); 900 | } 901 | 902 | if (args.digitalRead) 903 | { 904 | printDigitalReadings(selector); 905 | } 906 | } 907 | 908 | int main(int argc, char ** argv) 909 | { 910 | int exitCode = 0; 911 | 912 | try 913 | { 914 | run(argc, argv); 915 | } 916 | catch(const ExceptionWithExitCode & error) 917 | { 918 | std::cerr << "Error: " << error.what() << std::endl; 919 | exitCode = error.getCode(); 920 | } 921 | catch(const std::exception & error) 922 | { 923 | std::cerr << "Error: " << error.what() << std::endl; 924 | exitCode = PAVRPGM_ERROR_OPERATION_FAILED; 925 | } 926 | 927 | if (exitCode == PAVRPGM_ERROR_DEVICE_NOT_FOUND) 928 | { 929 | if (pgm03aPresent()) 930 | { 931 | std::cerr << std::endl; 932 | std::cerr << pgm03aMessage << std::endl; 933 | } 934 | } 935 | 936 | return exitCode; 937 | } 938 | -------------------------------------------------------------------------------- /cli/cli_info.rc.in: -------------------------------------------------------------------------------- 1 | #define VERSION ${SOFTWARE_VERSION_MAJOR},${SOFTWARE_VERSION_MINOR},${SOFTWARE_VERSION_PATCH},0 2 | #define VERSION_STR "${SOFTWARE_VERSION}" 3 | 4 | #include 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | FILEVERSION VERSION 8 | PRODUCTVERSION VERSION 9 | FILEOS VOS_NT_WINDOWS32 10 | FILETYPE VFT_APP 11 | BEGIN 12 | BLOCK "StringFileInfo" 13 | BEGIN 14 | BLOCK "040904E4" 15 | BEGIN 16 | VALUE "CompanyName", "Pololu Corporation" 17 | VALUE "FileDescription", "Pololu USB AVR Programmer v2 Command-line Utility" 18 | VALUE "FileVersion", VERSION_STR 19 | VALUE "ProductName", "Pololu USB AVR Programmer v2 Command-line Utility (${CMAKE_BUILD_TYPE})" 20 | VALUE "ProductVersion", VERSION_STR 21 | VALUE "LegalCopyright", "Copyright (C) ${YEAR} Pololu Corporation" 22 | END 23 | END 24 | BLOCK "VarFileInfo" 25 | BEGIN 26 | VALUE "Translation", 0x409, 1252 27 | END 28 | END 29 | 30 | -------------------------------------------------------------------------------- /cli/exception_with_exit_code.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ExceptionWithExitCode : public std::exception 6 | { 7 | public: 8 | explicit ExceptionWithExitCode(uint8_t code, std::string message) 9 | : code(code), msg(message) 10 | { 11 | } 12 | 13 | virtual const char * what() const noexcept 14 | { 15 | return msg.c_str(); 16 | } 17 | 18 | std::string message() const 19 | { 20 | return msg; 21 | } 22 | 23 | uint8_t getCode() const noexcept 24 | { 25 | return code; 26 | } 27 | 28 | private: 29 | uint8_t code; 30 | std::string msg; 31 | }; 32 | -------------------------------------------------------------------------------- /cli/exit_codes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Process exit codes 4 | #define PAVRPGM_ERROR_BAD_ARGS 1 5 | #define PAVRPGM_ERROR_OPERATION_FAILED 2 6 | #define PAVRPGM_ERROR_DEVICE_NOT_FOUND 3 7 | #define PAVRPGM_ERROR_DEVICE_MULTIPLE_FOUND 4 8 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | rec { 2 | nixcrpkgs = import {}; 3 | 4 | src = nixcrpkgs.filter ./.; 5 | 6 | build = (import ./nix/build.nix) src; 7 | 8 | win32 = build "win" nixcrpkgs.win32; 9 | linux-x86 = build "linux-x86" nixcrpkgs.linux-x86; 10 | linux-rpi = build "linux-rpi" nixcrpkgs.linux-rpi; 11 | macos = build "macos" nixcrpkgs.macos; 12 | 13 | installers = nixcrpkgs.bundle { 14 | win32 = win32.installer; 15 | linux-x86 = linux-x86.installer; 16 | linux-rpi = linux-rpi.installer; 17 | macos = macos.installer; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /drivers/pavr2-bootloader.inf: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Pololu Corporation 2 | [Strings] 3 | DriverPackageDisplayName="Pololu USB AVR Programmer v2 Bootloader" 4 | ManufacturerName="Pololu Corporation" 5 | ClassName="Universal Serial Bus devices" 6 | DeviceInterfaceGUID="{82959cfa-7a2d-431f-a9a1-500b55d90950}" 7 | p00AF="Pololu USB AVR Programmer v2 Bootloader" 8 | p00BA="Pololu USB AVR Programmer v2.1 Bootloader" 9 | 10 | [DefaultInstall] 11 | CopyINF=pavr2-bootloader.inf 12 | 13 | [Version] 14 | DriverVer=01/23/2018,1.0.0 15 | Signature=$Windows NT$ 16 | Class=USBDevice 17 | ClassGuid={88BAE032-5A81-49F0-BC3D-A4FF138216D6} 18 | Provider=%ManufacturerName% 19 | CatalogFile=pololu.cat 20 | DriverPackageDisplayName=%DriverPackageDisplayName% 21 | PnpLockdown=1 22 | 23 | [Manufacturer] 24 | %ManufacturerName%=Models,NTx86,NTamd64,NTarm 25 | 26 | [ClassInstall32] 27 | AddReg=ClassInstall_AddReg 28 | 29 | [ClassInstall_AddReg] 30 | HKR,,,0,%ClassName% 31 | HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20" 32 | HKR,,NoInstallClass,,1 33 | HKR,,BootCritical,,0 34 | HKR,,Configurable,,1 35 | 36 | [Models.NTx86] 37 | %p00AF%=DriverInstall, USB\VID_1FFB&PID_00AF 38 | %p00BA%=DriverInstall, USB\VID_1FFB&PID_00BA 39 | 40 | [Models.NTamd64] 41 | %p00AF%=DriverInstall, USB\VID_1FFB&PID_00AF 42 | %p00BA%=DriverInstall, USB\VID_1FFB&PID_00BA 43 | 44 | [Models.NTarm] 45 | %p00AF%=DriverInstall, USB\VID_1FFB&PID_00AF 46 | %p00BA%=DriverInstall, USB\VID_1FFB&PID_00BA 47 | 48 | [DriverInstall] 49 | Include=Winusb.inf 50 | Needs=WINUSB.NT 51 | 52 | [DriverInstall.Services] 53 | Include=Winusb.inf 54 | Needs=WINUSB.NT.Services 55 | 56 | [DriverInstall.HW] 57 | AddReg=Dev_AddReg 58 | 59 | [Dev_AddReg] 60 | HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceInterfaceGUID% 61 | -------------------------------------------------------------------------------- /drivers/pavr2-serial.inf: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Pololu Corporation 2 | 3 | [Strings] 4 | DriverPackageDisplayName="Pololu USB AVR Programmer v2 Ports" 5 | ManufacturerName="Pololu Corporation" 6 | ServiceName="Microsoft USB Serial Driver" 7 | p00B0m01="Pololu USB AVR Programmer v2 Programming Port" 8 | p00B0m03="Pololu USB AVR Programmer v2 TTL Serial Port" 9 | p00BBm01="Pololu USB AVR Programmer v2.1 Programming Port" 10 | p00BBm03="Pololu USB AVR Programmer v2.1 TTL Serial Port" 11 | 12 | [DefaultInstall] 13 | CopyINF=pavr2-serial.inf 14 | 15 | [Version] 16 | DriverVer=01/23/2018,1.0.0 17 | Signature=$Windows NT$ 18 | Class=Ports 19 | ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} 20 | Provider=%ManufacturerName% 21 | CatalogFile=pololu.cat 22 | DriverPackageDisplayName=%DriverPackageDisplayName% 23 | PnpLockdown=1 24 | 25 | [Manufacturer] 26 | %ManufacturerName%=Models,NTx86,NTamd64,NTarm 27 | 28 | [DestinationDirs] 29 | DefaultDestDir=12 30 | FakeModemCopyFileSection=12 31 | 32 | [Models.NTx86] 33 | %p00B0m01%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_01 34 | %p00B0m03%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_03 35 | %p00BBm01%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_01 36 | %p00BBm03%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_03 37 | 38 | [Models.NTamd64] 39 | %p00B0m01%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_01 40 | %p00B0m03%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_03 41 | %p00BBm01%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_01 42 | %p00BBm03%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_03 43 | 44 | [Models.NTarm] 45 | %p00B0m01%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_01 46 | %p00B0m03%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_03 47 | %p00BBm01%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_01 48 | %p00BBm03%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_03 49 | 50 | [DriverInstall] 51 | Include=mdmcpq.inf,usb.inf 52 | CopyFiles=FakeModemCopyFileSection 53 | AddReg=DriverAddReg 54 | 55 | [DriverAddReg] 56 | HKR,,DevLoader,,*ntkern 57 | HKR,,NTMPDriver,,usbser.sys 58 | HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" 59 | 60 | [DriverInstall.Services] 61 | Include=mdmcpq.inf 62 | AddService=usbser,0x2,DriverService 63 | 64 | [DriverService] 65 | DisplayName=%ServiceName% 66 | ServiceType=1 67 | StartType=3 68 | ErrorControl=1 69 | ServiceBinary=%12%\usbser.sys 70 | LoadOrderGroup=Base 71 | -------------------------------------------------------------------------------- /drivers/pavr2.inf: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Pololu Corporation 2 | 3 | ; This driver file is not needed on Windows 8.1 and later because each device 4 | ; implements Microsoft OS 2.0 Descriptors. 5 | [Strings] 6 | DriverPackageDisplayName="Pololu USB AVR Programmer v2 Drivers" 7 | ManufacturerName="Pololu Corporation" 8 | ClassName="Universal Serial Bus devices" 9 | DeviceInterfaceGUID="{7bcb89b1-f483-4852-87c3-9848c3c05be5}" 10 | p00B0="Pololu USB AVR Programmer v2" 11 | p00BB="Pololu USB AVR Programmer v2.1" 12 | 13 | [DefaultInstall] 14 | CopyINF=pavr2.inf 15 | 16 | [Version] 17 | DriverVer=01/23/2018,1.0.0 18 | Signature=$Windows NT$ 19 | Class=USBDevice 20 | ClassGuid={88BAE032-5A81-49F0-BC3D-A4FF138216D6} 21 | Provider=%ManufacturerName% 22 | CatalogFile=pololu.cat 23 | DriverPackageDisplayName=%DriverPackageDisplayName% 24 | PnpLockdown=1 25 | 26 | [Manufacturer] 27 | %ManufacturerName%=Models,NTx86,NTamd64,NTarm 28 | 29 | [ClassInstall32] 30 | AddReg=ClassInstall_AddReg 31 | 32 | [ClassInstall_AddReg] 33 | HKR,,,0,%ClassName% 34 | HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20" 35 | HKR,,NoInstallClass,,1 36 | HKR,,BootCritical,,0 37 | HKR,,Configurable,,1 38 | 39 | [Models.NTx86] 40 | %p00B0%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_00 41 | %p00BB%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_00 42 | 43 | [Models.NTamd64] 44 | %p00B0%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_00 45 | %p00BB%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_00 46 | 47 | [Models.NTarm] 48 | %p00B0%=DriverInstall, USB\VID_1FFB&PID_00B0&MI_00 49 | %p00BB%=DriverInstall, USB\VID_1FFB&PID_00BB&MI_00 50 | 51 | [DriverInstall] 52 | Include=Winusb.inf 53 | Needs=WINUSB.NT 54 | 55 | [DriverInstall.Services] 56 | Include=Winusb.inf 57 | Needs=WINUSB.NT.Services 58 | 59 | [DriverInstall.HW] 60 | AddReg=Dev_AddReg 61 | 62 | [Dev_AddReg] 63 | HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceInterfaceGUID% 64 | -------------------------------------------------------------------------------- /drivers/pololu.cat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pololu/pololu-usb-avr-programmer-v2/5bcce677e9595203e47e820e410d6e158e88684d/drivers/pololu.cat -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | use_cxx11() 2 | 3 | # Find includes in corresponding build directories 4 | set (CMAKE_INCLUDE_CURRENT_DIR ON) 5 | 6 | include_directories ( 7 | "${CMAKE_CURRENT_SOURCE_DIR}/qt" 8 | ) 9 | 10 | # Run Qt's moc automatically. 11 | set (CMAKE_AUTOMOC ON) 12 | 13 | find_package (Qt5Widgets) 14 | 15 | configure_file (gui_info.rc.in gui_info.rc) 16 | 17 | if (POLOLU_BUILD) 18 | if (APPLE) 19 | # Embed app.icns in the executable. 20 | qt5_add_resources(ICON_QRC qt/app_icns.qrc) 21 | else () 22 | # Embed app.ico in the executable. 23 | qt5_add_resources(ICON_QRC qt/app_ico.qrc) 24 | endif () 25 | endif () 26 | 27 | add_executable (gui 28 | main.cpp 29 | main_view.cpp 30 | main_controller.cpp 31 | main_model.cpp 32 | qt/main_window.cpp 33 | qt/voltage_spin_box.cpp 34 | qt/frequency_validator.cpp 35 | ${CMAKE_CURRENT_BINARY_DIR}/gui_info.rc 36 | ${ICON_QRC} 37 | ${ICON_FILE} 38 | ) 39 | 40 | set_target_properties (gui PROPERTIES 41 | OUTPUT_NAME ${GUI_NAME} 42 | ) 43 | 44 | if (WIN32) 45 | set_target_properties (gui PROPERTIES 46 | LINK_FLAGS "-mwindows" 47 | ) 48 | endif () 49 | 50 | target_link_libraries (gui Qt5::Widgets) 51 | 52 | target_link_libraries (gui lib) 53 | 54 | install(TARGETS gui DESTINATION bin) 55 | -------------------------------------------------------------------------------- /gui/gui_info.rc.in: -------------------------------------------------------------------------------- 1 | #define VERSION ${SOFTWARE_VERSION_MAJOR},${SOFTWARE_VERSION_MINOR},${SOFTWARE_VERSION_PATCH},0 2 | #define VERSION_STR "${SOFTWARE_VERSION}" 3 | 4 | #cmakedefine POLOLU_BUILD 5 | 6 | #include 7 | 8 | #ifdef POLOLU_BUILD 9 | IDI_ICON1 ICON DISCARDABLE "${CMAKE_SOURCE_DIR}/images/app.ico" 10 | #endif 11 | 12 | VS_VERSION_INFO VERSIONINFO 13 | FILEVERSION VERSION 14 | PRODUCTVERSION VERSION 15 | FILEOS VOS_NT_WINDOWS32 16 | FILETYPE VFT_APP 17 | BEGIN 18 | BLOCK "StringFileInfo" 19 | BEGIN 20 | BLOCK "040904E4" 21 | BEGIN 22 | VALUE "CompanyName", "Pololu Corporation" 23 | VALUE "FileDescription", "Pololu USB AVR Programmer v2 Configuration Utility" 24 | VALUE "FileVersion", VERSION_STR 25 | VALUE "ProductName", "Pololu USB AVR Programmer v2 Configuration Utility (${CMAKE_BUILD_TYPE})" 26 | VALUE "ProductVersion", VERSION_STR 27 | VALUE "LegalCopyright", "Copyright (C) ${YEAR} Pololu Corporation" 28 | END 29 | END 30 | BLOCK "VarFileInfo" 31 | BEGIN 32 | VALUE "Translation", 0x409, 1252 33 | END 34 | END 35 | 36 | -------------------------------------------------------------------------------- /gui/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "main_model.h" 3 | #include "main_controller.h" 4 | #include "main_view.h" 5 | 6 | int main(int argc, char ** argv) 7 | { 8 | QApplication app(argc, argv); 9 | MainModel model; 10 | MainController controller; 11 | MainView view; 12 | view.init(&model, &controller); 13 | controller.init(&model, &view); 14 | view.showWindow(); 15 | return app.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /gui/main_controller.cpp: -------------------------------------------------------------------------------- 1 | #include "main_controller.h" 2 | #include "main_view.h" 3 | #include "main_model.h" 4 | 5 | #include 6 | 7 | /** This is how often we fetch the variables from the device. */ 8 | static const uint32_t UPDATE_INTERVAL_MS = 1000; 9 | 10 | void MainController::init(MainModel * model, MainView * view) 11 | { 12 | this->model = model; 13 | this->view = view; 14 | } 15 | 16 | void MainController::start() 17 | { 18 | assert(!model->connected()); 19 | 20 | // Start the update timer so that update() will be called regularly. 21 | view->startUpdateTimer(UPDATE_INTERVAL_MS); 22 | 23 | // The program has just started, so try to connect to a device. 24 | 25 | bool successfullyUpdatedList = tryUpdateDeviceList(); 26 | if (!successfullyUpdatedList) 27 | { 28 | view->handleModelChanged(); 29 | return; 30 | } 31 | 32 | if (model->deviceList.size() > 0) 33 | { 34 | reallyConnect(); 35 | } 36 | else 37 | { 38 | // No device was found. Let's look for older versions of this 39 | // product and warn the user that this software does not work with 40 | // them. 41 | bool pgm03aFound = false; 42 | try 43 | { 44 | pgm03aFound = pgm03aPresent(); 45 | } 46 | catch (const std::exception & e) 47 | { 48 | showException(e, "There was an error while checking for older programmers."); 49 | } 50 | if (pgm03aFound) 51 | { 52 | view->showWarningMessage(pgm03aMessage); 53 | } 54 | view->handleModelChanged(); 55 | } 56 | } 57 | 58 | void MainController::connect() 59 | { 60 | // The user wants to connect to a device. 61 | 62 | if (model->connected()) 63 | { 64 | // Since we don't allow them to pick what device to connect to, and 65 | // we are already connected, just return. 66 | return; 67 | } 68 | 69 | bool successfullyUpdatedList = tryUpdateDeviceList(); 70 | if (!successfullyUpdatedList) 71 | { 72 | return; 73 | } 74 | 75 | if (model->deviceList.size() > 0) 76 | { 77 | reallyConnect(); 78 | } 79 | else 80 | { 81 | view->showErrorMessage( 82 | "No programmer was found. " 83 | "Please verify that the programmer is connected to the computer via USB." 84 | ); 85 | } 86 | } 87 | 88 | void MainController::disconnect() 89 | { 90 | if (!model->connected()) { return; } 91 | model->disconnectByUser(); 92 | view->handleModelChanged(); 93 | } 94 | 95 | void MainController::reloadSettings() 96 | { 97 | if (!model->connected()) { return; } 98 | try 99 | { 100 | model->reloadSettings(); 101 | } 102 | catch (const std::exception & e) 103 | { 104 | showException(e, "There was an error loading the settings from the device."); 105 | } 106 | view->handleSettingsChanged(); 107 | } 108 | 109 | void MainController::restoreDefaultSettings() 110 | { 111 | if (!model->connected()) { return; } 112 | 113 | std::string question = "This will reset all of your device's settings " 114 | "back to their default values. " 115 | "You will lose your custom settings. " 116 | "Are you sure you want to continue?"; 117 | if (!view->confirm(question)) 118 | { 119 | return; 120 | } 121 | 122 | bool restoreSuccess = false; 123 | try 124 | { 125 | model->deviceHandle.restoreDefaults(); 126 | restoreSuccess = true; 127 | } 128 | catch (const std::exception & e) 129 | { 130 | showException(e, "There was an error resetting to the default settings."); 131 | } 132 | 133 | // This takes care of reloading the settings and telling the view to update. 134 | reloadSettings(); 135 | 136 | if (restoreSuccess) 137 | { 138 | view->showInfoMessage( 139 | "Your device's settings have been reset to their default values."); 140 | } 141 | } 142 | 143 | /** Returns true if the device list includes the specified device. */ 144 | static bool deviceListIncludes( 145 | const std::vector & deviceList, 146 | const ProgrammerInstance & device) 147 | { 148 | std::string id = device.getOsId(); 149 | for (const ProgrammerInstance & candidate : deviceList) 150 | { 151 | if (candidate.getOsId() == id) 152 | { 153 | return true; 154 | } 155 | } 156 | return false; 157 | } 158 | 159 | void MainController::update() 160 | { 161 | // This is called regularly by the view when it is time to check for updates 162 | // to the state of the USB devices. This runs on the same thread as 163 | // everything else, so we should be careful not to do anything too slow 164 | // here. If the user tries to use the UI at all while this function is 165 | // running, the UI cannot respond until this function returns. 166 | 167 | if (model->connected()) 168 | { 169 | // First, see if the programmer we are connected to is still available. 170 | // Note: It would be nice if the libusbp::generic_handle class had a 171 | // function that tests if the actual handle we are using is still valid. 172 | // This would be better for tricky cases like if someone unplugs and 173 | // plugs the same programmer in very fast. 174 | 175 | bool successfullyUpdatedList = tryUpdateDeviceList(); 176 | if (!successfullyUpdatedList) 177 | { 178 | // Ignore this unexpected error. We are already successfully 179 | // connected to the device, so it will still be on our list, and we 180 | // will try to keep using it if we can. 181 | } 182 | 183 | bool deviceStillPresent = deviceListIncludes( 184 | model->deviceList, model->deviceHandle.getInstance()); 185 | 186 | if (deviceStillPresent) 187 | { 188 | // Reload the variables from the device. 189 | try 190 | { 191 | model->reloadVariables(); 192 | } 193 | catch (const std::exception & e) 194 | { 195 | // Ignore the exception. The model provides other ways to tell that 196 | // the variable update failed, and the exact message is probably 197 | // not that useful since it is probably just a generic problem with 198 | // the USB connection. 199 | } 200 | view->handleVariablesChanged(); 201 | } 202 | else 203 | { 204 | // The device is gone. 205 | model->disconnectByError("The connection to the device was lost."); 206 | view->handleModelChanged(); 207 | } 208 | } 209 | else 210 | { 211 | // We are not connected, so consider auto-connecting to a device. 212 | 213 | if (model->connectionError) 214 | { 215 | // There is an error related to a previous connection or connection 216 | // attempt, so don't automatically reconnect. That would be 217 | // confusing, because the user might be looking away and not notice 218 | // that the connection was lost and then regained, or they could be 219 | // trying to read the error message. 220 | } 221 | else if (model->disconnectedByUser) 222 | { 223 | // The user explicitly disconnected the last connection, so don't 224 | // automatically reconnect. 225 | } 226 | else 227 | { 228 | bool successfullyUpdatedList = tryUpdateDeviceList(); 229 | if (!successfullyUpdatedList) 230 | { 231 | // Since this is a background update, don't report 232 | // this error to the user. 233 | } 234 | 235 | if (successfullyUpdatedList && model->deviceList.size() > 0) 236 | { 237 | reallyConnect(); 238 | } 239 | } 240 | } 241 | } 242 | 243 | bool MainController::exit() 244 | { 245 | if (model->connected() && model->settingsModified) 246 | { 247 | std::string question = 248 | "The settings you changed have not been applied to the device. " 249 | "If you exit now, those changes will be lost. " 250 | "Are you sure you want to exit?"; 251 | return view->confirm(question); 252 | } 253 | else 254 | { 255 | return true; 256 | } 257 | } 258 | 259 | void MainController::reallyConnect() 260 | { 261 | assert(model->deviceList.size() > 0); 262 | 263 | try 264 | { 265 | model->connect(model->deviceList.at(0)); 266 | } 267 | catch (const std::exception & e) 268 | { 269 | showException(e, "There was an error connecting to the device."); 270 | view->handleModelChanged(); 271 | return; 272 | } 273 | 274 | try 275 | { 276 | model->reloadFirmwareVersionString(); 277 | } 278 | catch (const std::exception & e) 279 | { 280 | showException(e, "There was an error getting the firmware version."); 281 | } 282 | 283 | try 284 | { 285 | model->reloadSettings(); 286 | } 287 | catch (const std::exception & e) 288 | { 289 | showException(e, "There was an error loading settings from the device."); 290 | } 291 | 292 | try 293 | { 294 | model->reloadVariables(); 295 | } 296 | catch (const std::exception & e) 297 | { 298 | showException(e, "There was an error getting the status of the device."); 299 | } 300 | 301 | view->handleModelChanged(); 302 | } 303 | 304 | bool MainController::tryUpdateDeviceList() 305 | { 306 | try 307 | { 308 | model->updateDeviceList(); 309 | return true; 310 | } 311 | catch (const std::exception & e) 312 | { 313 | model->setConnectionError("Failed to get the list of devices."); 314 | showException(e, "There was an error getting the list of devices."); 315 | return false; 316 | } 317 | } 318 | 319 | void MainController::showException(const std::exception & e, 320 | const std::string & context = "") 321 | { 322 | std::string message; 323 | if (context.size() > 0) 324 | { 325 | message += context; 326 | message += " "; 327 | } 328 | message += e.what(); 329 | view->showErrorMessage(message); 330 | } 331 | 332 | void MainController::applySettings() 333 | { 334 | if (!model->connected()) { return; } 335 | 336 | try 337 | { 338 | model->applySettings(); 339 | } 340 | catch (const std::exception & e) 341 | { 342 | showException(e, "There was an error applying settings."); 343 | } 344 | 345 | view->handleSettingsChanged(); 346 | } 347 | 348 | void MainController::handleIspFrequencyInput(const std::string & input) 349 | { 350 | // If someone disconnects the device while this input box is selected, we 351 | // will disable the input box and that could cause a spurious user input 352 | // event to be sent from the view to here, which is not great. 353 | if (!model->connected()) { return; } 354 | 355 | try 356 | { 357 | Programmer::setFrequency(model->settings, input); 358 | model->settingsModified = true; 359 | } 360 | catch (const std::exception & e) 361 | { 362 | // This should not happen because the view already fixed up the input to 363 | // be valid. 364 | assert(0); 365 | showException(e, "There was an error setting the ISP frequency."); 366 | } 367 | 368 | // This change might affect the max ISP frequency 369 | // (and also enable the Apply Settings button). 370 | view->handleSettingsChanged(); 371 | } 372 | 373 | void MainController::handleMaxIspFrequencyInput(const std::string & input) 374 | { 375 | if (!model->connected()) { return; } 376 | 377 | try 378 | { 379 | Programmer::setMaxFrequency(model->settings, input); 380 | model->settingsModified = true; 381 | } 382 | catch (const std::exception & e) 383 | { 384 | // This should not happen because the view already fixed up the input to 385 | // be valid. 386 | assert(0); 387 | showException(e, "There was an error setting the Max ISP frequency."); 388 | } 389 | 390 | // This change might affect the displayed ISP frequency. 391 | // (and also enable the Apply Settings button). 392 | view->handleSettingsChanged(); 393 | } 394 | 395 | void MainController::handleRegulatorModeInput(uint8_t regulatorMode) 396 | { 397 | if (!model->connected()) { return; } 398 | model->settings.regulatorMode = regulatorMode; 399 | model->settingsModified = true; 400 | view->handleSettingsChanged(); 401 | } 402 | 403 | void MainController::handleVccOutputEnabledInput(bool vccOutputEnabled) 404 | { 405 | if (!model->connected()) { return; } 406 | model->settings.vccOutputEnabled = vccOutputEnabled; 407 | model->settingsModified = true; 408 | view->handleSettingsChanged(); 409 | } 410 | 411 | void MainController::handleVccOutputIndicatorInput(uint8_t indicator) 412 | { 413 | if (!model->connected()) { return; } 414 | model->settings.vccOutputIndicator = indicator; 415 | model->settingsModified = true; 416 | view->handleSettingsChanged(); 417 | } 418 | 419 | void MainController::handleLineAFunctionInput(uint8_t function) 420 | { 421 | if (!model->connected()) { return; } 422 | model->settings.lineAFunction = function; 423 | model->settingsModified = true; 424 | view->handleSettingsChanged(); 425 | } 426 | 427 | void MainController::handleLineBFunctionInput(uint8_t function) 428 | { 429 | if (!model->connected()) { return; } 430 | model->settings.lineBFunction = function; 431 | model->settingsModified = true; 432 | view->handleSettingsChanged(); 433 | } 434 | 435 | void MainController::handleVccVddMaxRangeInput(uint32_t mv) 436 | { 437 | if (!model->connected()) { return; } 438 | model->settings.vccVddMaxRange = mv; 439 | model->settingsModified = true; 440 | view->handleSettingsChanged(); 441 | } 442 | 443 | void MainController::handleVcc3v3MinInput(uint32_t mv) 444 | { 445 | if (!model->connected()) { return; } 446 | model->settings.vcc3v3Min = mv; 447 | model->settingsModified = true; 448 | view->handleSettingsChanged(); 449 | } 450 | 451 | void MainController::handleVcc3v3MaxInput(uint32_t mv) 452 | { 453 | if (!model->connected()) { return; } 454 | model->settings.vcc3v3Max = mv; 455 | model->settingsModified = true; 456 | view->handleSettingsChanged(); 457 | } 458 | 459 | void MainController::handleVcc5vMinInput(uint32_t mv) 460 | { 461 | if (!model->connected()) { return; } 462 | model->settings.vcc5vMin = mv; 463 | model->settingsModified = true; 464 | view->handleSettingsChanged(); 465 | } 466 | 467 | void MainController::handleVcc5vMaxInput(uint32_t mv) 468 | { 469 | if (!model->connected()) { return; } 470 | model->settings.vcc5vMax = mv; 471 | model->settingsModified = true; 472 | view->handleSettingsChanged(); 473 | } 474 | 475 | void MainController::handleStk500VersionInput(uint32_t hardwareVersion, 476 | uint32_t softwareVersionMajor, uint32_t softwareVersionMinor) 477 | { 478 | if (!model->connected()) { return; } 479 | model->settings.hardwareVersion = hardwareVersion; 480 | model->settings.softwareVersionMajor = softwareVersionMajor; 481 | model->settings.softwareVersionMinor = softwareVersionMinor; 482 | model->settingsModified = true; 483 | view->handleSettingsChanged(); 484 | } 485 | -------------------------------------------------------------------------------- /gui/main_controller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class MainView; 7 | class MainModel; 8 | 9 | /** The controller receives events from the view (like user actions or update 10 | * timer timeouts), calls methods on the model to change the state of hte 11 | * application, handles errors/exceptions from the model and then tells the view 12 | * what parts might need to be updated. 13 | * 14 | * The model can throw exceptions, and the controller should catch these 15 | * exceptions and handle them appropriately. If the controller allows any 16 | * exceptions to be thrown except under very unusual circumstances, that is 17 | * considered to be a bug in the controller. This is important, because you're 18 | * not supposed to throw exceptions from a Qt slot. 19 | */ 20 | class MainController 21 | { 22 | public: 23 | void init(MainModel *, MainView *); 24 | 25 | /** This is called when the program starts up. */ 26 | void start(); 27 | 28 | /** This is called when the user issues a connect command. */ 29 | void connect(); 30 | 31 | /** This is called when the user issues a disconnect command. */ 32 | void disconnect(); 33 | 34 | /** This is called when the user issues a command to reload settings from 35 | * the device. */ 36 | void reloadSettings(); 37 | 38 | /** This is called when the user wants to restore the device to its default 39 | * settings. */ 40 | void restoreDefaultSettings(); 41 | 42 | /** This is called when it is time to check if the status of the device has 43 | * changed. */ 44 | void update(); 45 | 46 | /** This is called when the user tries to exit the program. Returns true if 47 | * the program is actually allowed to exit. */ 48 | bool exit(); 49 | 50 | private: 51 | void reallyConnect(); 52 | 53 | /** Returns true for success, false for failure. */ 54 | bool tryUpdateDeviceList(); 55 | 56 | void showException(const std::exception & e, const std::string & context); 57 | 58 | public: 59 | 60 | /** This is called when the user wants to apply the settings. */ 61 | void applySettings(); 62 | 63 | // These are called when the user changes a setting. 64 | void handleIspFrequencyInput(const std::string & input); 65 | void handleMaxIspFrequencyInput(const std::string & input); 66 | void handleRegulatorModeInput(uint8_t regulatorMode); 67 | void handleVccOutputEnabledInput(bool vccOutputEnabled); 68 | void handleVccOutputIndicatorInput(uint8_t indicator); 69 | void handleLineAFunctionInput(uint8_t function); 70 | void handleLineBFunctionInput(uint8_t function); 71 | void handleVccVddMaxRangeInput(uint32_t mv); 72 | void handleVcc3v3MinInput(uint32_t mv); 73 | void handleVcc3v3MaxInput(uint32_t mv); 74 | void handleVcc5vMinInput(uint32_t mv); 75 | void handleVcc5vMaxInput(uint32_t mv); 76 | void handleStk500VersionInput(uint32_t hardwareVersion, 77 | uint32_t softwareVersionMajor, uint32_t softwareVersionMinor); 78 | 79 | private: 80 | 81 | MainModel * model; 82 | MainView * view; 83 | }; 84 | -------------------------------------------------------------------------------- /gui/main_model.cpp: -------------------------------------------------------------------------------- 1 | #include "main_model.h" 2 | 3 | #include 4 | 5 | void MainModel::updateDeviceList() 6 | { 7 | deviceList = programmerGetList(); 8 | } 9 | 10 | void MainModel::connect(const ProgrammerInstance & instance) 11 | { 12 | // Close the old handle in case one is already open. 13 | deviceHandle.close(); 14 | 15 | connectionError = false; 16 | disconnectedByUser = false; 17 | 18 | try 19 | { 20 | // Open a handle to the specified programmer. 21 | deviceHandle = ProgrammerHandle(instance); 22 | } 23 | catch (...) 24 | { 25 | setConnectionError("Failed to connect to device."); 26 | throw; 27 | } 28 | } 29 | 30 | void MainModel::reloadSettings() 31 | { 32 | assert(connected()); 33 | 34 | try 35 | { 36 | settings = deviceHandle.getSettings(); 37 | settingsModified = false; 38 | } 39 | catch (...) 40 | { 41 | settingsModified = true; 42 | throw; 43 | } 44 | } 45 | 46 | void MainModel::applySettings() 47 | { 48 | assert(connected()); 49 | deviceHandle.applySettings(settings); 50 | settingsModified = false; // this must be last in case exceptions are thrown 51 | } 52 | 53 | void MainModel::reloadVariables() 54 | { 55 | assert(connected()); 56 | 57 | try 58 | { 59 | variables = deviceHandle.getVariables(); 60 | variablesUpdateFailed = false; 61 | } 62 | catch (...) 63 | { 64 | variablesUpdateFailed = true; 65 | throw; 66 | } 67 | } 68 | 69 | void MainModel::reloadFirmwareVersionString() 70 | { 71 | try 72 | { 73 | firmwareVersionString = deviceHandle.getFirmwareVersionString(); 74 | } 75 | catch (...) 76 | { 77 | firmwareVersionString = "?"; 78 | throw; 79 | } 80 | } 81 | 82 | void MainModel::disconnectByUser() 83 | { 84 | disconnect(); 85 | disconnectedByUser = true; 86 | 87 | connectionError = false; 88 | } 89 | 90 | void MainModel::disconnectByError(std::string errorMessage) 91 | { 92 | disconnect(); 93 | setConnectionError(errorMessage); 94 | 95 | disconnectedByUser = false; 96 | } 97 | 98 | void MainModel::setConnectionError(std::string errorMessage) 99 | { 100 | connectionError = true; 101 | connectionErrorMessage = errorMessage; 102 | } 103 | 104 | void MainModel::disconnect() 105 | { 106 | deviceHandle.close(); 107 | settingsModified = false; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /gui/main_model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "programmer.h" 4 | 5 | /** The model holds the state of the application and it knows how to perform 6 | * simple operations that change the state. The model does not know about 7 | * error/exception handling, and it does not know how to string together 8 | * multiple simple operations into a higher-level operation (e.g. it does not 9 | * know that we should reload the settings after connecting to a device). */ 10 | class MainModel 11 | { 12 | public: 13 | /** Holds a list of the relevant devices that are connected to the computer. */ 14 | std::vector deviceList; 15 | 16 | /** Holds an open handle to a device or a null handle if we are not 17 | * connected. */ 18 | ProgrammerHandle deviceHandle; 19 | 20 | /** True if the last connection or connection attempt resulted in an error. 21 | * If true, connectionErrorMessage provides some information about the 22 | * error. */ 23 | bool connectionError = false; 24 | std::string connectionErrorMessage; 25 | 26 | /** True if we are disconnected now and the last connection was terminated 27 | * by the user. */ 28 | bool disconnectedByUser = false; 29 | 30 | /** Holds the settings from the device. */ 31 | ProgrammerSettings settings; 32 | 33 | /** True if the settings have been modified by user and could be different 34 | * from what is on the device. */ 35 | bool settingsModified = false; 36 | 37 | /** Holds the variables/status of the device. */ 38 | ProgrammerVariables variables; 39 | 40 | /** True if the last attempt to update the variables failed (typically due 41 | * to a USB error). */ 42 | bool variablesUpdateFailed = false; 43 | 44 | /** The firmware version string, including any modification codes 45 | * (e.g. "1.07nc"). */ 46 | std::string firmwareVersionString; 47 | 48 | /** Returns true if we are currently connected to a device. */ 49 | bool connected() const { return deviceHandle; } 50 | 51 | void updateDeviceList(); 52 | void reloadSettings(); 53 | void applySettings(); 54 | void reloadVariables(); 55 | void reloadFirmwareVersionString(); 56 | 57 | void connect(const ProgrammerInstance & instance); 58 | 59 | void disconnectByUser(); 60 | void disconnectByError(std::string errorMessage); 61 | 62 | void setConnectionError(std::string errorMessage); 63 | 64 | private: 65 | void disconnect(); 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /gui/main_view.cpp: -------------------------------------------------------------------------------- 1 | #include "main_view.h" 2 | #include "main_controller.h" 3 | #include "main_model.h" 4 | 5 | void MainView::init(const MainModel * model, MainController * controller) 6 | { 7 | this->model = model; 8 | this->controller = controller; 9 | this->window.setView(this); 10 | } 11 | 12 | void MainView::showWindow() 13 | { 14 | window.show(); 15 | } 16 | 17 | void MainView::startUpdateTimer(uint32_t intervalMs) 18 | { 19 | window.startUpdateTimer(intervalMs); 20 | } 21 | 22 | void MainView::showErrorMessage(const std::string & message) 23 | { 24 | window.showErrorMessage(message); 25 | } 26 | 27 | void MainView::showWarningMessage(const std::string & message) 28 | { 29 | window.showWarningMessage(message); 30 | } 31 | 32 | void MainView::showInfoMessage(const std::string & message) 33 | { 34 | window.showInfoMessage(message); 35 | } 36 | 37 | bool MainView::confirm(const std::string & question) 38 | { 39 | return window.confirm(question); 40 | } 41 | 42 | void MainView::handleModelChanged() 43 | { 44 | handleDeviceChanged(); 45 | handleVariablesChanged(); 46 | handleSettingsChanged(); 47 | } 48 | 49 | void MainView::handleDeviceChanged() 50 | { 51 | bool connected = model->connected(); 52 | 53 | if (connected) 54 | { 55 | const ProgrammerInstance & device = model->deviceHandle.getInstance(); 56 | window.setDeviceName(device.getName(), true); 57 | window.setSerialNumber(device.getSerialNumber()); 58 | window.setFirmwareVersion(model->firmwareVersionString); 59 | window.setProgPort(device.tryGetProgrammingPortName()); 60 | window.setTtlPort(device.tryGetTtlPortName()); 61 | } 62 | else 63 | { 64 | std::string value = "N/A"; 65 | window.setDeviceName(value, false); 66 | window.setSerialNumber(value); 67 | window.setFirmwareVersion(value); 68 | window.setProgPort(value); 69 | window.setTtlPort(value); 70 | } 71 | 72 | window.setConnectEnabled(!connected); 73 | window.setDisconnectEnabled(connected); 74 | window.setReloadSettingsEnabled(connected); 75 | window.setRestoreDefaultsEnabled(connected); 76 | window.setMainBoxesEnabled(connected); 77 | 78 | if (connected) 79 | { 80 | window.setConnectionStatus("Connected.", false); 81 | } 82 | else if (model->connectionError) 83 | { 84 | window.setConnectionStatus(model->connectionErrorMessage, true); 85 | } 86 | else if (model->disconnectedByUser) 87 | { 88 | window.setConnectionStatus("Not connected.", false); 89 | } 90 | else 91 | { 92 | // This is a subtle way of saying that we are not connected but we will 93 | // auto-connect when we see a device available. 94 | window.setConnectionStatus("Not connected yet...", false); 95 | } 96 | 97 | if (connected) 98 | { 99 | window.configureIspFrequencyControls( 100 | programmerAllowedFrequencyTable, 101 | programmerSuggestedFrequencyTable, 102 | programmerDefaultFrequency, 103 | programmerAllowedMaxFrequencyTable, 104 | programmerSuggestedMaxFrequencyTable, 105 | programmerDefaultMaxFrequency); 106 | } 107 | } 108 | 109 | /** Takes a number of millivolts and returns a string like "123 mV". */ 110 | static std::string convertMvToString(uint32_t mv) 111 | { 112 | return std::to_string(mv) + " mV"; 113 | } 114 | 115 | void MainView::handleVariablesChanged() 116 | { 117 | const ProgrammerVariables & vars = model->variables; 118 | 119 | window.setLastDeviceReset(Programmer::convertDeviceResetToString(vars.lastDeviceReset)); 120 | 121 | if (vars.programmingError == 0) 122 | { 123 | window.setProgrammingError("No error.", ""); 124 | } 125 | else 126 | { 127 | window.setProgrammingError(std::string("Error: ") + 128 | Programmer::convertProgrammingErrorToShortString(vars.programmingError), 129 | Programmer::convertProgrammingErrorToLongString(vars.programmingError)); 130 | } 131 | 132 | // Uncomment this code to test that the GUI can show the longest error 133 | // message: 134 | // window.setProgrammingError( 135 | // Programmer::convertProgrammingErrorToShortString(PAVR2_PROGRAMMING_ERROR_SYNCH), 136 | // Programmer::convertProgrammingErrorToLongString(PAVR2_PROGRAMMING_ERROR_SYNCH)); 137 | 138 | if (vars.hasResultsFromLastProgramming) 139 | { 140 | window.setMeasuredVccMin(convertMvToString(vars.targetVccMeasuredMinMv)); 141 | window.setMeasuredVccMax(convertMvToString(vars.targetVccMeasuredMaxMv)); 142 | window.setMeasuredVddMin(convertMvToString(vars.programmerVddMeasuredMinMv)); 143 | window.setMeasuredVddMax(convertMvToString(vars.programmerVddMeasuredMaxMv)); 144 | } 145 | else 146 | { 147 | std::string value = "N/A"; 148 | window.setMeasuredVccMin(value); 149 | window.setMeasuredVccMax(value); 150 | window.setMeasuredVddMin(value); 151 | window.setMeasuredVddMax(value); 152 | } 153 | 154 | window.setCurrentVcc(convertMvToString(vars.targetVccMv)); 155 | window.setCurrentVdd(convertMvToString(vars.programmerVddMv)); 156 | window.setRegulatorLevel( 157 | Programmer::convertRegulatorLevelToString(vars.regulatorLevel)); 158 | 159 | // Note: It would be nice to display some error indication if 160 | // model->variablesUpdateFailed is true. 161 | } 162 | 163 | void MainView::handleSettingsChanged() 164 | { 165 | // [all-settings] 166 | window.setIspFrequency(Programmer::getFrequencyName( 167 | model->settings.sckDuration, model->settings.ispFastestPeriod)); 168 | window.setMaxIspFrequency(Programmer::getMaxFrequencyName( 169 | model->settings.ispFastestPeriod)); 170 | window.setRegulatorMode(model->settings.regulatorMode); 171 | window.setVccOutputEnabled(model->settings.vccOutputEnabled); 172 | window.setVccOutputIndicator(model->settings.vccOutputIndicator); 173 | window.setLineAFunction(model->settings.lineAFunction); 174 | window.setLineBFunction(model->settings.lineBFunction); 175 | window.setVccVddMaxRange(model->settings.vccVddMaxRange); 176 | window.setVcc3v3Min(model->settings.vcc3v3Min); 177 | window.setVcc3v3Max(model->settings.vcc3v3Max); 178 | window.setVcc5vMin(model->settings.vcc5vMin); 179 | window.setVcc5vMax(model->settings.vcc5vMax); 180 | window.setStk500Versions( 181 | model->settings.hardwareVersion, 182 | model->settings.softwareVersionMajor, 183 | model->settings.softwareVersionMinor); 184 | 185 | window.setApplySettingsEnabled(model->connected() && model->settingsModified); 186 | } 187 | -------------------------------------------------------------------------------- /gui/main_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main_window.h" 4 | 5 | class MainModel; 6 | class MainController; 7 | 8 | /** The MainView class knows a lot about what we want to display on the screen 9 | * and when it needs to be updated, but it knows nothing about the underlying 10 | * GUI framework, so that it can hopefully be reused if we port to a different 11 | * GUI framework. 12 | * 13 | * It uses classes like MainWindow which know about the GUI framework. */ 14 | class MainView 15 | { 16 | public: 17 | void init(const MainModel *, MainController *); 18 | 19 | /** This is a shortcut that allows MainWindow to report user input 20 | events directly to the controller. The MainView class normally doesn't need 21 | to process any of those events, and it is annoying to have a ton of 22 | one-line functions that just forward all their arguments to the controller. */ 23 | MainController * userInputReceiver() { return controller; } 24 | 25 | /** This function tells the GUI framework that we want to eventually display 26 | * the window, but it might not actually draw the window properly before 27 | * returning. Window drawing will happen later after the GUI framework's 28 | * event loop is started. **/ 29 | void showWindow(); 30 | 31 | /** This causes the view to call the controller's update() function 32 | * periodically, on the same thread as everything else. 33 | * 34 | * intervalMs is the amount of time between updates, in milliseconds. 35 | */ 36 | void startUpdateTimer(uint32_t intervalMs); 37 | 38 | void showErrorMessage(const std::string & message); 39 | void showWarningMessage(const std::string & message); 40 | void showInfoMessage(const std::string & message); 41 | 42 | /** Show an OK/Cancel dialog, return true if the user selects OK. */ 43 | bool confirm(const std::string & question); 44 | 45 | /** This is called whenever something in the model has changed that might 46 | * require the view to be updated. It includes no details about what 47 | * exactly changed. */ 48 | void handleModelChanged(); 49 | 50 | private: 51 | /** This is called whenever it is possible that we have connected to a 52 | * different device. */ 53 | void handleDeviceChanged(); 54 | 55 | public: 56 | void handleVariablesChanged(); 57 | 58 | void handleSettingsChanged(); 59 | 60 | private: 61 | const MainModel * model; 62 | MainController * controller; 63 | 64 | MainWindow window; 65 | }; 66 | -------------------------------------------------------------------------------- /gui/qt/app_icns.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../../images/app.icns 4 | 5 | 6 | -------------------------------------------------------------------------------- /gui/qt/app_ico.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../../images/app.ico 4 | 5 | 6 | -------------------------------------------------------------------------------- /gui/qt/frequency_validator.cpp: -------------------------------------------------------------------------------- 1 | #include "frequency_validator.h" 2 | 3 | void FrequencyValidator::setAllowedFrequencies( 4 | const std::vector & allowedFrequencies) 5 | { 6 | this->allowedFrequencies = allowedFrequencies; 7 | } 8 | 9 | void FrequencyValidator::setDefaultFrequency( 10 | const ProgrammerFrequency & frequency 11 | ) 12 | { 13 | this->defaultFrequency = frequency; 14 | } 15 | 16 | QValidator::State FrequencyValidator::validate(QString & input, int & pos) const 17 | { 18 | Q_UNUSED(pos); 19 | 20 | // If the frequency is on the list, return Acceptable. 21 | for (const ProgrammerFrequency & freq : allowedFrequencies) 22 | { 23 | if (input == QString(freq.name) + " kHz") 24 | { 25 | return Acceptable; 26 | } 27 | } 28 | 29 | // If the input contains invalid characters, return Invalid. 30 | std::string inputStdString = input.toStdString(); 31 | for (size_t i = 0; i < inputStdString.size(); i++) 32 | { 33 | char c = inputStdString[i]; 34 | if (!((c >= '0' && c <= '9') || c == '.' || c == ' ' 35 | || c == 'k' || c == 'K' 36 | || c == 'm' || c == 'M' 37 | || c == 'h' || c == 'H' 38 | || c == 'z' || c == 'z')) 39 | { 40 | return Invalid; 41 | } 42 | } 43 | 44 | return Intermediate; 45 | } 46 | 47 | /** Converts a suffix the user typed to either " kHz" or " MHz", with " kHz" 48 | * being the default if we can't figure out what the user meant. */ 49 | static std::string normalize_suffix(const std::string & originalSuffix) 50 | { 51 | std::string suffix; 52 | 53 | // Convert to lower-case and remove all spaces. 54 | for (size_t i = 0; i < originalSuffix.size(); i++) 55 | { 56 | char c = originalSuffix[i]; 57 | if (std::isspace(c)) { continue; } 58 | suffix += std::tolower(c); 59 | } 60 | 61 | // If it's clear that the user means MHz, return that. 62 | if (suffix == "mhz" || suffix == "mh" || suffix == "m" || suffix == "mz") 63 | { 64 | return " MHz"; 65 | } 66 | 67 | // Otherwise, we default to kHz. 68 | return " kHz"; 69 | } 70 | 71 | void FrequencyValidator::fixup(QString & input) const 72 | { 73 | const std::string inputStdString = input.toStdString(); 74 | 75 | // Attempt to parse the beginning of the string as a number. 76 | bool ok = false; 77 | double value = 0; 78 | size_t suffixIndex = 0; 79 | try 80 | { 81 | value = std::stod(inputStdString, &suffixIndex); 82 | ok = true; 83 | } 84 | catch (const std::invalid_argument & e) 85 | { 86 | // It doesn't look the user even gave us a number. 87 | } 88 | catch (const std::out_of_range & e) 89 | { 90 | // The user gave a number that is out of range. 91 | } 92 | if (!ok) 93 | { 94 | // We don't have a numeric value, so just revert to the default. 95 | input = QString(defaultFrequency.name) + " kHz"; 96 | return; 97 | } 98 | 99 | // Extract the numeric part of the string. 100 | std::string valueStr = inputStdString.substr(0, suffixIndex); 101 | 102 | // Extract the part after the number; this is our suffix. 103 | std::string suffix = inputStdString.substr( 104 | suffixIndex, inputStdString.size() - suffixIndex); 105 | 106 | // Normalize the suffix to " kHz" or " MHz". 107 | suffix = normalize_suffix(suffix); 108 | 109 | // Normalize the floating-point value so it is in units of kHz. 110 | if (suffix == " MHz") 111 | { 112 | value *= 1000; 113 | } 114 | 115 | if (suffix == " kHz") 116 | { 117 | // If the suffix is kHz, we try to find an exact match to one of the 118 | // frequencies. This is important because it allows "57.4" to be 119 | // auto-corrected to "57.4 kHz" and instead of going down to 52.6 kHz. 120 | 121 | for (const ProgrammerFrequency & freq : allowedFrequencies) 122 | { 123 | if (valueStr == freq.name) 124 | { 125 | // Found an exact match. 126 | input = QString(freq.name) + " kHz"; 127 | return; 128 | } 129 | } 130 | } 131 | 132 | // Convert the frequency to a period in the native units of the programmer 133 | // (one twelfth microseconds). 134 | double period_approximation = 12000 / value; 135 | 136 | // Find the first frequency that is lower than this. 137 | for (const ProgrammerFrequency & freq : allowedFrequencies) 138 | { 139 | if (freq.period >= period_approximation - 0.00001) 140 | { 141 | input = QString(freq.name) + " kHz"; 142 | return; 143 | } 144 | } 145 | 146 | // Nothing we have is low enough; just use the lowest frequency. 147 | const ProgrammerFrequency & lowestFrequency = allowedFrequencies.back(); 148 | input = QString(lowestFrequency.name) + " kHz"; 149 | } 150 | -------------------------------------------------------------------------------- /gui/qt/frequency_validator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** This class is used on our ISP Frequency and Max ISP Frequency boxes to help 9 | * correct the user's input so it matches one of the allowed input values. 10 | * 11 | * You must call setAllowedFrequencies with a sorted (descending) list of 12 | * frequencies before the other methods will do anything useful. 13 | * 14 | * This class assumes that we want to have the suffix " kHz" after the 15 | * frequency. */ 16 | class FrequencyValidator : public QValidator 17 | { 18 | public: 19 | virtual State validate(QString & input, int & pos) const override; 20 | virtual void fixup(QString & input) const override; 21 | 22 | /** Note: For the purposes of fixup(), the table provided to this function 23 | * must be sorted by the frequency in kHz, descending. */ 24 | void setAllowedFrequencies(const std::vector &); 25 | 26 | void setDefaultFrequency(const ProgrammerFrequency &); 27 | private: 28 | std::vector allowedFrequencies; 29 | ProgrammerFrequency defaultFrequency; 30 | }; 31 | -------------------------------------------------------------------------------- /gui/qt/icon_resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../../images/app.ico 4 | 5 | 6 | -------------------------------------------------------------------------------- /gui/qt/main_window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | class FrequencyValidator; 8 | class QCheckBox; 9 | class QComboBox; 10 | class QGridLayout; 11 | class QGroupBox; 12 | class QLabel; 13 | class QMainWindow; 14 | class QPushButton; 15 | class QSpinBox; 16 | class MainView; 17 | 18 | class MainWindow : public QMainWindow 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | MainWindow(QWidget *parent = 0); 24 | ~MainWindow(); 25 | 26 | /** Stores a pointer to the MainView object so that we can report events. **/ 27 | void setView(MainView * view); 28 | 29 | /** This causes the window to call the view's handleUpdateEvent() function 30 | * periodically, on the same thread as everything else. 31 | * 32 | * intervalMs is the amount of time between updates, in milliseconds. 33 | */ 34 | void startUpdateTimer(uint32_t intervalMs); 35 | 36 | void showErrorMessage(const std::string & message); 37 | void showWarningMessage(const std::string & message); 38 | void showInfoMessage(const std::string & message); 39 | 40 | /** Show an OK/Cancel dialog, return true if the user selects OK. */ 41 | bool confirm(const std::string & question); 42 | 43 | /** Sets the label that shows the connection status/error. */ 44 | void setConnectionStatus(const std::string & status, bool error); 45 | 46 | /** Controls whether the main controls of the application are enabled or 47 | * disabled. **/ 48 | void setMainBoxesEnabled(bool enabled); 49 | 50 | /** Controls whether the Apply Settings action/button is enabled or 51 | * disabled. */ 52 | void setApplySettingsEnabled(bool enabled); 53 | 54 | /** Controls whether the connect action is enabled or disabled. */ 55 | void setConnectEnabled(bool enabled); 56 | 57 | /** Controls whether the disconnect action is enabled or disabled. */ 58 | void setDisconnectEnabled(bool enabled); 59 | 60 | /** Controls whether the reload settings from device action is enabled. */ 61 | void setReloadSettingsEnabled(bool enabled); 62 | 63 | /** Controls whether the restore defaults option is enabled. */ 64 | void setRestoreDefaultsEnabled(bool enabled); 65 | 66 | void setDeviceName(const std::string & name, bool linkEnabled); 67 | void setSerialNumber(const std::string & serialNumber); 68 | void setFirmwareVersion(const std::string & firmwareVersion); 69 | void setProgPort(const std::string & portName); 70 | void setTtlPort(const std::string & portName); 71 | void setLastDeviceReset(const std::string & lastDeviceReset); 72 | void setProgrammingError(const std::string & shortMessage, const std::string & details); 73 | void setMeasuredVccMin(const std::string & voltage); 74 | void setMeasuredVccMax(const std::string & voltage); 75 | void setMeasuredVddMin(const std::string & voltage); 76 | void setMeasuredVddMax(const std::string & voltage); 77 | void setCurrentVcc(const std::string & voltage); 78 | void setCurrentVdd(const std::string & voltage); 79 | void setRegulatorLevel(const std::string & level); 80 | 81 | void configureIspFrequencyControls( 82 | const std::vector & allowedFrequencyTable, 83 | const std::vector & suggestedFrequencyTable, 84 | const ProgrammerFrequency & defaultFrequency, 85 | const std::vector & allowedMaxFrequencyTable, 86 | const std::vector & suggestedMaxFrequencyTable, 87 | const ProgrammerFrequency & defaultMaxFrequency); 88 | 89 | void setIspFrequency(const std::string & frequency); 90 | void setMaxIspFrequency(const std::string & frequency); 91 | void setRegulatorMode(uint8_t regulatorMode); 92 | void setVccOutputEnabled(bool enabled); 93 | void setVccOutputIndicator(bool steady); 94 | void setLineAFunction(uint8_t function); 95 | void setLineBFunction(uint8_t function); 96 | void setVccVddMaxRange(uint32_t mv); 97 | void setVcc3v3Min(uint32_t mv); 98 | void setVcc3v3Max(uint32_t mv); 99 | void setVcc5vMin(uint32_t mv); 100 | void setVcc5vMax(uint32_t mv); 101 | void setStk500Versions(uint8_t hardwareVersion, 102 | uint8_t softwareVersionMajor, uint8_t softwareVersionMinor); 103 | 104 | private: 105 | /** Helper method for setting the index of a combo box, given the desired 106 | * uint8_t item value. Defaults to using the first entry in the combo box if 107 | * the specified value is not found. */ 108 | void setU8ComboBox(QComboBox * combo, uint8_t value); 109 | 110 | void setVoltageSetting(QSpinBox * box, uint32_t mv); 111 | 112 | void centerAtStartupIfNeeded(); 113 | 114 | protected: 115 | /** This is called by Qt just before the window is shown for the first time, 116 | * and is also called whenever the window becomes unminimized. */ 117 | void showEvent(QShowEvent *) override; 118 | 119 | /** This is called by Qt when the "close" slot is triggered, meaning that 120 | * the user wants to close the window. */ 121 | void closeEvent(QCloseEvent *) override; 122 | 123 | private slots: 124 | void on_connectAction_triggered(); 125 | void on_disconnectAction_triggered(); 126 | void on_reloadSettingsAction_triggered(); 127 | void on_restoreDefaultsAction_triggered(); 128 | void on_updateTimer_timeout(); 129 | void on_deviceNameValue_linkActivated(); 130 | void on_documentationAction_triggered(); 131 | void on_aboutAction_triggered(); 132 | 133 | /** This is called by Qt when the user wants to apply settings. */ 134 | void on_applySettingsAction_triggered(); 135 | 136 | /** This slot is called directly by Qt when the user presses enter in the 137 | * ISP frequency input or when the editing box loses focus (which can 138 | * actually happen while the user is just moving the window). That's not 139 | * quite good enough for our purposes, so we also call this slot from 140 | * on_ispFrequencyValue_activated, which gets called whenever the user 141 | * selects an item in the combo box. */ 142 | void on_ispFrequencyValueLineEdit_editingFinished(); 143 | 144 | void on_ispFrequencyValue_activated(int index); 145 | void on_maxIspFrequencyValueLineEdit_editingFinished(); 146 | void on_maxIspFrequencyValue_activated(int index); 147 | void on_regulatorModeValue_currentIndexChanged(int index); 148 | void on_vccOutputEnabledValue_currentIndexChanged(int index); 149 | void on_vccOutputIndicatorValue_currentIndexChanged(int index); 150 | void on_lineAFunctionValue_currentIndexChanged(int index); 151 | void on_lineBFunctionValue_currentIndexChanged(int index); 152 | void on_vccVddMaxRangeValue_valueChanged(int value); 153 | void on_vcc3v3MinValue_valueChanged(int value); 154 | void on_vcc3v3MaxValue_valueChanged(int value); 155 | void on_vcc5vMinValue_valueChanged(int value); 156 | void on_vcc5vMaxValue_valueChanged(int value); 157 | void on_stk500HardwareVersionValue_valueChanged(int value); 158 | void on_stk500SoftwareVersionMinorValue_valueChanged(int value); 159 | void on_stk500SoftwareVersionMajorValue_valueChanged(int value); 160 | 161 | private: 162 | /** We use these variables to hold the current values of the two frequency 163 | * settings (with the " kHz" suffix) and make sure we don't send spurious 164 | * user input events when the user didn't actually change anything. These 165 | * are only necessary because the signals provided by Qt are sometimes sent 166 | * too often, so these variables and functionality they allow belong 167 | * here. */ 168 | std::string cachedIspFrequency; 169 | std::string cachedMaxIspFrequency; 170 | 171 | private: 172 | MainView * view; 173 | bool startEventReported = false; 174 | 175 | /* We set this to true temporarily when programmatically setting the value 176 | * of an input in order to suppress sending a spurious user-input event to 177 | * the rest of the program. */ 178 | bool suppressEvents = false; 179 | 180 | QTimer * updateTimer; 181 | 182 | // These are low-level functions called in the constructor that set up the 183 | // GUI elements. 184 | void setupWindow(); 185 | void setupMenuBar(); 186 | void adjustSizes(); 187 | QWidget * setupDeviceInfoBox(); 188 | QWidget * setupProgrammingResultsBox(); 189 | QWidget * setupCurrentStatusBox(); 190 | QWidget * setupSettingsWidget(); 191 | QWidget * setupSettingsBox(); 192 | QWidget * setupFooter(); 193 | QWidget * setupConnectionStatus(); 194 | QWidget * setupApplyButton(); 195 | void retranslate(); 196 | 197 | QIcon programIcon; 198 | 199 | QMenuBar * menuBar; 200 | QMenu * fileMenu; 201 | QAction * exitAction; 202 | QMenu * deviceMenu; 203 | QAction * connectAction; 204 | QAction * disconnectAction; 205 | QAction * reloadSettingsAction; 206 | QAction * restoreDefaultsAction; 207 | QAction * applySettingsAction; 208 | QMenu * helpMenu; 209 | QAction * documentationAction; 210 | QAction * aboutAction; 211 | 212 | QWidget * centralWidget; 213 | QGridLayout * centralWidgetLayout; 214 | 215 | QGroupBox * deviceInfoBox; 216 | QGridLayout * deviceInfoBoxLayout; 217 | QLabel * deviceNameLabel; 218 | QLabel * deviceNameValue; 219 | QLabel * serialNumberLabel; 220 | QLabel * serialNumberValue; 221 | QLabel * firmwareVersionLabel; 222 | QLabel * firmwareVersionValue; 223 | QLabel * progPortLabel; 224 | QLabel * progPortValue; 225 | QLabel * ttlPortLabel; 226 | QLabel * ttlPortValue; 227 | QLabel * lastDeviceResetLabel; 228 | QLabel * lastDeviceResetValue; 229 | 230 | QGroupBox * programmingResultsBox; 231 | QGridLayout * programmingResultsBoxLayout; 232 | QLabel * programmingErrorValue; 233 | QLabel * measuredVccMinLabel; 234 | QLabel * measuredVccMinValue; 235 | QLabel * measuredVccMaxLabel; 236 | QLabel * measuredVccMaxValue; 237 | QLabel * measuredVddMinLabel; 238 | QLabel * measuredVddMinValue; 239 | QLabel * measuredVddMaxLabel; 240 | QLabel * measuredVddMaxValue; 241 | 242 | QGroupBox * currentStatusBox; 243 | QGridLayout * currentStatusBoxLayout; 244 | QLabel * currentVccLabel; 245 | QLabel * currentVccValue; 246 | QLabel * currentVddLabel; 247 | QLabel * currentVddValue; 248 | QLabel * currentRegulatorLevelLabel; 249 | QLabel * currentRegulatorLevelValue; 250 | 251 | QWidget * settingsWidget; 252 | QGridLayout * settingsWidgetLayout; 253 | 254 | // [all-settings] 255 | QGroupBox * settingsBox; 256 | QGridLayout * settingsBoxLayout; 257 | QLabel * ispFrequencyLabel; 258 | QComboBox * ispFrequencyValue; 259 | FrequencyValidator * ispFrequencyValidator; 260 | QLabel * maxIspFrequencyLabel; 261 | QComboBox * maxIspFrequencyValue; 262 | FrequencyValidator * maxIspFrequencyValidator; 263 | QLabel * regulatorModeLabel; 264 | QComboBox * regulatorModeValue; 265 | QLabel * vccOutputEnabledLabel; 266 | QComboBox * vccOutputEnabledValue; 267 | QLabel * vccOutputIndicatorLabel; 268 | QComboBox * vccOutputIndicatorValue; 269 | QLabel * lineAFunctionLabel; 270 | QComboBox * lineAFunctionValue; 271 | QLabel * lineBFunctionLabel; 272 | QComboBox * lineBFunctionValue; 273 | QLabel * vccVddMaxRangeLabel; 274 | QSpinBox * vccVddMaxRangeValue; 275 | QLabel * vcc3v3MinLabel; 276 | QSpinBox * vcc3v3MinValue; 277 | QLabel * vcc3v3MaxLabel; 278 | QSpinBox * vcc3v3MaxValue; 279 | QLabel * vcc5vMinLabel; 280 | QSpinBox * vcc5vMinValue; 281 | QLabel * vcc5vMaxLabel; 282 | QSpinBox * vcc5vMaxValue; 283 | QLabel * stk500HardwareVersionLabel; 284 | QSpinBox * stk500HardwareVersionValue; 285 | QLabel * stk500SoftwareVersionLabel; 286 | QSpinBox * stk500SoftwareVersionMajorValue; 287 | QSpinBox * stk500SoftwareVersionMinorValue; 288 | 289 | QWidget * footerWidget; 290 | QGridLayout * footerWidgetLayout; 291 | QLabel * connectionStatusValue; 292 | QPushButton * defaultsButton; 293 | QPushButton * cancelChangesButton; 294 | QPushButton * applySettingsButton; 295 | }; 296 | 297 | -------------------------------------------------------------------------------- /gui/qt/voltage_spin_box.cpp: -------------------------------------------------------------------------------- 1 | #include "voltage_spin_box.h" 2 | #include "pavr2_protocol.h" 3 | #include 4 | 5 | int VoltageSpinBox::numericValue(const QString & input) const 6 | { 7 | assert(prefix().isEmpty()); 8 | assert(input.endsWith(suffix())); // the input should end with " mV" 9 | QString numericInput = input.mid(0, input.size() - suffix().size()); 10 | bool ok; 11 | int num = numericInput.toInt(&ok); 12 | assert(ok); 13 | return num; 14 | } 15 | 16 | /** input is the current value of the input, e.g. "123 mV" 17 | * pos is the position of the user's cursor, between 0 and input.size(). 18 | */ 19 | QValidator::State VoltageSpinBox::validate(QString & input, int & pos) const 20 | { 21 | QValidator::State result = QSpinBox::validate(input, pos); 22 | if (result == QValidator::Acceptable) 23 | { 24 | // Our parent class thinks this input is acceptable. But if it is not 25 | // a multiple of 32, we want to consider it an intermediate value 26 | // instead. 27 | if ((numericValue(input) % PAVR2_VOLTAGE_UNITS) != 0) 28 | { 29 | result = QValidator::Intermediate; 30 | } 31 | } 32 | return result; 33 | } 34 | 35 | void VoltageSpinBox::fixup(QString & input) const 36 | { 37 | QSpinBox::fixup(input); 38 | 39 | // We don't have to worry about totally invalid inputs here; 40 | // QSpinBox::validate fixes them. 41 | 42 | int num = numericValue(input); 43 | if ((num % PAVR2_VOLTAGE_UNITS) != 0) 44 | { 45 | int fixedNum = (num + (PAVR2_VOLTAGE_UNITS / 2)) 46 | / PAVR2_VOLTAGE_UNITS * PAVR2_VOLTAGE_UNITS; 47 | input = QString::number(fixedNum); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /gui/qt/voltage_spin_box.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Customized spin box for selecting a voltage in millivolts. 6 | // QSpinBox was not adequate because there was no way to set a step size. 7 | class VoltageSpinBox : public QSpinBox 8 | { 9 | virtual QValidator::State validate(QString & input, int & pos) const override; 10 | virtual void fixup(QString & input) const override; 11 | private: 12 | int numericValue(const QString & input) const; 13 | }; 14 | -------------------------------------------------------------------------------- /images/.gitignore: -------------------------------------------------------------------------------- 1 | app.ico 2 | app.icns 3 | setup_banner_wix.bmp 4 | setup_welcome_wix.bmp 5 | -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | If you work for Pololu and want to build a Pololu-branded version of this 2 | software with an installer, then copy app.ico, app.icns, setup_banner_wix.bmp, 3 | and setup_welcome_wix.bmp to this directory. We don't include these files in 4 | source control because they contain the Pololu logo. 5 | 6 | Note: 7 | - setup_banner_wix.bmp should be 493x58 8 | - setup_welcome_wix.bmp should be 493x312 9 | 10 | -------------------------------------------------------------------------------- /include/pavr2_protocol.h: -------------------------------------------------------------------------------- 1 | // This file defines the constants needed to communicate with the native 2 | // USB interfaces of these devices: 3 | // 4 | // - Pololu USB AVR Programmer v2 (pgm04a) 5 | // - Pololu USB AVR Programmer v2.1 (pgm04b) 6 | 7 | #ifndef _PAVR2_NATIVE_USB_PROTOCOL_H 8 | #define _PAVR2_NATIVE_USB_PROTOCOL_H 9 | 10 | #define PAVR2_USB_VENDOR_ID 0x1FFB 11 | 12 | // USB product ID for pgm04a, Pololu USB AVR Programmer v2 13 | #define PAVR2_USB_PRODUCT_ID_V2 0x00B0 14 | 15 | // USB product ID for pgm04b, Pololu USB AVR Programmer v2.1 16 | #define PAVR2_USB_PRODUCT_ID_V2_1 0x00BB 17 | 18 | /* To get the value of a setting from the device, make a request like this: 19 | * bRequestType = 0xC0 20 | * bRequest = PAVR2_REQUEST_GET_SETTING 21 | * wValue = 0 22 | * wIndex = one of the PAVR2_SETTING_* defines below 23 | * wLength = length of the setting, in bytes */ 24 | #define PAVR2_REQUEST_GET_SETTING 0x81 25 | 26 | /* To set the value of a setting from the device, make a request like this: 27 | * bRequestType = 0x40 28 | * bRequest = REQUEST_SET_SETTING 29 | * wValue = value of setting 30 | * wIndex = one of the PAVR2_SETTING_* defines below 31 | * wLength = 0 */ 32 | #define PAVR2_REQUEST_SET_SETTING 0x82 33 | 34 | #define PAVR2_SETTING_NOT_INITIALIZED 0 35 | #define PAVR2_SETTING_SCK_DURATION 1 36 | #define PAVR2_SETTING_ISP_FASTEST_PERIOD 2 37 | #define PAVR2_SETTING_REGULATOR_MODE 3 38 | #define PAVR2_SETTING_VCC_OUTPUT_ENABLED 4 39 | #define PAVR2_SETTING_VCC_OUTPUT_INDICATOR 5 40 | #define PAVR2_SETTING_LINE_A_FUNCTION 6 41 | #define PAVR2_SETTING_LINE_B_FUNCTION 7 42 | #define PAVR2_SETTING_SOFTWARE_VERSION_MAJOR 8 43 | #define PAVR2_SETTING_SOFTWARE_VERSION_MINOR 9 44 | #define PAVR2_SETTING_HARDWARE_VERSION 10 45 | #define PAVR2_SETTING_RESET_POLARITY 11 46 | #define PAVR2_SETTING_VCC_VDD_MAX_RANGE 12 47 | #define PAVR2_SETTING_VCC_3V3_MIN 13 48 | #define PAVR2_SETTING_VCC_3V3_MAX 14 49 | #define PAVR2_SETTING_VCC_5V_MIN 15 50 | #define PAVR2_SETTING_VCC_5V_MAX 16 51 | 52 | /* To get the value of a variable from the device, make a request like this: 53 | * bRequestType = 0xC0 54 | * bRequest = PAVR2_REQUEST_GET_VARIABLE 55 | * wValue = 0 56 | * wIndex = one of the PAVR2_SETTING_* defines below 57 | * wLength = length of the variable, in bytes */ 58 | #define PAVR2_REQUEST_GET_VARIABLE 0x83 59 | 60 | #define PAVR2_VARIABLE_LAST_DEVICE_RESET 1 61 | #define PAVR2_VARIABLE_PROGRAMMING_ERROR 2 62 | #define PAVR2_VARIABLE_TARGET_VCC_MEASURED_MIN 3 63 | #define PAVR2_VARIABLE_TARGET_VCC_MEASURED_MAX 4 64 | #define PAVR2_VARIABLE_PROGRAMMER_VDD_MEASURED_MIN 5 65 | #define PAVR2_VARIABLE_PROGRAMMER_VDD_MEASURED_MAX 6 66 | #define PAVR2_VARIABLE_TARGET_VCC 7 67 | #define PAVR2_VARIABLE_PROGRAMMER_VDD 8 68 | #define PAVR2_VARIABLE_REGULATOR_LEVEL 9 69 | #define PAVR2_VARIABLE_IN_PROGRAMMING_MODE 10 70 | 71 | #define PAVR2_REQUEST_DIGITAL_READ 0x84 72 | 73 | // The minimum allowed value for the ISP_FASTEST_PERIOD setting. 74 | // Values faster/lower behave the same as 2 (6000 MHz). 75 | #define PAVR2_ISP_FASTEST_PERIOD_MIN 2 76 | 77 | // The largest allowed value for the ISP_FASTEST_PERIOD setting. Values 78 | // slower/higher than this are not needed, because they are slower than the 79 | // SCK_DURATION=1 (444 kHz) and allowing the user to set them would make the 80 | // name "fastest period" and "max frequency" be invalid. 81 | #define PAVR2_ISP_FASTEST_PERIOD_MAX 26 82 | 83 | /* The conversion factor between all the voltage variables above and 84 | * millivolts. */ 85 | #define PAVR2_VOLTAGE_UNITS 32 86 | 87 | /* VCC output indicator modes. */ 88 | #define PAVR2_VCC_OUTPUT_INDICATOR_BLINKING 0 89 | #define PAVR2_VCC_OUTPUT_INDICATOR_STEADY 1 90 | 91 | /* Line functions: */ 92 | #define PAVR2_LINE_IS_NOTHING 0 // Line is not used for anything. 93 | #define PAVR2_LINE_IS_DTR 1 // DB9 Pin 4 - OUT - Data Terminal Ready 94 | #define PAVR2_LINE_IS_RTS 2 // DB9 Pin 7 - OUT - Request to Send 95 | #define PAVR2_LINE_IS_CD 3 // DB9 Pin 1 - IN - Carrier Detect 96 | #define PAVR2_LINE_IS_DSR 4 // DB9 Pin 6 - IN - Data Set Ready 97 | #define PAVR2_LINE_IS_CLOCK 5 // Clock output (line B only) 98 | #define PAVR2_LINE_IS_DTR_RESET 6 // Open drain output, pulses low when DTR gets asserted. 99 | 100 | /* To enter bootloader mode, send this request: 101 | * bRequestType = 0x40 102 | * bRequest = REQUEST_START_BOOTLOADER 103 | * wValue = 0 104 | * wIndex = 0 105 | * wLength = 0 106 | */ 107 | #define PAVR2_REQUEST_START_BOOTLOADER 0xFF 108 | 109 | /* Causes of programmer resets. */ 110 | #define PAVR2_RESET_POWER_UP 0 111 | #define PAVR2_RESET_BROWNOUT 1 112 | #define PAVR2_RESET_RESET_LINE 2 113 | #define PAVR2_RESET_WATCHDOG 4 114 | #define PAVR2_RESET_SOFTWARE 8 115 | #define PAVR2_RESET_STACK_OVERFLOW 16 116 | #define PAVR2_RESET_STACK_UNDERFLOW 32 117 | 118 | /* Programming errors. */ 119 | #define PAVR2_PROGRAMMING_ERROR_TARGET_POWER_BAD 1 120 | #define PAVR2_PROGRAMMING_ERROR_SYNCH 2 121 | #define PAVR2_PROGRAMMING_ERROR_IDLE_FOR_TOO_LONG 3 122 | #define PAVR2_PROGRAMMING_ERROR_USB_NOT_CONFIGURED 4 123 | #define PAVR2_PROGRAMMING_ERROR_USB_SUSPEND 5 124 | #define PAVR2_PROGRAMMING_ERROR_PROGRAMMER_POWER_BAD 6 125 | 126 | /* Regulator levels (current setting of the regulator). */ 127 | #define PAVR2_REGULATOR_LEVEL_3V3 3 128 | #define PAVR2_REGULATOR_LEVEL_5V 5 129 | 130 | /* Regulator modes (policy that decides the regulator level). */ 131 | #define PAVR2_REGULATOR_MODE_AUTO 0 132 | #define PAVR2_REGULATOR_MODE_3V3 3 133 | #define PAVR2_REGULATOR_MODE_5V 5 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /include/pavrpgm_config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SOFTWARE_VERSION_STRING "@SOFTWARE_VERSION@" 4 | #define SOFTWARE_VERSION_MAJOR @SOFTWARE_VERSION_MAJOR@ 5 | #define SOFTWARE_VERSION_MINOR @SOFTWARE_VERSION_MINOR@ 6 | #define SOFTWARE_VERSION_PATCH @SOFTWARE_VERSION_PATCH@ 7 | 8 | #define SOFTWARE_YEAR "@YEAR@" 9 | 10 | #define DOCUMENTATION_URL "@DOCUMENTATION_URL@" 11 | 12 | #define CLI_NAME "@CLI_NAME@" 13 | #define GUI_NAME "@GUI_NAME@" 14 | -------------------------------------------------------------------------------- /include/programmer.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) Pololu Corporation. See www.pololu.com for details. 2 | 3 | /** Main header file for the C++ library libpavrpgm. 4 | * 5 | * This library supports communciating with the Pololu USB AVR Programmer v2. 6 | * 7 | * This is only intended to be used as a static library by the other projects in 8 | * this repository. The interface described in this header is considered to be 9 | * an internal implementation detail, and it may change at any time. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "pavr2_protocol.h" 20 | #include "programmer_frequency_tables.h" 21 | 22 | /** The maximum firmware major version supported by this program. If we make a 23 | * breaking change in the firmware, we can use this to be sure that old software 24 | * will not try to talk to new firmware and mess something up. */ 25 | #define PAVR2_FIRMWARE_VERSION_MAJOR_MAX 1 26 | 27 | struct ProgrammerSettings; 28 | struct ProgrammerVariables; 29 | 30 | class Programmer 31 | { 32 | public: 33 | Programmer() = delete; 34 | 35 | // Gets the name (string with a number of kHz) of the maximum ISP frequency. 36 | // This is only used by the programmer if SCK_DURATION is 0. 37 | static std::string getMaxFrequencyName(uint32_t ispFastestPeriod); 38 | 39 | // Sets the maximum frequency by its name. The name must exactly match 40 | // one of the allowed values. 41 | static void setMaxFrequency(ProgrammerSettings &, std::string maxFrequencyName); 42 | 43 | // Gets the name (string with a number of kHz) of the frequency actually 44 | // being used by the programmer, which depends on two parameters: 45 | // SCK_DURATION and ISP_FASTEST_PERIOD. 46 | static std::string getFrequencyName(uint32_t sckDuration, uint32_t ispFastestPeriod); 47 | 48 | // Sets the actual frequency being used by the programmer by its name. The 49 | // name must exactly match one of the allowed values. This function always 50 | // sets SCK_DURATION, and it only sets ISP_FASTEST_PERIOD if necessary. 51 | static void setFrequency(ProgrammerSettings &, std::string frequencyName); 52 | 53 | static std::string convertProgrammingErrorToShortString(uint8_t programmingError); 54 | 55 | // This long string does not stand alone (e.g. it could be empty if there was 56 | // no error or an unknown error). It should also be displayed below the 57 | // output of convertProgrammingErrorToShortString. 58 | static std::string convertProgrammingErrorToLongString(uint8_t programmingError); 59 | 60 | static std::string convertDeviceResetToString(uint8_t deviceReset); 61 | 62 | static std::string convertRegulatorModeToString(uint8_t regulatorMode); 63 | 64 | static std::string convertRegulatorLevelToString(uint8_t regulatorLevel); 65 | 66 | static std::string convertLineFunctionToString(uint8_t lineFunction); 67 | }; 68 | 69 | class ProgrammerInstance 70 | { 71 | public: 72 | ProgrammerInstance(); 73 | explicit ProgrammerInstance( 74 | libusbp::device, libusbp::generic_interface, uint16_t product_id, 75 | std::string serialNumber, uint16_t firmwareVersion); 76 | 77 | std::string getName() const; 78 | 79 | // An operating system-specific identifier for this device. 80 | std::string getOsId() const; 81 | 82 | std::string getSerialNumber() const; 83 | uint16_t getFirmwareVersion() const; 84 | 85 | // Returns a string like "1.03", not including any firmware modification codes. 86 | // See ProgrammerHandle::getFirmwareVersionString(). 87 | std::string getFirmwareVersionString() const; 88 | 89 | uint8_t getFirmwareVersionMajor() const; 90 | 91 | uint8_t getFirmwareVersionMinor() const; 92 | 93 | std::string getProgrammingPortName() const; 94 | std::string getTtlPortName() const; 95 | 96 | /** Same as getProgrammingPortName but it returns "(unknown)" instead of 97 | * throwing exceptions from the USB library. */ 98 | std::string tryGetProgrammingPortName() const; 99 | 100 | /** Same as getTtlPortName but it returns "(unknown)" instead of throwing 101 | * exceptions from the USB library. */ 102 | std::string tryGetTtlPortName() const; 103 | 104 | operator bool() const; 105 | 106 | libusbp::device usbDevice; 107 | libusbp::generic_interface usbInterface; 108 | 109 | private: 110 | uint16_t productId; 111 | std::string serialNumber; 112 | uint16_t firmwareVersion; 113 | }; 114 | 115 | std::vector programmerGetList(); 116 | 117 | // [all-settings] 118 | struct ProgrammerSettings 119 | { 120 | uint32_t sckDuration = 0; 121 | uint32_t ispFastestPeriod = 0; 122 | uint8_t regulatorMode = 0; 123 | bool vccOutputEnabled = 0; 124 | uint8_t vccOutputIndicator = 0; 125 | uint8_t lineAFunction = 0; 126 | uint8_t lineBFunction = 0; 127 | uint32_t vccVddMaxRange = 0; // units: mV 128 | uint32_t vcc3v3Min = 0; // units: mV 129 | uint32_t vcc3v3Max = 0; // units: mV 130 | uint32_t vcc5vMin = 0; // units: mV 131 | uint32_t vcc5vMax = 0; // units: mV 132 | uint32_t hardwareVersion = 0; 133 | uint32_t softwareVersionMajor = 0; 134 | uint32_t softwareVersionMinor = 0; 135 | }; 136 | 137 | struct ProgrammerVariables 138 | { 139 | uint8_t lastDeviceReset = 0; 140 | bool hasResultsFromLastProgramming = 0; 141 | uint8_t programmingError = 0; 142 | uint16_t targetVccMeasuredMinMv = 0; 143 | uint16_t targetVccMeasuredMaxMv = 0; 144 | uint16_t programmerVddMeasuredMinMv = 0; 145 | uint16_t programmerVddMeasuredMaxMv = 0; 146 | uint16_t targetVccMv = 0; 147 | uint16_t programmerVddMv = 0; 148 | uint8_t regulatorLevel = 0; 149 | bool inProgrammingMode = 0; 150 | }; 151 | 152 | struct ProgrammerDigitalReadings 153 | { 154 | uint8_t portA; 155 | uint8_t portB; 156 | uint8_t portC; 157 | }; 158 | 159 | class ProgrammerHandle 160 | { 161 | public: 162 | ProgrammerHandle(); 163 | explicit ProgrammerHandle(ProgrammerInstance); 164 | 165 | void close(); 166 | 167 | const ProgrammerInstance & getInstance() const; 168 | 169 | operator bool() const 170 | { 171 | return handle; 172 | } 173 | 174 | // Returns the firmware version string, including any modification 175 | // codes (e.g. "1.07nc"). 176 | std::string getFirmwareVersionString(); 177 | 178 | ProgrammerSettings getSettings(); 179 | void validateSettings(const ProgrammerSettings &); 180 | void applySettings(const ProgrammerSettings &); 181 | void restoreDefaults(); 182 | 183 | ProgrammerVariables getVariables(); 184 | 185 | ProgrammerDigitalReadings digitalRead(); 186 | 187 | private: 188 | uint8_t getRawSetting(uint8_t id); 189 | void setRawSetting(uint8_t id, uint8_t value); 190 | uint8_t getRawVariable(uint8_t id); 191 | 192 | std::string cachedFirmwareVersion; 193 | libusbp::generic_handle handle; 194 | ProgrammerInstance instance; 195 | }; 196 | 197 | // Returns true if a Pololu USB AVR Programmer (pgm03a) is connected 198 | // to the computer. 199 | bool pgm03aPresent(); 200 | extern const char * pgm03aMessage; 201 | -------------------------------------------------------------------------------- /include/programmer_frequency_tables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct ProgrammerFrequency 7 | { 8 | // The period in raw units (twelfths of a microsecond). 9 | uint16_t period; 10 | 11 | // The string representation of this period (in units of kHz but without any 12 | // suffix in the string). 13 | const char * name; 14 | }; 15 | 16 | /** This table defines how the meaning of the SCK_DURATION parameter of the 17 | * programmer's firmware works. 18 | * 19 | * If SCK_DURATION is 1 or more, the programmer (using a table similar to this 20 | * one) uses an ISP frequency that is obtained by treating the value of the 21 | * SCK_DURATION parameter as an index and looking it up in this table. 22 | * 23 | * If SCK_DURATION is 0, this table is ignored and the programmer uses the 24 | * ISP_FASTEST_PERIOD parameter instead. Element 0 of this table is a dummy 25 | * element that we set equal to the default frequency specified by 26 | * ISP_FASTEST_PERIOD. 27 | * 28 | * These values are not arbitrary: they were picked so that the frequency 29 | * returned by this table will be as high as possible, while (usually) not 30 | * exceeding the frequency used by the STK500 for the same SCK_DURATION byte. 31 | * This means our frequency will work for just as many AVRs, and someone can 32 | * confidently use the UI in Atmel Studio or AVRDUDE without worrying that they 33 | * are picking a frequency that is too fast. 34 | * 35 | * HOWEVER, an exception to the rule above is that we cannot have frequencies 36 | * below 1.465 kHz, so any frequency below that gets rounded. 37 | */ 38 | extern const std::vector programmerStk500FrequencyTable; 39 | 40 | /** This table defines the meaning of the ISP_FASTEST_PERIOD parameter of the 41 | * programmer's firmware. 42 | * 43 | * If SCK_DURATION is 0, the programmer will use the ISP frequency that is 44 | * obtained by treating the value of ISP_FASTEST_PERIOD as an index and looking 45 | * it up in this table. Except for the first two elements, the frequencies in 46 | * this table follow the formula: 47 | * 48 | * frequency = (12 MHz) / index 49 | */ 50 | extern const std::vector programmerFullMaxFrequencyTable; 51 | 52 | /** This table holds the set of frequencies that the software allows you to set 53 | * for the "Max ISP Frequency" setting, which corresponds to the 54 | * ISP_FASTEST_PERIOD parameter. 55 | * 56 | * This table could also be computed by selecting the elements from 57 | * programmerFullMaxFrequencyTable that have a period between 58 | * PAVR2_ISP_FASTEST_PERIOD_MIN and PAVR2_ISP_FASTEST_PERIOD_MAX. */ 59 | extern const std::vector programmerAllowedMaxFrequencyTable; 60 | 61 | /** This table holds a useful subset of programmerAllowedMaxFrequencyTable. 62 | * This allows us to show a nice selection of choices in a user interface 63 | * without overwhelming the user with too many choices. 64 | * 65 | * This table could also be computed by taking all the frequencies in both 66 | * programmerSuggestedFrequencyTable and programmerAllowedMaxFrequencyTable. */ 67 | extern const std::vector programmerSuggestedMaxFrequencyTable; 68 | 69 | /** This table holds the set of frequencies that the software allows you to set 70 | * for the "ISP Frequency" setting, which is related to two firmware parameters: 71 | * SCK_DURATION and ISP_FASTEST_PERIOD. 72 | * 73 | * This table could have also been computed by removing the first element of 74 | * programmerStk500FrequencyTable, prepending the table with 75 | * programmerAllowedMaxFrequencyTable, removing the duplicates at the end, and 76 | * sorting the table in descending order by frequency. */ 77 | extern const std::vector programmerAllowedFrequencyTable; 78 | 79 | /** This table holds a useful subset of programmerAllowedFrequencyTable. The highest 80 | * frequencies are picked somewhat arbitrarily. Many of these frequencies were 81 | * picked to match the user interface of Atmel Studio, which has notches for 82 | * theose frequencies that make them easy to select by just using the mouse. */ 83 | extern const std::vector programmerSuggestedFrequencyTable; 84 | 85 | /** The default frequency of the firmware. */ 86 | extern const ProgrammerFrequency programmerDefaultFrequency; 87 | 88 | /** The default max frequency of the firmware. */ 89 | extern const ProgrammerFrequency programmerDefaultMaxFrequency; 90 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | use_cxx11() 2 | 3 | add_library (lib STATIC 4 | programmer.cpp 5 | isp_freq_table.cpp 6 | ) 7 | 8 | include_directories ( 9 | "${CMAKE_CURRENT_SOURCE_DIR}" 10 | ) 11 | 12 | set_target_properties(lib PROPERTIES 13 | OUTPUT_NAME pavr2 14 | ) 15 | 16 | target_link_libraries (lib "${LIBUSBP_LDFLAGS}") 17 | -------------------------------------------------------------------------------- /lib/isp_freq_table.cpp: -------------------------------------------------------------------------------- 1 | /** The data in this file was auto-generated. See programmer.h for comments 2 | * explaining these tables. */ 3 | 4 | #include 5 | 6 | const std::vector programmerStk500FrequencyTable = 7 | { 8 | {7, "1714"}, 9 | {27, "444"}, 10 | {105, "114"}, 11 | {209, "57.4"}, 12 | {189, "63.5"}, 13 | {228, "52.6"}, 14 | {268, "44.8"}, 15 | {306, "39.2"}, 16 | {346, "34.7"}, 17 | {386, "31.1"}, 18 | {424, "28.3"}, 19 | {464, "25.9"}, 20 | {502, "23.9"}, 21 | {544, "22.1"}, 22 | {584, "20.5"}, 23 | {624, "19.2"}, 24 | {664, "18.1"}, 25 | {704, "17.0"}, 26 | {736, "16.3"}, 27 | {776, "15.5"}, 28 | {816, "14.7"}, 29 | {856, "14.0"}, 30 | {896, "13.4"}, 31 | {936, "12.8"}, 32 | {976, "12.3"}, 33 | {1016, "11.8"}, 34 | {1056, "11.4"}, 35 | {1088, "11.0"}, 36 | {1128, "10.6"}, 37 | {1168, "10.3"}, 38 | {1208, "9.93"}, 39 | {1248, "9.62"}, 40 | {1288, "9.32"}, 41 | {1328, "9.04"}, 42 | {1368, "8.77"}, 43 | {1400, "8.57"}, 44 | {1440, "8.33"}, 45 | {1480, "8.11"}, 46 | {1520, "7.89"}, 47 | {1560, "7.69"}, 48 | {1600, "7.50"}, 49 | {1640, "7.32"}, 50 | {1680, "7.14"}, 51 | {1720, "6.98"}, 52 | {1752, "6.85"}, 53 | {1792, "6.70"}, 54 | {1832, "6.55"}, 55 | {1872, "6.41"}, 56 | {1912, "6.28"}, 57 | {1952, "6.15"}, 58 | {1992, "6.02"}, 59 | {2032, "5.91"}, 60 | {2080, "5.77"}, 61 | {2112, "5.68"}, 62 | {2144, "5.60"}, 63 | {2208, "5.43"}, 64 | {2240, "5.36"}, 65 | {2272, "5.28"}, 66 | {2304, "5.21"}, 67 | {2368, "5.07"}, 68 | {2400, "5.00"}, 69 | {2432, "4.93"}, 70 | {2464, "4.87"}, 71 | {2496, "4.81"}, 72 | {2560, "4.69"}, 73 | {2592, "4.63"}, 74 | {2624, "4.57"}, 75 | {2656, "4.52"}, 76 | {2720, "4.41"}, 77 | {2752, "4.36"}, 78 | {2784, "4.31"}, 79 | {2816, "4.26"}, 80 | {2848, "4.21"}, 81 | {2912, "4.12"}, 82 | {2944, "4.08"}, 83 | {2976, "4.03"}, 84 | {3008, "3.99"}, 85 | {3072, "3.91"}, 86 | {3104, "3.87"}, 87 | {3136, "3.83"}, 88 | {3168, "3.79"}, 89 | {3200, "3.75"}, 90 | {3264, "3.68"}, 91 | {3296, "3.64"}, 92 | {3328, "3.61"}, 93 | {3360, "3.57"}, 94 | {3392, "3.54"}, 95 | {3456, "3.47"}, 96 | {3488, "3.44"}, 97 | {3520, "3.41"}, 98 | {3552, "3.38"}, 99 | {3616, "3.32"}, 100 | {3648, "3.29"}, 101 | {3680, "3.26"}, 102 | {3712, "3.23"}, 103 | {3744, "3.21"}, 104 | {3808, "3.15"}, 105 | {3840, "3.13"}, 106 | {3872, "3.10"}, 107 | {3904, "3.07"}, 108 | {3968, "3.02"}, 109 | {4000, "3.00"}, 110 | {4032, "2.98"}, 111 | {4064, "2.95"}, 112 | {4096, "2.93"}, 113 | {4160, "2.88"}, 114 | {4192, "2.86"}, 115 | {4224, "2.84"}, 116 | {4256, "2.82"}, 117 | {4320, "2.78"}, 118 | {4352, "2.76"}, 119 | {4384, "2.74"}, 120 | {4416, "2.72"}, 121 | {4448, "2.70"}, 122 | {4512, "2.66"}, 123 | {4544, "2.64"}, 124 | {4576, "2.62"}, 125 | {4608, "2.60"}, 126 | {4672, "2.57"}, 127 | {4704, "2.55"}, 128 | {4736, "2.53"}, 129 | {4768, "2.52"}, 130 | {4800, "2.50"}, 131 | {4864, "2.47"}, 132 | {4896, "2.45"}, 133 | {4928, "2.44"}, 134 | {4960, "2.42"}, 135 | {5024, "2.39"}, 136 | {5056, "2.37"}, 137 | {5088, "2.36"}, 138 | {5120, "2.34"}, 139 | {5152, "2.33"}, 140 | {5216, "2.30"}, 141 | {5248, "2.29"}, 142 | {5280, "2.27"}, 143 | {5312, "2.26"}, 144 | {5376, "2.23"}, 145 | {5408, "2.22"}, 146 | {5440, "2.21"}, 147 | {5472, "2.19"}, 148 | {5504, "2.18"}, 149 | {5568, "2.16"}, 150 | {5600, "2.14"}, 151 | {5632, "2.13"}, 152 | {5664, "2.12"}, 153 | {5728, "2.09"}, 154 | {5760, "2.08"}, 155 | {5792, "2.07"}, 156 | {5824, "2.06"}, 157 | {5856, "2.05"}, 158 | {5920, "2.03"}, 159 | {5952, "2.02"}, 160 | {5984, "2.01"}, 161 | {6016, "1.995"}, 162 | {6080, "1.974"}, 163 | {6112, "1.963"}, 164 | {6144, "1.953"}, 165 | {6176, "1.943"}, 166 | {6208, "1.933"}, 167 | {6272, "1.913"}, 168 | {6304, "1.904"}, 169 | {6336, "1.894"}, 170 | {6368, "1.884"}, 171 | {6400, "1.875"}, 172 | {6464, "1.856"}, 173 | {6496, "1.847"}, 174 | {6528, "1.838"}, 175 | {6560, "1.829"}, 176 | {6624, "1.812"}, 177 | {6656, "1.803"}, 178 | {6688, "1.794"}, 179 | {6720, "1.786"}, 180 | {6752, "1.777"}, 181 | {6816, "1.761"}, 182 | {6848, "1.752"}, 183 | {6880, "1.744"}, 184 | {6912, "1.736"}, 185 | {6976, "1.720"}, 186 | {7008, "1.712"}, 187 | {7040, "1.705"}, 188 | {7072, "1.697"}, 189 | {7104, "1.689"}, 190 | {7168, "1.674"}, 191 | {7200, "1.667"}, 192 | {7232, "1.659"}, 193 | {7264, "1.652"}, 194 | {7328, "1.638"}, 195 | {7360, "1.630"}, 196 | {7392, "1.623"}, 197 | {7424, "1.616"}, 198 | {7456, "1.609"}, 199 | {7520, "1.596"}, 200 | {7552, "1.589"}, 201 | {7584, "1.582"}, 202 | {7616, "1.576"}, 203 | {7680, "1.563"}, 204 | {7712, "1.556"}, 205 | {7744, "1.550"}, 206 | {7776, "1.543"}, 207 | {7808, "1.537"}, 208 | {7872, "1.524"}, 209 | {7904, "1.518"}, 210 | {7936, "1.512"}, 211 | {7968, "1.506"}, 212 | {8032, "1.494"}, 213 | {8064, "1.488"}, 214 | {8096, "1.482"}, 215 | {8128, "1.476"}, 216 | {8160, "1.471"}, 217 | {8192, "1.465"}, 218 | {8192, "1.465"}, 219 | {8192, "1.465"}, 220 | {8192, "1.465"}, 221 | {8192, "1.465"}, 222 | {8192, "1.465"}, 223 | {8192, "1.465"}, 224 | {8192, "1.465"}, 225 | {8192, "1.465"}, 226 | {8192, "1.465"}, 227 | {8192, "1.465"}, 228 | {8192, "1.465"}, 229 | {8192, "1.465"}, 230 | {8192, "1.465"}, 231 | {8192, "1.465"}, 232 | {8192, "1.465"}, 233 | {8192, "1.465"}, 234 | {8192, "1.465"}, 235 | {8192, "1.465"}, 236 | {8192, "1.465"}, 237 | {8192, "1.465"}, 238 | {8192, "1.465"}, 239 | {8192, "1.465"}, 240 | {8192, "1.465"}, 241 | {8192, "1.465"}, 242 | {8192, "1.465"}, 243 | {8192, "1.465"}, 244 | {8192, "1.465"}, 245 | {8192, "1.465"}, 246 | {8192, "1.465"}, 247 | {8192, "1.465"}, 248 | {8192, "1.465"}, 249 | {8192, "1.465"}, 250 | {8192, "1.465"}, 251 | {8192, "1.465"}, 252 | {8192, "1.465"}, 253 | {8192, "1.465"}, 254 | {8192, "1.465"}, 255 | {8192, "1.465"}, 256 | {8192, "1.465"}, 257 | {8192, "1.465"}, 258 | {8192, "1.465"}, 259 | {8192, "1.465"}, 260 | {8192, "1.465"}, 261 | {8192, "1.465"}, 262 | {8192, "1.465"}, 263 | {8192, "1.465"}, 264 | }; 265 | 266 | const std::vector programmerFullMaxFrequencyTable = 267 | { 268 | {2, "6000"}, 269 | {2, "6000"}, 270 | {2, "6000"}, 271 | {3, "4000"}, 272 | {4, "3000"}, 273 | {5, "2400"}, 274 | {6, "2000"}, 275 | {7, "1714"}, 276 | {8, "1500"}, 277 | {9, "1333"}, 278 | {10, "1200"}, 279 | {11, "1091"}, 280 | {12, "1000"}, 281 | {13, "923"}, 282 | {14, "857"}, 283 | {15, "800"}, 284 | {16, "750"}, 285 | {17, "706"}, 286 | {18, "667"}, 287 | {19, "632"}, 288 | {20, "600"}, 289 | {21, "571"}, 290 | {22, "545"}, 291 | {23, "522"}, 292 | {24, "500"}, 293 | {25, "480"}, 294 | {26, "462"}, 295 | {27, "444"}, 296 | {28, "429"}, 297 | {29, "414"}, 298 | {30, "400"}, 299 | {31, "387"}, 300 | {32, "375"}, 301 | {33, "364"}, 302 | {34, "353"}, 303 | {35, "343"}, 304 | {36, "333"}, 305 | {37, "324"}, 306 | {38, "316"}, 307 | {39, "308"}, 308 | {40, "300"}, 309 | {41, "293"}, 310 | {42, "286"}, 311 | {43, "279"}, 312 | {44, "273"}, 313 | {45, "267"}, 314 | {46, "261"}, 315 | {47, "255"}, 316 | {48, "250"}, 317 | {49, "245"}, 318 | {50, "240"}, 319 | {51, "235"}, 320 | {52, "231"}, 321 | {53, "226"}, 322 | {54, "222"}, 323 | {55, "218"}, 324 | {56, "214"}, 325 | {57, "211"}, 326 | {58, "207"}, 327 | {59, "203"}, 328 | {60, "200"}, 329 | {61, "197"}, 330 | {62, "194"}, 331 | {63, "190"}, 332 | {64, "188"}, 333 | {65, "185"}, 334 | {66, "182"}, 335 | {67, "179"}, 336 | {68, "176"}, 337 | {69, "174"}, 338 | {70, "171"}, 339 | {71, "169"}, 340 | {72, "167"}, 341 | {73, "164"}, 342 | {74, "162"}, 343 | {75, "160"}, 344 | {76, "158"}, 345 | {77, "156"}, 346 | {78, "154"}, 347 | {79, "152"}, 348 | {80, "150"}, 349 | {81, "148"}, 350 | {82, "146"}, 351 | {83, "145"}, 352 | {84, "143"}, 353 | {85, "141"}, 354 | {86, "140"}, 355 | {87, "138"}, 356 | {88, "136"}, 357 | {89, "135"}, 358 | {90, "133"}, 359 | {91, "132"}, 360 | {92, "130"}, 361 | {93, "129"}, 362 | {94, "128"}, 363 | {95, "126"}, 364 | {96, "125"}, 365 | {97, "124"}, 366 | {98, "122"}, 367 | {99, "121"}, 368 | {100, "120"}, 369 | {101, "119"}, 370 | {102, "118"}, 371 | {103, "117"}, 372 | {104, "115"}, 373 | {105, "114"}, 374 | {106, "113"}, 375 | {107, "112"}, 376 | {108, "111"}, 377 | {109, "110"}, 378 | {110, "109"}, 379 | {111, "108"}, 380 | {112, "107"}, 381 | {113, "106"}, 382 | {114, "105"}, 383 | {115, "104"}, 384 | {116, "103"}, 385 | {117, "103"}, 386 | {118, "102"}, 387 | {119, "101"}, 388 | {120, "100"}, 389 | {121, "99.2"}, 390 | {122, "98.4"}, 391 | {123, "97.6"}, 392 | {124, "96.8"}, 393 | {125, "96.0"}, 394 | {126, "95.2"}, 395 | {127, "94.5"}, 396 | {128, "93.8"}, 397 | {129, "93.0"}, 398 | {130, "92.3"}, 399 | {131, "91.6"}, 400 | {132, "90.9"}, 401 | {133, "90.2"}, 402 | {134, "89.6"}, 403 | {135, "88.9"}, 404 | {136, "88.2"}, 405 | {137, "87.6"}, 406 | {138, "87.0"}, 407 | {139, "86.3"}, 408 | {140, "85.7"}, 409 | {141, "85.1"}, 410 | {142, "84.5"}, 411 | {143, "83.9"}, 412 | {144, "83.3"}, 413 | {145, "82.8"}, 414 | {146, "82.2"}, 415 | {147, "81.6"}, 416 | {148, "81.1"}, 417 | {149, "80.5"}, 418 | {150, "80.0"}, 419 | {151, "79.5"}, 420 | {152, "78.9"}, 421 | {153, "78.4"}, 422 | {154, "77.9"}, 423 | {155, "77.4"}, 424 | {156, "76.9"}, 425 | {157, "76.4"}, 426 | {158, "75.9"}, 427 | {159, "75.5"}, 428 | {160, "75.0"}, 429 | {161, "74.5"}, 430 | {162, "74.1"}, 431 | {163, "73.6"}, 432 | {164, "73.2"}, 433 | {165, "72.7"}, 434 | {166, "72.3"}, 435 | {167, "71.9"}, 436 | {168, "71.4"}, 437 | {169, "71.0"}, 438 | {170, "70.6"}, 439 | {171, "70.2"}, 440 | {172, "69.8"}, 441 | {173, "69.4"}, 442 | {174, "69.0"}, 443 | {175, "68.6"}, 444 | {176, "68.2"}, 445 | {177, "67.8"}, 446 | {178, "67.4"}, 447 | {179, "67.0"}, 448 | {180, "66.7"}, 449 | {181, "66.3"}, 450 | {182, "65.9"}, 451 | {183, "65.6"}, 452 | {184, "65.2"}, 453 | {185, "64.9"}, 454 | {186, "64.5"}, 455 | {187, "64.2"}, 456 | {188, "63.8"}, 457 | {189, "63.5"}, 458 | {190, "63.2"}, 459 | {191, "62.8"}, 460 | {192, "62.5"}, 461 | {193, "62.2"}, 462 | {194, "61.9"}, 463 | {195, "61.5"}, 464 | {196, "61.2"}, 465 | {197, "60.9"}, 466 | {198, "60.6"}, 467 | {199, "60.3"}, 468 | {200, "60.0"}, 469 | {201, "59.7"}, 470 | {202, "59.4"}, 471 | {203, "59.1"}, 472 | {204, "58.8"}, 473 | {205, "58.5"}, 474 | {206, "58.3"}, 475 | {207, "58.0"}, 476 | {208, "57.7"}, 477 | {209, "57.4"}, 478 | {210, "57.1"}, 479 | {211, "56.9"}, 480 | {212, "56.6"}, 481 | {213, "56.3"}, 482 | {214, "56.1"}, 483 | {215, "55.8"}, 484 | {216, "55.6"}, 485 | {217, "55.3"}, 486 | {218, "55.0"}, 487 | {219, "54.8"}, 488 | {220, "54.5"}, 489 | {221, "54.3"}, 490 | {222, "54.1"}, 491 | {223, "53.8"}, 492 | {224, "53.6"}, 493 | {225, "53.3"}, 494 | {226, "53.1"}, 495 | {227, "52.9"}, 496 | {228, "52.6"}, 497 | {229, "52.4"}, 498 | {230, "52.2"}, 499 | {231, "51.9"}, 500 | {232, "51.7"}, 501 | {233, "51.5"}, 502 | {234, "51.3"}, 503 | {235, "51.1"}, 504 | {236, "50.8"}, 505 | {237, "50.6"}, 506 | {238, "50.4"}, 507 | {239, "50.2"}, 508 | {240, "50.0"}, 509 | {241, "49.8"}, 510 | {242, "49.6"}, 511 | {243, "49.4"}, 512 | {244, "49.2"}, 513 | {245, "49.0"}, 514 | {246, "48.8"}, 515 | {247, "48.6"}, 516 | {248, "48.4"}, 517 | {249, "48.2"}, 518 | {250, "48.0"}, 519 | {251, "47.8"}, 520 | {252, "47.6"}, 521 | {253, "47.4"}, 522 | {254, "47.2"}, 523 | {255, "47.1"}, 524 | }; 525 | 526 | const std::vector programmerAllowedMaxFrequencyTable = 527 | { 528 | {2, "6000"}, 529 | {3, "4000"}, 530 | {4, "3000"}, 531 | {5, "2400"}, 532 | {6, "2000"}, 533 | {7, "1714"}, 534 | {8, "1500"}, 535 | {9, "1333"}, 536 | {10, "1200"}, 537 | {11, "1091"}, 538 | {12, "1000"}, 539 | {13, "923"}, 540 | {14, "857"}, 541 | {15, "800"}, 542 | {16, "750"}, 543 | {17, "706"}, 544 | {18, "667"}, 545 | {19, "632"}, 546 | {20, "600"}, 547 | {21, "571"}, 548 | {22, "545"}, 549 | {23, "522"}, 550 | {24, "500"}, 551 | {25, "480"}, 552 | {26, "462"}, 553 | }; 554 | 555 | const std::vector programmerSuggestedMaxFrequencyTable = 556 | { 557 | {4, "3000"}, 558 | {5, "2400"}, 559 | {6, "2000"}, 560 | {7, "1714"}, 561 | {8, "1500"}, 562 | {10, "1200"}, 563 | {12, "1000"}, 564 | {16, "750"}, 565 | }; 566 | 567 | const std::vector programmerAllowedFrequencyTable = 568 | { 569 | {2, "6000"}, 570 | {3, "4000"}, 571 | {4, "3000"}, 572 | {5, "2400"}, 573 | {6, "2000"}, 574 | {7, "1714"}, 575 | {8, "1500"}, 576 | {9, "1333"}, 577 | {10, "1200"}, 578 | {11, "1091"}, 579 | {12, "1000"}, 580 | {13, "923"}, 581 | {14, "857"}, 582 | {15, "800"}, 583 | {16, "750"}, 584 | {17, "706"}, 585 | {18, "667"}, 586 | {19, "632"}, 587 | {20, "600"}, 588 | {21, "571"}, 589 | {22, "545"}, 590 | {23, "522"}, 591 | {24, "500"}, 592 | {25, "480"}, 593 | {26, "462"}, 594 | {27, "444"}, 595 | {105, "114"}, 596 | {189, "63.5"}, 597 | {209, "57.4"}, 598 | {228, "52.6"}, 599 | {268, "44.8"}, 600 | {306, "39.2"}, 601 | {346, "34.7"}, 602 | {386, "31.1"}, 603 | {424, "28.3"}, 604 | {464, "25.9"}, 605 | {502, "23.9"}, 606 | {544, "22.1"}, 607 | {584, "20.5"}, 608 | {624, "19.2"}, 609 | {664, "18.1"}, 610 | {704, "17.0"}, 611 | {736, "16.3"}, 612 | {776, "15.5"}, 613 | {816, "14.7"}, 614 | {856, "14.0"}, 615 | {896, "13.4"}, 616 | {936, "12.8"}, 617 | {976, "12.3"}, 618 | {1016, "11.8"}, 619 | {1056, "11.4"}, 620 | {1088, "11.0"}, 621 | {1128, "10.6"}, 622 | {1168, "10.3"}, 623 | {1208, "9.93"}, 624 | {1248, "9.62"}, 625 | {1288, "9.32"}, 626 | {1328, "9.04"}, 627 | {1368, "8.77"}, 628 | {1400, "8.57"}, 629 | {1440, "8.33"}, 630 | {1480, "8.11"}, 631 | {1520, "7.89"}, 632 | {1560, "7.69"}, 633 | {1600, "7.50"}, 634 | {1640, "7.32"}, 635 | {1680, "7.14"}, 636 | {1720, "6.98"}, 637 | {1752, "6.85"}, 638 | {1792, "6.70"}, 639 | {1832, "6.55"}, 640 | {1872, "6.41"}, 641 | {1912, "6.28"}, 642 | {1952, "6.15"}, 643 | {1992, "6.02"}, 644 | {2032, "5.91"}, 645 | {2080, "5.77"}, 646 | {2112, "5.68"}, 647 | {2144, "5.60"}, 648 | {2208, "5.43"}, 649 | {2240, "5.36"}, 650 | {2272, "5.28"}, 651 | {2304, "5.21"}, 652 | {2368, "5.07"}, 653 | {2400, "5.00"}, 654 | {2432, "4.93"}, 655 | {2464, "4.87"}, 656 | {2496, "4.81"}, 657 | {2560, "4.69"}, 658 | {2592, "4.63"}, 659 | {2624, "4.57"}, 660 | {2656, "4.52"}, 661 | {2720, "4.41"}, 662 | {2752, "4.36"}, 663 | {2784, "4.31"}, 664 | {2816, "4.26"}, 665 | {2848, "4.21"}, 666 | {2912, "4.12"}, 667 | {2944, "4.08"}, 668 | {2976, "4.03"}, 669 | {3008, "3.99"}, 670 | {3072, "3.91"}, 671 | {3104, "3.87"}, 672 | {3136, "3.83"}, 673 | {3168, "3.79"}, 674 | {3200, "3.75"}, 675 | {3264, "3.68"}, 676 | {3296, "3.64"}, 677 | {3328, "3.61"}, 678 | {3360, "3.57"}, 679 | {3392, "3.54"}, 680 | {3456, "3.47"}, 681 | {3488, "3.44"}, 682 | {3520, "3.41"}, 683 | {3552, "3.38"}, 684 | {3616, "3.32"}, 685 | {3648, "3.29"}, 686 | {3680, "3.26"}, 687 | {3712, "3.23"}, 688 | {3744, "3.21"}, 689 | {3808, "3.15"}, 690 | {3840, "3.13"}, 691 | {3872, "3.10"}, 692 | {3904, "3.07"}, 693 | {3968, "3.02"}, 694 | {4000, "3.00"}, 695 | {4032, "2.98"}, 696 | {4064, "2.95"}, 697 | {4096, "2.93"}, 698 | {4160, "2.88"}, 699 | {4192, "2.86"}, 700 | {4224, "2.84"}, 701 | {4256, "2.82"}, 702 | {4320, "2.78"}, 703 | {4352, "2.76"}, 704 | {4384, "2.74"}, 705 | {4416, "2.72"}, 706 | {4448, "2.70"}, 707 | {4512, "2.66"}, 708 | {4544, "2.64"}, 709 | {4576, "2.62"}, 710 | {4608, "2.60"}, 711 | {4672, "2.57"}, 712 | {4704, "2.55"}, 713 | {4736, "2.53"}, 714 | {4768, "2.52"}, 715 | {4800, "2.50"}, 716 | {4864, "2.47"}, 717 | {4896, "2.45"}, 718 | {4928, "2.44"}, 719 | {4960, "2.42"}, 720 | {5024, "2.39"}, 721 | {5056, "2.37"}, 722 | {5088, "2.36"}, 723 | {5120, "2.34"}, 724 | {5152, "2.33"}, 725 | {5216, "2.30"}, 726 | {5248, "2.29"}, 727 | {5280, "2.27"}, 728 | {5312, "2.26"}, 729 | {5376, "2.23"}, 730 | {5408, "2.22"}, 731 | {5440, "2.21"}, 732 | {5472, "2.19"}, 733 | {5504, "2.18"}, 734 | {5568, "2.16"}, 735 | {5600, "2.14"}, 736 | {5632, "2.13"}, 737 | {5664, "2.12"}, 738 | {5728, "2.09"}, 739 | {5760, "2.08"}, 740 | {5792, "2.07"}, 741 | {5824, "2.06"}, 742 | {5856, "2.05"}, 743 | {5920, "2.03"}, 744 | {5952, "2.02"}, 745 | {5984, "2.01"}, 746 | {6016, "1.995"}, 747 | {6080, "1.974"}, 748 | {6112, "1.963"}, 749 | {6144, "1.953"}, 750 | {6176, "1.943"}, 751 | {6208, "1.933"}, 752 | {6272, "1.913"}, 753 | {6304, "1.904"}, 754 | {6336, "1.894"}, 755 | {6368, "1.884"}, 756 | {6400, "1.875"}, 757 | {6464, "1.856"}, 758 | {6496, "1.847"}, 759 | {6528, "1.838"}, 760 | {6560, "1.829"}, 761 | {6624, "1.812"}, 762 | {6656, "1.803"}, 763 | {6688, "1.794"}, 764 | {6720, "1.786"}, 765 | {6752, "1.777"}, 766 | {6816, "1.761"}, 767 | {6848, "1.752"}, 768 | {6880, "1.744"}, 769 | {6912, "1.736"}, 770 | {6976, "1.720"}, 771 | {7008, "1.712"}, 772 | {7040, "1.705"}, 773 | {7072, "1.697"}, 774 | {7104, "1.689"}, 775 | {7168, "1.674"}, 776 | {7200, "1.667"}, 777 | {7232, "1.659"}, 778 | {7264, "1.652"}, 779 | {7328, "1.638"}, 780 | {7360, "1.630"}, 781 | {7392, "1.623"}, 782 | {7424, "1.616"}, 783 | {7456, "1.609"}, 784 | {7520, "1.596"}, 785 | {7552, "1.589"}, 786 | {7584, "1.582"}, 787 | {7616, "1.576"}, 788 | {7680, "1.563"}, 789 | {7712, "1.556"}, 790 | {7744, "1.550"}, 791 | {7776, "1.543"}, 792 | {7808, "1.537"}, 793 | {7872, "1.524"}, 794 | {7904, "1.518"}, 795 | {7936, "1.512"}, 796 | {7968, "1.506"}, 797 | {8032, "1.494"}, 798 | {8064, "1.488"}, 799 | {8096, "1.482"}, 800 | {8128, "1.476"}, 801 | {8160, "1.471"}, 802 | {8192, "1.465"}, 803 | }; 804 | 805 | const std::vector programmerSuggestedFrequencyTable = 806 | { 807 | {4, "3000"}, 808 | {5, "2400"}, 809 | {6, "2000"}, 810 | {7, "1714"}, 811 | {8, "1500"}, 812 | {10, "1200"}, 813 | {12, "1000"}, 814 | {16, "750"}, 815 | {27, "444"}, 816 | {105, "114"}, 817 | {424, "28.3"}, 818 | {856, "14.0"}, 819 | {1720, "6.98"}, 820 | {3456, "3.47"}, 821 | {6880, "1.744"}, 822 | {8192, "1.465"}, 823 | }; 824 | 825 | const ProgrammerFrequency programmerDefaultFrequency = {105, "114"}; 826 | 827 | const ProgrammerFrequency programmerDefaultMaxFrequency = {7, "1714"}; 828 | -------------------------------------------------------------------------------- /lib/programmer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // A setup packet bRequest value from USB 2.0 Table 9-4 10 | #define USB_REQUEST_GET_DESCRIPTOR 6 11 | 12 | // A descriptor type from USB 2.0 Table 9-5 13 | #define USB_DESCRIPTOR_TYPE_STRING 3 14 | 15 | // The programmer uses voltage units of 32 mV, so the maximum representable 16 | // voltage is 8160 mV. 17 | static const uint32_t maxRepresentableVoltage = 255 * PAVR2_VOLTAGE_UNITS; 18 | #if PAVR2_VOLTAGE_UNITS == 32 19 | #define MAX_REPRESENTABLE_VOLTAGE_STR "8160 mV" 20 | #endif 21 | 22 | // Searches for a frequency with the given name inside a vector of frequencies 23 | // and returns its index. Returns -1 if it is not found. 24 | static int32_t frequencyFindByName( 25 | const std::vector & table, 26 | int32_t start, int32_t end, 27 | std::string name) 28 | { 29 | for(int32_t i = start; i <= end; i++) 30 | { 31 | if (table[i].name == name) 32 | { 33 | return i; 34 | } 35 | } 36 | return -1; 37 | } 38 | 39 | // Look up a frequency in the list of options that are a user can select when 40 | // they are setting the "Max ISP Frequency" setting. 41 | static int32_t allowedMaxFrequencyFind(std::string name) 42 | { 43 | return frequencyFindByName(programmerAllowedMaxFrequencyTable, 44 | 0, programmerAllowedMaxFrequencyTable.size() - 1, name); 45 | } 46 | 47 | static int32_t stk500FrequencyFind(std::string name) 48 | { 49 | return frequencyFindByName(programmerStk500FrequencyTable, 0, 255, name); 50 | } 51 | 52 | std::string Programmer::getMaxFrequencyName(uint32_t ispFastestPeriod) 53 | { 54 | if (ispFastestPeriod <= 255) 55 | { 56 | return programmerFullMaxFrequencyTable[ispFastestPeriod].name; 57 | } 58 | else 59 | { 60 | throw std::runtime_error("The period for the maximum ISP frequency must be 255 or less."); 61 | } 62 | } 63 | 64 | void Programmer::setMaxFrequency(ProgrammerSettings & settings, 65 | std::string maxFrequencyName) 66 | { 67 | int32_t index = allowedMaxFrequencyFind(maxFrequencyName); 68 | 69 | if (index < 0) 70 | { 71 | throw std::runtime_error( 72 | std::string("Invalid maximum frequency name: '") + 73 | maxFrequencyName + "'."); 74 | } 75 | 76 | settings.ispFastestPeriod = programmerAllowedMaxFrequencyTable[index].period; 77 | } 78 | 79 | std::string Programmer::getFrequencyName(uint32_t sckDuration, 80 | uint32_t ispFastestPeriod) 81 | { 82 | if (sckDuration == 0) 83 | { 84 | // When SCK_DURATION is 0, that means that the frequency is controlled 85 | // by the ISP_FASTEST_PERIOD setting. 86 | return getMaxFrequencyName(ispFastestPeriod); 87 | } 88 | else if (sckDuration <= 255) 89 | { 90 | return programmerStk500FrequencyTable[sckDuration].name; 91 | } 92 | else 93 | { 94 | throw std::runtime_error("SCK duration must be 255 or less."); 95 | } 96 | } 97 | 98 | void Programmer::setFrequency(ProgrammerSettings & settings, std::string frequencyName) 99 | { 100 | int32_t index = stk500FrequencyFind(frequencyName); 101 | if (index > 0) 102 | { 103 | // The comparison above is "> 0" on purpose. If index == 0, it means that 104 | // we that we found the first element, which is really just a dummy element. 105 | // Setting SCK_DURATION to 0 does not use mean that element will be used 106 | // (see below). 107 | 108 | // This is a frequency we can achieve by just setting the SCK_DURATION 109 | // parameter, so do that. 110 | settings.sckDuration = index; 111 | } 112 | else 113 | { 114 | index = allowedMaxFrequencyFind(frequencyName); 115 | 116 | if (index >= 0) 117 | { 118 | // This is a frequency we can achieve by setting SCK_DURATION to 0, 119 | // which means to use ISP_FASTEST_PERIOD, and then setting the 120 | // ISP_FASTEST_PERIOD. 121 | settings.sckDuration = 0; 122 | settings.ispFastestPeriod = programmerAllowedMaxFrequencyTable[index].period; 123 | } 124 | else 125 | { 126 | throw std::runtime_error( 127 | std::string("Invalid frequency name: '") + frequencyName + "'."); 128 | } 129 | } 130 | } 131 | 132 | std::string Programmer::convertProgrammingErrorToShortString(uint8_t programmingError) 133 | { 134 | switch (programmingError) 135 | { 136 | case 0: 137 | return "No error."; 138 | 139 | case PAVR2_PROGRAMMING_ERROR_TARGET_POWER_BAD: 140 | return "Target power error."; 141 | 142 | case PAVR2_PROGRAMMING_ERROR_SYNCH: 143 | return "Initial SPI command failed."; 144 | 145 | case PAVR2_PROGRAMMING_ERROR_IDLE_FOR_TOO_LONG: 146 | return "Idle error."; 147 | 148 | case PAVR2_PROGRAMMING_ERROR_USB_NOT_CONFIGURED: 149 | return "USB not configured."; 150 | 151 | case PAVR2_PROGRAMMING_ERROR_USB_SUSPEND: 152 | return "USB suspended."; 153 | 154 | case PAVR2_PROGRAMMING_ERROR_PROGRAMMER_POWER_BAD: 155 | return "Programmer power error."; 156 | 157 | default: 158 | return std::string("Unknown code ") + std::to_string(programmingError) + "."; 159 | } 160 | } 161 | 162 | std::string Programmer::convertProgrammingErrorToLongString(uint8_t programmingError) 163 | { 164 | switch (programmingError) 165 | { 166 | case 0: 167 | return ""; 168 | 169 | case PAVR2_PROGRAMMING_ERROR_TARGET_POWER_BAD: 170 | return "Target VCC went outside of the allowed range, " 171 | "so programming was aborted. " 172 | "Make sure that the target is powered on and its batteries " 173 | "are not too low (if applicable)."; 174 | 175 | case PAVR2_PROGRAMMING_ERROR_SYNCH: 176 | return "The SPI command for entering programming mode was sent, " 177 | "but the expected response from the target was not received. " 178 | "Make sure that the ISP frequency is less than one sixth " 179 | "of the target's clock frequency."; 180 | 181 | case PAVR2_PROGRAMMING_ERROR_IDLE_FOR_TOO_LONG: 182 | return "The programmer received no programming commands from the " 183 | "computer for a time longer than the timeout period, " 184 | "so programming was aborted."; 185 | 186 | case PAVR2_PROGRAMMING_ERROR_USB_NOT_CONFIGURED: 187 | return "The computer's USB controller deconfigured the programmer, " 188 | "so programming was aborted."; 189 | 190 | case PAVR2_PROGRAMMING_ERROR_USB_SUSPEND: 191 | return "The computer's USB controller put the programmer into suspend mode, " 192 | "so programming was aborted."; 193 | 194 | case PAVR2_PROGRAMMING_ERROR_PROGRAMMER_POWER_BAD: 195 | return "The programmer's VDD either went too low or had too much range, " 196 | "so programming was aborted."; 197 | 198 | default: 199 | return ""; 200 | } 201 | } 202 | 203 | std::string Programmer::convertDeviceResetToString(uint8_t deviceReset) 204 | { 205 | switch(deviceReset) 206 | { 207 | case PAVR2_RESET_POWER_UP: 208 | return "Power-on reset"; 209 | 210 | case PAVR2_RESET_BROWNOUT: 211 | return "Brown-out reset"; 212 | 213 | case PAVR2_RESET_RESET_LINE: 214 | return "Reset pin driven low"; 215 | 216 | case PAVR2_RESET_WATCHDOG: 217 | return "Watchdog reset"; 218 | 219 | case PAVR2_RESET_SOFTWARE: 220 | return "Software reset (bootloader)"; 221 | 222 | case PAVR2_RESET_STACK_OVERFLOW: 223 | return "Stack overflow"; 224 | 225 | case PAVR2_RESET_STACK_UNDERFLOW: 226 | return "Stack underflow"; 227 | 228 | default: 229 | return std::string("Unknown code ") + std::to_string(deviceReset) + "."; 230 | } 231 | } 232 | 233 | std::string Programmer::convertRegulatorModeToString(uint8_t regulatorMode) 234 | { 235 | switch (regulatorMode) 236 | { 237 | case PAVR2_REGULATOR_MODE_3V3: return "3.3 V"; 238 | case PAVR2_REGULATOR_MODE_5V: return "5 V"; 239 | default: return "auto"; 240 | } 241 | } 242 | 243 | std::string Programmer::convertRegulatorLevelToString(uint8_t regulatorLevel) 244 | { 245 | // The levels are a subset of the modes. 246 | return convertRegulatorModeToString(regulatorLevel); 247 | } 248 | 249 | std::string Programmer::convertLineFunctionToString(uint8_t lineFunction) 250 | { 251 | switch (lineFunction) 252 | { 253 | case PAVR2_LINE_IS_DTR: return "DTR"; 254 | case PAVR2_LINE_IS_RTS: return "RTS"; 255 | case PAVR2_LINE_IS_CD: return "CD"; 256 | case PAVR2_LINE_IS_DSR: return "DSR"; 257 | case PAVR2_LINE_IS_CLOCK: return "Clock"; 258 | case PAVR2_LINE_IS_DTR_RESET: return "DTR reset"; 259 | default: return "None"; 260 | } 261 | } 262 | 263 | ProgrammerInstance::ProgrammerInstance() 264 | { 265 | } 266 | 267 | ProgrammerInstance::ProgrammerInstance( 268 | libusbp::device usbDevice, 269 | libusbp::generic_interface usbInterface, 270 | uint16_t productId, 271 | std::string serialNumber, 272 | uint16_t firmwareVersion) 273 | : usbDevice(usbDevice), usbInterface(usbInterface), productId(productId), 274 | serialNumber(serialNumber), firmwareVersion(firmwareVersion) 275 | { 276 | } 277 | 278 | ProgrammerInstance::operator bool() const 279 | { 280 | return usbInterface; 281 | } 282 | 283 | std::string ProgrammerInstance::getName() const 284 | { 285 | if (productId == PAVR2_USB_PRODUCT_ID_V2) 286 | { 287 | return "Pololu USB AVR Programmer v2"; 288 | } 289 | else if (productId == PAVR2_USB_PRODUCT_ID_V2_1) 290 | { 291 | return "Pololu USB AVR Programmer v2.1"; 292 | } 293 | else 294 | { 295 | // Should not happen. 296 | return "Pololu USB AVR Programmer v2.x?"; 297 | } 298 | } 299 | 300 | std::string ProgrammerInstance::getOsId() const 301 | { 302 | return usbDevice.get_os_id(); 303 | } 304 | 305 | std::string ProgrammerInstance::getSerialNumber() const 306 | { 307 | return serialNumber; 308 | } 309 | 310 | uint16_t ProgrammerInstance::getFirmwareVersion() const 311 | { 312 | return firmwareVersion; 313 | } 314 | 315 | static uint8_t bcdToDecimal(uint8_t bcd) 316 | { 317 | return (bcd & 0xF) + 10 * (bcd >> 4); 318 | } 319 | 320 | std::string ProgrammerInstance::getFirmwareVersionString() const 321 | { 322 | char buffer[8]; 323 | snprintf(buffer, sizeof(buffer), "%d.%02d", 324 | getFirmwareVersionMajor(), getFirmwareVersionMinor()); 325 | return std::string(buffer); 326 | } 327 | 328 | uint8_t ProgrammerInstance::getFirmwareVersionMajor() const 329 | { 330 | return bcdToDecimal(firmwareVersion >> 8); 331 | } 332 | 333 | uint8_t ProgrammerInstance::getFirmwareVersionMinor() const 334 | { 335 | return bcdToDecimal(firmwareVersion & 0xFF); 336 | } 337 | 338 | std::string ProgrammerInstance::getProgrammingPortName() const 339 | { 340 | libusbp::serial_port port(usbDevice, 1, true); 341 | return port.get_name(); 342 | } 343 | 344 | std::string ProgrammerInstance::getTtlPortName() const 345 | { 346 | libusbp::serial_port port(usbDevice, 3, true); 347 | return port.get_name(); 348 | } 349 | 350 | std::string ProgrammerInstance::tryGetProgrammingPortName() const 351 | { 352 | try 353 | { 354 | return getProgrammingPortName(); 355 | } 356 | catch (const libusbp::error & exception) 357 | { 358 | return "(unknown)"; 359 | } 360 | } 361 | 362 | std::string ProgrammerInstance::tryGetTtlPortName() const 363 | { 364 | try 365 | { 366 | return getTtlPortName(); 367 | } 368 | catch (const libusbp::error & exception) 369 | { 370 | return "(unknown)"; 371 | } 372 | } 373 | 374 | std::vector programmerGetList() 375 | { 376 | std::vector list; 377 | for (const libusbp::device & device : libusbp::list_connected_devices()) 378 | { 379 | if (device.get_vendor_id() != PAVR2_USB_VENDOR_ID) { continue; } 380 | 381 | uint16_t productId = device.get_product_id(); 382 | 383 | bool isProgrammer = 384 | productId == PAVR2_USB_PRODUCT_ID_V2 || 385 | productId == PAVR2_USB_PRODUCT_ID_V2_1; 386 | 387 | if (!isProgrammer) { continue; } 388 | 389 | libusbp::generic_interface usbInterface; 390 | try 391 | { 392 | uint8_t interfaceNumber = 0; 393 | bool composite = true; 394 | usbInterface = libusbp::generic_interface(device, interfaceNumber, composite); 395 | } 396 | catch(const libusbp::error & error) 397 | { 398 | if (error.has_code(LIBUSBP_ERROR_NOT_READY)) 399 | { 400 | // An error occurred that is normal if the interface is simply 401 | // not ready to use yet. Silently ignore it. 402 | continue; 403 | } 404 | throw; 405 | } 406 | ProgrammerInstance instance(device, usbInterface, productId, 407 | device.get_serial_number(), device.get_revision()); 408 | list.push_back(instance); 409 | } 410 | return list; 411 | } 412 | 413 | ProgrammerHandle::ProgrammerHandle() 414 | { 415 | } 416 | 417 | ProgrammerHandle::ProgrammerHandle(ProgrammerInstance instance) 418 | { 419 | assert(instance); 420 | 421 | if (instance.getFirmwareVersionMajor() > PAVR2_FIRMWARE_VERSION_MAJOR_MAX) 422 | { 423 | throw std::runtime_error( 424 | "The device has new firmware that is not supported by this software. " 425 | "Try using the latest version of this software from " DOCUMENTATION_URL); 426 | } 427 | 428 | this->instance = instance; 429 | handle = libusbp::generic_handle(instance.usbInterface); 430 | 431 | // Set a timeout for all control transfers to prevent the CLI from hanging 432 | // indefinitely if something goes wrong with the USB communication. 433 | handle.set_timeout(0, 300); 434 | } 435 | 436 | void ProgrammerHandle::close() 437 | { 438 | handle.close(); 439 | instance = ProgrammerInstance(); 440 | } 441 | 442 | const ProgrammerInstance & ProgrammerHandle::getInstance() const 443 | { 444 | return instance; 445 | } 446 | 447 | uint8_t ProgrammerHandle::getRawSetting(uint8_t id) 448 | { 449 | uint8_t value; 450 | size_t transferred; 451 | try 452 | { 453 | handle.control_transfer(0xC0, PAVR2_REQUEST_GET_SETTING, 454 | 0, id, &value, 1, &transferred); 455 | } 456 | catch (const libusbp::error & error) 457 | { 458 | throw std::runtime_error(std::string("Failed to read a setting. ") 459 | + error.message()); 460 | } 461 | 462 | if (transferred != 1) 463 | { 464 | throw std::runtime_error(std::string("Failed to read a setting. ") + 465 | "Expected 1 byte, got " + std::to_string(transferred)); 466 | } 467 | 468 | return value; 469 | } 470 | 471 | void ProgrammerHandle::setRawSetting(uint8_t id, uint8_t value) 472 | { 473 | try 474 | { 475 | handle.control_transfer(0x40, PAVR2_REQUEST_SET_SETTING, value, id); 476 | } 477 | catch(const libusbp::error & error) 478 | { 479 | throw std::runtime_error(std::string("Failed to set a setting. ") + 480 | error.message()); 481 | } 482 | } 483 | 484 | uint8_t ProgrammerHandle::getRawVariable(uint8_t id) 485 | { 486 | uint8_t value; 487 | size_t transferred; 488 | try 489 | { 490 | handle.control_transfer(0xC0, PAVR2_REQUEST_GET_VARIABLE, 491 | 0, id, &value, 1, &transferred); 492 | } 493 | catch (const libusbp::error & error) 494 | { 495 | throw std::runtime_error(std::string("Failed to get a variable. ") 496 | + error.message()); 497 | } 498 | 499 | if (transferred != 1) 500 | { 501 | throw std::runtime_error(std::string("Failed to get a variable. ") + 502 | "Expected 1 byte, got " + std::to_string(transferred)); 503 | } 504 | 505 | return value; 506 | } 507 | 508 | std::string ProgrammerHandle::getFirmwareVersionString() 509 | { 510 | if (cachedFirmwareVersion.size() > 0) 511 | { 512 | return cachedFirmwareVersion; 513 | } 514 | 515 | std::string version = instance.getFirmwareVersionString(); 516 | 517 | // Get the firmware modification string. 518 | const uint8_t stringIndex = 6; 519 | size_t transferred = 0; 520 | uint8_t buffer[64]; 521 | try 522 | { 523 | handle.control_transfer(0x80, USB_REQUEST_GET_DESCRIPTOR, 524 | (USB_DESCRIPTOR_TYPE_STRING << 8) | stringIndex, 525 | 0, buffer, sizeof(buffer), &transferred); 526 | } 527 | catch (const libusbp::error & error) 528 | { 529 | // Let's make this be a non-fatal error because it's not so important. 530 | // Just add a question mark so we can tell if something is wrong. 531 | version += "?"; 532 | 533 | // Uncomment this line to debug the error: 534 | // throw std::runtime_error(std::string("Failed to get firmware modification string.")); 535 | } 536 | 537 | // Add the modification string to the firmware version string, assuming it 538 | // is ASCII. 539 | std::string mod; 540 | for (size_t i = 2; i < transferred; i += 2) 541 | { 542 | mod += buffer[i]; 543 | } 544 | if (mod != "-") { version += mod; } 545 | 546 | cachedFirmwareVersion = version; 547 | return version; 548 | } 549 | 550 | // [all-settings] 551 | ProgrammerSettings ProgrammerHandle::getSettings() 552 | { 553 | ProgrammerSettings settings; 554 | settings.sckDuration = getRawSetting(PAVR2_SETTING_SCK_DURATION); 555 | settings.ispFastestPeriod = getRawSetting(PAVR2_SETTING_ISP_FASTEST_PERIOD); 556 | settings.regulatorMode = getRawSetting(PAVR2_SETTING_REGULATOR_MODE); 557 | settings.vccOutputEnabled = getRawSetting(PAVR2_SETTING_VCC_OUTPUT_ENABLED) ? 1 : 0; 558 | settings.vccOutputIndicator = getRawSetting(PAVR2_SETTING_VCC_OUTPUT_INDICATOR) ? 1 : 0; 559 | settings.lineAFunction = getRawSetting(PAVR2_SETTING_LINE_A_FUNCTION); 560 | settings.lineBFunction = getRawSetting(PAVR2_SETTING_LINE_B_FUNCTION); 561 | settings.softwareVersionMajor = getRawSetting(PAVR2_SETTING_SOFTWARE_VERSION_MAJOR); 562 | settings.softwareVersionMinor = getRawSetting(PAVR2_SETTING_SOFTWARE_VERSION_MINOR); 563 | settings.hardwareVersion = getRawSetting(PAVR2_SETTING_HARDWARE_VERSION); 564 | settings.vccVddMaxRange = getRawSetting(PAVR2_SETTING_VCC_VDD_MAX_RANGE) 565 | * PAVR2_VOLTAGE_UNITS; 566 | settings.vcc3v3Min = getRawSetting(PAVR2_SETTING_VCC_3V3_MIN) * PAVR2_VOLTAGE_UNITS; 567 | settings.vcc3v3Max = getRawSetting(PAVR2_SETTING_VCC_3V3_MAX) * PAVR2_VOLTAGE_UNITS; 568 | settings.vcc5vMin = getRawSetting(PAVR2_SETTING_VCC_5V_MIN) * PAVR2_VOLTAGE_UNITS; 569 | settings.vcc5vMax = getRawSetting(PAVR2_SETTING_VCC_5V_MAX) * PAVR2_VOLTAGE_UNITS; 570 | 571 | // We don't read the reset polarity here because that gets set by programming 572 | // software before each session; it is not really a persistent setting 573 | // and it might be confusing to present it that way. 574 | 575 | return settings; 576 | } 577 | 578 | // [all-settings] 579 | void ProgrammerHandle::validateSettings(const ProgrammerSettings & settings) 580 | { 581 | if (settings.sckDuration > 255) 582 | { 583 | throw std::runtime_error("The SCK duration should be at most 255."); 584 | } 585 | 586 | if (settings.ispFastestPeriod < PAVR2_ISP_FASTEST_PERIOD_MIN 587 | || settings.ispFastestPeriod > PAVR2_ISP_FASTEST_PERIOD_MAX) 588 | { 589 | throw std::runtime_error("The ISP fastest period is not valid."); 590 | } 591 | 592 | if (settings.regulatorMode > PAVR2_REGULATOR_MODE_5V) 593 | { 594 | throw std::runtime_error("Invalid regulator mode."); 595 | } 596 | 597 | if (settings.regulatorMode == PAVR2_REGULATOR_MODE_AUTO && 598 | settings.vccOutputEnabled) 599 | { 600 | throw std::runtime_error("VCC cannot be an output if the regulator mode is auto."); 601 | } 602 | 603 | if (settings.lineAFunction > PAVR2_LINE_IS_DTR_RESET) 604 | { 605 | throw std::runtime_error("Invalid line A function."); 606 | } 607 | 608 | if (settings.lineAFunction == PAVR2_LINE_IS_CLOCK) 609 | { 610 | throw std::runtime_error("Line A cannot be a clock output."); 611 | } 612 | 613 | if (settings.lineBFunction > PAVR2_LINE_IS_DTR_RESET) 614 | { 615 | throw std::runtime_error("Invalid line B function."); 616 | } 617 | 618 | if (settings.softwareVersionMajor > 255) 619 | { 620 | throw std::runtime_error("Invalid software major version."); 621 | } 622 | 623 | if (settings.softwareVersionMinor > 255) 624 | { 625 | throw std::runtime_error("Invalid software minor version."); 626 | } 627 | 628 | if (settings.hardwareVersion > 255) 629 | { 630 | throw std::runtime_error("Invalid hardware version."); 631 | } 632 | 633 | if (settings.vccVddMaxRange > maxRepresentableVoltage) 634 | { 635 | throw std::runtime_error( 636 | "The VCC/VDD maximum range cannot be larger than " 637 | MAX_REPRESENTABLE_VOLTAGE_STR); 638 | } 639 | 640 | if (settings.vcc3v3Max > maxRepresentableVoltage) 641 | { 642 | throw std::runtime_error( 643 | "The VCC 3.3 V maximum cannot be larger than " 644 | MAX_REPRESENTABLE_VOLTAGE_STR); 645 | } 646 | 647 | if (settings.vcc5vMax > maxRepresentableVoltage) 648 | { 649 | throw std::runtime_error( 650 | "The VCC 5 V maximum cannot be larger than " 651 | MAX_REPRESENTABLE_VOLTAGE_STR); 652 | } 653 | 654 | if (settings.vcc3v3Min > settings.vcc3v3Max) 655 | { 656 | throw std::runtime_error( 657 | "The VCC 3.3 V minimum cannot be greater than the maximum."); 658 | } 659 | 660 | if (settings.vcc5vMin > settings.vcc5vMax) 661 | { 662 | throw std::runtime_error( 663 | "The VCC 5 V minimum cannot be greater than the maximum."); 664 | } 665 | } 666 | 667 | // Divide by PAVR2_VOLTAGE_UNITs, rounding to the nearest integer. 668 | static uint8_t convertMvToRawUnits(uint32_t mv) 669 | { 670 | uint32_t x = (mv + PAVR2_VOLTAGE_UNITS / 2 - 1) / PAVR2_VOLTAGE_UNITS; 671 | if (x > 0xFF) 672 | { 673 | x = 0xFF; 674 | 675 | // Bounds are checked before we get to this point, 676 | // so this block should never run. 677 | assert(0); 678 | } 679 | return x; 680 | } 681 | 682 | // [all-settings] 683 | void ProgrammerHandle::applySettings(const ProgrammerSettings & settings) 684 | { 685 | validateSettings(settings); 686 | 687 | // We set vccOutputEnabled in a special way to ensure that if it is changing, 688 | // we won't accidentally output the wrong voltage on VCC for some time. 689 | if (!settings.vccOutputEnabled) 690 | { 691 | setRawSetting(PAVR2_SETTING_VCC_OUTPUT_ENABLED, 0); 692 | } 693 | 694 | setRawSetting(PAVR2_SETTING_SCK_DURATION, settings.sckDuration); 695 | setRawSetting(PAVR2_SETTING_ISP_FASTEST_PERIOD, settings.ispFastestPeriod); 696 | setRawSetting(PAVR2_SETTING_REGULATOR_MODE, settings.regulatorMode); 697 | setRawSetting(PAVR2_SETTING_VCC_OUTPUT_INDICATOR, settings.vccOutputIndicator); 698 | setRawSetting(PAVR2_SETTING_LINE_A_FUNCTION, settings.lineAFunction); 699 | setRawSetting(PAVR2_SETTING_LINE_B_FUNCTION, settings.lineBFunction); 700 | setRawSetting(PAVR2_SETTING_SOFTWARE_VERSION_MAJOR, settings.softwareVersionMajor); 701 | setRawSetting(PAVR2_SETTING_SOFTWARE_VERSION_MINOR, settings.softwareVersionMinor); 702 | setRawSetting(PAVR2_SETTING_HARDWARE_VERSION, settings.hardwareVersion); 703 | setRawSetting(PAVR2_SETTING_VCC_VDD_MAX_RANGE, 704 | convertMvToRawUnits(settings.vccVddMaxRange)); 705 | setRawSetting(PAVR2_SETTING_VCC_3V3_MIN, 706 | convertMvToRawUnits(settings.vcc3v3Min)); 707 | setRawSetting(PAVR2_SETTING_VCC_3V3_MAX, 708 | convertMvToRawUnits(settings.vcc3v3Max)); 709 | setRawSetting(PAVR2_SETTING_VCC_5V_MIN, 710 | convertMvToRawUnits(settings.vcc5vMin)); 711 | setRawSetting(PAVR2_SETTING_VCC_5V_MAX, 712 | convertMvToRawUnits(settings.vcc5vMax)); 713 | 714 | if (settings.vccOutputEnabled) 715 | { 716 | setRawSetting(PAVR2_SETTING_VCC_OUTPUT_ENABLED, 1); 717 | } 718 | } 719 | 720 | void ProgrammerHandle::restoreDefaults() 721 | { 722 | setRawSetting(PAVR2_SETTING_NOT_INITIALIZED, 0xFF); 723 | 724 | // The request above returns before the settings are actually initialized. 725 | // Wait until the programmer succeeds in reinitializing its settings. 726 | uint32_t timeMs = 0; 727 | while (1) 728 | { 729 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 730 | timeMs += 10; 731 | 732 | uint8_t notInitialized = getRawSetting(PAVR2_SETTING_NOT_INITIALIZED); 733 | if (!notInitialized) 734 | { 735 | break; 736 | } 737 | 738 | if (timeMs > 300) 739 | { 740 | throw std::runtime_error( 741 | "A timeout occurred while resetting to default settings."); 742 | } 743 | } 744 | } 745 | 746 | ProgrammerVariables ProgrammerHandle::getVariables() 747 | { 748 | ProgrammerVariables vars; 749 | 750 | vars.lastDeviceReset = getRawVariable(PAVR2_VARIABLE_LAST_DEVICE_RESET); 751 | 752 | vars.programmingError = getRawVariable(PAVR2_VARIABLE_PROGRAMMING_ERROR); 753 | 754 | vars.targetVccMeasuredMinMv = 755 | getRawVariable(PAVR2_VARIABLE_TARGET_VCC_MEASURED_MIN) * PAVR2_VOLTAGE_UNITS; 756 | 757 | vars.targetVccMeasuredMaxMv = 758 | getRawVariable(PAVR2_VARIABLE_TARGET_VCC_MEASURED_MAX) * PAVR2_VOLTAGE_UNITS; 759 | 760 | vars.programmerVddMeasuredMinMv = 761 | getRawVariable(PAVR2_VARIABLE_PROGRAMMER_VDD_MEASURED_MIN) * PAVR2_VOLTAGE_UNITS; 762 | 763 | vars.programmerVddMeasuredMaxMv = 764 | getRawVariable(PAVR2_VARIABLE_PROGRAMMER_VDD_MEASURED_MAX) * PAVR2_VOLTAGE_UNITS; 765 | 766 | vars.hasResultsFromLastProgramming = 767 | (vars.programmingError != 0) || 768 | (vars.targetVccMeasuredMinMv != 255 * PAVR2_VOLTAGE_UNITS) || 769 | (vars.targetVccMeasuredMaxMv != 0) || 770 | (vars.programmerVddMeasuredMinMv != 255 * PAVR2_VOLTAGE_UNITS) || 771 | (vars.programmerVddMeasuredMaxMv != 0); 772 | 773 | vars.targetVccMv = 774 | getRawVariable(PAVR2_VARIABLE_TARGET_VCC) * PAVR2_VOLTAGE_UNITS; 775 | 776 | vars.programmerVddMv = 777 | getRawVariable(PAVR2_VARIABLE_PROGRAMMER_VDD) * PAVR2_VOLTAGE_UNITS; 778 | 779 | vars.regulatorLevel = getRawVariable(PAVR2_VARIABLE_REGULATOR_LEVEL); 780 | 781 | vars.inProgrammingMode = getRawVariable(PAVR2_VARIABLE_IN_PROGRAMMING_MODE) ? 1 : 0; 782 | 783 | return vars; 784 | } 785 | 786 | ProgrammerDigitalReadings ProgrammerHandle::digitalRead() 787 | { 788 | uint8_t buffer[3]; 789 | size_t transferred; 790 | try 791 | { 792 | handle.control_transfer(0xC0, PAVR2_REQUEST_DIGITAL_READ, 793 | 0, 0, &buffer, 3, &transferred); 794 | } 795 | catch (const libusbp::error & error) 796 | { 797 | throw std::runtime_error(std::string("Failed to get a variable. ") 798 | + error.message()); 799 | } 800 | 801 | if (transferred != 3) 802 | { 803 | throw std::runtime_error(std::string("Failed to get a variable. ") + 804 | "Expected 3 bytes, got " + std::to_string(transferred)); 805 | } 806 | 807 | ProgrammerDigitalReadings readings; 808 | readings.portA = buffer[0]; 809 | readings.portB = buffer[1]; 810 | readings.portC = buffer[2]; 811 | return readings; 812 | } 813 | 814 | bool pgm03aPresent() 815 | { 816 | for (const libusbp::device & device : libusbp::list_connected_devices()) 817 | { 818 | bool isPgm03a = device.get_vendor_id() == 0x1FFB && 819 | device.get_product_id() == 0x0081; 820 | if (isPgm03a) { return true; } 821 | } 822 | return false; 823 | } 824 | 825 | 826 | /** This message doesn't look great when printed in a CLI because it has lines 827 | * longer than 80 characters. But if we add wrapping to it then it will look 828 | * bad when it is printed in a messagebox in a typical GUI. **/ 829 | const char * pgm03aMessage = 830 | "This utility only supports the Pololu USB AVR Programmer v2 and v2.1 " 831 | "(blue-colored, labeled \"pgm04a\" or \"pgm04b\").\n" 832 | "\n" 833 | "It looks you have an older programmer, the Pololu USB AVR Programmer (pgm03a). " 834 | "You can find documentation and software for the older programmer here:\n" 835 | "\n" 836 | "https://www.pololu.com/docs/0J36"; 837 | -------------------------------------------------------------------------------- /nix/build.nix: -------------------------------------------------------------------------------- 1 | src: config_name: env: 2 | 3 | let 4 | payload = env.make_derivation { 5 | builder = ./builder.sh; 6 | inherit src; 7 | cross_inputs = [ env.libusbp env.qt ]; 8 | dejavu = (if env.os == "linux" then env.dejavu-fonts else null); 9 | }; 10 | 11 | license_set = 12 | (if env.os == "linux" then env.dejavu-fonts.license_set else {}) // 13 | env.libusbp.license_set // 14 | env.qt.license_set // 15 | env.global_license_set; 16 | 17 | license = env.make_derivation { 18 | name = "license"; 19 | builder.ruby = ./license_builder.rb; 20 | inherit src; 21 | commit = builtins.getEnv "commit"; 22 | nixcrpkgs_commit = builtins.getEnv "nixcrpkgs_commit"; 23 | nixpkgs_commit = builtins.getEnv "nixpkgs_commit"; 24 | license_names = builtins.attrNames license_set; 25 | licenses = builtins.attrValues license_set; 26 | }; 27 | 28 | installer = env.make_derivation { 29 | name = "${config_name}-installer"; 30 | builder.ruby = 31 | if env.os == "windows" then ./windows_installer_builder.rb 32 | else if env.os == "linux" then ./linux_installer_builder.rb 33 | else if env.os == "macos" then ./macos_installer_builder.rb 34 | else throw "?"; 35 | inherit src config_name payload license; 36 | libusbp = env.libusbp; 37 | }; 38 | 39 | in 40 | payload // { inherit env license installer; } 41 | -------------------------------------------------------------------------------- /nix/build_installer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This is a Ruby script that uses Git and NIX_PATH to figure out the commit 4 | # numbers of the important repositories used to build this code. It then sets 5 | # the appropriate environment variables and calls nix-build to build an 6 | # installer for the specified operating system. 7 | 8 | require 'pathname' 9 | 10 | if ARGV.empty? 11 | $stderr.puts "usage: #{$PROGRAM_NAME} ATTRPATH" 12 | $stderr.puts "where ATTRPATH is one of installers, win32.installer, linux-x86.installer, etc." 13 | exit 1 14 | end 15 | 16 | attr_name = ARGV.shift 17 | 18 | # Read in the NIX_PATH as a hash. 19 | nix_path = {} 20 | ENV['NIX_PATH'].split(":").each do |entry| 21 | if md = entry.match(/(\w+)=(.+)/) 22 | nix_path[md[1]] = md[2] 23 | end 24 | end 25 | 26 | def get_git_commit(path) 27 | path = Pathname(path) 28 | if File.exists?(path + '.git') 29 | return %x(git -C #{path} rev-parse HEAD).strip 30 | end 31 | dest = path.readlink 32 | if md = dest.to_s.match(/\bnixpkgs-[.0-9a-z]+\.([0-9a-f]+)\b/) 33 | return md[1] 34 | end 35 | raise "unable to get git commit for #{path}" 36 | end 37 | 38 | env = {} 39 | env['commit'] = get_git_commit('.') 40 | env['nixcrpkgs_commit'] = get_git_commit(nix_path.fetch('nixcrpkgs')) 41 | env['nixpkgs_commit'] = get_git_commit(nix_path.fetch('nixpkgs')) 42 | cmd = "nix-build -A #{attr_name}" 43 | exec(env, cmd) 44 | -------------------------------------------------------------------------------- /nix/builder.sh: -------------------------------------------------------------------------------- 1 | source $setup 2 | 3 | cmake-cross $src \ 4 | -DCMAKE_INSTALL_PREFIX=$out 5 | 6 | make 7 | make install 8 | 9 | $host-strip $out/bin/* 10 | cp version.txt $out/ 11 | 12 | if [ $os = "linux" ]; then 13 | cp $dejavu/ttf/DejaVuSans.ttf $out/bin/ 14 | fi 15 | -------------------------------------------------------------------------------- /nix/license_builder.rb: -------------------------------------------------------------------------------- 1 | # Make sure the user did not forget to run nix/build_installer.rb. 2 | ['commit', 'nixcrpkgs_commit', 'nixpkgs_commit'].each do |var| 3 | if ENV.fetch(var).empty? 4 | raise "Required environment variable $#{var} is empty." 5 | end 6 | end 7 | 8 | license_names = ENV.fetch('license_names').split(' ') 9 | license_files = ENV.fetch('licenses').split(' ') 10 | 11 | File.open(ENV.fetch('out'), 'w') do |f| 12 | f.puts < 14 | 15 | 16 | 23 | 24 | License Information 25 | 26 |

27 | The Pololu USB AVR Programmer v2 software distribution is licensed under the 28 | GPLv3, subject to the restrictions described below. 29 |

30 | Source code and instructions for building this software can be found in the 31 | pololu-usb-avr-programmer-v2 repository. 32 |

33 | This release was built from 34 | 35 | pololu-usb-avr-programmer-v2 commit #{ENV.fetch('commit')[0, 7]}, 36 | 37 | nixcrpkgs commit #{ENV.fetch('nixcrpkgs_commit')[0, 7]}, and 38 | 39 | nixpkgs commit #{ENV.fetch('nixpkgs_commit')[0, 7]}. 40 |

41 | Table of contents: 42 |

43 | EOF 44 | 45 | f.puts "
    " 46 | license_names.each do |name| 47 | f.puts "
  • #{name}" 48 | end 49 | f.puts "
" 50 | 51 | license_names.zip(license_files).each do |name, filename| 52 | f.puts "
" 53 | f.puts File.read(filename) 54 | f.puts "
\n" 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /nix/linux_installer_builder.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'pathname' 3 | include FileUtils 4 | 5 | ENV['PATH'] = ENV.fetch('_PATH') 6 | 7 | ConfigName = ENV.fetch('config_name') 8 | OutDir = Pathname(ENV.fetch('out')) 9 | PayloadDir = Pathname(ENV.fetch('payload')) 10 | SrcDir = Pathname(ENV.fetch('src')) 11 | Version = File.read(PayloadDir + 'version.txt') 12 | TarName = "pololu-usb-avr-programmer-v2-#{Version}-#{ConfigName}" 13 | StagingDir = Pathname(TarName) 14 | OutTar = OutDir + "#{TarName}.tar.xz" 15 | 16 | mkdir_p StagingDir 17 | cp_r Dir.glob(PayloadDir + 'bin' + '*'), StagingDir 18 | cp_r SrcDir + 'udev-rules' + '99-pololu.rules', StagingDir 19 | cp ENV.fetch('license'), StagingDir + 'LICENSE.html' 20 | 21 | File.open(StagingDir + 'README.txt', 'w') do |f| 22 | f.puts < 42 | 43 | 44 | 45 | CFBundleDevelopmentRegion 46 | en 47 | CFBundleExecutable 48 | #{AppExe} 49 | CFBundleIconFile 50 | app.icns 51 | CFBundleIdentifier 52 | #{PkgId}.app 53 | CFBundleInfoDictionaryVersion 54 | 6.0 55 | CFBundleName 56 | #{AppName} 57 | CFBundlePackageType 58 | APPL 59 | CFBundleShortVersionString 60 | #{Version} 61 | CFBundleSignature 62 | ???? 63 | CFBundleVersion 64 | #{Version} 65 | NSHumanReadableCopyright 66 | Copyright (C) #{Time.now.year} Pololu Corporation 67 | 68 | 69 | EOF 70 | end 71 | 72 | cp SrcDir + 'images' + 'app.icns', AppResDir 73 | 74 | File.open(PathDir + '99-pololu-avr2', 'w') do |f| 75 | f.puts "/Applications/#{AppName}.app/Contents/MacOS" 76 | end 77 | 78 | File.open(ResDir + 'welcome.html', 'w') do |f| 79 | f.puts < 81 | 82 | 85 | Welcome 86 |

87 | This package installs the configuration software for the 88 | Pololu USB AVR Programmer v2 on your computer. 89 | Please note that v2 refers to the versions of the 90 | hardware products. 91 | The version number of this software is #{Version}. 92 |

93 | This software only supports the Pololu USB AVR Programmer v2 and v2.1, 94 | which are blue-colored. If you have an older Pololu 95 | programmer, refer to the product page and user's guide of that programmer for 96 | software. 97 |

98 | This package will install two programs: 99 |

    100 |
  • Pololu USB AVR Programmer v2 Configuration Utility (pavr2gui) 101 |
  • Pololu USB AVR Programmer v2 Command-line Utility (pavr2cmd) 102 |
103 |
104 |

105 | This software can only configure the programmer or read information from it. 106 | To actually program an AVR, you will need AVR programming software such as 107 | AVRDUDE or the Arduino IDE. 108 | EOF 109 | end 110 | 111 | File.open('distribution.xml', 'w') do |f| 112 | f.puts < 114 | 115 | #{AppName} #{Version} 116 | 117 | app.pkg 118 | path.pkg 119 | 120 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | EOF 140 | end 141 | 142 | File.open('build.sh', 'w') do |f| 143 | f.puts < 21 | 22 | #{Version} 23 | 15e2e35e-f56e-47da-9536-ded64d31f1ac 24 | pololu-usb-avr-programmer-v2-$(ProductVersion)-win 25 | 26 | ProductVersion=$(ProductVersion); 27 | DocumentationUrl=https://www.pololu.com/docs/0J67; 28 | 29 | Debug 30 | x86 31 | 2.0 32 | Package 33 | false 34 | $(MSBuildExtensionsPath)\\Microsoft\\WiX\\v3.x\\Wix.targets 35 | 36 | 37 | bin\\$(Configuration)\\ 38 | obj\\$(Configuration)\\ 39 | Debug;ProductVersion=$(ProductVersion) 40 | 41 | 42 | bin\\$(Configuration)\\ 43 | obj\\$(Configuration)\\ 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | EOF 55 | end 56 | 57 | File.open(OutDir + 'app.wxs', 'w') do |f| 58 | f.write < 60 | 62 | 68 | 69 | 74 | 75 | 78 | 79 | 80 | 81 | 82 | Software and drivers for the Pololu USB AVR Programmer v2. 83 | 84 | Pololu 85 | $(var.DocumentationUrl) 86 | 702-262-6648 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 105 | 106 | 107 | 108 | 109 | 112 | 114 | MODIFYPATH=1 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 148 | 149 | 150 | 153 | 156 | 157 | 158 | 159 | 160 | 161 | 163 | 164 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 186 | 187 | 189 | 191 | 192 | 194 | 196 | 197 | 199 | 201 | 202 | 203 | 204 | (NOT Installed) OR REINSTALLMODE 205 | 206 | (NOT Installed) OR REINSTALLMODE 207 | 208 | (NOT Installed) OR REINSTALLMODE 209 | 210 | 211 | 212 | 214 | 216 | 217 | 218 | 219 | 220 | EOF 221 | end 222 | 223 | File.open(OutDir + 'en-us.wxl', 'w') do |f| 224 | f.write < 226 | 227 | The Setup Wizard will install the software and drivers for the Pololu USB AVR Programmer v2 and v2.1 on your computer. Please note that "v2" refers to the versions of the hardware products. The version number of this software is [ProductVersion]. Click Next to continue or Cancel to exit the Setup Wizard. 228 | {\\WixUI_Font_Title}Installation Options 229 | Click Next to use the default options or change the options below. 230 | Add the bin directory to the PATH environment variable. 231 | This is not required but will make it easier to run pavr2cmd from the Command Prompt. 232 | 233 | EOF 234 | end 235 | 236 | File.open(OutDir + 'ui.wxs', 'w') do |f| 237 | f.write < 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 |

257 | 258 | 259 | 260 | 1 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 1 286 | "1"]]> 287 | 288 | 1 289 | 290 | NOT Installed 291 | 292 | 1 293 | 1 294 | NOT WIXUI_DONTVALIDATEPATH 295 | "1"]]> 296 | WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" 297 | 1 298 | 1 299 | 300 | 1 301 | 1 302 | 303 | NOT Installed 304 | Installed AND NOT PATCH 305 | 306 | 1 307 | 308 | 1 309 | 1 310 | 1 311 | 312 | 313 | 314 | 315 | EOF 316 | end 317 | 318 | File.open(OutDir + 'build.sh', 'w') do |f| 319 | f.write <