Custom Mountaineer Firmware (GCC) / Accurately Timed Pulses

I am using a Mountaineer board to simulate input going into a pulse counter and need moderately accurate timing on the generation of the pulse train. More importantly, I need to accurately control the total number of pulses sent to the device.

The specific requirements are to generate a fixed number of pulses, between 1 and 125, during a 250 ms time window.

At any given time, I have at least one other thread running - performing a NetworkStream.Read call - which may be blocking.

Since the Gadgeteer.Timer class is not intended for this level of timing accuracy, what are my other options?

Should I use a blocking function call that toggles the pin state with for loops? If I am only generating 3 - 4 concurrent threads, can I assume a specific thread will be active a certain amount of time?

SignalGenerator is what you need, but it exists on Premium boards only (from GHI). @ taylorza has implemented it for Hydra. You can take a look at his implementation and try to add it to Mountaineer’s open source code and compile custom firmware that way.

Well that’s a bit of a bummer. I’d be up to the challenge of modifying the firmware, but I do not have the compiler necessary to do it.

I also believe that I am unable to use RLP Lite on Mountaineer SDK 4.2 as the OSHW Hardware library is not supported.

Is there any simple hardware that would fit the solution?

Would a 555 be helpful? I know very little about them, and what I know seems that they are setup via hardware, so not sure if they can be changed on the fly?

Well there are a number of hardware options for variable rate pulse generation, but I am not aware of any which would be easy to implement and have the capability of stopping the pulses at a specific count.

Sounds like you need an arduino!

In all seriousness, though, you could implement this as a native interop in about 30 seconds once you got the porting kit set up. And NETMF 4.3 has good support for ARM-GCC 4.6, so you wouldn’t need to buy a compiler.

Once you get the porting kit set up and your interop created, the code to do your task is quite simple:


int pinNumber = 0; // Pin A0
CPU_GPIO_EnableOutputPin(pinNumber, 0);
for(int i = 0; i<125; i++)
{
    CPU_GPIO_SetPinState(pinNumber, 1); // set A0 high
    HAL_Time_Sleep_MicroSeconds(500);  // wait half a millisecond.
    CPU_GPIO_SetPinState(pinNumber, 0); // set it low
    HAL_Time_Sleep_MicroSeconds(500); // wait another half a millisecond.
}

If you want help setting up the porting kit, we’ll be glad to assist you – but try to take a stab at it yourself first, and post any questions you may have (the Native section of this forum is the most relevant place to post questions about the porting kit). You’ll need to grab a copy of NETMF4.3 porting kit, and then copy the Mountaineer source tree on top of it. Try to start by just building the Mountaineer firmware first. Once you get it built, follow the above directions (which, even though they’re for a Hydra, will apply to your board as well) to create an interop library.

Once you get it working, you’ll feel awesome! Guaranteed!

@ jay, thanks for the great info. I will give it a go and let you know if I have any questions.

About NETMF version numbers: you specifically mention the 4.3 porting kit, but all of our development to date is using the 4.2 SDK. Do the porting kit and SDK version numbers have to match?

Yes, absolutely. You’ll either want to switch to 4.3 or use the 4.2 porting kit (which is harder to set-up for your processor).

@ jay, well I made it pretty far on my own, ran through the tutorial and was able to compile the Hydra firmware from source. However, when I attempt to do the same procedure for the Mountaineer hardware I am getting a build error:

arm-none-eabi-gcc.exe : error : missing argument to ‘-mcpu=’

which occurs 205 times. I can trace this back to the file C:\MicroFrameworkPK_v4_2\tools\Targets\Microsoft.Spot.system.gcc.targets where it is looking for a variable DEVICE_TYPE, which is apparently undefined.

Any idea what file / command line option is missing to supply this. Note that the Mountaineer uses the same MCU as the Hydra: STM32F4.

Hydra is different MCU.

Oops. I guess I should have practiced with the Cerberus firmware then. I was mistaken by looking at the codeplex project name: stm32f4.

Architect is right; the Hydra uses the AT91SAM7, which is an older CPU. The STM32F4 is a Cortex-M4 CPU. I would search the forums on compiling FEZ Cerberus using 4.2 and GCC.

Again, let me reiterate – NETMF 4.3 is much easier to get working with GCC.

Is the build procedure the same for using NETMF 4.3 PK? i.e., copy the specific project files on top of the PK folder, set the gcc environment variables, and build?

I will start over, and try to get a Cerberus firmware build to work, and then have another go at the Mountaineer.

We had been hesitant to upgrade the SDK up to this point since Mountaineer only provides full compatibility with 4.2 QFE1. When Gadgeteer upgraded to QFE2, we were forced to update the Mountaineer to a “plain vanilla” firmware since the core Gadgeteer libraries include dependencies on types that do not exist in the QFE1 assemblies. This has completely locked us out of using important features such as OSHW.Hardware.LowLevel.

All that said, if we are building our own firmware, I guess none of this is an issue. Thanks for the support, guys, and I will let you know as soon as I am successful.

Yes, it’s identical.

Shameless self-promotion: I maintain a GitHub repository that contains a full NETMF 4.3 build tree with community contributions located here:

This is, as far as I know, the only place which contains an entire build tree that will compile out-of-the-box with GCC 4.6 or MDK4.54+. Otherwise, you’ll need to grab the NETMF PK, then copy GHI’s codebase on top of it, and then apply fixes to get it to work with GCC.

Since we use STM32F4 microcontrollers in my lab, the build tree should never “break” compatibility with that microcontroller. I believe your Mountaineer board is binary-compatible with TinyBooter/TinyCLR from the FEZ Cerberus solution, so you should be able to get things working.

Just remember to grab GCC 4.6 – that’s the only supported GCC compiler.

Request: If you get your PulseOut code working, and you don’t mind sharing, I’d appreciate a pull request! The idea is to provide a lot of built-in code that community members can add to their port by selecting it in the Solution Wizard.

I am taking the netmf-community-ports project and giving it a try. I also note that there is a similar project by NicolasG on the codeshare.

You mentioned “applying fixes” to get it to work with GCC. Exactly how intensive is that? I am imagining that in order to get the MountaineerEth solution to compile, I will have to make many of the same changes required to port the Cerberus.

All PulseGeneration code aside, if I do successfully get the Mountaineer to build using GCC, I think it would be useful to share it somewhere (such as in the community-ports).

As for a development environment, I have the 4.3 SDK and PK installed; however, my managed code development will have to stay in 4.2 since my company manages which version of Visual Studio we use. Sadly, when 4.3 added 2012 support that also means it dropped 2010 support.

@ jay, I am to the point where I think I need help. I have gotten the compilation errors to 0, however, in the final build stage I encounter the error “region `RAM’ overflowed by 26280 bytes”

Presumably this is because I just copied the GCC scatterfiles from the Cerberus folder. Looking at the MDK version of the Mountaineer scatterfiles, I note the memory address mapping is quite different, and most notably, there is more RAM on the Mountaineer.

I tried changing the file myself, however, the naming conventions between the MDK and GCC versions are significantly different.

Any tips on creating an appropriate GCC scatterfile for the Mountaineer?

Are you building release or debug versions? The scatterfile is really tight-fitting to provide as much deployment space as possible. It’s built for release mode; GCC’s debug build is too big. Try compiling in release mode.

If you don’t need Ethernet, you can also take that out of the solution file using either Solution Wizard or just editing the .proj file. That will trim down the size, too.

EDIT: I just re-read your post and realized you said RAM, not flash. What command line are you using to build it? You should use:


msbuild /t:build /p:flavor=release;memory=flash

To build it in release mode and put it in flash. NETMF also gives you the option of putting it in RAM which is great for debugging (since it can load really fast), but you don’t have enough RAM for that.

I am using the build command you mentioned.

I have tweaked the GCC scatterfile to match the MDK scatterfile (I had to guess on some of the variables, since the names don’t match exactly).

On my latest build attempt I have the more detailed message:

[quote]tinyclr.axf section .bss' will not fit in regionRAM’
region `RAM’ overflowed by 9076 bytes[/quote]

The corresponding region in the scatterfile (copied from the Cerberus build) is:

    <ExecRegion Name=".bss" Align="0x10" Options=">RAM">
      <FileMapping Name="*" Options="(.zidata*)" />
      <FileMapping Name="*" Options="(.bss*)" />
      <FileMapping Name="PROVIDE(__exidx_start = .);" />
      <FileMapping Name="PROVIDE(__exidx_end = .);" />
    </ExecRegion>

Why is it trying to put bss data into RAM? isn’t bss data for statically-assigned variables?

Are you absolutely sure you’re passing in memory=flash and not memory=ram?

My RAM start and end are:


<Set Name="RAM_Start"           Value="0x20000000"/>
<Set Name="RAM_Size"            Value="0x00020000"/>

Here’s my entire scatter file:


<?xml version="1.0"?>
<ScatterFile xmlns="http://schemas.microsoft.com/netmf/ScatterfileSchema.xsd">

  <!-- STM32 with 1M internal Flash and 192k internal RAM -->

  <Set Name="Valid" Value="false"/>

  <!-- ################################################################################ -->

  <Set Name="RAM_Start"           Value="0x20000000"/>
  <Set Name="RAM_Size"            Value="0x00020000"/> <!--128k-->
  <Set Name="CCM_Start"           Value="0x10000000"/>
  <Set Name="CCM_Size"            Value="0x00010000"/>  <!--64k-->

  <Set Name="Stack_Bottom"            Value="0x10000000"/>
  <Set Name="Stack_Size"              Value="0x00008000"/>  <!--32k-->
  <Set Name="Heap_Begin"              Value="0x20000000"/>
  <Set Name="Heap_End"                Value="0x2001CDF8"/>  <!--115k-->
  <Set Name="RLP_Region_Begin"        Value="0x2001D000"/>  <!--11k-->
  <!-- Custom heap used for interrupt handler table only -->
  <Set Name="Custom_Heap_Begin"       Value="0x2001FE00"/>
  <Set Name="Custom_Heap_End"         Value="0x2001FFF8"/>

  <IfDefined Name="PROFILE_BUILD">
    <Error Message="Configuration not implemented for GCC"/>
  </IfDefined>

  <If Name="TARGETLOCATION" In="FLASH">
    <Set Name="Config_BaseAddress"  Value="0x0800C000"/>
    <Set Name="Config_Size"         Value="0x00004000"/>
    <Set Name="Code_BaseAddress"    Value="0x08010000"/>
    <Set Name="Deploy_BaseAddress"  Value="0x08080000"/>
    <Set Name="Code_Size"           Value="%Deploy_BaseAddress - Code_BaseAddress%" />
    <Set Name="Valid"               Value="true"/>
  </If>

  <!-- ################################################################################ -->

  <If Name="Valid" Value="false">
    <Error Message="Configuration not recognized"/>
  </If>

  <NamedGroup Name="MEMORY">
    <LoadRegion Name="LR_%TARGETLOCATION%" Base="%Code_BaseAddress%" Size="%Code_Size%"/>
    <IfDefined Name="Config_BaseAddress">
      <LoadRegion Name="LR_CONFIG" Base="%Config_BaseAddress%" Size="%Config_Size%"/>
    </IfDefined>
    <IfDefined Name="Data_BaseAddress">
      <LoadRegion Name="LR_DAT" Base="%Data_BaseAddress%" Size="%Data_Size%"/>
    </IfDefined>
    <LoadRegion Name="HEAP" Base="%Heap_Begin%" Size="%Heap_End - Heap_Begin% + 1"/>
    <LoadRegion Name="STACK" Base="%Stack_Bottom%" Size="%Stack_Size%"/>
    <LoadRegion Name="RAM" Base="%CCM_Start + Stack_Size%" Size="%CCM_Size - Stack_Size%"/>
  </NamedGroup>

  <EntryPoint Name="EntryPoint"/>

  <NamedGroup Name="SECTIONS">
    <!--<ExecRegion Name="ER_BOOT" Options=">LR_BOOT">
      <FileMapping Name="*" Options="(i.EntryPoint)"/>
    </ExecRegion>-->
    <ExecRegion Name="ER_%TARGETLOCATION%" Options=">LR_%TARGETLOCATION%">
      <FileMapping Name="*" Options="(i.EntryPoint)"/>
      <FileMapping Name="*" Options="(SectionForBootstrapOperations)" />
      <FileMapping Name="*" Options="(.text*)" />
      <FileMapping Name="*" Options="(i.*)" />
      <FileMapping Name="*" Options="(t.*)" />
      <FileMapping Name="*" Options="(.rodata*)" />
      <FileMapping Name="*" Options="(rodata)" />
      <FileMapping Name="*" Options="(.constdata*)" />
      <FileMapping Name="*" Options="(.conststring*)" />
       <FileMapping Name="*" Options="(.glue*)" />
      <FileMapping Name="*" Options="(tinyclr_metadata)" />
      <FileMapping Name="*" Options="(SectionForFlashOperations)" />
     <!--<IfNotDefined Name="Data_BaseAddress">
        <FileMapping Name="*" Options="(tinyclr_metadata)" />
      </IfNotDefined>
      <IfNotDefined Name="Config_BaseAddress">
        <FileMapping Name="*" Options="(SectionForConfig)" />
      </IfNotDefined>-->
      <!-- libc thinks that it needs these routines, but they are never called -->
      <Provide Name="_sbrk" />
      <Provide Name="_write" />
      <Provide Name="_close" />
      <Provide Name="_fstat" />
      <Provide Name="_lseek" />
      <Provide Name="_read" />
      <Provide Name="_exit" />
      <Provide Name="_getpid" />
      <Provide Name="_kill" />
      <Provide Name="abort" />
      <Provide Name="__errno" />
      <Provide Name="_read" />
      <Provide Name="isatty" />
      <Provide Name="_isatty" />
      <FileMapping Name="LONG(0xE12FFF1E);" />
    </ExecRegion>
    <ExecRegion Name="ER_RAM_RO" Align="0x10" Options=">RAM AT>LR_%TARGETLOCATION%">
    </ExecRegion>
    <ExecRegion Name="ER_RAM_RW" Align="0x10" Options=">RAM AT>LR_%TARGETLOCATION%">
      <FileMapping Name="*" Options="(rwdata)" />
      <FileMapping Name="*" Options="(.data*)" />
    </ExecRegion>
    <ExecRegion Name=".bss" Align="0x10" Options=">RAM">
      <FileMapping Name="*" Options="(.zidata*)" />
      <FileMapping Name="*" Options="(.bss*)" />
      <FileMapping Name="PROVIDE(__exidx_start = .);" />
      <FileMapping Name="PROVIDE(__exidx_end = .);" />
    </ExecRegion>

    <ExecRegion Name="ER_HEAP_BEGIN" Base="%Heap_Begin%">
      <FileMapping Name="*" Options="(SectionForHeapBegin)" />
    </ExecRegion>
    <!-- everything between heapbegin and heapend will be allocated for a heap -->
    <ExecRegion Name="ER_HEAP_END" Base="%Heap_End%">
      <FileMapping Name="*" Options="(SectionForHeapEnd)" />
    </ExecRegion>

    <ExecRegion Name="ER_CUSTOM_HEAP_BEGIN" Base="%Custom_Heap_Begin%">
      <FileMapping Name="*" Options="(SectionForCustomHeapBegin)" />
    </ExecRegion>
    <!-- everything between heapbegin and heapend will be allocated for the unmanaged SimpleHeap -->
    <ExecRegion Name="ER_CUSTOM_HEAP_END" Base="%Custom_Heap_End%">
      <FileMapping Name="*" Options="(SectionForCustomHeapEnd)" />
    </ExecRegion>

    <ExecRegion Name="ER_STACK_BOTTOM" Base="%Stack_Bottom%">
      <FileMapping Name="*" Options="(SectionForStackBottom)" />
    </ExecRegion>
    <ExecRegion Name="ER_STACK_TOP" Base="%Stack_Bottom + Stack_Size%">
      <FileMapping Name="*" Options="(SectionForStackTop)" />
    </ExecRegion>

    <ExecRegion Name="/DISCARD/">
      <FileMapping Name="*" Options="(.ARM.exidx*)" />
      <FileMapping Name="*" Options="(.ARM.extab*)" />
    </ExecRegion>

    <IfDefined Name="Config_BaseAddress">
      <ExecRegion Name="ER_CONFIG" Options=">LR_CONFIG">
        <FileMapping Name="*" Options="(SectionForConfig)" />
      </ExecRegion>
    </IfDefined>
    <IfDefined Name="Data_BaseAddress">
      <ExecRegion Name="ER_DAT" Options=">LR_DAT">
        <FileMapping Name="*" Options="(tinyclr_metadata)" />
      </ExecRegion>
    </IfDefined>
  </NamedGroup>

  <!-- The following variables are used to simulate the ones autogenerated by RVDS -->
  <GlobalVariable Name="Load$$ER_%TARGETLOCATION%$$Base"          Value="ADDR(ER_%TARGETLOCATION%)"/>
  <GlobalVariable Name="Image$$ER_%TARGETLOCATION%$$Length"       Value="SIZEOF(ER_%TARGETLOCATION%)"/>

  <GlobalVariable Name="Image$$ER_RAM_RO$$Base"                   Value="ADDR(ER_RAM_RO)"/>
  <GlobalVariable Name="Image$$ER_RAM_RO$$Length"                 Value="SIZEOF(ER_RAM_RO)"/>
  <GlobalVariable Name="Load$$ER_RAM_RO$$Base"                    Value="LOADADDR(ER_RAM_RO)"/>

  <GlobalVariable Name="Image$$ER_RAM_RW$$Base"                   Value="ADDR(ER_RAM_RW)"/>
  <GlobalVariable Name="Image$$ER_RAM_RW$$Length"                 Value="SIZEOF(ER_RAM_RW)"/>
  <GlobalVariable Name="Load$$ER_RAM_RW$$Base"                    Value="LOADADDR(ER_RAM_RW)"/>
  <GlobalVariable Name="Image$$ER_RAM_RW$$ZI$$Base"               Value="ADDR(.bss)"/>
  <GlobalVariable Name="Image$$ER_RAM_RW$$ZI$$Length"             Value="SIZEOF(.bss)"/>
  <GlobalVariable Name="__use_no_semihosting_swi"                 Value="0"/>

</ScatterFile>

@ jay if I recall correctly, BSS is un-initialized static data and/or static scoped (Unix & C world) data initialized to zero (NULL). It is READ/WRITE.