MBED board as programmer for CC254x chips. Visit https://github.com/RedBearLab/CCLoader for more details.

Dependencies:   mbed

Devices running Android 8.0 (or newer) are not able to discover cheap CC41-A Bluetooth LE modules anymore - read "Should you throw away your CC41 HM-10 clones now that Android 8 is here?".

Since RedBearLab figured out how to use Arduino as programmer for CC254x chips a simple fix is flashing a genuine HM-10 firmware to CC41-A modules. Visit Bluetooth BLE Adventures to learn how to do it.

To use an Mbed board rather than Arduino this repository provides RedBearLab's CCLoader ported to Mbed. After compiling and flashing, it converts an Mbed board to a CC254x programmer.

Usage:

  • Compile and download this program to an Mbed board equipped with Arduino header.
  • Keep the Mbed board connected to the PC over a USB cable.
  • Connect the Mbed board to the CC41-A BLE module as shown in the picture below: /media/uploads/hudakz/ccloader1.png
  • Run RedBearLab's CCLoader.exe on your PC to flash the CC41-A module using the MBED board as a programmer.
    For example:

> ccloader_x86_64 3 cc2541hm10v540.bin 1
Committer:
hudakz
Date:
Wed Jun 19 13:40:42 2019 +0000
Revision:
0:0834641c241a
Child:
1:c874ea9c1afb
Burn CC254x firmware using an MBED board.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 0:0834641c241a 1 /*
hudakz 0:0834641c241a 2
hudakz 0:0834641c241a 3 Copyright (c) 2012-2014 RedBearLab
hudakz 0:0834641c241a 4
hudakz 0:0834641c241a 5 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
hudakz 0:0834641c241a 6
hudakz 0:0834641c241a 7 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
hudakz 0:0834641c241a 8
hudakz 0:0834641c241a 9 Ported to MBED by Zoltan Hudak 2019
hudakz 0:0834641c241a 10
hudakz 0:0834641c241a 11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
hudakz 0:0834641c241a 12
hudakz 0:0834641c241a 13 */
hudakz 0:0834641c241a 14 /******************************************************************************
hudakz 0:0834641c241a 15 * INCLUDES
hudakz 0:0834641c241a 16 */
hudakz 0:0834641c241a 17 #include "mbed.h"
hudakz 0:0834641c241a 18
hudakz 0:0834641c241a 19 /******************************************************************************
hudakz 0:0834641c241a 20 * DEFINES
hudakz 0:0834641c241a 21 */
hudakz 0:0834641c241a 22 // Start addresses on DUP (Increased buffer size improves performance)
hudakz 0:0834641c241a 23 #define ADDR_BUF0 0x0000 // Buffer (512 bytes)
hudakz 0:0834641c241a 24 #define ADDR_DMA_DESC_0 0x0200 // DMA descriptors (8 bytes)
hudakz 0:0834641c241a 25 #define ADDR_DMA_DESC_1 (ADDR_DMA_DESC_0 + 8)
hudakz 0:0834641c241a 26
hudakz 0:0834641c241a 27 // DMA channels used on DUP
hudakz 0:0834641c241a 28 #define CH_DBG_TO_BUF0 0x01 // Channel 0
hudakz 0:0834641c241a 29 #define CH_BUF0_TO_FLASH 0x02 // Channel 1
hudakz 0:0834641c241a 30
hudakz 0:0834641c241a 31 // Debug commands
hudakz 0:0834641c241a 32 #define CMD_CHIP_ERASE 0x10
hudakz 0:0834641c241a 33 #define CMD_WR_CONFIG 0x19
hudakz 0:0834641c241a 34 #define CMD_RD_CONFIG 0x24
hudakz 0:0834641c241a 35 #define CMD_READ_STATUS 0x30
hudakz 0:0834641c241a 36 #define CMD_RESUME 0x4C
hudakz 0:0834641c241a 37 #define CMD_DEBUG_INSTR_1B (0x54|1)
hudakz 0:0834641c241a 38 #define CMD_DEBUG_INSTR_2B (0x54|2)
hudakz 0:0834641c241a 39 #define CMD_DEBUG_INSTR_3B (0x54|3)
hudakz 0:0834641c241a 40 #define CMD_BURST_WRITE 0x80
hudakz 0:0834641c241a 41 #define CMD_GET_CHIP_ID 0x68
hudakz 0:0834641c241a 42
hudakz 0:0834641c241a 43 // Debug status bitmasks
hudakz 0:0834641c241a 44 #define STATUS_CHIP_ERASE_BUSY_BM 0x80 // New debug interface
hudakz 0:0834641c241a 45 #define STATUS_PCON_IDLE_BM 0x40
hudakz 0:0834641c241a 46 #define STATUS_CPU_HALTED_BM 0x20
hudakz 0:0834641c241a 47 #define STATUS_PM_ACTIVE_BM 0x10
hudakz 0:0834641c241a 48 #define STATUS_HALT_STATUS_BM 0x08
hudakz 0:0834641c241a 49 #define STATUS_DEBUG_LOCKED_BM 0x04
hudakz 0:0834641c241a 50 #define STATUS_OSC_STABLE_BM 0x02
hudakz 0:0834641c241a 51 #define STATUS_STACK_OVERFLOW_BM 0x01
hudakz 0:0834641c241a 52
hudakz 0:0834641c241a 53 // DUP registers (XDATA space address)
hudakz 0:0834641c241a 54 #define DUP_DBGDATA 0x6260 // Debug interface data buffer
hudakz 0:0834641c241a 55 #define DUP_FCTL 0x6270 // Flash controller
hudakz 0:0834641c241a 56 #define DUP_FADDRL 0x6271 // Flash controller addr
hudakz 0:0834641c241a 57 #define DUP_FADDRH 0x6272 // Flash controller addr
hudakz 0:0834641c241a 58 #define DUP_FWDATA 0x6273 // Clash controller data buffer
hudakz 0:0834641c241a 59 #define DUP_CLKCONSTA 0x709E // Sys clock status
hudakz 0:0834641c241a 60 #define DUP_CLKCONCMD 0x70C6 // Sys clock configuration
hudakz 0:0834641c241a 61 #define DUP_MEMCTR 0x70C7 // Flash bank xdata mapping
hudakz 0:0834641c241a 62 #define DUP_DMA1CFGL 0x70D2 // Low byte, DMA config ch. 1
hudakz 0:0834641c241a 63 #define DUP_DMA1CFGH 0x70D3 // Hi byte , DMA config ch. 1
hudakz 0:0834641c241a 64 #define DUP_DMA0CFGL 0x70D4 // Low byte, DMA config ch. 0
hudakz 0:0834641c241a 65 #define DUP_DMA0CFGH 0x70D5 // Low byte, DMA config ch. 0
hudakz 0:0834641c241a 66 #define DUP_DMAARM 0x70D6 // DMA arming register
hudakz 0:0834641c241a 67
hudakz 0:0834641c241a 68 // Utility macros
hudakz 0:0834641c241a 69 //! Low nibble of 16bit variable
hudakz 0:0834641c241a 70 #define LOBYTE(w) ((unsigned char)(w))
hudakz 0:0834641c241a 71 //! High nibble of 16bit variable
hudakz 0:0834641c241a 72 #define HIBYTE(w) ((unsigned char)(((unsigned short)(w) >> 8) & 0xFF))
hudakz 0:0834641c241a 73
hudakz 0:0834641c241a 74 // Commands to Bootloader
hudakz 0:0834641c241a 75 #define SBEGIN 0x01
hudakz 0:0834641c241a 76 #define SDATA 0x02
hudakz 0:0834641c241a 77 #define SRSP 0x03
hudakz 0:0834641c241a 78 #define SEND 0x04
hudakz 0:0834641c241a 79 #define ERRO 0x05
hudakz 0:0834641c241a 80 #define WAITING 0x00
hudakz 0:0834641c241a 81 #define RECEIVING 0x01
hudakz 0:0834641c241a 82
hudakz 0:0834641c241a 83 // Debug control pins & the indicate LED
hudakz 0:0834641c241a 84 DigitalOut reset(D4);
hudakz 0:0834641c241a 85 DigitalOut dc(D5);
hudakz 0:0834641c241a 86 DigitalInOut dd(D6);
hudakz 0:0834641c241a 87 DigitalOut led(LED1);
hudakz 0:0834641c241a 88 Serial serial(USBTX, USBRX);
hudakz 0:0834641c241a 89
hudakz 0:0834641c241a 90 /******************************************************************************
hudakz 0:0834641c241a 91 VARIABLES*/
hudakz 0:0834641c241a 92 //! DUP DMA descriptor
hudakz 0:0834641c241a 93 const unsigned char dmaDesc0[8] =
hudakz 0:0834641c241a 94 {
hudakz 0:0834641c241a 95 // Debug Interface -> Buffer
hudakz 0:0834641c241a 96 HIBYTE(DUP_DBGDATA), // src[15:8]
hudakz 0:0834641c241a 97 LOBYTE(DUP_DBGDATA), // src[7:0]
hudakz 0:0834641c241a 98 HIBYTE(ADDR_BUF0), // dest[15:8]
hudakz 0:0834641c241a 99 LOBYTE(ADDR_BUF0), // dest[7:0]
hudakz 0:0834641c241a 100 0, // len[12:8] - filled in later
hudakz 0:0834641c241a 101 0, // len[7:0]
hudakz 0:0834641c241a 102 31, // trigger: DBG_BW
hudakz 0:0834641c241a 103 0x11 // increment destination
hudakz 0:0834641c241a 104 };
hudakz 0:0834641c241a 105 //! DUP DMA descriptor
hudakz 0:0834641c241a 106 const unsigned char dmaDesc1[8] =
hudakz 0:0834641c241a 107 {
hudakz 0:0834641c241a 108 // Buffer -> Flash controller
hudakz 0:0834641c241a 109 HIBYTE(ADDR_BUF0), // src[15:8]
hudakz 0:0834641c241a 110 LOBYTE(ADDR_BUF0), // src[7:0]
hudakz 0:0834641c241a 111 HIBYTE(DUP_FWDATA), // dest[15:8]
hudakz 0:0834641c241a 112 LOBYTE(DUP_FWDATA), // dest[7:0]
hudakz 0:0834641c241a 113 0, // len[12:8] - filled in later
hudakz 0:0834641c241a 114 0, // len[7:0]
hudakz 0:0834641c241a 115 18, // trigger: FLASH
hudakz 0:0834641c241a 116 0x42, // increment source
hudakz 0:0834641c241a 117 };
hudakz 0:0834641c241a 118
hudakz 0:0834641c241a 119 /**************************************************************************/
hudakz 0:0834641c241a 120 /**
hudakz 0:0834641c241a 121 * @brief Writes a byte on the debug interface. Requires DD to be
hudakz 0:0834641c241a 122 * output when function is called.
hudakz 0:0834641c241a 123 * @param data Byte to write
hudakz 0:0834641c241a 124 * @return None.
hudakz 0:0834641c241a 125 ******************************************************************************/
hudakz 0:0834641c241a 126 #pragma inline
hudakz 0:0834641c241a 127
hudakz 0:0834641c241a 128 /**
hudakz 0:0834641c241a 129 * @brief
hudakz 0:0834641c241a 130 * @note
hudakz 0:0834641c241a 131 * @param
hudakz 0:0834641c241a 132 * @retval
hudakz 0:0834641c241a 133 */
hudakz 0:0834641c241a 134 void writeDebugByte(unsigned char data)
hudakz 0:0834641c241a 135 {
hudakz 0:0834641c241a 136 unsigned char i;
hudakz 0:0834641c241a 137 for (i = 0; i < 8; i++) {
hudakz 0:0834641c241a 138 // Set clock high and put data on DD line
hudakz 0:0834641c241a 139 dc = 1;
hudakz 0:0834641c241a 140 if (data & 0x80) {
hudakz 0:0834641c241a 141 dd = 1;
hudakz 0:0834641c241a 142 }
hudakz 0:0834641c241a 143 else {
hudakz 0:0834641c241a 144 dd = 0;
hudakz 0:0834641c241a 145 }
hudakz 0:0834641c241a 146
hudakz 0:0834641c241a 147 data <<= 1;
hudakz 0:0834641c241a 148 dc = 0; // set clock low (DUP capture flank)
hudakz 0:0834641c241a 149 }
hudakz 0:0834641c241a 150 }
hudakz 0:0834641c241a 151
hudakz 0:0834641c241a 152 /**************************************************************************/
hudakz 0:0834641c241a 153 /**
hudakz 0:0834641c241a 154 * @brief Reads a byte from the debug interface. Requires DD to be
hudakz 0:0834641c241a 155 * input when function is called.
hudakz 0:0834641c241a 156 * @return Returns the byte read.
hudakz 0:0834641c241a 157 ******************************************************************************/
hudakz 0:0834641c241a 158 #pragma inline
hudakz 0:0834641c241a 159
hudakz 0:0834641c241a 160 /**
hudakz 0:0834641c241a 161 * @brief
hudakz 0:0834641c241a 162 * @note
hudakz 0:0834641c241a 163 * @param
hudakz 0:0834641c241a 164 * @retval
hudakz 0:0834641c241a 165 */
hudakz 0:0834641c241a 166 unsigned char readDebugByte(void)
hudakz 0:0834641c241a 167 {
hudakz 0:0834641c241a 168 unsigned char i;
hudakz 0:0834641c241a 169 unsigned char data = 0x00;
hudakz 0:0834641c241a 170
hudakz 0:0834641c241a 171 for (i = 0; i < 8; i++) {
hudakz 0:0834641c241a 172 dc = 1; // DC high
hudakz 0:0834641c241a 173 data <<= 1;
hudakz 0:0834641c241a 174 if (dd == 1) {
hudakz 0:0834641c241a 175 data |= 0x01;
hudakz 0:0834641c241a 176 }
hudakz 0:0834641c241a 177
hudakz 0:0834641c241a 178 dc = 0; // DC low
hudakz 0:0834641c241a 179 }
hudakz 0:0834641c241a 180
hudakz 0:0834641c241a 181 return data;
hudakz 0:0834641c241a 182 }
hudakz 0:0834641c241a 183
hudakz 0:0834641c241a 184 /**************************************************************************/
hudakz 0:0834641c241a 185 /**
hudakz 0:0834641c241a 186 * @brief Function waits for DUP to indicate that it is ready. The DUP will
hudakz 0:0834641c241a 187 * pulls DD line low when it is ready. Requires DD to be input when
hudakz 0:0834641c241a 188 * function is called.
hudakz 0:0834641c241a 189 * @return Returns 0 if function timed out waiting for DD line to go low
hudakz 0:0834641c241a 190 * @return Returns 1 when DUP has indicated it is ready.
hudakz 0:0834641c241a 191 ******************************************************************************/
hudakz 0:0834641c241a 192 #pragma inline
hudakz 0:0834641c241a 193
hudakz 0:0834641c241a 194 /**
hudakz 0:0834641c241a 195 * @brief
hudakz 0:0834641c241a 196 * @note
hudakz 0:0834641c241a 197 * @param
hudakz 0:0834641c241a 198 * @retval
hudakz 0:0834641c241a 199 */
hudakz 0:0834641c241a 200 unsigned char waitDupReady(void)
hudakz 0:0834641c241a 201 {
hudakz 0:0834641c241a 202 // DUP pulls DD low when ready
hudakz 0:0834641c241a 203 unsigned int count = 0;
hudakz 0:0834641c241a 204 while ((dd == 1) && count < 16) {
hudakz 0:0834641c241a 205 // Clock out 8 bits before checking if DD is low again
hudakz 0:0834641c241a 206 readDebugByte();
hudakz 0:0834641c241a 207 count++;
hudakz 0:0834641c241a 208 }
hudakz 0:0834641c241a 209
hudakz 0:0834641c241a 210 return(count == 16) ? 0 : 1;
hudakz 0:0834641c241a 211 }
hudakz 0:0834641c241a 212
hudakz 0:0834641c241a 213 /**************************************************************************/
hudakz 0:0834641c241a 214
hudakz 0:0834641c241a 215 /**
hudakz 0:0834641c241a 216 * @brief Issues a command on the debug interface. Only commands that return
hudakz 0:0834641c241a 217 * one output byte are supported.
hudakz 0:0834641c241a 218 * @param cmd Command byte
hudakz 0:0834641c241a 219 * @param cmd_bytes Pointer to the array of data bytes following the
hudakz 0:0834641c241a 220 * command byte [0-3]
hudakz 0:0834641c241a 221 * @param num_cmd_bytes The number of data bytes (input to DUP) [0-3]
hudakz 0:0834641c241a 222 * @return Data returned by command
hudakz 0:0834641c241a 223 ******************************************************************************/
hudakz 0:0834641c241a 224 unsigned char debugCommand(unsigned char cmd, unsigned char* cmd_bytes, unsigned short num_cmd_bytes)
hudakz 0:0834641c241a 225 {
hudakz 0:0834641c241a 226 unsigned short i;
hudakz 0:0834641c241a 227 unsigned char output = 0;
hudakz 0:0834641c241a 228 // Make sure DD is output
hudakz 0:0834641c241a 229 dd.output();
hudakz 0:0834641c241a 230 // Send command
hudakz 0:0834641c241a 231 writeDebugByte(cmd);
hudakz 0:0834641c241a 232 // Send bytes
hudakz 0:0834641c241a 233 for (i = 0; i < num_cmd_bytes; i++) {
hudakz 0:0834641c241a 234 writeDebugByte(cmd_bytes[i]);
hudakz 0:0834641c241a 235 }
hudakz 0:0834641c241a 236
hudakz 0:0834641c241a 237 // Set DD as input
hudakz 0:0834641c241a 238 dd.input();
hudakz 0:0834641c241a 239 dd.mode(PullUp);
hudakz 0:0834641c241a 240 // Wait for data to be ready
hudakz 0:0834641c241a 241 waitDupReady();
hudakz 0:0834641c241a 242 // Read returned byte
hudakz 0:0834641c241a 243 output = readDebugByte();
hudakz 0:0834641c241a 244 // Set DD as output
hudakz 0:0834641c241a 245 dd.output();
hudakz 0:0834641c241a 246
hudakz 0:0834641c241a 247 return output;
hudakz 0:0834641c241a 248 }
hudakz 0:0834641c241a 249
hudakz 0:0834641c241a 250 /**************************************************************************/
hudakz 0:0834641c241a 251
hudakz 0:0834641c241a 252 /**
hudakz 0:0834641c241a 253 * @brief Resets the DUP into debug mode. Function assumes that
hudakz 0:0834641c241a 254 * the programmer I/O has already been configured using e.g.
hudakz 0:0834641c241a 255 * ProgrammerInit().
hudakz 0:0834641c241a 256 * @return None.
hudakz 0:0834641c241a 257 ******************************************************************************/
hudakz 0:0834641c241a 258 void debugInit(void)
hudakz 0:0834641c241a 259 {
hudakz 0:0834641c241a 260 volatile unsigned char i;
hudakz 0:0834641c241a 261
hudakz 0:0834641c241a 262 // Send two flanks on DC while keeping RESET_N low
hudakz 0:0834641c241a 263 // All low (incl. RESET_N)
hudakz 0:0834641c241a 264 dd = 0;
hudakz 0:0834641c241a 265 dc = 0;
hudakz 0:0834641c241a 266 reset = 0;
hudakz 0:0834641c241a 267 wait_ms(10); // Wait
hudakz 0:0834641c241a 268 dc = 1; // DC high
hudakz 0:0834641c241a 269 wait_ms(10); // Wait
hudakz 0:0834641c241a 270 dc = 0; // DC low
hudakz 0:0834641c241a 271 wait_ms(10); // Wait
hudakz 0:0834641c241a 272 dc = 1; // DC high
hudakz 0:0834641c241a 273 wait_ms(10); // Wait
hudakz 0:0834641c241a 274 dc = 0; // DC low
hudakz 0:0834641c241a 275 wait_ms(10); // Wait
hudakz 0:0834641c241a 276 reset = 1; // Release RESET_N
hudakz 0:0834641c241a 277 wait_ms(10); // Wait
hudakz 0:0834641c241a 278 }
hudakz 0:0834641c241a 279
hudakz 0:0834641c241a 280 /**************************************************************************/
hudakz 0:0834641c241a 281
hudakz 0:0834641c241a 282 /**
hudakz 0:0834641c241a 283 * @brief Reads the chip ID over the debug interface using the
hudakz 0:0834641c241a 284 * GET_CHIP_ID command.
hudakz 0:0834641c241a 285 * @return Returns the chip id returned by the DUP
hudakz 0:0834641c241a 286 ******************************************************************************/
hudakz 0:0834641c241a 287 unsigned char readChipId(void)
hudakz 0:0834641c241a 288 {
hudakz 0:0834641c241a 289 unsigned char id = 0;
hudakz 0:0834641c241a 290
hudakz 0:0834641c241a 291 // Make sure DD is output
hudakz 0:0834641c241a 292 dd.output();
hudakz 0:0834641c241a 293 wait_ms(1);
hudakz 0:0834641c241a 294 // Send command
hudakz 0:0834641c241a 295 writeDebugByte(CMD_GET_CHIP_ID);
hudakz 0:0834641c241a 296 // Set DD as input
hudakz 0:0834641c241a 297 dd.input();
hudakz 0:0834641c241a 298 dd.mode(PullUp);
hudakz 0:0834641c241a 299 wait_ms(1);
hudakz 0:0834641c241a 300 // Wait for data to be ready
hudakz 0:0834641c241a 301 if (waitDupReady() == 1) {
hudakz 0:0834641c241a 302 // Read ID and revision
hudakz 0:0834641c241a 303 id = readDebugByte(); // ID
hudakz 0:0834641c241a 304 readDebugByte(); // Revision (discard)
hudakz 0:0834641c241a 305 }
hudakz 0:0834641c241a 306
hudakz 0:0834641c241a 307 // Set DD as output
hudakz 0:0834641c241a 308 dd.output();
hudakz 0:0834641c241a 309
hudakz 0:0834641c241a 310 return id;
hudakz 0:0834641c241a 311 }
hudakz 0:0834641c241a 312
hudakz 0:0834641c241a 313 /**************************************************************************/
hudakz 0:0834641c241a 314
hudakz 0:0834641c241a 315 /**
hudakz 0:0834641c241a 316 * @brief Sends a block of data over the debug interface using the
hudakz 0:0834641c241a 317 * BURST_WRITE command.
hudakz 0:0834641c241a 318 * @param src Pointer to the array of input bytes
hudakz 0:0834641c241a 319 * @param num_bytes The number of input bytes
hudakz 0:0834641c241a 320 * @return None.
hudakz 0:0834641c241a 321 ******************************************************************************/
hudakz 0:0834641c241a 322 void burstWriteBlock(unsigned char* src, unsigned short num_bytes)
hudakz 0:0834641c241a 323 {
hudakz 0:0834641c241a 324 unsigned short i;
hudakz 0:0834641c241a 325
hudakz 0:0834641c241a 326 // Make sure DD is output
hudakz 0:0834641c241a 327 dd.output();
hudakz 0:0834641c241a 328
hudakz 0:0834641c241a 329 writeDebugByte(CMD_BURST_WRITE | HIBYTE(num_bytes));
hudakz 0:0834641c241a 330 writeDebugByte(LOBYTE(num_bytes));
hudakz 0:0834641c241a 331 for (i = 0; i < num_bytes; i++) {
hudakz 0:0834641c241a 332 writeDebugByte(src[i]);
hudakz 0:0834641c241a 333 }
hudakz 0:0834641c241a 334
hudakz 0:0834641c241a 335 // Set DD as input
hudakz 0:0834641c241a 336 dd.input();
hudakz 0:0834641c241a 337 dd.mode(PullUp);
hudakz 0:0834641c241a 338 // Wait for DUP to be ready
hudakz 0:0834641c241a 339 waitDupReady();
hudakz 0:0834641c241a 340 readDebugByte(); // ignore output
hudakz 0:0834641c241a 341 // Set DD as output
hudakz 0:0834641c241a 342 dd.output();
hudakz 0:0834641c241a 343 }
hudakz 0:0834641c241a 344
hudakz 0:0834641c241a 345 /**************************************************************************/
hudakz 0:0834641c241a 346
hudakz 0:0834641c241a 347 /**
hudakz 0:0834641c241a 348 * @brief Issues a CHIP_ERASE command on the debug interface and waits for it
hudakz 0:0834641c241a 349 * to complete.
hudakz 0:0834641c241a 350 * @return None.
hudakz 0:0834641c241a 351 ******************************************************************************/
hudakz 0:0834641c241a 352 void chipErase(void)
hudakz 0:0834641c241a 353 {
hudakz 0:0834641c241a 354 volatile unsigned char status;
hudakz 0:0834641c241a 355 // Send command
hudakz 0:0834641c241a 356 debugCommand(CMD_CHIP_ERASE, 0, 0);
hudakz 0:0834641c241a 357
hudakz 0:0834641c241a 358 // Wait for status bit 7 to go low
hudakz 0:0834641c241a 359 do {
hudakz 0:0834641c241a 360 status = debugCommand(CMD_READ_STATUS, 0, 0);
hudakz 0:0834641c241a 361 } while ((status & STATUS_CHIP_ERASE_BUSY_BM));
hudakz 0:0834641c241a 362 }
hudakz 0:0834641c241a 363
hudakz 0:0834641c241a 364 /**************************************************************************/
hudakz 0:0834641c241a 365
hudakz 0:0834641c241a 366 /**
hudakz 0:0834641c241a 367 * @brief Writes a block of data to the DUP's XDATA space.
hudakz 0:0834641c241a 368 * @param address XDATA start address
hudakz 0:0834641c241a 369 * @param values Pointer to the array of bytes to write
hudakz 0:0834641c241a 370 * @param num_bytes Number of bytes to write
hudakz 0:0834641c241a 371 * @return None.
hudakz 0:0834641c241a 372 ******************************************************************************/
hudakz 0:0834641c241a 373 void writeXdataMemoryBlock(unsigned short address, const unsigned char* values, unsigned short num_bytes)
hudakz 0:0834641c241a 374 {
hudakz 0:0834641c241a 375 unsigned char instr[3];
hudakz 0:0834641c241a 376 unsigned short i;
hudakz 0:0834641c241a 377
hudakz 0:0834641c241a 378 // MOV DPTR, address
hudakz 0:0834641c241a 379 instr[0] = 0x90;
hudakz 0:0834641c241a 380 instr[1] = HIBYTE(address);
hudakz 0:0834641c241a 381 instr[2] = LOBYTE(address);
hudakz 0:0834641c241a 382 debugCommand(CMD_DEBUG_INSTR_3B, instr, 3);
hudakz 0:0834641c241a 383
hudakz 0:0834641c241a 384 for (i = 0; i < num_bytes; i++) {
hudakz 0:0834641c241a 385 // MOV A, values[i]
hudakz 0:0834641c241a 386 instr[0] = 0x74;
hudakz 0:0834641c241a 387 instr[1] = values[i];
hudakz 0:0834641c241a 388 debugCommand(CMD_DEBUG_INSTR_2B, instr, 2);
hudakz 0:0834641c241a 389
hudakz 0:0834641c241a 390 // MOV @DPTR, A
hudakz 0:0834641c241a 391 instr[0] = 0xF0;
hudakz 0:0834641c241a 392 debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 393
hudakz 0:0834641c241a 394 // INC DPTR
hudakz 0:0834641c241a 395 instr[0] = 0xA3;
hudakz 0:0834641c241a 396 debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 397 }
hudakz 0:0834641c241a 398 }
hudakz 0:0834641c241a 399
hudakz 0:0834641c241a 400 /**************************************************************************/
hudakz 0:0834641c241a 401
hudakz 0:0834641c241a 402 /**
hudakz 0:0834641c241a 403 * @brief Writes a byte to a specific address in the DUP's XDATA space.
hudakz 0:0834641c241a 404 * @param address XDATA address
hudakz 0:0834641c241a 405 * @param value Value to write
hudakz 0:0834641c241a 406 * @return None.
hudakz 0:0834641c241a 407 ******************************************************************************/
hudakz 0:0834641c241a 408 void writeXdataMemory(unsigned short address, unsigned char value)
hudakz 0:0834641c241a 409 {
hudakz 0:0834641c241a 410 unsigned char instr[3];
hudakz 0:0834641c241a 411
hudakz 0:0834641c241a 412 // MOV DPTR, address
hudakz 0:0834641c241a 413 instr[0] = 0x90;
hudakz 0:0834641c241a 414 instr[1] = HIBYTE(address);
hudakz 0:0834641c241a 415 instr[2] = LOBYTE(address);
hudakz 0:0834641c241a 416 debugCommand(CMD_DEBUG_INSTR_3B, instr, 3);
hudakz 0:0834641c241a 417
hudakz 0:0834641c241a 418 // MOV A, values[i]
hudakz 0:0834641c241a 419 instr[0] = 0x74;
hudakz 0:0834641c241a 420 instr[1] = value;
hudakz 0:0834641c241a 421 debugCommand(CMD_DEBUG_INSTR_2B, instr, 2);
hudakz 0:0834641c241a 422
hudakz 0:0834641c241a 423 // MOV @DPTR, A
hudakz 0:0834641c241a 424 instr[0] = 0xF0;
hudakz 0:0834641c241a 425 debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 426 }
hudakz 0:0834641c241a 427
hudakz 0:0834641c241a 428 /**************************************************************************/
hudakz 0:0834641c241a 429
hudakz 0:0834641c241a 430 /**
hudakz 0:0834641c241a 431 * @brief Read a byte from a specific address in the DUP's XDATA space.
hudakz 0:0834641c241a 432 * @param address XDATA address
hudakz 0:0834641c241a 433 * @return Value read from XDATA
hudakz 0:0834641c241a 434 ******************************************************************************/
hudakz 0:0834641c241a 435 unsigned char readXdataMemory(unsigned short address)
hudakz 0:0834641c241a 436 {
hudakz 0:0834641c241a 437 unsigned char instr[3];
hudakz 0:0834641c241a 438
hudakz 0:0834641c241a 439 // MOV DPTR, address
hudakz 0:0834641c241a 440 instr[0] = 0x90;
hudakz 0:0834641c241a 441 instr[1] = HIBYTE(address);
hudakz 0:0834641c241a 442 instr[2] = LOBYTE(address);
hudakz 0:0834641c241a 443 debugCommand(CMD_DEBUG_INSTR_3B, instr, 3);
hudakz 0:0834641c241a 444
hudakz 0:0834641c241a 445 // MOVX A, @DPTR
hudakz 0:0834641c241a 446 instr[0] = 0xE0;
hudakz 0:0834641c241a 447 return debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 448 }
hudakz 0:0834641c241a 449
hudakz 0:0834641c241a 450 /**************************************************************************/
hudakz 0:0834641c241a 451
hudakz 0:0834641c241a 452 /**
hudakz 0:0834641c241a 453 * @brief Reads 1-32767 bytes from DUP's flash to a given buffer on the
hudakz 0:0834641c241a 454 * programmer.
hudakz 0:0834641c241a 455 * @param bank Flash bank to read from [0-7]
hudakz 0:0834641c241a 456 * @param address Flash memory start address [0x0000 - 0x7FFF]
hudakz 0:0834641c241a 457 * @param values Pointer to destination buffer.
hudakz 0:0834641c241a 458 * @return None.
hudakz 0:0834641c241a 459 ******************************************************************************/
hudakz 0:0834641c241a 460 void readFlashMemoryBlock
hudakz 0:0834641c241a 461 (
hudakz 0:0834641c241a 462 unsigned char bank,
hudakz 0:0834641c241a 463 unsigned short flash_addr,
hudakz 0:0834641c241a 464 unsigned short num_bytes,
hudakz 0:0834641c241a 465 unsigned char* values
hudakz 0:0834641c241a 466 )
hudakz 0:0834641c241a 467 {
hudakz 0:0834641c241a 468 unsigned char instr[3];
hudakz 0:0834641c241a 469 unsigned short i;
hudakz 0:0834641c241a 470 unsigned short xdata_addr = (0x8000 + flash_addr);
hudakz 0:0834641c241a 471
hudakz 0:0834641c241a 472 // 1. Map flash memory bank to XDATA address 0x8000-0xFFFF
hudakz 0:0834641c241a 473 writeXdataMemory(DUP_MEMCTR, bank);
hudakz 0:0834641c241a 474
hudakz 0:0834641c241a 475 // 2. Move data pointer to XDATA address (MOV DPTR, xdata_addr)
hudakz 0:0834641c241a 476 instr[0] = 0x90;
hudakz 0:0834641c241a 477 instr[1] = HIBYTE(xdata_addr);
hudakz 0:0834641c241a 478 instr[2] = LOBYTE(xdata_addr);
hudakz 0:0834641c241a 479 debugCommand(CMD_DEBUG_INSTR_3B, instr, 3);
hudakz 0:0834641c241a 480
hudakz 0:0834641c241a 481 for (i = 0; i < num_bytes; i++) {
hudakz 0:0834641c241a 482 // 3. Move value pointed to by DPTR to accumulator (MOVX A, @DPTR)
hudakz 0:0834641c241a 483 instr[0] = 0xE0;
hudakz 0:0834641c241a 484 values[i] = debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 485
hudakz 0:0834641c241a 486 // 4. Increment data pointer (INC DPTR)
hudakz 0:0834641c241a 487 instr[0] = 0xA3;
hudakz 0:0834641c241a 488 debugCommand(CMD_DEBUG_INSTR_1B, instr, 1);
hudakz 0:0834641c241a 489 }
hudakz 0:0834641c241a 490 }
hudakz 0:0834641c241a 491
hudakz 0:0834641c241a 492 /**************************************************************************/
hudakz 0:0834641c241a 493
hudakz 0:0834641c241a 494 /**
hudakz 0:0834641c241a 495 * @brief Writes 4-2048 bytes to DUP's flash memory. Parameter \c num_bytes
hudakz 0:0834641c241a 496 * must be a multiple of 4.
hudakz 0:0834641c241a 497 * @param src Pointer to programmer's source buffer (in XDATA space)
hudakz 0:0834641c241a 498 * @param start_addr FLASH memory start address [0x0000 - 0x7FFF]
hudakz 0:0834641c241a 499 * @param num_bytes Number of bytes to transfer [4-1024]
hudakz 0:0834641c241a 500 * @return None.
hudakz 0:0834641c241a 501 ******************************************************************************/
hudakz 0:0834641c241a 502 void writeFlashMemoryBlock(unsigned char* src, unsigned long start_addr, unsigned short num_bytes)
hudakz 0:0834641c241a 503 {
hudakz 0:0834641c241a 504 // 1. Write the 2 DMA descriptors to RAM
hudakz 0:0834641c241a 505 writeXdataMemoryBlock(ADDR_DMA_DESC_0, dmaDesc0, 8);
hudakz 0:0834641c241a 506 writeXdataMemoryBlock(ADDR_DMA_DESC_1, dmaDesc1, 8);
hudakz 0:0834641c241a 507
hudakz 0:0834641c241a 508 // 2. Update LEN value in DUP's DMA descriptors
hudakz 0:0834641c241a 509 unsigned char len[2] = { HIBYTE(num_bytes), LOBYTE(num_bytes) };
hudakz 0:0834641c241a 510 writeXdataMemoryBlock((ADDR_DMA_DESC_0 + 4), len, 2); // LEN, DBG => ram
hudakz 0:0834641c241a 511 writeXdataMemoryBlock((ADDR_DMA_DESC_1 + 4), len, 2); // LEN, ram => flash
hudakz 0:0834641c241a 512 // 3. Set DMA controller pointer to the DMA descriptors
hudakz 0:0834641c241a 513 writeXdataMemory(DUP_DMA0CFGH, HIBYTE(ADDR_DMA_DESC_0));
hudakz 0:0834641c241a 514 writeXdataMemory(DUP_DMA0CFGL, LOBYTE(ADDR_DMA_DESC_0));
hudakz 0:0834641c241a 515 writeXdataMemory(DUP_DMA1CFGH, HIBYTE(ADDR_DMA_DESC_1));
hudakz 0:0834641c241a 516 writeXdataMemory(DUP_DMA1CFGL, LOBYTE(ADDR_DMA_DESC_1));
hudakz 0:0834641c241a 517
hudakz 0:0834641c241a 518 // 4. Set Flash controller start address (wants 16MSb of 18 bit address)
hudakz 0:0834641c241a 519 writeXdataMemory(DUP_FADDRH, HIBYTE((start_addr))); //>>2) ));
hudakz 0:0834641c241a 520 writeXdataMemory(DUP_FADDRL, LOBYTE((start_addr))); //>>2) ));
hudakz 0:0834641c241a 521 // 5. Arm DBG=>buffer DMA channel and start burst write
hudakz 0:0834641c241a 522 writeXdataMemory(DUP_DMAARM, CH_DBG_TO_BUF0);
hudakz 0:0834641c241a 523 burstWriteBlock(src, num_bytes);
hudakz 0:0834641c241a 524
hudakz 0:0834641c241a 525 // 6. Start programming: buffer to flash
hudakz 0:0834641c241a 526 writeXdataMemory(DUP_DMAARM, CH_BUF0_TO_FLASH);
hudakz 0:0834641c241a 527 writeXdataMemory(DUP_FCTL, 0x0A); //0x06
hudakz 0:0834641c241a 528 // 7. Wait until flash controller is done
hudakz 0:0834641c241a 529 while (readXdataMemory(DUP_FCTL) & 0x80);
hudakz 0:0834641c241a 530 }
hudakz 0:0834641c241a 531
hudakz 0:0834641c241a 532 /**
hudakz 0:0834641c241a 533 * @brief
hudakz 0:0834641c241a 534 * @note
hudakz 0:0834641c241a 535 * @param
hudakz 0:0834641c241a 536 * @retval
hudakz 0:0834641c241a 537 */
hudakz 0:0834641c241a 538 void runDUP(void)
hudakz 0:0834641c241a 539 {
hudakz 0:0834641c241a 540 volatile unsigned char i;
hudakz 0:0834641c241a 541
hudakz 0:0834641c241a 542 // Send two flanks on DC while keeping RESET_N low
hudakz 0:0834641c241a 543 // All low (incl. RESET_N)
hudakz 0:0834641c241a 544 dd = 0;
hudakz 0:0834641c241a 545 dc = 0;
hudakz 0:0834641c241a 546 reset = 0;
hudakz 0:0834641c241a 547 wait_ms(10); // Wait
hudakz 0:0834641c241a 548 reset = 1;
hudakz 0:0834641c241a 549 wait_ms(10); // Wait
hudakz 0:0834641c241a 550 }
hudakz 0:0834641c241a 551
hudakz 0:0834641c241a 552 /**
hudakz 0:0834641c241a 553 * @brief
hudakz 0:0834641c241a 554 * @note
hudakz 0:0834641c241a 555 * @param
hudakz 0:0834641c241a 556 * @retval
hudakz 0:0834641c241a 557 */
hudakz 0:0834641c241a 558 void programmerInit(void)
hudakz 0:0834641c241a 559 {
hudakz 0:0834641c241a 560 dd.output();
hudakz 0:0834641c241a 561 dd = 0;
hudakz 0:0834641c241a 562 dc = 0;
hudakz 0:0834641c241a 563 reset = 1;
hudakz 0:0834641c241a 564 led = 0;
hudakz 0:0834641c241a 565 }
hudakz 0:0834641c241a 566
hudakz 0:0834641c241a 567 /**
hudakz 0:0834641c241a 568 * @brief
hudakz 0:0834641c241a 569 * @note
hudakz 0:0834641c241a 570 * @param
hudakz 0:0834641c241a 571 * @retval
hudakz 0:0834641c241a 572 */
hudakz 0:0834641c241a 573 int main()
hudakz 0:0834641c241a 574 {
hudakz 0:0834641c241a 575 programmerInit();
hudakz 0:0834641c241a 576 serial.baud(115200);
hudakz 0:0834641c241a 577 // If using Leonado as programmer,
hudakz 0:0834641c241a 578 // it should add below code,otherwise,comment it.
hudakz 0:0834641c241a 579 //while (!Serial);
hudakz 0:0834641c241a 580 while (true) {
hudakz 0:0834641c241a 581 unsigned char chipId = 0;
hudakz 0:0834641c241a 582 unsigned char debugConfig = 0;
hudakz 0:0834641c241a 583 unsigned char goOn = 0;
hudakz 0:0834641c241a 584 unsigned char verify = 0;
hudakz 0:0834641c241a 585
hudakz 0:0834641c241a 586 while (!goOn) {
hudakz 0:0834641c241a 587 // Wait for starting
hudakz 0:0834641c241a 588 if (serial.readable()) {
hudakz 0:0834641c241a 589 if (serial.getc() == SBEGIN) {
hudakz 0:0834641c241a 590 verify = serial.getc();
hudakz 0:0834641c241a 591 goOn = 1;
hudakz 0:0834641c241a 592 }
hudakz 0:0834641c241a 593 else {
hudakz 0:0834641c241a 594 serial.getc(); // Clear RX buffer
hudakz 0:0834641c241a 595 }
hudakz 0:0834641c241a 596 }
hudakz 0:0834641c241a 597 }
hudakz 0:0834641c241a 598
hudakz 0:0834641c241a 599 debugInit();
hudakz 0:0834641c241a 600 chipId = readChipId();
hudakz 0:0834641c241a 601 if (chipId == 0) {
hudakz 0:0834641c241a 602 serial.putc(ERRO);
hudakz 0:0834641c241a 603 return 1; // No chip detected, run loop again.
hudakz 0:0834641c241a 604 }
hudakz 0:0834641c241a 605
hudakz 0:0834641c241a 606 runDUP();
hudakz 0:0834641c241a 607 debugInit();
hudakz 0:0834641c241a 608
hudakz 0:0834641c241a 609 chipErase();
hudakz 0:0834641c241a 610 runDUP();
hudakz 0:0834641c241a 611 debugInit();
hudakz 0:0834641c241a 612
hudakz 0:0834641c241a 613 // Switch DUP to external crystal osc. (XOSC) and wait for it to be stable.
hudakz 0:0834641c241a 614 // This is recommended if XOSC is available during programming. If
hudakz 0:0834641c241a 615 // XOSC is not available, comment out these two lines.
hudakz 0:0834641c241a 616 //writeXdataMemory(DUP_CLKCONCMD, 0x80);
hudakz 0:0834641c241a 617 //while (readXdataMemory(DUP_CLKCONSTA) != 0x80);
hudakz 0:0834641c241a 618 //0x80)
hudakz 0:0834641c241a 619 // Enable DMA (Disable DMA_PAUSE bit in debug configuration)
hudakz 0:0834641c241a 620 debugConfig = 0x22;
hudakz 0:0834641c241a 621 debugCommand(CMD_WR_CONFIG, &debugConfig, 1);
hudakz 0:0834641c241a 622
hudakz 0:0834641c241a 623 // Program data (start address must be word aligned [32 bit])
hudakz 0:0834641c241a 624 serial.putc(SRSP); // Request data blocks
hudakz 0:0834641c241a 625 led = 1;
hudakz 0:0834641c241a 626
hudakz 0:0834641c241a 627 unsigned char done = 0;
hudakz 0:0834641c241a 628 unsigned char state = WAITING;
hudakz 0:0834641c241a 629 unsigned char rxBuf[514];
hudakz 0:0834641c241a 630 unsigned int bufIndex = 0;
hudakz 0:0834641c241a 631 unsigned int addr = 0x0000;
hudakz 0:0834641c241a 632 while (!done) {
hudakz 0:0834641c241a 633 while (serial.readable()) {
hudakz 0:0834641c241a 634 unsigned char ch;
hudakz 0:0834641c241a 635 ch = serial.getc();
hudakz 0:0834641c241a 636 switch (state) {
hudakz 0:0834641c241a 637 // Bootloader is waiting for a new block, each block begin with a flag byte
hudakz 0:0834641c241a 638 case WAITING:
hudakz 0:0834641c241a 639 {
hudakz 0:0834641c241a 640 if (SDATA == ch) {
hudakz 0:0834641c241a 641 // Incoming bytes are data
hudakz 0:0834641c241a 642 state = RECEIVING;
hudakz 0:0834641c241a 643 }
hudakz 0:0834641c241a 644 else
hudakz 0:0834641c241a 645 if (SEND == ch) {
hudakz 0:0834641c241a 646 // End receiving firmware
hudakz 0:0834641c241a 647 done = 1; // Exit while(1) in main function
hudakz 0:0834641c241a 648 }
hudakz 0:0834641c241a 649 break;
hudakz 0:0834641c241a 650 }
hudakz 0:0834641c241a 651
hudakz 0:0834641c241a 652 // Bootloader is receiving block data
hudakz 0:0834641c241a 653 case RECEIVING:
hudakz 0:0834641c241a 654 {
hudakz 0:0834641c241a 655 rxBuf[bufIndex] = ch;
hudakz 0:0834641c241a 656 bufIndex++;
hudakz 0:0834641c241a 657 if (bufIndex == 514) {
hudakz 0:0834641c241a 658 // If received one block, write it to flash
hudakz 0:0834641c241a 659 bufIndex = 0;
hudakz 0:0834641c241a 660
hudakz 0:0834641c241a 661 uint16_t checkSum = 0x0000;
hudakz 0:0834641c241a 662 for (unsigned int i = 0; i < 512; i++) {
hudakz 0:0834641c241a 663 checkSum += rxBuf[i];
hudakz 0:0834641c241a 664 }
hudakz 0:0834641c241a 665
hudakz 0:0834641c241a 666 uint16_t checkSum_t = rxBuf[512] << 8 | rxBuf[513];
hudakz 0:0834641c241a 667 if (checkSum_t != checkSum) {
hudakz 0:0834641c241a 668 state = WAITING;
hudakz 0:0834641c241a 669 serial.putc(ERRO);
hudakz 0:0834641c241a 670 chipErase();
hudakz 0:0834641c241a 671 return 1;
hudakz 0:0834641c241a 672 }
hudakz 0:0834641c241a 673
hudakz 0:0834641c241a 674 writeFlashMemoryBlock(rxBuf, addr, 512); // src, address, count
hudakz 0:0834641c241a 675 if (verify) {
hudakz 0:0834641c241a 676 unsigned char bank = addr / (512 * 16);
hudakz 0:0834641c241a 677 unsigned int offset = (addr % (512 * 16)) * 4;
hudakz 0:0834641c241a 678 unsigned char readData[512];
hudakz 0:0834641c241a 679 readFlashMemoryBlock(bank, offset, 512, readData); // Bank, address, count, dest.
hudakz 0:0834641c241a 680 for (unsigned int i = 0; i < 512; i++) {
hudakz 0:0834641c241a 681 if (readData[i] != rxBuf[i]) {
hudakz 0:0834641c241a 682 // Fail
hudakz 0:0834641c241a 683 state = WAITING;
hudakz 0:0834641c241a 684 serial.putc(ERRO);
hudakz 0:0834641c241a 685 chipErase();
hudakz 0:0834641c241a 686 return 1;
hudakz 0:0834641c241a 687 }
hudakz 0:0834641c241a 688 }
hudakz 0:0834641c241a 689 }
hudakz 0:0834641c241a 690
hudakz 0:0834641c241a 691 addr += (unsigned int)128;
hudakz 0:0834641c241a 692 state = WAITING;
hudakz 0:0834641c241a 693 serial.putc(SRSP);
hudakz 0:0834641c241a 694 }
hudakz 0:0834641c241a 695 break;
hudakz 0:0834641c241a 696 }
hudakz 0:0834641c241a 697
hudakz 0:0834641c241a 698 default:
hudakz 0:0834641c241a 699 break;
hudakz 0:0834641c241a 700 }
hudakz 0:0834641c241a 701 }
hudakz 0:0834641c241a 702 }
hudakz 0:0834641c241a 703
hudakz 0:0834641c241a 704 led = 0;
hudakz 0:0834641c241a 705 runDUP();
hudakz 0:0834641c241a 706 }
hudakz 0:0834641c241a 707 }