0% found this document useful (0 votes)
28 views

Secondary Micro User Guide

Secondary Micro User Guide

Uploaded by

schourasiyavecv
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views

Secondary Micro User Guide

Secondary Micro User Guide

Uploaded by

schourasiyavecv
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 52

M5xx Secondary Micro User Guide

Status: ©Copyright 2021 Issue Date: 3/20/2021

Author: OpenECU Software Team


Telephone: Tel: +1 (734) 656 0140 Fax: +1 (734) 656 0141
Address: Pi Innovo LLC
47047 W. Five Mile Road, Plymouth, MI 48170-3765, USA
Incorporated in Delaware 20-5693756

Revision History see version control tool

Confidential Page 1 of 52
Revision History
Change SVN
Change Author Date
Version
First draft checked into version control chibner 192 23Aug2016
Added CAN, UDS reprogramming, and UART
chibner 945 01Dec2017
updates
Added CCP and UDS reprogramming inhibit
Chibner 1469 17Apr2018
APIs
Added UDS Diagnostic API MDuBois 1552 05May2018
Added SWT APIs JStepchuk 1558 08May2018
Added loop period guidance. Move
software watchdog timer to its own section chibner 1788 09Jul2018
under peripheral APIs
Updated loop_usage_pct APIs JStepchuk 2063 30Oct2018
Updating Pi Innovo logo in document
Updates to section 5 to remove the obsolete
SOS_RTNTBL_TimerRtnTable[] references JStepchuk 75723 02Apr2020
Updating return values for suart_transmit
and suart_receive
Update docs for setting loop rates and new
Cbalcom 78505 10/20/2020
Init/loop function standards
Refinements to API docs and added
CBalcom 79254 1/12/2020
Migration Guide for 3.1

©Copyright 2020 Page 2 of 52


Contents
1. Document Purpose & Structure 6
1.1 Terms and Abbreviations 6
1.2 References 6
2. Secondary Microcontroller Software Overview 7
3. Operating System 8
3.1 Initialization 9
3.1.1 Clear RAM 9
3.1.2Peripheral Configuration 9
3.1.3Application RAM Initialization 9
3.1.4 IO Initialization 9
3.2 Execution 9
3.2.1Timer Loop 9
3.2.1.1 Timer Loop Period Guidance 10
3.2.1.2 Timer Loop APIs 10
3.2.2 Background Loop 11
3.2.2.1 Background Loop Period 12
3.2.2.2 Background Loop APIs 13
3.2.3 Slow Loop 14
3.3 Software Version 17
3.4 OS Timer 21
3.5 Expected App Functions 22
4. Peripheral API 24
4.1 Analog inputs 24
4.1.1 ADC Channel Enumeration 24
4.1.2 Return Value 24
4.1.3 ADC Status 25
4.2 Digital inputs 25
4.2.1 Digital Input Channel Enumeration 25
4.2.2 Return Value 25
4.3 Digital outputs 26
4.3.1 Digital Output Channel Enumeration 26
4.3.2 Return Value 26
4.4 UART 26
4.4.1 UART Transmit 27
4.4.2 UART Receive 27
4.4.3 UART Comm Established 28
4.5 CAN 28
4.5.1 CAN Addressing Mode Enumeration 28
4.5.2 Baud configuration 28
4.5.3 CCP configuration 29
4.5.4 UDS / ISO15765 configuration 30
4.5.5 CCP / UDS Reprogramming Inhibit 30
4.5.6 CAN messaging 31
4.5.6.1 Message declaration 31
4.5.6.2 Message transmission 32
4.5.6.3 Message reception 32
4.5.6.4 Transmission status 33
4.5.6.5 CAN bus status 34
4.5.7 UDS Diagnostics 34
4.6 Software Watchdog Timer 35
4.7 Software Stack Usage 36
5. Example 37
5.1 Example application description 37
5.2 Step 1 – Set loop periods 37
5.3 Step 3 – Create the C file 38
5.3.1General design 38
5.3.2 Add #includes 39
5.3.3 Add macros and state machine typedef 39
5.3.4 Create local static variables 40
5.3.5 Create RAM initialization function + CAN configuration 41
5.3.6 Create RAM integrity check function 42
5.3.7 Create the background loop function 42
5.3.8 Create the timer loop function 45
5.3.9 Create the slow loop function 46
6. Building the Application 49
6.1 Requirements 49
6.2 Quick Start 49
6.3 Build Process 50
6.3.1Build Steps 50
7. Migration Guide 51
7.1 Intro 51
7.2 Migrating from version 3.0 to 3.1 51
1. Document Purpose & Structure
This document specifies the user guide and application programming interface (API) for the secondary
microcontroller for the OpenECU M5xx ECU. This document will start with a description of the operating
system and API and will include an example showing how to add a new application software module.

1.1 Terms and Abbreviations


ADC Analog to Digital Converter

API Application Programming Interface

CAN Controller Area Network

CCP CAN Calibration Protocol

DIO Digital Input/Output

ECU Electronic Control Unit

HCU Hybrid Control Unit

OS Operating System

NVM Non-Volatile Memory

PHEV Plug-in Hybrid Electric Vehicle

SCI Serial Communication Interface

UART Universal Asynchronous Receiver/Transmitter

UDS Unified Diagnostic Services

1.2 References
[Ref 1] M560 Technical Specification
2. Secondary Microcontroller Software Overview
The secondary micro software package is comprised of a bootloader, the application platform software,
and the header (configuration).

All secondary microcontroller source files created by Pi Innovo will start with the letter ‘s’ to denote that
they belong to the secondary microcontroller, followed by a prefix that is an abbreviation of the software
module. For instance, “sos.h” is the secondary microcontroller operating system header file, and “sdio.h”
is the header file for secondary microcontroller digital IO. All other secondary micro source files that do
not begin with the letter ‘s’, as well as some that do, were supplied by the microcontroller manufacturer.

Note: Floats, doubles, and floating-point math should not be used in secondary applications. The
CodeWarrior compiler has been known to occasionally cause issues that give incorrect data with floating-
point variables and arithmetic.
3. Operating System
The Operating System (OS) is responsible for initialization after reset, scheduling of tasks (routines), and
providing timer services. An overview of the operating system is shown in the following block diagram:

Init
Clear RAM

Config Peripherals

Application RAM Init

Init IO

Execution
Note: Background Loop, Slow Loop, and Timer Loop run in parallel

Background Loop Slow Loop Timer Loop


X ms Y ms Z ms
RAM Integrity Check

Set IO

Background Loop
Routines

One Slow Loop


Routine

Figure 1 – Secondary Micro Operating System Block Diagram

Each bubble in Figure 1 refers to a task table that is executed by the operating system. Details for each
task table are given in the following sections.
3.1 Initialization

3.1.1 Clear RAM


All RAM bytes will be written to 0 by the platform during initialization. This is important to note as

initialized variable declarations may be overwritten to 0 by the platform. All application variables should

initialized in a RAM initialization routine as described in section 3.1.3.

3.1.2 Peripheral Configuration


All peripherals will be configured by the platform.

3.1.3 Application RAM Initialization


Each software module is required to have a function to initialize its static RAM variables prior to execution.
Note that since the platform clears all RAM to 0 during initialization, initial static variable values should
be set in the software module’s RAM initialization function instead of during the variable declaration.

The RAM init function must take no arguments and have no returns. The following function prototype
must be adhered to for the RAM initialization routine:

Syntax: void app_ram_init (void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to initialize static local and global variables defined by the software module.
Caveats: Must be present in the application.
Calls: None
Called By: Called by the platform.

3.1.4 IO Initialization
All IO initialization will be handled by the platform.

3.2 Execution

3.2.1 Timer Loop


The timer loop is the fastest loop in the software and contains all software that must be executed at a
precise time interval. The period of the timer loop is set by a periodic interrupt timer. When the periodic
interrupt occurs, the timer loop begins regardless of what other software is executing, including other
parts of the timer loop. Therefore, it is extremely important that all software within the timer loop
complete execution prior to the next timer interrupt. It is up to the application software developer to
select an appropriate loop period.
The timer loop period is build-time configurable by the application software by using the
app_timer_loop_period constant defined in app_info.c.

3.2.1.1 Timer Loop Period Guidance

app_timer_loop_period must be greater than or equal to 500 microseconds. If UDS communications are
used, app_timer_loop_period must be less than or equal to 15000 microseconds (15ms).

CAN and UART messages are serviced by the low-level drivers periodically by the platform in the timer
loop. Therefore, increasing app_timer_loop_period will result in slower UDS and CCP communication.

To ensure no missed application defined CAN or UART messages, app_timer_loop_period should be set
to less than or equal to half of the period of the most frequent periodic CAN or UART messages that are
received by the application code running in the timer loop.

3.2.1.2 Timer Loop APIs

Application routines can be added to the timer loop by adding functionality or a call to a function in the
application timer loop routine. The application timer loop routine must take no arguments and have no
returns. The following function prototype must be adhered to for the application timer loop routine:

Syntax: void app_timer_routine(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to execute application routines at the timer loop period.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

The following APIs are provided to determine how long the timer loop is taking to execute:

Syntax: U8 sos_timer_loop_usage_pct(void)
Parameters(in): none
Parameter(out): none
Return value: U8 Percentage(actually fraction) of timer loop used, scaled by 256
Returns the fraction of the total loop time that was used by code executing in the timer
Description:
loop, for the most recent timer loop.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: Application

Syntax: U8 sos_max_timer_loop_usage_pct(void)
Parameters(in): none
Parameter(out): none
Maximum percentage (actually fraction) of timer loop used, scaled by
Return value: U8
256
Returns the maximum fraction of the total loop time that was used by code executing in
Description:
the timer loop since the last microcontroller reset.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: Application

Note 1: The returned loop usage is an 8-bit fixed-point variable with LSB of 2^-8. This means that a return
of 255 is 0.99609 (99.609% loop usage), 128 is 0.5 (50% loop usage), etc.

Note 2: since the code that calculates the timer loop usage percentage is executed at the end of the timer
loop, the loop time usage percentage may not be calculated if the code execution time exceeds the loop
time. This is because the periodic timer interrupt will occur before the usage calculation occurs, and the
timer loop will start over again prior to executing code that calculates the processor usage.

Syntax: U32 sos_timer_loop_overruns(void)


Parameters(in): none
Parameter(out): none
Return value: U32 Number of timer loop overruns
Description: Returns the number of times the timer loop has overrun.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: Application

3.2.2 Background Loop


The background loop contains all software that is not time critical, and can run slower than the timer loop.
Examples of such software are fast diagnostics, device initialization routines, and non-time-critical
communication routines.

The background loop begins by running the RAM integrity routine. The RAM integrity routine can be used
to test for RAM corruption of the variables used by the software module.

If the application RAM integrity routine returns FALSE, the platform will force the ECU to reset.. The
following function prototype must be adhered to for the application RAM integrity routine:
Syntax: U8 app_ram_integrity (void)
Parameters(in): none
Parameter(out): none
Return value: U8 1 (TRUE) if RAM integrity test passes, 0 (FALSE) otherwise
Description: Function to run application-defined RAM integrity checks.
Caveats: Must be present in the application. If the return is FALSE, the ECU will reset.
Calls: User defined
Called By: Called by the platform.

Application routines can be added to the background loop by adding functionality or a call to a function
in the application background loop routine. The application background loop routine must take no
arguments and have no returns. The following function prototype must be adhered to for the application
background loop routine:

Syntax: void app_background_routine(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to run in the background loop.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

Once all routines are executed in the background loop, one slow loop pass (as described in 3.2.3) is
executed and then the loop waits until a delay timer expires. Note that the background loop can be pre-
empted at any time by the timer loop or external interrupts.

3.2.2.1 Background Loop Period

The background loop time is build-time configurable by the application software by setting the
app_background_loop_period constant defined in app_info.c.

It is important to note that the background loop is timed by the expiration of a timer, rather than being
triggered by a timer interrupt. This means that if the code executing in the background loop does not
complete within the loop period, the background loop will continue to execute until completion, thus
overrunning its allotted time. The subsequent background loop will NOT be shortened by the time
overrun.

app_background_loop_period should be less than 250ms. Any tasks that need to run at a period greater
than 250ms should go into the slow loop.

app_background_loop_period should be greater than app_timer_loop_period. To ensure no missed


CAN or UART messages, app_timer_loop_period should be set to less than or equal to half of the
period of the most frequent periodic CAN or UART messages that are received by the application code
running in the background loop.

3.2.2.2 Background Loop APIs

The following APIs are provided to determine how long the code in the background loop is taking to
execute:

Syntax: U8 sos_bkgnd_loop_usage_pct(void)
Parameters(in): none
Parameter(out): none
Return value: U8 Percentage (actually fraction) of background loop used, scaled by 256
Returns the fraction of the total loop time that was used by code executing in the
Description:
background loop, for the most recent background loop.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

Syntax: U8 sos_max_bkgnd_loop_usage_pct(void)
Parameters(in): none
Parameter(out): none
Maximum percentage (actually fraction) of background loop used,
Return value: U8
scaled by 256
Returns the maximum fraction of the total loop time that was used by code executing in
Description:
the background loop since the last microcontroller reset.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application
Note: The returned loop usage is an 8-bit fixed-point variable with LSB of 2^-8. This means that a return
of 255 is 0.99609 (99.609% loop usage), 128 is 0.5 (50% loop usage), etc.

Syntax: U32 sos_background_loop_overruns(void)


Parameters(in): none
Parameter(out): none
Return value: U32 Number of background loop overruns
Description: Returns the number of times the background loop has overrun.
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application
3.2.3 Slow Loop
The slow loop contains all software that can be run at very slow rate, such as 250 – 500 msec. Examples
of such routines include memory checking and slow diagnostics.

As mention in section 3.2.2 above, one slow loop routine pass is executed at the end of the background
loop. Because of this, the slow loop period is defined as a multiple of background loops rather than as a
time period. The slow loop period can be set by using the app_background_loops_per_slow_loop
constant defined in app_info.c. For example, if you set app_background_loop_period to 2000
microseconds, and app_background_loops_per_slow_loop to 125, then the slow loop period will be
0.250 seconds.

The OS has a built-in mechanism to allow each slow loop routine to be executed over multiple passes.
This allows a long background routine to be broken into several passes that each take a shorter period of
time to execute, which helps the background loop finish on time.

A summary of the slow loop execution is shown in the following diagram in which the first slow loop task
is broken into 3 passes, the second task has only one pass, and the third task has two passes.
Slow Loop (X*N ms)

Task 1 Pass 1

NotCompete
LoopCnt++;
Wait for next X ms background loop

Task 1 Pass 2

NotCompete
LoopCnt++;
Wait for next X ms background loop

Task 1 Pass 3

Compete LoopCnt >= N


LoopCnt++;
Wait for next X ms background loop

Task 2 Pass 1

Compete
LoopCnt++;
Wait for next X ms background loop

Task 3 Pass 1

NotCompete
LoopCnt++;
Wait for next X ms background loop

Task 3 Pass 2

Compete
LoopCnt++;
Wait for nextX ms background loop

Wait For N loops


LoopCnt++
to expire

LoopCnt < N

Figure 2 – Slow Loop Execution Flow

Note that it is up to the application software developer to keep the total number of passes of all slow
loop routines to be less than app_background_loops_per_slow_loop.
The slow loop routine function must have one argument of type PASS_COUNTER_E which allows the
function to know which pass number it is currently executing. The slow loop routine must return an
unsigned 8-bit integer which must be one of two macros, DONE or NOT_DONE, which signifies to the
operating system if the slow routine has finished executing all of its passes. This will be further
explained in the example in section 5.3.9. The slow loop function must adhere to the following function
description:

Syntax: U8 app_slow_routine(const PASS_COUTNER_E)


const
Parameters(in): PASS_COUNTE enumeration informing the function which pass it is running
R_E
Parameter(out): none
Return value: U8 macros DONE (if routine is finished) or NOT_DONE (if not finished)
Description: Function to run in the slow loop.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

Syntax: U8 sos_verify_active_slow_routine(const SOS_ADDRESSABLE_SLOW_ROUTINE_PTR)


const
SOS_ADDRESS
Parameters(in): The name of the slow routine that should be active
ABLE_SLOW_R
OUTINE_PTR
Parameter(out): none
TRUE if the OS agrees the active slow routine is currently scheduled
Return value: U8
FALSE if the slow routine is running unexpectedly
Description: Check if the active slow routine has been entered unexpectedly
Caveats: Call from slow routine with the slow routine function name as the parameter
Calls: None
Called By: Application, internal platform slow routines

Syntax: void sos_set_pass_counter(const PASS_COUNTER_E)


const
Parameters(in): PASS_COUNTE enumeration informing the function which pass to run
R_E
Parameter(out): None
Return value: None
Description: Directly set the pass counter of the active slow routine
Caveats: None
Calls: None
Called By: Application

Syntax: void sos_increment_pass_counter (void)


Parameters(in): None
Parameter(out): None
Return value: None
Description: Increments the pass counter of the active slow routine
Caveats: None
Calls: None
Called By: Application

Syntax: void sos_decrement_pass_counter (void)


Parameters(in): None
Parameter(out): None
Return value: None
Description: Decrements the pass counter of the active slow routine
Caveats: None
Calls: None
Called By: Application

Syntax: void sos_reset_slow_routines (void)


Parameters(in): None
Parameter(out): None
Return value: None
Description: Begins all slow routines from the first pass of the first routine
Caveats: None
Calls: None
Called By: Application

3.3 Software Version


The platform provides an API to read the software version information of the firmware, platform, and
application.
SSC_RC_T ssc_get_boot_part_number (U8 *sscf_group, U8 *sscf letter, U32 *sscf_id, U16
Syntax:
*sscf_issue)
Parameters(in): None
U8* Pointer to store the group ID

U8* Pointer to store the group letter in ASCII format


Parameter(out):
U32* Pointer to store the part number

U16* Pointer to store the issue number


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the firmware part number
Caveats: None
Calls: None
Called By: application

SSC_RC_T ssc_get_platform_part_number (U8 *sscf_group, U8 *sscf letter, U32 *sscf_id,


Syntax:
U16 *sscf_issue)
Parameters(in): none
U8* Pointer to store the group ID

U8* Pointer to store the group letter in ASCII format


Parameter(out):
U32* Pointer to store the part number

U16* Pointer to store the issue number


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the platform part number
Caveats: None
Calls: None
Called By: application

SSC_RC_T ssc_get_boot_version (U16 *sscf_major_ver, U16 *sscf minor_ver, U16


Syntax:
*sscf_sub_minor_ver)
Parameters(in): none
U16* Pointer to store the major version number
Parameter(out):
U16* Pointer to store the minor version number

U16* Pointer to store the sub-minor version number


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the firmware version number
Caveats: None
Calls: None
Called By: application

SSC_RC_T ssc_get_platform_version (U16 *sscf_major_ver, U16 *sscf minor_ver, U16


Syntax:
*sscf_sub_minor_ver)
Parameters(in): none
U16* Pointer to store the major version number

Parameter(out): U16* Pointer to store the minor version number

U16* Pointer to store the sub-minor version number


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the platform version number
Caveats: None
Calls: None
Called By: application

SSC_RC_T ssc_get_application_version (U16 *sscf_major_ver, U16 *sscf minor_ver, U16


Syntax:
*sscf_sub_minor_ver)
Parameters(in): none
U16* Pointer to store the major version number

Parameter(out): U16* Pointer to store the minor version number

U16* Pointer to store the sub-minor version number


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the application version number
Caveats: This information is built into the application from the app_info.c file
Calls: None
Called By: application
Syntax: SSC_RC_T ssc_get_boot_build_date (U16 *sscf_year, U16 *sscf month, U16 *sscf_day)
Parameters(in): none
U16* Pointer to store the build year

Parameter(out): U16* Pointer to store the build month

U16* Pointer to store the build day


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the firmware build date
Caveats: None
Calls: None
Called By: application

Syntax: SSC_RC_T ssc_get_platform_build_date (U16 *sscf_year, U16 *sscf month, U16 *sscf_day)
Parameters(in): none
U16* Pointer to store the build year

Parameter(out): U16* Pointer to store the build month

U16* Pointer to store the build day


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the platform build date
Caveats: None
Calls: None
Called By: application

SSC_RC_T ssc_get_application_build_date (U16 *sscf_year, U16 *sscf month, U16


Syntax:
*sscf_day)
Parameters(in): none
U16* Pointer to store the build year

Parameter(out): U16* Pointer to store the build month

U16* Pointer to store the build day


SSC_RC_BAD_ARGS if at least one argument is NULL, SSC_RC_OK
Return value: SSC_RC_T
otherwise
Description: Retrieves the application build date
Caveats: None
Calls: None
Called By: application

3.4 OS Timer
The platform provides a 32-bit free-running timer that increments once at the beginning of every Timer
Loop. The API provides functions to facilitate simple timeout behavior.

The following APIs are provided for the secondary OS timer:

Syntax: U32 sos_timer_get_32bit_timer_var(void)


Parameters(in): none
Parameter(out): none
Return value: U32 The current timer value
Description: Retrieves the current OS timer value
Caveats: None
Calls: None
Called By: application

Syntax: U32 sos_timer_get_32bit_elapsed_time(const U32 captured_time)


Parameters(in): const U32 Initial time
Parameter(out): none
Return value: U32 Time elapsed since initial time
Description: Determines the amount of time that has elapsed since a specified initial time
If the value of the OS timer is less than the initial time when the function is called, the
Caveats:
function will assume the timer has wrapped once.
Calls: None
Called By: application

Syntax: U32 sos_timer_get_32bit_end_time(const U32 captured_time, const U32 timeout)


const U32 Initial time
Parameters(in):
const U32 Length of time from initial time until timeout
Parameter(out): none
Return value: U32 What the OS timer value will be at timeout
Determines what the value of the OS timer will be when a length of time has passed after
Description:
a specified initial time
If the value after timeout would be greater than MAX_U32, the function will return the
Caveats:
value of the OS timer after it has wrapped once.
Calls: None
Called By: application

Syntax: U8 sos_timer_32bit_expired(const U32 captured_time, const U32 timeout)


const U32 Initial Time
Parameters(in):
const U32 Length of time
Parameter(out): none
U8 1 if the time elapsed since captured_time is greater than timeout
Return value:
0 Otherwise
Description: Determines if a length of time after a specified initial time has been exceeded
If the value of the OS timer is less than the initial time when the function is called, the
Caveats:
function will assume the timer has wrapped once.
Calls: None
Called By: application

3.5 Expected App Functions


The following functions are required to be implemented by the app, regardless of usage. More explicit
instructions on their individual usage can be found in sections 3.1 and 3.2.

Syntax: void app_ram_init (void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to initialize static local and global variables defined by the software module.
Caveats: Must be present in the application.
Calls: None
Called By: Called by the platform.

Syntax: U8 app_ram_integrity (void)


Parameters(in): none
Parameter(out): none
Return value: U8 1 (TRUE) if RAM integrity test passes, 0 (FALSE) otherwise
Description: Function to run application-defined RAM integrity checks.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

Syntax: void app_background_routine(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to run in the background loop.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

Syntax: U8 app_slow_routine(const PASS_COUNTER_E)


const
Parameters(in): PASS_COUNTE enumeration informing the function which pass it is running
R_E
Parameter(out): none
Return value: U8 macros DONE (if routine is finished) or NOT_DONE (if not finished)
Description: Function to run in the slow loop.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.

Syntax: void app_timer_routine(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Function to execute application routines at the timer loop period.
Caveats: Must be present in the application.
Calls: User defined
Called By: Called by the platform.
4. Peripheral API
The secondary micro platform provides an API for the following devices:

▪ Analog Inputs
▪ Digital I/O
▪ UART
▪ CAN

4.1 Analog inputs


The platform provides the following API for reading analog inputs:

Syntax: SADC_RC sadc_read(SADC_CHANNEL_E, U16*)


SADC_CHANNE
Parameters(in): enumeration for the desired ADC channel to read
L_E
Parameter(out): U16* 10-bit ADC value for requested channel, right justified
Return value: SADC_RC Return code
Description: Reads specified ADC channel
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

4.1.1 ADC Channel Enumeration


The ADC channel enumeration is defined in sadc.h. One of the enumerated channels must be used when
calling sadc_read(). The enumerated channels are named after either connector pins, or internal
signals. For example, connector pin XD3 would be enumerated as SADC_AIN_XD3, while the internal
monitor of connector pin ZD1 would be enumerated as SADC_VMON_ZD1.

4.1.2 Return Value


The return code of this function will be one of the following, which are defined in sadc.h:

SADC_OK – If normal operation


SADC_BAD_ARGS – If an invalid channel is specified or the pointer to the ADC value is NULL
4.1.3 ADC Status
The status of the analog-to-digital converter can be checked by calling the sadc_status_good() API.
The platform monitors the internal ADC voltage reference as well as the micro core voltage. If these
voltages are outside of the range needed for accurate analog to digital conversion,
sadc_status_good() will return FALSE.

Syntax: BOOL sadc_status_good(void)


Parameters(in): none
Parameter(out): none
Return value: BOOL Boolean representing ADC status. 1 = status is good, 0 = status is bad
Description: Returns ADC conversion accuracy status
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

4.2 Digital inputs


The platform provides the following API for reading digital inputs:

Syntax: SDIO_RC sdio_input(SDIO_INPUT_CHANNEL_E, U8*)


SDIO_INPUT_C
Parameters(in): enumeration for the desired digital input channel to read
HANNEL_E
Parameter(out): U8* Digital value for requested channel
Return value: SDIO_RC Return code
Description: Reads specified digital input channel
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

4.2.1 Digital Input Channel Enumeration


The digital input channel enumeration is defined in sdio.h. One of the enumerated channels must be
used when calling sdio_input(). The enumerated channels are named after either connector pins, or
internal signals. For example, connector pin YG3 would be enumerated as SDIO_INPUT_YG3.

4.2.2 Return Value


The return code of this function will be one of the following, which are defined in sdio.h:
SDIO_OK – If normal operation
SDIO_BAD_ARGS – If an invalid channel is specified or the pointer to the value is NULL

4.3 Digital outputs


The platform provides the following API for setting digital outputs:

Syntax: SDIO_RC sdio_set_output(SDIO_OUTPUT_CHANNEL_E, U8)


SDIO_OUTPUT_
enumeration for the desired digital output channel to set
CHANNEL_E
Parameters(in):
Digital value for requested channel
U8
Parameter(out): none
Return value: SDIO_RC Return code
Description: Sets specified digital output channel to specified state
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

4.3.1 Digital Output Channel Enumeration


The digital output channel enumeration is defined in sdio.h. One of the enumerated channels must be
used when calling sdio_set_output(). The enumerated channels are named after either connector
pins, or internal signals. For example, connector pin (high-side-output) YG1 would be enumerated as
SDIO_OUTPUT_HSO_YG1, while the primary micro reset would be SDIO_OUTPUT_RESET_MAIN.

Note that when setting a digital output, state 1 is logic high, while state 0 is logic low. Two macros are
provided to help reduce confusion: DIO_HIGH and DIO_LOW.

4.3.2 Return Value


The return code of this function will be one of the following:

SDIO_OK – If normal operation


SDIO_BAD_ARGS – If an invalid channel is specified

4.4 UART
The main microcontroller and secondary microcontroller communicate via a UART serial port. The UART
API provides the ability to transmit and receive arbitrary UART messages, as well as receive a heartbeat
message from the primary microcontroller for the purpose of implementing a watchdog feature.
4.4.1 UART Transmit
The platform provides the following API for transmitting UART messages to the primary micro:

Syntax: SUART_RC_T suart_transmit(U8 length, U8* data)


U8 Length of data array to send (maximum of 251)
Parameters(in):
U8* Array of bytes to send
Parameter(out): none
Return value: SUART_RC_T Return code
Description: Transmits array of bytes to primary microcontroller via UART
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

The return code of this function will be one of the following, which are defined in suart.h:

SUART_RC_OK - if successful action

SUART_RC_OVERFLOW – if the UART buffer overflows

SUART_BAD_ARGS - if function inputs are invalid

4.4.2 UART Receive


The platform provides the following API for receiving UART messages from the primary micro:

Syntax: SUART_RC_T suart_receive(U8* length, U8* data, BOOL* rx_flag)


Parameters(in): none
U8* Length of data array received

Parameter(out): U8* Array of bytes received

BOOL* Flag indicating if new data has been received


Return value: SUART_RC_T Return code
Description: Receives array of bytes from primary microcontroller via UART
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

The return code of this function will be one of the following:

SUART_RC_OK - if successful action


SUART_RC_OVERFLOW – if the UART buffer overflows

4.4.3 UART Comm Established


The platform provides the following API to indicate whether any UART packets have been successfully
received from the primary microcontroller:

Syntax: U8 suart_comm_established(void)
Parameters(in): None
Parameter(out): None
Boolean representing UART status.

1 = UART communication has successfully been established with


Return value: U8
primary micro

0 = UART communication not yet established


Description: Indicates whether primary communications are established
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: Application

The return code of this function will be one of the following:

SUART_RC_OK - if successful action

SUART_RC_OVERFLOW – if the UART buffer overflows

4.5 CAN
The secondary microcontroller communicates via CAN on CAN bus A (pins YE4+YF4). The CAN API
provides the ability to transmit and receive arbitrary CAN messages, as well as set up CCP and UDS
communication channels.

4.5.1 CAN Addressing Mode Enumeration


The CAN Addressing mode enumeration is defined in scan_api.h.

SCAN_ADDR_STANDARD – 11-bit ID

SCAN_ADDR_EXTENDED – 29-bit ID

4.5.2 Baud configuration


The platform provides the following API for configuring the CAN baud:
Syntax: BOOL scan_config(SCAN_BAUD_RATE_E baud)
Parameters(in): SCAN_BAUD_RATE_E Baud enumeration
Parameter(out): none
Return value: BOOL Return code
Description: Sets CAN channel baud
Caveats: Must be called before CAN communication begins
Calls: None
Called By: application

The return code is currently unused and should be cast away to void during the function call.

The baud enumeration can be set to any valid enumerated value defined in scan_api.h:

SCAN_BAUD_33_333_KBPS – 33.333 kbps

SCAN_BAUD_50_KBPS – 50.0 kbps

SCAN_BAUD_62_5_KBPS – 62.5 kbps

SCAN_BAUD_83_333_KBPS – 83.333 kbps

SCAN_BAUD_100_KBPS – 100 kbps

SCAN_BAUD_125_KBPS – 125 kbps

SCAN_BAUD_250_KBPS – 250 kbps

SCAN_BAUD_500_KBPS – 500 kbps

SCAN_BAUD_1000_KBPS – 1000 kbps

4.5.3 CCP configuration


The platform provides the following API for configuration CCP communications:

Syntax: BOOL scan_ccp_config(U32 cro, U32 dto, U8 station, SCAN_ADDR_MODE_E addr_mode)


U32 CCP CRO message ID

U32 CCP DTO message ID


Parameters(in):
U8 CCP Station Address

SCAN_ADDR_MODE_E CCP Addressing mode


Parameter(out): none
Return value: BOOL Return code
Description: Sets CCP communication message IDs
Caveats: Must be called before CAN communication begins
Calls: None
Called By: application

The return code is currently unused and should be cast away to void during the function call.

4.5.4 UDS / ISO15765 configuration


The platform provides the following API for configuring UDS / ISO15765 communications:

BOOL scan_iso_config(U32 rx_func, U32 rx_phys, U32 tx, SCAN_ADDR_MODE_E


Syntax:
addr_mode)
U32 Receive functional ID

U32 Receive physical ID


Parameters(in):
U32 Transmit ID

SCAN_ADDR_MODE_E Iso addressing mode


Parameter(out): none
Return value: BOOL validity
Description: Sets UDS / ISO15765 communication message IDs
Caveats: Must be called before CAN communication begins
Calls: None
Called By: application

The return value will be TRUE if the passed-in IDs were valid, FALSE otherwise.

4.5.5 CCP / UDS Reprogramming Inhibit


The platform provides the following API for inhibiting reprogramming over CCP:

Syntax: void scan_inhibit_ccp_reprogramming(U8 scanf_ccp_inhibit)


Parameters(in): U8 Inhibit flag
Parameter(out): none
Return value: None
Description: Enables / inhibits CCP reprogramming
Caveats: None
Calls: None
Called By: application
A single call to this API with a TRUE or 1 input argument will inhibit reprogramming over CCP until this
API is called with and input argument of FALSE or 0.

The platform provides the following API for inhibiting reprogramming over UDS:

Syntax: void scan_inhibit_uds_reprogramming(U8 scanf_uds_inhibit)


Parameters(in): U8 Inhibit flag
Parameter(out): none
Return value: None
Description: Enables / inhibits UDS reprogramming
Caveats: None
Calls: None
Called By: application

A single call to this API with a TRUE or 1 input argument will inhibit reprogramming over UDS until this
API is called with and input argument of FALSE or 0.

4.5.6 CAN messaging


The platform provides APIs for generic CAN messaging that is not related to CCP or UDS.

All CAN messages use one hardware message buffer per message ID. This design means that hardware
ID filtering is used for all received messages, which reduces CPU overhead. This design also means that
all CAN messages must be declared during application initialization.

The secondary microcontroller has 32 message buffers built in to the CAN peripheral. Five of these
message buffers are taken by CCP and UDS messages. This leaves 27 buffers available for user defined
CAN messages.

4.5.6.1 Message declaration

The platform provides the following API for assigning a CAN message to a buffer:

SCAN_MSG_HANDLE_T scan_declare_message(SCAN_MSG_DIR_E direction, U8


Syntax:
useExtendedID, U8 length, U32 messageID)
SCAN_MSG_DIR_E Message direction (tx/rx)

SCAN_ADDR_MODE_E Addressing mode


Parameters(in):
U8 Message length (bytes)

U32 Message ID
Parameter(out): none
message handle used in subsequent communication
Return value: SCAN_MSG_HANDLE_T
APIs
Description: Declares CAN message and associates it with a message buffer
Caveats: Must be called before CAN communication begins
Calls: None
Called By: application

If all message buffers have already been assigned, this function will return -1 as the message handle.

SCAN_MSG_DIR_E is defined in scan_api.h and can be one of the following values:

SCAN_RX_MSG: for a message received by the application

SCAN_TX_MSG: for a message transmitted by the application

4.5.6.2 Message transmission

The platform provides the following API for transmitting a CAN message:

Syntax: BOOL scan_transmit(SCAN_MSG_HANDLE_T handle, U8* data)


SCAN_MSG_HANDLE_T Message handle returned from scan_declare_message()
Parameters(in):
U8* Array of data to be transmitted
Parameter(out): none
Return value: BOOL TRUE if message handle was valid, FALSE otherwise
Description: Transmits a message on the CAN bus
Caveats:
Calls: None
Called By: application

The message is transmitted immediately or as soon as arbitration allows.

4.5.6.3 Message reception

The platform provides the following API for receiving a CAN message:

Syntax: BOOL scan_receive(SCAN_MSG_HANDLE_T handle, U8* data, U32* timestamp)


Parameters(in): SCAN_MSG_HANDLE_T Message handle returned from scan_declare_message()
U8* Array of data bytes received
Parameter(out):
U32* time stamp of received message
TRUE if message handle was valid AND at least one CAN
Return value: BOOL
message was received for this handle, FALSE otherwise
Description: Retrieves the last received message from the message buffer
Caveats:
Calls: None
Called By: application

Data is copied to the passed in array ONLY if the message handle was valid AND at least one CAN message
was received for this handle, otherwise the passed in array remains unchanged.

The application should use the timestamp to determine if a new message has been received since the last
call to scan_receive().

4.5.6.4 Transmission status

The platform provides the following API for checking the transmission status of a CAN message buffer:

SCAN_RC_T scan_get_transmit_status(SCAN_MSG_HANDLE_T handle, U16* tx_requests,


Syntax:
U16* tx_overwrites, u16* tx_acks)
Parameters(in): SCAN_MSG_HANDLE_T Message handle returned from scan_declare_message()
Number of transmission requests for this message
U16*

Number of times a transmission was requested for this


Parameter(out): U16*
message while a transmission was still pending

U16*
Number of transmission acknowledgements for this message
SCAN_RC_BAD_ARGS if an invalid message buffer was
Return value: BOOL
specified, SCAN_RC_OK otherwise
Description: Returns the transmission status for a given message buffer
Caveats:
Calls: None
Called By: application

Behavior of the status data items are as follows:

• tx_requests for the given message buffer will be incremented every time scan_transmit() is
called for that message buffer.

• tx_acks will only increment once the CAN peripheral confirms that a message was transmitted on
the bus for the given message buffer.

• tx_overwrites is incremented whenever scan_transmit() is called for a given message when


a transmission of that message is still pending. In this case, the old message is discarded and
overwritten with the new message.
4.5.6.5 CAN bus status

The platform provides the following API for retrieving the status of the CAN bus:

Syntax: SCAN_BUS_STATE_E scan_get_bus_state(void)


Parameters(in): none
Parameter(out): none
Return value: SCAN_BUS_STATE_E Enumerated bus state
Description: Retrieves the CAN bus status
Caveats:
Calls: None
Called By: application

SCAN_BUS_STATE_E is defined in scan_api.h and can have the following values:

SCAN_BUS_ERROR_ACTIVE – no error on CAN bus

SCAN_BUS_ERROR_PASSIVE – CAN bus is in passive state

SCAN_BUS_OFF – CAN bus is in bus off state

SCAN_BUS_INVALID – The CAN bus was not configured for any messages

4.5.7 UDS Diagnostics

The following UDS services are supported by the secondary microcontroller:

UDS Service Service ID Description

DiagnosticSessionControl 0x10 Enable different diagnostic sessions

ECUReset 0x11 Reset ECU

ReadMemoryByAddress 0x23 Read data in memory by address and size

CommunicationControl 0x28 Enable/Disable transmission or reception of non_UDS


CAN messages

TesterPresent 0x3E Maintain UDS session connection

The following UDS services are supported for reprogramming the secondary microcontroller. For UDS
programming guidance, please contact OpenECU support:

UDS Service Service ID Description


RoutineControl 0x31 Only the routine identifier of 0xFF00 is supported, to
erase flash memory

RequestDownload 0x34 Initiate Data Transfer

TransferData 0x36 Transfer data to the ECU

RequestTransferExit 0x37 Terminate Data Transfer

4.6 Software Watchdog Timer


The platform provides a software watchdog timer. The software watchdog timer must be initialized before
it is enabled. The software watchdog timer has an API to enable it, an API to service it, and an API to
disable it. If enabled, the software watchdog timer must be serviced at least every 120ms or the ECU will
reset. Since the watchdog needs to be serviced at least every 120ms the service API needs to be called in
the timer loop or background loop.

The API also provides a function to cause an immediate reset by enabling the watchdog timer with a
timeout of 16 microseconds.The following APIs are provided for the secondary software watchdog timer:

Syntax: void swt_enable_watchdog(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Enables the Software Watchdog Timer
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

Syntax: void swt_service_watchdog(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Services the Software Watchdog Timer
Caveats: Called from the timer or background loop
Calls: None
Called By: application

Syntax: void swt_disable_watchdog(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Disables the Software Watchdog Timer
Caveats: Called from timer, background, or slow loop
Calls: None
Called By: application

Syntax: void sos_force_reset(void)


Parameters(in): none
Parameter(out): none
Return value: none
Description: Forces the ECU to reset
Caveats: None
Calls: None
Called By: application

4.7 Software Stack Usage


The platform includes a stack usage diagnostic which determines the maximum amount of storage used
by the stack since the last microcontroller reset. If this exceeds the size of the stack, the microcontroller
will reset. This diagnostic is run automatically every time the timer loop is executed. The maximum stack
used is provided to the application.

The following APIs are provided for the secondary software stack usage:

Syntax: U16 sos_stack_used(void)


Parameters(in): none
Parameter(out): none
Return value: U16 The maximum number of bytes used by the stack
Description: Returns the maximum amount of stack storage used by the application
Caveats: None.
Calls: None
Called By: application
5. Example
The following example is provided to help explain how to integrate application code into the secondary
microcontroller platform. Note that the following example is included in the release package as
simpleApp.h and simpleApp.c.

5.1 Example application description


Let’s assume that our simple application must meet the following requirements (note that these
requirements were generated for the sole purpose of forcing the example application to illustrate the
various parts of the secondary micro platform – in no means do these requirements imply that your
application should have similar requirements):

1. Read an analog and digital input, and then set a digital output pin high if the analog input and
digital input disagree. This is time critical and must execute every 500 microseconds.

2. A watchdog feature must reset the primary microcontroller if a UART message is not received
from the primary microcontroller within a certain time interval.

3. Read an analog input, and set a digital output pin high if the analog input crosses a threshold.
This is not time critical, but maximum allowable latency is 3 ms.

4. Send a UART message to the main micro and check that it responds with a known repeatable
response. This shall be used to periodically check the integrity of the UART communication four
times per second. Note: this requires that the primary microcontroller application includes the
code to expect this message from the secondary micro and respond appropriately.

5. No inputs shall be read and no outputs shall be set until UART communication has been
established between the two microcontrollers. Note: this requires that the primary
microcontroller application includes the code to expect this message from the secondary micro
and respond appropriately.

6. Transmit and receive two CAN messages with a period of 1 second.

5.2 Step 1 – Set loop periods


Timing is critical for requirements 1 and 2, so this code should execute in the timer loop, and the timer
loop must be set to 500 microseconds to satisfy requirement 1.

Requirement 3 is not time critical, and the maximum latency is 3 ms. This code can go in the background
loop, and we decided that it should be executed every 2 ms to satisfy the latency requirement.

The UART integrity check must happen only four times per second, so we will put this code in the slow
loop and we will set the slow loop to execute every 250 ms. Since the slow loop is defined by how many
background loops are executed per slow loop, we need 125 background loops at 2ms each to set the
slow loop to 250 ms.

Set the loop times in app_info.c:


/*****************************************************************************
*
* Define loop periods here - all loop periods are in microseconds
*
*****************************************************************************/
/* OS task timer settings */
const U32 app_timer_loop_period = 500;
const U32 app_background_loop_period = 2000;

/*****************************************************************************
*
* Define slow loop period here
* SOS_BKGND_LOOPS_PER_SLOW_LOOP is a multipe of background loop time. For
* example, if the background loop is 2 ms, then 125 background loops = 250 ms
*
*****************************************************************************/
const U32 app_background_loops_per_slow_loop = 150;

5.3 Step 3 – Create the C file

5.3.1 General design


As mentioned earlier in section 5.2, we will use the timer loop to check the digital and analog input
rationality, as well as implement the primary micro watchdog feature. The UART integrity check will be
done in the slow loop. The background loop will be used to set a digital pin high if an analog input is
above a threshold, as well as transmit and receive the CAN messages.

In addition to the parts of the code mentioned in the previous paragraph, there is one requirement that
is a little tricky: requirement 5: the inputs and outputs will only be enabled once UART communication is
established between the two micros. This will require an initialization sequence to be added to the
application to establish communication. The example application uses the following state machine to
execute this initialization sequence in the background loop:
Start

Delay for main micro


startup

Timer > delay_time

Establish Comms: send


UART request

Retries <= limit

Response Incorrect
Check Comm Response c

Retries > limit


Response Correct

Normal Operation Comm Error

Figure 3 – example application initialization sequence

5.3.2 Add #includes


/* Standard data types and macros expected by the platform */
#include "soe_includes.h"

/* Add code module headers here */


#include "simpleApp.h"

5.3.3 Add macros and state machine typedef


/** MACROS AND TYPEDEFs **/
#define APP_A2D_DIN_RATIONALITY_THRESH (300)
#define APP_A2D_DOUT_THRESH (400)
#define APP_WDOG_TIMEOUT_THRESH (100)
#define APP_STARTUP_DELAY_THRESH (10) /* delay 10 background loops before starting */
#define APP_EST_COMM_TRIES (5)
#define APP_COMM_CHECK_LENGTH (5)
#define APP_COMM_WAIT_CNT (10) /* number of background loops to wait for UART response */
typedef enum
{
APP_STATE_DELAY,
APP_STATE_ESTABLISH_COMM,
APP_STATE_CHECK_COMM,
APP_STATE_COMM_ERR,
APP_STATE_NORMAL
} APP_STATE_E;

5.3.4 Create local static variables


/** PRIVATE DATA **/

/** PUBLIC DATA **/


U8 appc_enable_outputs;

/** PRIVATE DATA **/


static U8 app_enable_outputs_prev;

// DEBUG:
#define static
static U8 app_comm_check_msg[APP_COMM_CHECK_LENGTH] = {0xDE,0xAD,3,4,5};
static U8 app_comm_check_response[APP_COMM_CHECK_LENGTH] = {0xBE, 0xEF, 3, 2, 1};

static ADC_DATA_BIT_10_T adc_pin_xe4;


static ADC_DATA_BIT_10_T adc_pin_za1;
static U8 dio_output_reset_main;
static U8 dio_output_xa3;
static U8 dio_input_xa4;
static U8 adc_status_good;

static APP_STATE_E app_state;


static U16 startup_counter;
static U16 comm_wait_counter;
static U16 watchdog_counter;
static U8 app_rationality_good;
static U8 app_uart_fault;
static U8 app_reset_main;

static U16 timer_loop_pct;


static U16 max_timer_loop_pct;
static U16 bkgnd_loop_pct;
static U16 max_bkgnd_loop_pct;

static U16 toggle_cnt;

static U16 timer_loop_cnt;


U32 app_run_time;

SCAN_MSG_HANDLE_T sio_tx_msg_handle[2];
SCAN_MSG_HANDLE_T sio_rx_msg_handle[2];
U8 sio_tx_msg0_data[8];
U8 sio_tx_msg1_data[8];
U8 sio_rx_msg0_data[8];
U8 sio_rx_msg1_data[8];
U16 sio_tx_msg0_cnt;
U16 sio_tx_msg1_cnt;
U16 sio_rx_msg0_cnt;
U16 sio_rx_msg1_cnt;
U32 sio_rx_msg0_timestamp;
U32 sio_rx_msg1_timestamp;
volatile static U16 sioc_can_msg_period_ms = 50;
U32 sio_can_timer_us;

/* inhibit reprogramming */
volatile static U8 app_inhibit_reprog_ccp;
volatile static U8 app_inhibit_reprog_uds;
static U8 app_inhibit_reprog_input;
static U8 app_enable_reprog_input;

5.3.5 Create RAM initialization function + CAN configuration


Since the OS clears all RAM to zero, all local static variables must be initialized in the RAM initialization
function. We also configure can messages here.
void app_can_init(void);

/******************************************************************************
* app_ram_init:
*
* PARAMETERS: None.
*
* CONSTRAINTS: None
*
* DESCRIPTION: Initializes the simpleApp RAM.
*
* RETURNS: None
*
******************************************************************************
*/
void app_ram_init(void)
{
U8 i;

adc_pin_xe4 = 0;
adc_pin_za1 = 0;
dio_output_xa3 = DIO_LOW;
dio_output_reset_main = DIO_HIGH; /* reset line is active low */
dio_input_xa4 = 0;
adc_status_good = FALSE;

app_state = APP_STATE_DELAY;
startup_counter = 0;
comm_wait_counter = 0;
watchdog_counter = 0;
app_uart_fault = FALSE;
app_reset_main = FALSE;
app_rationality_good = FALSE;

/* Initialize CAN data */


for (i=0; i<8; i++)
{
sio_tx_msg0_data[i] = 0;
sio_tx_msg1_data[i] = 0;
sio_rx_msg0_data[i] = 0;
sio_rx_msg1_data[i] = 0;
}
sio_tx_msg_handle[0] = 0;
sio_tx_msg_handle[1] = 0;
sio_rx_msg_handle[0] = 0;
sio_rx_msg_handle[1] = 0;
sio_tx_msg0_cnt = 0;
sio_tx_msg1_cnt = 0;
sio_rx_msg0_cnt = 0;
sio_rx_msg1_cnt = 0;
sio_rx_msg0_timestamp = 0;
sio_rx_msg1_timestamp = 0;
sio_can_timer_us = 0;

/* set output enables HIGH by default


*/
appc_enable_outputs = TRUE;
app_enable_outputs_prev = FALSE;
toggle_cnt = 0;

timer_loop_cnt = 0;
app_run_time = 0;

app_inhibit_reprog_ccp = FALSE;
app_inhibit_reprog_uds = FALSE;
app_inhibit_reprog_input = FALSE;
app_enable_reprog_input = TRUE;

app_can_init();
}

void app_can_init(void)
{
scan_config(SCAN_BAUD_500_KBPS);

(void) scan_ccp_config(0x6FB, 0x6FA, 1, SCAN_ADDR_STANDARD);


(void) scan_iso_config(0x7DF, 0x7E3, 0x7EB, SCAN_ADDR_STANDARD);

sio_tx_msg_handle[0] = scan_declare_message(SCAN_TX_MSG, SCAN_ADDR_STANDARD, 8, 0x101);


sio_tx_msg_handle[1] = scan_declare_message(SCAN_TX_MSG, SCAN_ADDR_STANDARD, 8, 0x102);
sio_rx_msg_handle[0] = scan_declare_message(SCAN_RX_MSG, SCAN_ADDR_STANDARD, 8, 0x201);
sio_rx_msg_handle[1] = scan_declare_message(SCAN_RX_MSG, SCAN_ADDR_STANDARD, 8, 0x202);
}

5.3.6 Create RAM integrity check function


This function can check any local static variable to confirm that it is within the limits that are possible due
to your software. For illustrative purposes, we will check that the state machine state app_state is within
the range of its enumerated type.
/******************************************************************************
* app_ram_integrity:
*
* PARAMETERS: None.
*
* CONSTRAINTS:None
*
* DESCRIPTION:Checks RAM variables to be within range
*
* RETURNS: None
*
*******************************************************************************
*/
U8 app_ram_integrity(void)
{
U8 ret = TRUE;

if (app_state > APP_STATE_NORMAL)


{
ret = FALSE;
}

return (ret);
}

5.3.7 Create the background loop function


The background loop function will implement the start-up sequence as well as setting a digital output
based on an analog input. The following code highlights the APIs for UART tx/rx, reading an analog input,
and setting a digital output. This code also illustrates how to handle an initialization sequence.
void simpleApp_UpdateCan(void);
/*****************************************************************************
* app_background_routine:
*
* PARAMETERS: None
*
* CONSTRAINTS: None
*
* DESCRIPTION: This function runs in the background loop.
*
* RETURNS: None
*
*****************************************************************************
*/
void app_background_routine(void)
{
U8 rx_length;
U8 rx_msg[APP_COMM_CHECK_LENGTH];
U8 rx_trig;
U8 i;
U8 comm_chk_good = TRUE;

U16 j;
U16 x = 0;

/* Cause a delay */
for (j = 0; j < 2000; j++)
{
/* do nothing but wait */
x++;
}

/* update output enables only when variable changes


*/
if (appc_enable_outputs != app_enable_outputs_prev)
{
U8 state = (appc_enable_outputs) ? DIO_HIGH : DIO_LOW;
(void)sdio_set_output(SDIO_OUTPUT_HSO_ZE3, state);
(void)sdio_set_output(SDIO_OUTPUT_HSO_ZE4, state);
(void)sdio_set_output(SDIO_OUTPUT_HSO_YJ1, state);
(void)sdio_set_output(SDIO_OUTPUT_HSO_YG1, state);
(void)sdio_set_output(SDIO_OUTPUT_LSO_YH1, state);
(void)sdio_set_output(SDIO_OUTPUT_LSO_YK1, state);
(void)sdio_set_output(SDIO_OUTPUT_LSO_ZF2, state);
(void)sdio_set_output(SDIO_OUTPUT_LSO_ZF1, state);
(void)sdio_set_output(SDIO_OUTPUT_EN_CAN_B_YJ4_YK4, state);
}

/* Reset the main based on a variable */


(void)sdio_set_output(SDIO_OUTPUT_RESET_MAIN, dio_output_reset_main);

app_enable_outputs_prev = appc_enable_outputs;

/* Inhibit CAN reprogramming? */


(void)sdio_input(SDIO_INPUT_INHIBIT_PRG, &app_enable_reprog_input);
app_inhibit_reprog_input = !app_enable_reprog_input;
scan_inhibit_ccp_reprogramming(app_inhibit_reprog_ccp);
scan_inhibit_uds_reprogramming(app_inhibit_reprog_uds);

/* Code module state machine */


switch (app_state)
{
case APP_STATE_DELAY:
if (startup_counter >= APP_STARTUP_DELAY_THRESH)
{
app_state = APP_STATE_ESTABLISH_COMM;
startup_counter = 0;
}
else
{
/* Set digital outputs to default state */
(void)sdio_set_output(SDIO_OUTPUT_RESET_MAIN, DIO_HIGH);
startup_counter++;
}
break;

case APP_STATE_ESTABLISH_COMM: /* requirement 5 */


suart_transmit(APP_COMM_CHECK_LENGTH, app_comm_check_msg);
app_state = APP_STATE_CHECK_COMM;
break;

case APP_STATE_CHECK_COMM:
if (startup_counter >= APP_EST_COMM_TRIES)
{
app_state = APP_STATE_COMM_ERR;
app_uart_fault = TRUE;
startup_counter = 0;
}
else
{
suart_receive(&rx_length, rx_msg, &rx_trig);
if (TRUE == rx_trig)
{
startup_counter++;
for (i = 0; i < APP_COMM_CHECK_LENGTH; i++)
{
if (rx_msg[i] != app_comm_check_response[i])
{
comm_chk_good = FALSE;
}
}

if (FALSE == comm_chk_good)
{
app_state = APP_STATE_ESTABLISH_COMM;
}
else
{
/* requirement 5 - go to normal state since comm check is good */
app_state = APP_STATE_NORMAL;
}
}
}
break;

case APP_STATE_NORMAL:
case APP_STATE_COMM_ERR:
default:
{
/* Do nothing for state machine */
}

} /* state machine end */

/* if in the normal state, execute the background loop code */


if (APP_STATE_NORMAL == app_state) /* requirement 5 - only execute if comm is established */
{
/* requirement 3 - set digital pin high if ZA1 > threshold */
(void)sadc_read(SADC_VINA, &adc_pin_za1);

sio_can_timer_us += SOS_BACKGROUND_LOOP_PERIOD;
if ((sio_can_timer_us / 1000) >= sioc_can_msg_period_ms)
{
sio_can_timer_us = 0;
// simpleApp_UpdateCan();
}

/* Service the watchdog each loop */


swt_service_watchdog();

} /* End simpleApp_Bkgnd */

Along with the called function simpleApp_UpdateCan():


/*****************************************************************************
* simpleApp_UpdateCan:
*
* PARAMETERS: None
*
* CONSTRAINTS: None
*
* DESCRIPTION: This function transmits and receives the CAN messages.
*
* RETURNS: None
*
*****************************************************************************
*/
void simpleApp_UpdateCan(void)
{
sio_tx_msg0_data[0] = (U8)((sio_tx_msg0_cnt >> 8) & 0xFF);
sio_tx_msg0_data[1] = (U8)(sio_tx_msg0_cnt & 0xFF);
sio_tx_msg1_data[0] = (U8)((sio_tx_msg1_cnt >> 8) & 0xFF);
sio_tx_msg1_data[1] = (U8)(sio_tx_msg1_cnt & 0xFF);

sio_tx_msg0_cnt += (U16)scan_transmit(sio_tx_msg_handle[0], sio_tx_msg0_data);


sio_tx_msg1_cnt += (U16)scan_transmit(sio_tx_msg_handle[1], sio_tx_msg1_data);
sio_rx_msg0_cnt += (U16)scan_receive(sio_rx_msg_handle[0], sio_rx_msg0_data,
&sio_rx_msg0_timestamp);
sio_rx_msg1_cnt += (U16)scan_receive(sio_rx_msg_handle[1], sio_rx_msg1_data,
&sio_rx_msg1_timestamp);
}

5.3.8 Create the timer loop function


The timer loop function will implement the rationality check between a digital and analog input, as well
as a primary microcontroller watchdog feature. The following code highlights the APIs for receiving UART
messages from the primary, reading an analog input, and reading a digital input.

/*****************************************************************************
* app_timer_routine:
*
* PARAMETERS: None
*
* CONSTRAINTS: None
*
* DESCRIPTION: This function runs every 500 us loop.
*
* RETURNS: None
*
*****************************************************************************
*/
void app_timer_routine(void)
{
/* Increment the timer loop count */
timer_loop_cnt++;
if (timer_loop_cnt >= (U16)((U32)1000000 / (U32)SOS_TIMER_LOOP_PERIOD))
{
timer_loop_cnt = 0;
app_run_time++;
}

/* requirement 5: only set/read IO if comms have been established */


if (APP_STATE_NORMAL == app_state)
{
toggle_cnt++;

if (toggle_cnt >= APP_TOGGLE_PERIOD)


{
toggle_cnt = 0;
/* watchdog counter exceeded - reset primary micro - reset is active LOW */
(void)sdio_set_output(SDIO_OUTPUT_RESET_MAIN, DIO_LOW);
}
else
{
/* watchdog counter not exceeded - set reset line HIGH */
/* watchdog counter exceeded - reset primary micro - reset is active LOW */
(void)sdio_set_output(SDIO_OUTPUT_RESET_MAIN, DIO_HIGH);
}
}
else
{
/* comms have not been established yet - do nothing */
}

/* Check CPU usage */


timer_loop_pct = sos_timer_loop_usage_pct();
max_timer_loop_pct = sos_max_timer_loop_usage_pct();
bkgnd_loop_pct = sos_bkgnd_loop_usage_pct();
max_bkgnd_loop_pct = sos_max_bkgnd_loop_usage_pct();

5.3.9 Create the slow loop function


The slow loop function will implement the periodic check of UART integrity. This is done by sending the
primary micro a request to send back a known set of bytes. Since the response should never change, the
UART link can be considered faulty if the secondary micro receives a different response or doesn’t receive
a response at all. This code uses the same message and response that was used to establish
communication between the two micros in the start-up routine in the background loop.

The following code highlights how the slow loop can be broken into multiple passes, and how a slow loop
routine signals to the OS if it is finished or not. Also highlighted are the APIs for UART transmit and
receive.
/*****************************************************************************
* app_slow_routine:
*
* PARAMETERS: current pass to run
*
* CONSTRAINTS: None
*
* DESCRIPTION: This function runs once every background loop.
*
* RETURNS: None
*
*****************************************************************************
*/
U8 app_slow_routine(const PASS_COUNTER_E active_pass)
{
U8 rx_length;
U8 rx_msg[APP_COMM_CHECK_LENGTH];
U8 rx_trig;
U8 i;
U8 comm_chk_good = TRUE;
U8 ret = NOT_DONE;
/* requirement 5: only set/read IO if comms have been established */
if (APP_STATE_NORMAL == app_state)
{
/* Verfiy the active routine as a way to look for micro corruption */
if (TRUE == sos_verify_active_slow_routine(app_slow_routine))
{
switch (active_pass)
{
case PASS01:
/* send the request in pass 1 */
suart_transmit(APP_COMM_CHECK_LENGTH, app_comm_check_msg);
sos_increment_pass_counter();
break;

case PASS02:
/* wait for response and check respopnse in pass 2 */
suart_receive(&rx_length, rx_msg, &rx_trig);
if (TRUE == rx_trig)
{
/* Message has been received - check for correct response */
for (i = 0; i < APP_COMM_CHECK_LENGTH; i++)
{
if (rx_msg[i] != app_comm_check_response[i])
{
comm_chk_good = FALSE;
}
}

if (FALSE == comm_chk_good)
{
app_uart_fault = TRUE;
}
else
{
app_uart_fault = FALSE;
}
ret = DONE;
}
else
{
comm_wait_counter++;
if (comm_wait_counter > APP_COMM_WAIT_CNT)
{
/* message not received in time - declare UART fault */
app_uart_fault = TRUE;
ret = DONE;
}
else
{
/* still waiting for a response */
ret = NOT_DONE;
}
}
break;
}
}
else
{
/* this function was entered inadvertently - take remedial action */
sos_force_reset();
}
}
else
{
/* not yet in normal state - set return to DONE so we don't hold up
the rest of the software */
ret = DONE;
}
return(ret);
}
6. Building the Application

6.1 Requirements
To build an application for the M5xx secondary micro, you will need to install the following:

1. The OpenECU M5xx secondary platform software. This contains the platform library, an example
application, and several tools needed in the build process.
2. The CodeWarrior IDE. This contains the compiler for the secondary micro. A 30-day evaluation
version is available for free at
http://www.nxp.com/products/software-and-tools/software-development-tools/codewarrior-
development-tools/codewarrior-development-studios/codewarrior-for-microcontrollers/codewarrior-
development-studio-for-mpc55xx-mpc56xx-classic-ide-v2.10:CW-MPC55XX_56XX

NOTE: after the 30-day trial period, CodeWarrior will limit the code size to 128KB. A licensed version
will be required to build applications which exceed 128KB.

6.2 Quick Start


The OpenECU platform comes with an example application that is ready to build and flash to the ECU by
following these steps:

1. Install the CodeWarrior IDE

2. Edit the OE_Paths.bat file (in the OpenECU M560 install folder). Make sure the CodeWarrior
installation path is correct – it should look something like this: SET CODEWARRIOR_IDE="C:\Program Files
(x86)\Freescale\CW for MPC55xx and MPC56xx 2.7\bin\CmdIDE.exe"

3. Run the BuildM560ApplicationCLIStandalone.bat batch file. After a successful build, these files
should have been added to the
“(OpenECU M560 install folder)\targets\m560_secondary\build\M560_SecondaryUserApp” folder:

M560_SecondaryUserApp.s37 – this is the s-record that gets flashed to ECU memory.

M560_SecondaryUserApp_tool_**.a2l – this is the ASAP2 database file that can be used with a
calibration tool to read and write memory addresses.

4. Flash the ECU using a CCP calibration tool connected to CAN bus A (pins YE4+YF4). Use the CCP IDs
from simpleApp.c. Defaults are:

a. CRO = 0x6FB

b. DTO = 0x6FA

c. station address = 1
6.3 Build Process
The M5xx secondary micro platform functions are provided as a library file
(M560_SecondaryPlatform.lib), that gets linked to the application at build time. The recommended
build procedure is to start with the example project and build scripts, and modify as necessary. The
most common modification is to add or rename files in the user application. To do this, open the
CodeWarrior project file in the CodeWarrior IDE. The Files tab of the project configuration lists all the
source files. Save any changes to the project file, close CodeWarrior, and then use the batch file to
build.

The file app_info.c contains several variables for tracking build time and version information. These
variables are automatically added to the ASAP2 databases so they can be retrieved with a calibration
tool.

Due to internal flash limits, the application size is limited to 122.88 kb. If this size is exceeded, the
linker will generate the warning “Specified length of memory directive 'internal_flash' was exceeded”.

6.3.1 Build Steps


The batch files automate the following steps in the build process:

1. Generate the build-time variables.

The platform references several automatic variables that are generated at build time. The build
timestamp variables are updated from app_build_time.h, which has to be generated before the project
can be compiled.

2. Compile the application

The application is built in CodeWarrior with the configuration settings in the


M560_SecondaryUserApp.mcp project file. You can edit this file in CodeWarrior to add or remove files,
or change the build settings. You can also build the application directly from CodeWarrior, though it
is recommended to use the batch files to ensure that the build time information gets updated.

3. Modify the S-record addresses

After the application has been built, the s-record is modified by adding the memory image checksum
to the correct address. The output file from CodeWarrior is M560_SecondaryUserApp.mot. The
srecMod tool adds checksum, and writes the new file to M560_SecondaryUserApp_srecmod.s37.

4. Generate the ASAP2 databases

The final step in the build process is to generate the A2L files (ASAP2 databases). These are used by
calibration tools to map internal variables and calibrations to memory addresses in the ECU. The symbols
and addresses are obtained from M560_SecondaryUserApp.elf, which is generated by CodeWarrior. This
file is translated into a text file M560_SecondaryUserApp.objdump by the GCC objdump tool. The capi tool
then parses the .objdump file for all the variables and calibrations in the data dictionary file
(M560_SecondaryUserApp.dde), and writes them to the ASAP2 database. All errors and warnings during
the build process are written to text files with a .err extension.
7. Migration Guide

7.1 Intro
This section outlines changes in application code that are necessary when upgrading to newer versions
of OpenECU.

7.2 Migrating from version 3.0 to 3.1


In OpenECU 3.1 two c files were migrated from being user-accessible into the core platform. Any user
changes previously made to these two files must be re-integrated elsewhere in the app code. Routines
that were previously referenced by name in sos_routine_tables must also be renamed to match
standardized function names(app_ram_init, app_timer_routine, etc).

• sos_timer_loop.c

o This previously could have been modified for further customization of the secondary’s
task scheduling behavior.

• sos_routine_tables.c

o This previously could have been modified to change the lists of routines to be called
upon certain platform events (timer loop, ram init, etc). These have been reworked to call
specifically-named application functions (app_ram_init, app_timer_routine, etc).

In addition, the controls for setting different loop periods have been migrated from sos.h to app_info.c.

Example migration steps:


• Add the following to app_info.c
/* OS task timer settings
* These settings are used by the operating system and should not be deleted.
* They may be edited to alter the frequency of OS tasks. All loop periods
* are in microseconds. See the user guide for further guidance. */
const U32 app_timer_loop_period = 500;
const U32 app_background_loop_period = 2000;
const U32 app_background_loops_per_slow_loop = 150;

• Review sos.h for previous timer period customizations and migrate them to the new definitions
in app_info.c

• Rename or migrate the following functions in your main app .c file (simpleApp.c in OpenECU
examples). Function prototypes can be found in sos_routine_tables.h

o simpleApp_RamInit > app_ram_init

o simpleApp_CanInit > (integrate with with app_ram_init or call from app_ram_init)

o simpleApp_RamIntegrity > app_ram_integrity


o simpleApp_Bkgnd > app_background_routine

o simpleApp_TimerRoutine > app_timer_routine

o simpleApp_SlowRoutine > app_slow_routine

• Review sos_timer_loop.c and sos_routine_tables.c for previous customizations and re-apply them
elsewhere.

• Remove sos_timer_loop.c and sos_routine_tables.c from the project files

You might also like