Skip to content
Snippets Groups Projects
Commit 0b795bd5 authored by Sergi Hernandez's avatar Sergi Hernandez
Browse files

Added a tool to download firmware to the STM32 using a computer.

parents
No related branches found
No related tags found
No related merge requests found
# Pre-requisites about cmake itself
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0005 NEW)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
# The project name and the type of project
PROJECT(fw-downloader)
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
SET(CMAKE_INSTALL_PREFIX /usr/local)
IF (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "DEBUG")
ENDIF (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -D_REENTRANT")
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -D_REENTRANT")
ADD_SUBDIRECTORY(src)
FIND_PACKAGE(Doxygen)
FIND_PATH(IRI_DOC_DIR doxygen.conf ${CMAKE_SOURCE_DIR}/doc/iri_doc/)
IF (IRI_DOC_DIR)
ADD_CUSTOM_TARGET (doc ${DOXYGEN_EXECUTABLE} ${CMAKE_SOURCE_DIR}/doc/iri_doc/doxygen.conf)
ELSE (IRI_DOC_DIR)
ADD_CUSTOM_TARGET (doc ${DOXYGEN_EXECUTABLE} ${CMAKE_SOURCE_DIR}/doc/doxygen.conf)
ENDIF (IRI_DOC_DIR)
ADD_CUSTOM_TARGET (distclean @echo cleaning cmake files)
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "distribution clean"
COMMAND make ARGS clean
COMMAND rm ARGS -rf ${CMAKE_SOURCE_DIR}/build/*
TARGET distclean
)
ELSE(UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "distclean only implemented in unix"
TARGET distclean
)
ENDIF(UNIX)
ADD_CUSTOM_TARGET (uninstall @echo uninstall package)
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall package"
COMMAND xargs ARGS rm < install_manifest.txt
TARGET uninstall
)
ELSE(UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall only implemented in unix"
TARGET uninstall
)
ENDIF(UNIX)
Copyright (C) 2009-2010 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
Author sergi (sergi@iri.upc.edu)
All rights reserved.
This file is part of stm32F4 firmware downloader library
stm32F4 firmware downloader library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
# Doxyfile 1.5.5
@INCLUDE_PATH = ../doc/
@INCLUDE = doxygen_project_name.conf
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NUMBER =
OUTPUT_DIRECTORY = ../doc
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = NO
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
TYPEDEF_HIDES_STRUCT = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = YES
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../src \
../doc/main.dox
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.h \
*.cpp
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = *.tab.c \
*.tab.h \
lex* \
*glr.h \
*llr.h \
*glr.c \
*llr.c \
*general.h
EXCLUDE_SYMBOLS =
EXAMPLE_PATH = ../src/examples
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH = ../doc/images
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
HTML_DYNAMIC_SECTIONS = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = _USE_MPI=1
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = NO
INCLUDED_BY_GRAPH = NO
CALL_GRAPH = YES
CALLER_GRAPH = YES
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = NO
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 2
DOT_TRANSPARENT = YES
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
PROJECT_NAME = "stm32F4 firmware downloader"
/*! \mainpage stm32F4 firmware downloader
\section Introduction
\subsection Pre-Requisites
This package requires of the following libraries and packages
- <A href="http://www.cmake.org">cmake</A>, a cross-platform build system.
- <A href="http://www.doxygen.org">doxygen</a> and
<A href="http://www.graphviz.org">graphviz</a> to generate the documentation.
- stdc++,
.
Under linux all of these utilities are available in ready-to-use packages.
Under MacOS most of the packages are available via <a href="http://www.finkproject.org/">fink</a>. <br>
\subsection Compilation
Just download this package, uncompress it, and execute
- cd build
- cmake ..
.
to generate the makefile and then
- make
.
to obtain the shared library (in this case called <em>iriutils.so</em>) and
also all the example programs.
The <em>cmake</em> only need to be executed once (make will automatically call
<em>cmake</em> if you modify one of the <em>CMakeList.txt</em> files).
To generate this documentation type
- make doc
.
The files in the <em>build</em> directory are genetated by <em>cmake</em>
and <em>make</em> and can be safely removed.
After doing so you will need to call cmake manually again.
\subsection Configuration
The default build mode is DEBUG. That is, objects and executables
include debug information.
The RELEASE build mode optimizes for speed. To build in this mode
execute
- cmake .. -DCMAKE_BUILD_TYPE=RELEASE
.
The release mode will be kept until next time cmake is executed.
\subsection Installation
In order to be able to use the library, it it necessary to copy it into the system.
To do that, execute
- make install
.
as root and the shared libraries will be copied to <em>/usr/local/lib/iriutils</em> directory
and the header files will be copied to <em>/usr/local/include/iriutils</em> dierctory. At
this point, the library may be used by any user.
To remove the library from the system, exceute
- make uninstall
.
as root, and all the associated files will be removed from the system.
\section Customization
To build a new application using these library, first it is necessary to locate if the library
has been installed or not using the following command
- FIND_PACKAGE(fw_downloader REQUIRED)
In the case that the package is present, it is necessary to add the header files directory to
the include directory path by using
- INCLUDE_DIRECTORIES(${fw_downloader_INCLUDE_DIR})
Finally, it is also nevessary to link with the desired libraries by using the following command
- TARGET_LINK_LIBRARIES(<executable name> ${fw_downloader_LIBRARY})
.
\section License
This package is licensed under a
<a href="http://www.gnu.org/licenses/lgpl.html">
LGPL 3.0 License</a>.
\section Disclaimer
This is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# locate the necessary dependencies
FIND_PACKAGE(iriutils REQUIRED)
FIND_PACKAGE(comm REQUIRED)
# add the necessary include directories
INCLUDE_DIRECTORIES(.)
INCLUDE_DIRECTORIES(${iriutils_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${comm_INCLUDE_DIR})
# application source files
SET(sources fw_downloader.cpp stm32.cpp hex.cpp)
# application header files
SET(headers fw_downloader.h stm32.h hex.h)
# create the executable file
ADD_EXECUTABLE(fw_downloader ${sources})
# link necessary libraries
TARGET_LINK_LIBRARIES(fw_downloader ${iriutils_LIBRARY})
TARGET_LINK_LIBRARIES(fw_downloader ${comm_LIBRARY})
/*
stm32flash - Open Source ST STM32 flash program for *nix
Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "binary.h"
typedef struct {
int fd;
char write;
struct stat stat;
} binary_t;
void* binary_init() {
return calloc(sizeof(binary_t), 1);
}
parser_err_t binary_open(void *storage, const char *filename, const char write) {
binary_t *st = storage;
if (write) {
st->fd = open(
filename,
O_WRONLY | O_CREAT | O_TRUNC,
#ifndef __WIN32__
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
#else
0
#endif
);
st->stat.st_size = 0;
} else {
if (stat(filename, &st->stat) != 0)
return PARSER_ERR_INVALID_FILE;
st->fd = open(filename, O_RDONLY);
}
st->write = write;
return st->fd == -1 ? PARSER_ERR_SYSTEM : PARSER_ERR_OK;
}
parser_err_t binary_close(void *storage) {
binary_t *st = storage;
if (st->fd) close(st->fd);
free(st);
return PARSER_ERR_OK;
}
unsigned int binary_size(void *storage) {
binary_t *st = storage;
return st->stat.st_size;
}
parser_err_t binary_read(void *storage, void *data, unsigned int *len) {
binary_t *st = storage;
unsigned int left = *len;
if (st->write) return PARSER_ERR_WRONLY;
ssize_t r;
while(left > 0) {
r = read(st->fd, data, left);
if (r < 0) return PARSER_ERR_SYSTEM;
left -= r;
data += r;
}
*len = *len - left;
return PARSER_ERR_OK;
}
parser_err_t binary_write(void *storage, void *data, unsigned int len) {
binary_t *st = storage;
if (!st->write) return PARSER_ERR_RDONLY;
ssize_t r;
while(len > 0) {
r = write(st->fd, data, len);
if (r < 1) return PARSER_ERR_SYSTEM;
st->stat.st_size += r;
len -= r;
data += r;
}
return PARSER_ERR_OK;
}
parser_t PARSER_BINARY = {
"Raw BINARY",
binary_init,
binary_open,
binary_close,
binary_size,
binary_read,
binary_write
};
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include "hex.h"
#include "stm32.h"
#include "exceptions.h"
#include "eventserver.h"
typedef enum {flash_read,flash_write,flash_erase,flash_none} flash_op;
void show_help(char *name) {
fprintf(stderr,
"Usage: %s [-bvngfhc] [-[rw] filename] -d /dev/ttyS0\n"
" -b rate Baud rate (default 57600)\n"
" -r filename Read flash to file\n"
" -w filename Write flash to file\n"
" -u Disable the flash write-protection\n"
" -e n Only erase n pages before writing the flash\n"
" -v Verify writes\n"
" -g address Start execution at specified address (0 = flash start)\n"
" -h Show this help\n"
" -d device serial device name\n"
"\n"
"Examples:\n"
" Get device information:\n"
" %s /dev/ttyS0\n"
"\n"
" Write with verify and then start execution:\n"
" %s -w filename -v -g 0x0 /dev/ttyS0\n"
"\n"
" Read flash to file:\n"
" %s -r filename /dev/ttyS0\n"
"\n"
" Start execution:\n"
" %s -g 0x0 /dev/ttyS0\n",
name,
name,
name,
name,
name
);
};
int main(int argc,char *argv[])
{
unsigned int baudrate=57600,size,left,addr,start_addr=0x00000000,len;
unsigned short int erase_pages=0xFFFF,device_id;
unsigned char bootloader_ver,buffer[256];
flash_op operation=flash_none;
const stm32_dev *device_info;
bool start_exec=false;
std::string device="";
std::string filename;
stm32_cmd commands;
bool verify=false;
off_t offset = 0;
parser_err error;
hex_t *hex_file;
int option;
/* parse the input arguments */
while((option = getopt(argc, argv, "b:r:w:e:vg:hd:")) != -1) {
switch(option) {
case 'b':
baudrate=strtoul(optarg,NULL,10);
break;
case 'r':
if(operation!=flash_none)
{
std::cout << "ERROR: Invalid options, can't read & write at the same time\n" << std::endl;
return 1;
}
else
{
operation=flash_read;
filename = optarg;
}
break;
case 'w':
if(operation!=flash_none)
{
std::cout << "ERROR: Invalid options, can't read & write at the same time\n" << std::endl;
return 1;
}
else
{
operation=flash_write;
filename = optarg;
}
break;
case 'e':
erase_pages = strtoul(optarg, NULL, 0);
operation=flash_erase;
break;
case 'v':
verify = true;
break;
case 'g':
start_exec = true;
start_addr = strtoul(optarg, NULL, 0);
break;
case 'd':
device=optarg;
break;
case 'h':
show_help(argv[0]);
return 1;
}
}
if (device.size() == 0) {
std::cout << "ERROR: Device not specified\n" << std::endl;
show_help(argv[0]);
return 1;
}
/* open and config the serial port */
try{
stm32_init(device,baudrate);
if(stm32_discovery())
{
stm32_get_commands(&commands);
stm32_get_bootloader_ver(&bootloader_ver);
std::cout << "bootloader version: 0x" << std::hex << (int)bootloader_ver << std::endl;
stm32_get_chip_id(&device_id);
std::cout << "product id: 0x" << std::hex << device_id << std::endl;
stm32_get_chip_info(&device_info);
std::cout << "device name: " << device_info->name << std::endl;
std::cout << "RAM : " << std::dec << (device_info->ram_end - 0x20000000) / 1024 << "KiB (" << device_info->ram_start - 0x20000000 << "b reserved by bootloader)" << std::endl;
std::cout << "Flash : " << std::dec << (device_info->fl_end - device_info->fl_start) / 1024 << "KiB (sector size: " << device_info->fl_pps << "x" << device_info->fl_ps << ")" << std::endl;
std::cout << "Option RAM : " << std::dec << device_info->opt_end - device_info->opt_start << "b" << std::endl;
std::cout << "System RAM : " << std::dec << (device_info->mem_end - device_info->mem_start) / 1024 << "KiB" << std::endl;
// perform desired operation
switch(operation)
{
case flash_none:
break;
case flash_erase:
stm32_erase_memory(erase_pages);
break;
case flash_read:
break;
case flash_write:
// parse the input file
hex_file=hex_init();
if((error=hex_open(hex_file,filename.c_str()))!=PARSER_ERR_OK)
{
hex_close(hex_file);
if(error==PARSER_ERR_SYSTEM)
{
std::cout << filename << "file not found" << std::endl;
return 1;
}
if(error==PARSER_ERR_INVALID_FILE)
{
// try to open the file as binary
}
}
else
std::cout << "Input file in Intel HEX format" << std::endl;
// write the data into the memory
size=hex_size(hex_file);
if (size > device_info->fl_end - device_info->fl_start)
{
std::cout << "File provided larger then available flash space" << std::endl;
hex_close(hex_file);
return 1;
}
std::cout << "erasing memory ...";
if(!stm32_erase_memory(0))
std::cout << " failed" << std::endl;
else
std::cout << " done" << std::endl;
addr = device_info->fl_start;
std::cout << '\xd';
while(addr < device_info->fl_end && offset < size)
{
left = device_info->fl_end - addr;
len = sizeof(buffer) > left ? left : sizeof(buffer);
len = len > size - offset ? size - offset : len;
if (hex_read(hex_file, buffer, &len) != PARSER_ERR_OK)
{
std::cout << "Error reading the hex file" << std::endl;
hex_close(hex_file);
return 1;
}
if (!stm32_write_memory(addr,len,buffer))
{
std::cout << "Failed to write memory at address 0x" << std::hex << addr << std::endl;
hex_close(hex_file);
return 1;
}
addr += len;
offset += len;
std::cout << '\xd';
std::cout.width(3);
std::cout << "write done: " << (int)((100.0f / size) * offset) << "%";
std::cout.flush();
}
hex_close(hex_file);
std::cout << std::endl;
if(start_exec)
{
std::cout << "starting execution at 0x" << std::hex << start_addr+device_info->fl_start << std::endl;
stm32_start_exec(start_addr+device_info->fl_start);
}
break;
}
}
}catch(CException &e){
std::cout << e.what() << std::endl;
}
}
#include "hex.h"
hex_t* hex_init()
{
hex_t *hex_file=new hex_t;
hex_file->data_len=0;
hex_file->offset=0;
hex_file->data=NULL;
hex_file->base=0;
return hex_file;
}
parser_err hex_open(hex_t *file,const char *filename)
{
int fd;
uint32_t base = 0,c;
char buffer[9],mark;
uint8_t *record,*tmp_data;
unsigned int reclen, address, type, last_address = 0x0,checksum,i;
fd = open(filename, O_RDONLY);
if (fd < 0)
return PARSER_ERR_SYSTEM;
/* read in the file */
while(read(fd, &mark, 1) != 0)
{
if (mark == '\n' || mark == '\r') continue;
if (mark != ':')
return PARSER_ERR_INVALID_FILE;
/* get the reclen, address, and type */
buffer[8] = 0;
if (read(fd, &buffer, 8) != 8)
return PARSER_ERR_INVALID_FILE;
if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3)
{
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* setup the checksum */
checksum = reclen + ((address & 0xFF00) >> 8) + ((address & 0x00FF) >> 0) + type;
switch(type) {
/* data record */
case 0:
c = address - last_address;
tmp_data=new unsigned char[file->data_len + c + reclen];
memcpy(tmp_data,file->data,file->data_len);
delete[] file->data;
file->data=tmp_data;
/* if there is a gap, set it to 0xff and increment the length */
if (c > 0) {
memset(&file->data[file->data_len], 0xff, c);
file->data_len += c;
}
last_address = address + reclen;
record = &file->data[file->data_len];
file->data_len += reclen;
break;
/* extended segment address record */
case 2:
base = 0;
break;
/* extended linear address record */
case 4:
base = address;
break;
}
buffer[2] = 0;
for(i = 0; i < reclen; ++i)
{
if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1)
{
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* add the byte to the checksum */
checksum += c;
switch(type)
{
case 0:
record[i] = c;
break;
case 2:
case 4:
base = (base << 8) | c;
break;
}
}
/* read, scan, and verify the checksum */
if (read(fd, &buffer, 2 ) != 2 || sscanf(buffer, "%2x", &c) != 1 || (uint8_t)(checksum + c) != 0x00)
{
close(fd);
return PARSER_ERR_INVALID_FILE;
}
switch(type)
{
/* EOF */
case 1:
close(fd);
return PARSER_ERR_OK;
/* address record */
case 2: base = base << 4;
case 4: base = ((base & 0xFF000000) >> 24) | ((base & 0x00FF0000) >> 8) | ((base & 0x0000FF00) << 8) | ((base & 0x000000FF) << 24);
/* Reset last_address since our base changed */
last_address = 0;
if (file->base == 0)
{
file->base = base;
break;
}
/* we cant cope with files out of order */
if (base < file->base)
{
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* if there is a gap, enlarge and fill with zeros */
unsigned int len = base - file->base;
if (len > file->data_len)
{
tmp_data=new unsigned char[len];
memcpy(tmp_data,file->data,file->data_len);
memset(&tmp_data[file->data_len], 0, len - file->data_len);
file->data_len = len;
delete[] file->data;
file->data=tmp_data;
}
break;
}
}
close(fd);
return PARSER_ERR_OK;
}
parser_err hex_close(hex_t *file)
{
if(file)
{
if(file->data!=NULL)
delete[] file->data;
}
delete file;
return PARSER_ERR_OK;
}
unsigned int hex_size(hex_t *file)
{
return file->data_len;
}
parser_err hex_read(hex_t *file,unsigned char *data, unsigned int *len)
{
unsigned int left = file->data_len - file->offset;
unsigned int get = left > *len ? *len : left;
memcpy(data, &file->data[file->offset], get);
file->offset += get;
*len = get;
return PARSER_ERR_OK;
}
parser_err hex_write(hex_t *file, unsigned char *data, unsigned int len)
{
return PARSER_ERR_RDONLY;
}
#ifndef HEX_H
#define HEX_H
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "parsers.h"
typedef struct {
size_t data_len, offset;
uint8_t *data;
uint8_t base;
} hex_t;
hex_t* hex_init();
parser_err hex_open(hex_t *file,const char *filename);
parser_err hex_close(hex_t *file);
unsigned int hex_size(hex_t *file);
parser_err hex_read(hex_t *file,unsigned char *data, unsigned int *len);
parser_err hex_write(hex_t *file, unsigned char *data, unsigned int len);
#endif
#ifndef _PARSERS_H
#define _PARSERS_H
enum parser_err {
PARSER_ERR_OK,
PARSER_ERR_SYSTEM,
PARSER_ERR_INVALID_FILE,
PARSER_ERR_WRONLY,
PARSER_ERR_RDONLY
};
#endif
#include <iostream>
#include "stm32.h"
#include "eventserver.h"
#include "eventexceptions.h"
// private variables
/* device table */
const stm32_dev devices[] = {
{0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
{0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
{0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
{0x413, "STM32F4xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
{0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
{0x418, "Connectivity line", 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800},
{0x420, "Medium-density VL", 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
{0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800},
{0x0}
};
CRS232 serial("stm32_device");
CEventServer *event_server=CEventServer::instance();
std::list<std::string> events;
stm32_cmd commands;
const stm32_dev *current_device=NULL;
void stm32_init(std::string &dev,unsigned int baud)
{
TRS232_config serial_config;
serial_config.baud=baud;
serial_config.num_bits=8;
serial_config.parity=even;
serial_config.stop_bits=1;
try{
serial.open((void *)&dev);
serial.config(&serial_config);
events.push_back(serial.get_rx_event_id());
}catch(CException &e){
serial.close();
throw;
}
}
bool stm32_discovery(void)
{
unsigned char cmd,ack;
int written,num;
std::cout << "set the BOOT0 pint to VDD, reset the device and the hit a key ..." << std::endl;
std::cin.ignore();
cmd=STM32_CMD_INIT;
if((written=serial.write(&cmd,1))!=1)
{
std::cout << "Error while sending the discovery command" << std::endl;
return false;
}
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
if(num!=1)
{
std::cout << "Invalid answer, expected only one byte" << std::endl;
return false;
}
serial.read(&ack,num);
if(ack!=STM32_ACK)
{
std::cout << "Failed to get init ACK from device" << std::endl;
return false;
}
}catch(CEventTimeoutException &e){
std::cout << "No device detected" << std::endl;
return false;
}
return true;
}
bool stm32_get_commands(stm32_cmd *command)
{
unsigned char cmd[2],answer[15];
int written,num,read=0;
cmd[0]=STM32_CMD_GET;
cmd[1]=STM32_CMD_GET ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << STM32_CMD_GET << std::endl;
return false;
}
while(read<15)
{
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer[read],num);
read+=num;
}catch(CEventTimeoutException &e){
if(num==1 && answer[0]==STM32_NACK)
std::cout << "Device answered NACK" << std::endl;
else
std::cout << "Device did not respond in time" << std::endl;
return false;
}
}
// process data
if(answer[1]!=11)
{
std::cout << "Received more bytes than expected" << std::endl;
return false;
}
command->get=commands.get = answer[3];
command->gvr=commands.gvr = answer[4];
command->gid=commands.gid = answer[5];
command->rm=commands.rm = answer[6];
command->go=commands.go = answer[7];
command->wm=commands.wm = answer[8];
command->er=commands.er = answer[9];
command->wp=commands.wp = answer[10];
command->uw=commands.uw = answer[11];
command->rp=commands.rp = answer[12];
command->ur=commands.ur = answer[13];
if(answer[14]!=STM32_ACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
return true;
}
bool stm32_get_bootloader_ver(unsigned char *bl_ver)
{
unsigned char cmd[2],answer[5];
int written,num,read=0;
cmd[0]=commands.gvr;
cmd[1]=commands.gvr ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.gvr << std::endl;
return false;
}
while(read<5)
{
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer[read],num);
read+=num;
}catch(CEventTimeoutException &e){
if(num==1 && answer[0]==STM32_NACK)
std::cout << "Device answered NACK" << std::endl;
else
std::cout << "Device did not respond in time" << std::endl;
return false;
}
}
// process data
*bl_ver=answer[1];
if(answer[4]!=STM32_ACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
return true;
}
bool stm32_get_chip_id(unsigned short int *pid)
{
unsigned char cmd[2],answer[5];
int written,num,read=0,i;
cmd[0]=commands.gid;
cmd[1]=commands.gid ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.gid << std::endl;
return false;
}
while(read<5)
{
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer[read],num);
read+=num;
}catch(CEventTimeoutException &e){
if(num==1 && answer[0]==STM32_NACK)
std::cout << "Device answered NACK" << std::endl;
else
std::cout << "Device did not respond in time" << std::endl;
return false;
}
}
// process data
if(answer[1]!=1)
{
std::cout << "Received more bytes than expected" << std::endl;
return false;
}
*pid=(answer[2] << 8) | answer[3];
for(i=0;i<8;i++)
{
if(devices[i].id==*pid)
current_device=&devices[i];
}
if(current_device==NULL)
{
std::cout << "Unknown device" << std::endl;
return false;
}
if(answer[4]!=STM32_ACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
return true;
}
bool stm32_get_chip_info(const stm32_dev **info)
{
*info=current_device;
return true;
}
bool stm32_read_memory(unsigned int address,unsigned int length,unsigned char *data)
{
return true;
}
bool stm32_start_exec(unsigned char address)
{
unsigned char cmd[5],answer;
unsigned char cs=0;
int written,num;
// send the erase command
cmd[0]=commands.go;
cmd[1]=commands.go ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.go << std::endl;
return false;
}
// wait for the response
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
// send the address to start execution
cmd[0]=(address&0xFF000000)>>24;
cs^=cmd[0];
cmd[1]=(address&0x00FF0000)>>16;
cs^=cmd[1];
cmd[2]=(address&0x0000FF00)>>8;
cs^=cmd[2];
cmd[3]=(address&0x000000FF);
cs^=cmd[3];
cmd[4]=cs;
if((written=serial.write(cmd,5))!=5)
{
std::cout << "Error while sending the command " << commands.wm << std::endl;
return false;
}
// wait for the response
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
return true;
}
bool stm32_write_memory(unsigned int address, unsigned int length, unsigned char *data)
{
unsigned char cmd[5],answer,*mem;
unsigned int i,len,written,num;
unsigned char cs=0;
if((address%4)!=0)
{
std::cout << "Address must be 32 bits aligned" << std::endl;
return false;
}
if(length<0 || length>256)
{
std::cout << "Only 256 can be written at once" << std::endl;
return false;
}
// send the erase command
cmd[0]=commands.wm;
cmd[1]=commands.wm ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.wm << std::endl;
return false;
}
// wait for the response
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
// send the address to write to
cmd[0]=(address&0xFF000000)>>24;
cs^=cmd[0];
cmd[1]=(address&0x00FF0000)>>16;
cs^=cmd[1];
cmd[2]=(address&0x0000FF00)>>8;
cs^=cmd[2];
cmd[3]=(address&0x000000FF);
cs^=cmd[3];
cmd[4]=cs;
if((written=serial.write(cmd,5))!=5)
{
std::cout << "Error while sending the command " << commands.wm << std::endl;
return false;
}
// wait for the response
try{
event_server->wait_all(events,1000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
// send the data
len=length-1+(length%4);
mem=new unsigned char[len+3];
cs=len;
mem[0]=len;
for(i=0;i<length;i++)
{
mem[i+1]=data[i];
cs^=mem[i+1];
}
// aligment padding
for(;i<len;i++)
{
mem[i+1]=0xFF;
cs^=0xFF;
}
mem[i+1]=cs;
if((written=serial.write(mem,len+3))!=len+3)
{
std::cout << "Error while sending the command " << commands.wm << std::endl;
delete[] mem;
return false;
}
// wait for the response
try{
event_server->wait_all(events,5000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
delete[] mem;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
delete[] mem;
return false;
}
delete[] mem;
return true;
}
bool stm32_erase_memory(unsigned short int num_pages, unsigned short int *pages)
{
unsigned char cmd[3],answer=STM32_NACK,*data;
int written,num,i;
// send the erase command
cmd[0]=commands.er;
cmd[1]=commands.er ^ 0xFF;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.er << std::endl;
return false;
}
// wait for the response
try{
event_server->wait_all(events,2000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
if(num_pages==0)
{
if(commands.er==0x44)// extended erase
{
cmd[0]=0xFF;
cmd[1]=0xFF;
cmd[2]=0x00;
if((written=serial.write(cmd,3))!=3)
{
std::cout << "Error while sending the command " << commands.er << std::endl;
return false;
}
}
else// standard erase
{
cmd[0]=0xFF;
cmd[1]=0x00;
if((written=serial.write(cmd,2))!=2)
{
std::cout << "Error while sending the command " << commands.er << std::endl;
return false;
}
}
// wait for the operation to complete
try{
event_server->wait_all(events,20000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
return false;
}
}
else
{
if(commands.er==0x44)
{
unsigned short int cs=0x0000;
data=new unsigned char[(num_pages+1)*2];
data[0]=num_pages>>8;
cs^=data[0];
data[1]=num_pages&0x00FF;
cs^=data[1];
for(i=0;i<num_pages;i++)
{
data[i*2+2]=pages[i]>>8;
cs^=data[i*2+2];
data[i*2+3]=pages[i]&0x00FF;
cs^=data[i*2+3];
}
if((written=serial.write(data,(num_pages+1)*2))!=(num_pages+1)*2)
{
std::cout << "Error while sending the command " << commands.er << std::endl;
delete[] data;
return false;
}
}
else
{
unsigned char cs=0x00;
data=new unsigned char[num_pages+1];
data[0]=num_pages&0x00FF;
cs^=data[0];
for(i=0;i<num_pages;i++)
{
data[i+1]=pages[i]&0x00FF;
cs^=data[i+1];
}
if((written=serial.write(data,num_pages+1))!=num_pages+1)
{
std::cout << "Error while sending the command " << commands.er << std::endl;
delete[] data;
return false;
}
}
// wait for the operation to complete
try{
event_server->wait_all(events,20000);
num=serial.get_num_data();
serial.read(&answer,num);
if(answer==STM32_NACK)
{
std::cout << "Device answered NACK" << std::endl;
delete[] data;
return false;
}
}catch(CException &e){
std::cout << "Device did not respond in time" << std::endl;
delete[] data;
return false;
}
delete[] data;
}
return true;
}
bool stm32_write_protect(int num_sectors, unsigned short int *sectors)
{
return true;
}
bool stm32_write_unprotect(int num_sectors, unsigned short int *sectors)
{
return true;
}
bool stm32_read_protect(int num_sectors, unsigned short int *sectors)
{
return true;
}
bool stm32_read_unprotect(int num_sectors, unsigned short int *sectors)
{
return true;
}
#ifndef _STM32_H
#define _STM32_H
#include <stdint.h>
#include "rs232.h"
#define STM32_ACK 0x79
#define STM32_NACK 0x1F
#define STM32_CMD_INIT 0x7F
#define STM32_CMD_GET 0x00 /* get the version and command supported */
typedef struct {
uint8_t get;
uint8_t gvr;
uint8_t gid;
uint8_t rm;
uint8_t go;
uint8_t wm;
uint8_t er; /* this may be extended erase */
// uint8_t ee;
uint8_t wp;
uint8_t uw;
uint8_t rp;
uint8_t ur;
}stm32_cmd;
typedef struct {
uint16_t id;
std::string name;
uint32_t ram_start, ram_end;
uint32_t fl_start, fl_end;
uint16_t fl_pps; // pages per sector
uint16_t fl_ps; // page size
uint32_t opt_start, opt_end;
uint32_t mem_start, mem_end;
}stm32_dev;
// public functions
void stm32_init(std::string &dev,unsigned int baud);
void stm32_enter_boot(void);
void stm32_exit_boot(void);
bool stm32_discovery(void);
bool stm32_get_commands(stm32_cmd *cmd);
bool stm32_get_bootloader_ver(unsigned char *bl_ver);
bool stm32_get_chip_id(unsigned short int *pid);
bool stm32_get_chip_info(const stm32_dev **info);
bool stm32_read_memory(unsigned int address,unsigned int length,unsigned char *data);
bool stm32_start_exec(unsigned char address);
bool stm32_write_memory(unsigned int address, unsigned int length, unsigned char *data);
bool stm32_erase_memory(unsigned short num_pages, unsigned short int *pages=NULL);
bool stm32_write_protect(int num_sectors, unsigned short int *sectors);
bool stm32_write_unprotect(int num_sectors, unsigned short int *sectors);
bool stm32_read_protect(int num_sectors, unsigned short int *sectors);
bool stm32_read_unprotect(int num_sectors, unsigned short int *sectors);
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment