Update files on SD Card through Teensy without removing card or reprogramming Teensy

Status
Not open for further replies.

embedded-creations

Well-known member
I'm running an Arduino sketch that uses an SD card for storage, but didn't like having to remove the card and connect it to my computer every time I wanted to update files. The uTasker project has code that can share the SD card over USB-MSD so the Teensy 3.1 looks like a memory stick, but I didn't want to have to reprogram the Teensy twice each time I want to update the SD card and go back to the sketch, or port my Arduino sketches over to uTasker.

I had the idea of creating a small uTasker project (<32kB) that shares the SD card like a memory stick, and putting that program at the end of flash, leaving 224kB left for my Arduino sketch. The work I did already on jumping from one Arduino sketch to another lets me jump from the Arduino sketch to the uTasker project, and go back to the Arduino sketch after a reset.

I released an example Arduino sketch that shows how to jump to the uTasker project, and binaries for two uTasker projects - one with SPI pinouts for the SmartMatrix Shield, and another for the Teensy Audio Adapter Board.
JumpToUsbMsdAppWithOffset - Github

Please note that the uTasker project is licensed software, and in keeping with the terms of their license, the binaries are for non-commercial use. I do plan to open source my changes to the stock uTasker project so you can purchase a license (or get a free non-commercial license) and compile the software yourself. Please post here if you are interested so I can prioritize sharing the changes.
 
Last edited:
Wow, that would be incredible. Having a relatively easy way to add USB-MSD to a data logger is extremely attractive. Color me very interested.

I've been trying to get my own USB-MSD up and running but have not been making a lot of progress. I'd definitely appreciate even a diff of your config.h, what else did you have to change other than setting the MK20 defines, enabling USB-MSD, and SDCARD_SUPPORT? Pinouts didn't seem to be my issue, as I found where to change those as appropriate.
 
To update, I also double checked that the target processor was a cortex-m4.

Did you have to reduce the clock speed to 48MHz?
 
This might be hard to use, but here's a patch file showing all changes I committed to a private git repo starting from uTasker 1.4.7. There's some stuff in there that probably shouldn't be, like .hex files and settings files. Let me know if that helps, or you need something different:

https://db.tt/bpIv8WiG
 
Excellent, I'm digging through it now. The diff looks fine to determine the changes to make. I'll report back and get a little guide of sorts posted once I get it running.

I should also note that I had your compiled version up and running immediately, works perfectly.
 
Success!

Went through your diffs and have it working with the default SPI0 pins. Working on adding your defines in for the different boards, although I might change them up a bit to allow for more flexibility. Will post a quick-start guide detailing the changes I had to make in case someone would like to follow in your footsteps.

I've yet to add in the flash offset. Did you offset the utasker hex, or the arduino hex? I assume you moved the utasker hex flash location and kept the arduino at default?
 
The macros for configuring pins are a little tricky, I couldn't find a way to define SPI_PORT for example and use that for all the config statements. I had to put "D" directly into the _CONFIG_PERIPHERAL macro, SPI_PORT wouldn't work.
 
Oh, I see what you ran into. I ran into it face first as well.

In kinetis.h, line ~9142:

Code:
#define _CONFIG_DRIVE_PORT_OUTPUT_VALUE(ref, pins, value, chars) SIM_SCGC5 |= SIM_SCGC5_PORT##ref; fnConnectGPIO(PORT##ref, pins, chars); GPIO##ref##_PDOR = ((GPIO##ref##_PDOR & ~(pins)) | (value)); GPIO##ref##_PDDR |= (pins); _SIM_PORT_CHANGE

Requires one to pass 'C' or 'D' directly, and doesn't like macro expansions. I'm admittedly clueless when it comes to expansion order of operations, but I'm going to read up on it now.

Seems this is ok:

Code:
_CONFIG_DRIVE_PORT_OUTPUT_VALUE(C, SPI_CS1_0, SPI_CS1_0, (PORT_SRE_FAST | PORT_DSE_HIGH)); \

Where this is not:

Code:
#define TEENSY_3_1_CS1_PORT		C

_CONFIG_DRIVE_PORT_OUTPUT_VALUE(TEENSY_3_1_CS1_PORT, SPI_CS1_0, SPI_CS1_0, (PORT_SRE_FAST | PORT_DSE_HIGH)); \
 
Last edited:
Hi

I can confirm that this 'restriction' is due to the compiler pre-processor not being able to use defines for its macro input.

One 'help' is to use the port reference in the pin name:


_CONFIG_DRIVE_PORT_OUTPUT_VALUE(D, PORTD_BIT3, PORTD_BIT3, (PORT_SRE_FAST | PORT_DSE_HIGH));
is in fact quite easy to handle because if the pin is changed to PORTC_BIT7, for example, it is very obvious that also the port (C) needs to be adjusted but of course working directly with the port bit is not normally the best solution.

Therefore
#define SPI_CS1_0_ON_PTD3 PORTD_BIT3
would automatically give that extra bit of information to avoid 'forgetting' to adjust the port parameter when moving around.
_CONFIG_DRIVE_PORT_OUTPUT_VALUE(D, SPI_CS1_0_ON_PTD3, SPI_CS1_0_ON_PTD3, (PORT_SRE_FAST | PORT_DSE_HIGH));
would be a good compromise.


Note that peripheral pin definitions used in the peripheral configuration, eg.
_CONFIG_PERIPHERAL(A, 6, (PA_6_TPM0_CH0 | PORT_SRE_FAST | PORT_DSE_HIGH)); // TPM0_CH0 on PA.6 (alt. function 2)

are all defined to work like this - as is seen, the name of the peripheral function automatically gives the port and bit references that are to be used.


Final point, note that there are also _CONFIG_DRIVE_PORT_OUTPUT_VALUE_FAST_LOW() and _CONFIG_DRIVE_PORT_OUTPUT_VALUE_FAST_HIGH() macros which make use of the processor's PORTx_GPCLR and PORTx_GPCHR registers to configure quicker, with the restriction that LOW only handles the ports bits 0..15 and HIGH handles only the port bits 16..31, whereas the general (slower) _CONFIG_DRIVE_PORT_OUTPUT_VALUE() handles all 32 bits of a port (either one or multiple pins requiring the same settings).

The complete macro list is available at http://www.utasker.com/forum/index.php?topic=1875.0

Regards

Mark
 
Hi

Note on processor speed configuration. This is in app_hw_kinetis.h - for the Teensy 3.1 it is 72MHz by default, which is set by this block

#define OSC_LOW_GAIN_MODE
#define CRYSTAL_FREQUENCY 16000000 // 16 MHz crystal
#define _EXTERNAL_CLOCK CRYSTAL_FREQUENCY
#define CLOCK_DIV 8 // input must be divided to 2MHz..4MHz range (/1 to /25 possible)
#define CLOCK_MUL 36 // the PLL multiplication factor to achieve operating frequency of 48MHz (x24 to x55 possible)
#define USB_CLOCK_GENERATED_INTERNALLY // use USB clock from internal source rather than external pin


Low gain mode is used when there is little loading on the ocillator (with caps and feedback resistor the high gain mode would be needed to allow the oscillator to start reliably).
This will use the PLL from the 16MHz crystal / 8 = 2MHz, multiplied by 36 = 72MHz.
The USB clock is derived internally from this, rather than being taken from a 48MHz source on the dedicated USB clock input pin.

To modify the CPU frequency you can play around with CLOCK_MUL; the build will fail if anything is out-of-spec so there is no risk.
The only thing to be weary of is that the internal 48MHz USB clock frequency cannot be derived from all speeds (when running in the simulator this will be signalled if not possile) but 48MHz, 72MHz and 96MHz are certainly fine. To run at 96MHz (overclocking) you will however need to remove the error catch in the code which will cause the build to fail - for real products 72MHz would be the limit since higher is not guaranteed [by Freescale] to be reliable.

All other settings are adjusted automatically to match the CPU speed.

Regards

Mark
 
Mark,

Thanks for the advice. Here's my changes so far. I've tested all of the defined pins, although I didn't run through every combination. Have a few more things to change, mostly the offset and watchdog, and then will put this on github in case people would like to try it on their Teensy. Really impressed with this capability - it really makes the Arduino side simpler with regards to not needing a serial downloader/uploader for data.

Starting at line 3757 in app_hw_kinetis.h:

Code:
#elif defined TEENSY_3_1 || defined TWR_K20D72M
        // Configure to suit SD card SPI mode at between 100k and 400k
        // SD Card SPI default pin configuration for Teensy 3.1
		#define TEENSY_3_1_SCK()		_CONFIG_PERIPHERAL(D, 1, PD_1_SPI0_SCK) 									// Teensy 3.1 pin D14/A0
		#define TEENSY_3_1_SCK_ALT1()	_CONFIG_PERIPHERAL(C, 5, PC_5_SPI0_SCK) 									// Teensy 3.1 pin D13/LED

		#define TEENSY_3_1_DOUT()		_CONFIG_PERIPHERAL(C, 6, (PC_6_SPI0_SOUT | PORT_SRE_FAST | PORT_DSE_HIGH))	// Teensy 3.1 pin D11
		#define TEENSY_3_1_DOUT_ALT1()	_CONFIG_PERIPHERAL(D, 2, (PD_2_SPI0_SOUT | PORT_SRE_FAST | PORT_DSE_HIGH))	// Teensy 3.1 pin D7

		#define TEENSY_3_1_DIN()		_CONFIG_PERIPHERAL(C, 7, (PC_7_SPI0_SIN | PORT_PS_UP_ENABLE))				// Teensy 3.1 pin D12
		#define TEENSY_3_1_DIN_ALT1()	_CONFIG_PERIPHERAL(D, 3, (PD_3_SPI0_SIN | PORT_PS_UP_ENABLE))				// Teensy 3.1 pin D8

		#define SPI_CS1_0_ON_PTD4	PORTD_BIT4 		// Teensy 3.1 pin D6 - TODO Change to pin D10 to match teensy 3.1 pinout card default
		#define SPI_CS1_0_ON_PTC3	PORTC_BIT3		// Teensy 3.1 pin D9
		#define SPI_CS1_0_ON_PTD5	PORTD_BIT5		// Teensy 3.1 pin D20/A6
		#define SPI_CS1_0_ON_PTD6	PORTD_BIT6		// Teensy 3.1 pin D21/A7
		#define SPI_CS1_0_ON_PTD1	PORTD_BIT1		// Teensy 3.1 pin D15/A1
		#define SPI_CS1_0_ON_PTC4	PORTC_BIT4		// Teensy 3.1 pin D10

        // *****************************************************************
        // Edit pin assignments below to match your SPI SD card connections!

        // Define Chip Select (CS) Pin for Teensy 3.1
        //	Choose from the above list (eg SPI_CS1_0_ON_PTD4)
        #define SPI_CS1_0 			SPI_CS1_0_ON_PTC3

        // Define SCK, DOUT, DIN Pins for Teensy 3.1
        //	Choose from the above list for each assignment.
        //  NOTE - Modify _CONFIG_DRIVE_PORT_OUTPUT_VALUE(D -> accordingly depending on choice of SPI_CS1_0 port (C or D)
        #define INITIALISE_SPI_SD_INTERFACE() POWER_UP(6, SIM_SCGC6_SPI0); \
        TEENSY_3_1_SCK_ALT1(); \
        TEENSY_3_1_DOUT(); \
        TEENSY_3_1_DIN(); \
        _CONFIG_DRIVE_PORT_OUTPUT_VALUE(C, SPI_CS1_0, SPI_CS1_0, (PORT_SRE_FAST | PORT_DSE_HIGH)); \
        SPI0_CTAR0 = (SPI_CTAR_ASC_6 | SPI_CTAR_FMSZ_8 | SPI_CTAR_CPHA | SPI_CTAR_CPOL | SPI_CTAR_BR_128); SPI0_MCR = (SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF | SPI_MCR_MSTR | SPI_MCR_DCONF_SPI | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS_CS0 | SPI_MCR_PCSIS_CS1 | SPI_MCR_PCSIS_CS2 | SPI_MCR_PCSIS_CS3 | SPI_MCR_PCSIS_CS4 | SPI_MCR_PCSIS_CS5)


        // NOTE - Change the first parameter in each function call to either C or D, depending on the PORTx letter for TEENSY_3_1_CS_X.
        // 	e.g. for TEENSY_3_1_CS_ALT1, keep them C
        //	for TEENSY_3_1_CS_ALT2, change them to D.
        // Also do the same for the first parameter in _CONFIG_DRIVE_PORT_OUTPUT_VALUE() above.
		#define SET_SD_DI_CS_HIGH()  _SETBITS(C, SPI_CS1_0)              // force DI and CS lines high ready for the initialisation sequence
        #define SET_SD_CS_LOW()      _CLEARBITS(C, SPI_CS1_0)            // assert the CS line of the SD card to be read
        #define SET_SD_CS_HIGH()     _SETBITS(C, SPI_CS1_0)              // negate the CS line of the SD card to be read

        // End pin modification section for Teensy 3.1
        // *****************************************************************
 
Just curious, why do you need to change the watchdog? The uTasker application is set to use it, and it gets reset when you go back to running a Teensyduino application, so no harm done.

What offset are you going to use? In case you didn't see it in my changes, if you remove the USE_MAINTENANCE definition the application gets much smaller.

I talked to Mark about posting my uTasker code on GitHub, and I'm pretty sure he doesn't want a full project including code that is copyright uTasker to go on GitHub. A patch file showing changes from a uTasker release would be fine. I don't have enough experience with patch files to know the best option here.
 
Just curious, why do you need to change the watchdog? The uTasker application is set to use it, and it gets reset when you go back to running a Teensyduino application, so no harm done.

Hmm - after thinking on it, the watchdog status doesn't matter to me, but I would prefer to shut the LED off. I'll likely keep the watchdog in - as you said, it can't hurt.

What offset are you going to use? In case you didn't see it in my changes, if you remove the USE_MAINTENANCE definition the application gets much smaller.

I'm not adding anything additional into the code, so I was going to keep the offset you came up with. I have a little dedicated data logger that would benefit greatly from being able to show up as a flash drive for data downloading, and this seems like a really easy to use solution.

I did turn off USE_MAINTENANCE, saw a 50% decrease in the flash memory usage from that change.

I talked to Mark about posting my uTasker code on GitHub, and I'm pretty sure he doesn't want a full project including code that is copyright uTasker to go on GitHub. A patch file showing changes from a uTasker release would be fine. I don't have enough experience with patch files to know the best option here.

Yes, I should have rephrased my statement above - I think it would be helpful for others to perhaps see (or even just describe) the changes needed to config.h to get this working for default pinouts, and perhaps a short snippet to detail how to change the pins. I'm out of my depth when working with a huge code base like the uTasker project; I wouldn't have made much progress myself if it were not for your diff. I'll certainly talk to Mark before putting anything at all up.
 
I'm looking to create something very similar to this with uTasker.

1. Program microcontroller in bootloader mode, either with USB-MSD (using now), or with SD CARD
2. Able to access SD card to change files on 4gB SD card, delete/copy/paste files in Windows file explorer to SD card
3. Go back out of bootloader/SD card mode and go to main Arduino application (revert to UART mode for USB port)

Any ideas on how to accomplish this task? OR is jumping around from bootloader to SD CARD to Arduino Application the only way to do this?
--would get very tedious to keep combining applications every time I want to test a small firmware change..
** can enter bootloader mode by holding a button down while power up, default otherwise always to main Arduino application
*** if unable to combine bootloader mode with SD card, then maybe enter separate SD card program by holding different button on bootup, with default if no button held always back to main arduino program, no need to enter bootload or SD card mode until recycle power honestly.
 
Last edited:
I'm basically doing what you suggest with three separate applications. I don't know of a better way.

You might have already seen this:
https://github.com/pixelmatix/uTaskerBootWithArduinoApp

It does a lot of what you're suggesting. Holding a button down to jump to the SD program from boot should be easy. Just change the application address from 0x8080 for the Arduino app to 0x38080 and use uTasker's jumpToApplication()

Agreed it's tedious to do all that for a simple change, but the srec_cat scripts make it faster. I suggest doing development using a Teensy (or the Teensy Loader Chip) and not using the uTasker bootloader, then changing the linker script and test everything including the bootloader when you're done.
 
Is there a way to just combine the SD card loader and SD card USB_MSD capability into one program?

Then just upload firmware updates via SD card loader instead of teensy loader?

I want to develop with teensy but load with uTasker, and just need to add SD card support outside of the main application somehow.
 
Zachtos

I don't recommend USB-MSD to do firmware loading and SD card (memory stick) on the same interface due to the fact that this would involve having multiple LUMs (two disk drives appear to the PC) whereby one would be an emulated disk and the other would be the SD card.
This is possible but complicated since each LUM would have to be handled totally differently - meaning a special USB-MSD interface to handle it (needing to be developed).

The simplest solution is to configure the USB interface as a USB-MSD to SD card interface and also enable the SD card loader (also works with the SD card). Whilst in the loader mode the SD card will be visible as a memory stick type disk drive so files can be read, written, renamed, deleted etc. Also, new firmware can be copied to the card, with a specified name (or wildcard) at a specified location. The SD loader will search for this file at each reset and update the appliation if it is found (and is different - it can also automatically delete it after the update) and so you effecively have the full-featured USB-MSD on SD card operation, plus a firmware update capabilty thanks to the SD card loader.

This won't involve any complication or new developments since it is just a configuration (just enable SD card and USB-MSD and use the USB-MSD interface task from the TaskerV1.4 project, rather than the emulated task of the serial loader).

Regards

Mark
 
Zachtos

I don't recommend USB-MSD to do firmware loading and SD card (memory stick) on the same interface due to the fact that this would involve having multiple LUMs (two disk drives appear to the PC) whereby one would be an emulated disk and the other would be the SD card.
This is possible but complicated since each LUM would have to be handled totally differently - meaning a special USB-MSD interface to handle it (needing to be developed).

The simplest solution is to configure the USB interface as a USB-MSD to SD card interface and also enable the SD card loader (also works with the SD card). Whilst in the loader mode the SD card will be visible as a memory stick type disk drive so files can be read, written, renamed, deleted etc. Also, new firmware can be copied to the card, with a specified name (or wildcard) at a specified location. The SD loader will search for this file at each reset and update the appliation if it is found (and is different - it can also automatically delete it after the update) and so you effecively have the full-featured USB-MSD on SD card operation, plus a firmware update capabilty thanks to the SD card loader.

This won't involve any complication or new developments since it is just a configuration (just enable SD card and USB-MSD and use the USB-MSD interface task from the TaskerV1.4 project, rather than the emulated task of the serial loader).

Regards

Mark

Excellent, this is exactly what I need. I will work and see if I can get a modified version of the uTaskerSerialLoader project running in VisualBasic10. I will post if I get it working, and update my license to the full cost version from the bootloader only version.
Capture.JPG
Which of these should I select to start configuring in simulator?
 
Status
Not open for further replies.
Back
Top