Author Topic: implementing ICSP (program Microchip MCU) on a linux SBC  (Read 625 times)

0 Members and 1 Guest are viewing this topic.

Offline JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3523
  • Country: it
implementing ICSP (program Microchip MCU) on a linux SBC
« on: September 05, 2024, 02:22:04 pm »
It's an idea i have been toying with from time to time... I have on my todo list to build a product programmer/tester, the product main feature is CANBus, and it's used also to update the firmware.
Currently the workflow for program/test is program the bootloader, then attach a CAN interface to a PC and download firmware, run test suite, and the aim is to condense everything in one device, press one button and run all tasks.

I had plans to do everything with a suitable microcontroller, however writing the firmware in a way that was easy to extend/adapt has not been easy, instead the PC software was a breeze to write, which made me thinking: what if i designed a board running embedded linux?

I say desgned as it would be a great learning experience:
- designing a board for an ARM9/A5/A7 that is not "impossible" to route and hand assemble (jaycarlson's website comes to mind.)
- getting linux to boot
- optimizing linux so it hopefully starts in seconds, not minutes
- understand what to do in order to not brick the filesystem when the user inevitably remove power without shutting down (because realistically this is going to happen: connect board to power, sit waiting until booted, program one device, remove power and toss away.)

But what i haven't figured out is how to program the bootloader in the device, in other words how to implement ICSP (the target boards have a microchip microcontroller).
I already written in the past the code to implement ICSP in a microcontroller, so it could program a secondary controller on board, it's almost trivial on todays devices, as you can just use SPI and logic level signals.
But i wouldn't know where to start on linux: i think i should write a driver? so i can call - say - the function "ReadDeviceID" and the driver would enable the output, toggle MCRL, clock out the command, disable the output, clock in the response...  But how to do it?

Unfortunately, i can't use an ARM device with a microcontroller coprocessor.. i haven't found one that i could call suitable (I'm probably going to use an ATSAMA9X60, i was also eyeing the ATSAMA75 which would have 4+ canbus, so i could do 4 boards at once instead of 2, but it seems much more difficult to design a PCB)
I also don't want to use a separate controller.. though that would be easy (separate controller that runs ICSP code, control it via UART..) i would prefer to use one single programmable part.

I also don't want to use an existing SBC + Hat/Cape/Whatever, i would instead do everything with a microcontroller, as umpleasant as it is.
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6851
  • Country: fi
    • My home page and email address
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #1 on: September 05, 2024, 04:42:53 pm »
I do something similar with many embedded Linux appliances – devices like routers, TV boxes, et cetera.  Instead of ICSP, I typically add small displays describing operational state to nontechnical users, and buttons to trigger specific things (especially custom stuff on OpenWRT routers).

On embedded boards, from tiny ones like Milk-V Duo and Ox64 to larger ones like Odroids, the GPIO pins and UART/SPI/I²C buses are typically easily accessible, sometimes needing a small Device Tree Overlay to enable (if the default peripheral for those specific pins differs) – a compiled structure describing attached hardware to kernel drivers, read at boot time.  The problem is always timing.  In Linux, userspace has no guarantees of being able to respond within any specific interval, so only pins whose state change or state change acknowledgement may be delayed (for up to several hundred milliseconds in some cases) can really be controlled from userspace.

My preferred solution is to use a microcontroller with native USB to handle the timing, and control the microcontroller via e.g. USB serial.  When there is bulk data to be transferred, I like to use dual USB serial, i.e. two pairs of USB endpoints on the same device, and use one for bulk data and one for control messages.  (That single USB device then provides two TTY ACM character devices, and you can control their access modes and create a suitable symlink in /dev using suitable udev rules.)  For Teensy microcontrollers, the Teensyduino integration to the Arduino environment means that one only needs to select that option from the menus, and the Serial (main USB Serial) and SerialUSB1 (secondary USB Serial) will automatically be available.  The cores even expose whether the corresponding tty device is opened on the host by the object evaluating to True, and to False otherwise.  Teensy 4.0 is way overkill for something like this, but they aren't that expensive, so I like to use them.

Low- and Full-Speed USB 2.0 transfers data in up to 64 byte (of payload) packets, and High-Speed in up to 512 byte packets, so the MCU-Host protocol is best designed around such packets, even when using USB Serial.  Using termios on the host side –– forget libusb, it does not do all error checks, only most of those that occur often enough –– you will end up using tcdrain(devfd) after each command to ensure it is sent to the device, but assuming you do check errors properly (including short reads and writes with read() and write), the end result is extremely robust.  I say this from experience, testing my Teensy communications often for a few gigabytes.  (Teensy 4.x can easily sustain 200+ Mbit/s over USB serial in one direction.)  Using "raw" termios, either from C or Python, works extremely well for me, and the data is explicitly binary, no need to limit yourself to ASCII or text commands and responses.

The main difficulty is designing the Host-MCU communications in an easily maintained manner, without generalizing it too much.

For displays, I like to limit to a small number of "commands", the most used one being a bulk data update of a rectangular region in the display, which often is the whole display, and is something many display controllers like ILI93488, ST7899, et cetera support.  While they also support scrolling operations, I've discovered I don't really need those in practice.  The optimum complexity level for me is when I can replace the display, reflash the microcontroller to match the display controller, and in the host software at most change the display width and height in pixels, but no other functional changes.  In fact, it is the MCU alone that responds to the "identify display" request with display dimensions and approximate frame rate.  It has taken me several years of hobby-level playing with this to end up with this conclusion/opinion, though, so expect others to disagree.  I particularly like asynchronous binary commands, with one byte reserved for ID, so that when the command completes, the MCU will respond with the ID and the status.  I imagine that for ICSP, a synchronous (one query at a time, responded to before next query is worked on) one is more suitable, but I could be wrong there.

That is also why I cannot suggest you a specific ICSP command set: it should be based on your experience, and your view of how it might change in future.  Extending is easy and perfectly okay, but making things behave differently for the same command depending on the interface version is horribly nasty.  Thus, reserving extra parameter or parameters that you specifically initialize to all zeros can be useful for some commands you expect you may need to vary for later hardware.  I warmly recommend the idea of implementing timing and target device specific complexity in the MCU, keeping the host software pretty generic, even though I know very well that host software development is much easier and much faster: it is all about managing complexity, and keeping sensitive/difficult code volume minimal and not diffused among generic code – here, keeping the low-level ICSP stuff in the MCU, letting the host software work on a more abstract level ignoring device-specific details.  I've found that this just makes things work more reliably, without requiring superhuman coding skills.

Apologies for the long wall of text.
 

Offline JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3523
  • Country: it
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #2 on: September 05, 2024, 08:00:24 pm »
Apologies for the long wall of text.

No worries!
So that you know, ICSP is "In Circuit Serial Programming", which is the protocol to program the firmware inside PIC MCUs, in current generation devices it has been simplified so at the hardware level it is just SPI (albeit with MOSI/MISO on the same pin, so tie both and MOSI is tristated when reading data from the device)
as i said, i could of course add a small MCU and have it talk to the SBC via UART (or USB of course) but i'm trying to see if it's feasible to do directly from linux.

Maybe i have a misconception of what a driver does, but in my mind it's a small program that read/write from/to the peripheral and GPIO, but running at a higher "priority", and when required it either returns the function, or calls a callback so the software in userspace can go on
I guess my question would be if i understood it correctly, and what should i do to implement such driver
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6851
  • Country: fi
    • My home page and email address
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #3 on: September 05, 2024, 11:36:34 pm »
So that you know, ICSP is "In Circuit Serial Programming", which is the protocol to program the firmware inside PIC MCUs
I know, and I actually know most of the ICSP commands various models of PICs use, too.  Nevertheless, as I haven't made my own PIC ICSP dongle, I don't know the optimal abstract-command set.  You mentioned you have, so you'd be better able to define that.

Maybe i have a misconception of what a driver does, but in my mind it's a small program that read/write from/to the peripheral and GPIO, but running at a higher "priority", and when required it either returns the function, or calls a callback so the software in userspace can go on
Yes.  In Linux, the timing requirements mean you'd need to create a driver (specifically a character device driver), in the form of a kernel module.

Linux Device Drivers, 3rd Edition describes the implementation and background, although the in-kernel interfaces have changed somewhat.  The idea is that the driver exposes a character device under /dev that an userspace process (with suitable privileges, requirements configured via udev rules as the default is root-only) opens the device as normal, operates on it, the driver implementing the operations.

The userspace-facing interface has several mechanisms, but two are simple and recommended: read()/pread() and write()/pwrite() to read or write data from/to the device, starting at a specific offset or position (default is current file position); and ioctl() messages with a single userspace memory structure as an input/output parameter.  (This obviously suggests using read/pread/write/pwrite for Flash/RAM access, noting the offsets are 64 bit even on 32-bit architectures, and ioctl()s for commands and requests, but again, not having implemented a PIC ICSP dongle myself, I don't presume to claim that is the best option.  In fact, the Linux event device interface –– exactly this kind of a character device, but one that reads/writes only complete struct input_event structures, containing a kernel timestamp, unsigned 16-bit type, unsigned 16-bit code, and signed 32-bit value –– uses reads and writes only, ignoring "file position", and although some idiots tried to move it to an ioctl(), the read/write model has been found best for Human Interface Device messages.  A set of messages that occur at the same time are always separated by a single .type=EV_SYN,.code=SYN_REPORT,.value=0 message.)

It is doable, yes.  I recommend using Bootlin Elixir Cross Referencer for investigating the Linux kernel source tree for specific kernel versions, especially when reading LDD3 (2.6.39.4 tree), as it makes all identifiers links with cross references.



Compare to using a microcontroller with native full-speed (12 Mbit/s) or high-speed (480 Mbit/s) USB, implementing one or more USB Serial (USB CDC) endpoints.  Again, my preference is Teensy 4.0 with Teensyduino (although bare metal is quite possible, as the bootloader is on a separate chip; only means you don't really have access to JTAG/SWD pins on the i.MX RT1062), with TXU0102 (UART) or TXU0304 (SPI) voltage translators, or ISO6721/7721 (UART) or ISO6741/7741 (SPI) isolators, both only needing a couple of 100nF supply bypass capacitors.

You implement a request-response (or command-response) protocol, noting that with high-speed USB, each data packet can have up to 512 bytes of payload data: that much data can appear "atomically" in your receive buffers.  I recommend splitting longer data transfers into smaller packets, so you won't need larger buffers, although Teensy 4.x do have 1Mbyte of RAM (in two 512k parts).

On the host computer, you configure an udev rule to set the proper owner, group, and mode for the device node, and create a symlink (nobody wants to scan which /dev/ttyACMN it might be!), say /dev/usb-icsp-interface.  You write an userspace application in whatever language you want (but I do recommend using termios and not the serial libraries; they're all crappy in my opinion), but note that as explained in man 2 read and man 2 write, each such call can return a smaller value ("short count") than requested, in which case you simply need to retry with the rest of the data.  In Python, you use open(devicepath, "rb", buffering=0) to get raw I/O, so that the object will be of io.RawIOBase class or a derivative; and termios module to set it to raw mode.  It is almost always done by obtaining the current settings, saving them for restoring just before closing the device and copying as the basis of the new settings.  I can show you exact code if you have decided what programming language you'll be using.

In essence, moving the "icsp driver" code from kernel to a separate microcontroller you simplify the programming interface, as you aren't restricted to the kernel interfaces, nor do you need to consider any code running in parallel at all.  You have full control and essentially full isolation.  Depending on your needs, you can use a microcontroller from any number of families, from the cheap WCH CH55x to the NXP i.MX RT1062 used on Teensy 4, including many Microchip ones.

The main difference is that you need to convert from function calls to serialized command structures or requests and their responses passed along a bidirectional pipe, and on the MCU, implement the function from commands/requests received serially via USB.



The Linux userspace API for SPI devices is documented here and in the <linux/spi/spidev.h> header file, both part of the Linux kernel docs.  It is based on 'spidev' character devices, and is rather limited.  In particular, read() and write() are half-duplex, with the other side discarded, although that should be fine for a shared MOSI/MISO/DI/DO pin (which I only realized after writing the above).

If and only if that interface is sufficient and implemented for the SBC you want, you can use the SPI interface from an userspace application.  Obviously, you can also use GPIO pins in conjunction, but with the timing caveat: you must allow for any operation to be delayed by up to several hundred milliseconds when the SBC is otherwise busy.  The same applies to separate transfers over SPI, unless you use the ioctl() interface with one or more struct spi_ioc_transfer structures, in which case they are almost continuous and the chip select is kept asserted for all transfers in the same ioctl command.  Changing the userspace process and I/O priority can shrink that to maybe two dozen milliseconds under load –– less than that when not loaded, of course ––, but then a bug in it causing a busy loop can make the machine nearly unusable; as in terminating that process take a couple of minutes.

Personally, I'd test the SPI interface on the SBC you consider using first, using a microcontroller as the SPI slave.  For bulk data, I like to use the 32 high bits of the Xorshift64* pseudo-random number generator with userspace-specified 64-bit seed (any nonzero 64-bit state is okay); here, one test command would read the current seed and error counter, another set the seed and reset error counter, one send a sequence of data that the MCU would check against Xorshift64*, and another receive a sequence of data (userspace comparing to Xorshift64*).  This is one of the rare generators that passes all BigCrush statistical tests for randomness, which not even Mersenne Twister does. The period is 2⁶⁴-1, which is sufficient.  Plus, it is extremely fast on microcontrollers, consisting of (64-bit) bit shifts and exclusive-ors, with a final 64-bit multiplication (of which only 32 most significant bits are actually used) for mixing.  This is what I used for USB Serial testing on Teensy 4.x, getting 200+ Mbit/s sustained indefinitely.

Only after such testing would I trust the SPI and spidev implementation for ICSP use.  (It is not commonly enough used to be automatically trustworthy, in my opinion, you see.)
« Last Edit: September 05, 2024, 11:41:23 pm by Nominal Animal »
 

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 4229
  • Country: gb
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #4 on: September 06, 2024, 12:55:10 am »
Ideed, when, many many years ago, the bdm was implemented in kernel space (on the top of the parallel port, actually an hack) it never worked well, it broke with every kernel source new version, and above all, since nobody uses the parallel port anymore, nobody was interested in the thing anymore, not even if we could have replicated the PC-parallel port with the GPIOs in its place.

Instead, the step next was exactly the approach proposed above: an USB MPU connected to the GNU/Linux SBC. It takes on the load of the commands, passed from an app in userspace (talks bulk-only), and translates them into the time signals to be fed to the target to be debugged and/or programmed.

For the last 10 years, this worked significantly better, and has proven to be much easier to maintain and update, than the previous solution.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 4229
  • Country: gb
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #5 on: September 06, 2024, 01:07:46 am »
It has taken me several years of hobby-level playing with this to end up with this conclusion/opinion, though, so expect others to disagree

As counterintuitive as it still seems to me today from a purist system design perspective: same pragmatic opinion!
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3523
  • Country: it
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #6 on: September 06, 2024, 06:50:29 am »
Selective quotes due to specific questions (I actually read everything, appreciated the input)

You mentioned you have, so you'd be better able to define that.

I guess i would define an API along these lines
Code: [Select]
typedef struct {
  uint16_t deviceId;  //0x0000 is not valid, means no device detected
  uint16_t deviceRevision;
} device_info_t
typedef struct {
  uint32_t address;
  uint8_t data[192*3*8];
} flashPage_t;

void vGetDeviceInfo(device_info_t *deviceInfo);  //Toggles MCRL and send MCHP in the format expected for dsPIC33C, we are only looking for dsPIC33C
bool xEraseFlashPage(uint32_t address);  //Also used for configuration erase, return false on error
bool xWriteFlashPage(flashPage_t page);
bool xWriteConfiguration(flashPage_t page); //manipulates flashpage to keep specific bits set/cleared then call xWriteFlashPage

as i don't need or want to make anything generic, at this time, but specifically tied to the target hardware (one model of MCU)

I understand how everything in unix is expected to be a file that is written/read, i wonder how this apply to, for example, libusb? Or is it already at an higher level of abstraction?

Will look into the book  :-+ I do have a couple of SBCs at home i can make some tests with..

Again, definetly agree with both that separate MCU connected via USB would be essentially a "problem solved" (but at this moment this is more of an abstract excercise of the "can it be done?" type. At that point i could maybe just do everything in MCU and control it through the PC: one bulk endpoint OUT/IN for each CANbus channel, one bulk endpoint out/in for the programmer part, simillar to what i just did for a LIN type of thing to program/test devices. But i would lose another chance to try to actually do something in a linux SBC)
« Last Edit: September 06, 2024, 06:55:25 am by JPortici »
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6851
  • Country: fi
    • My home page and email address
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #7 on: September 06, 2024, 03:53:02 pm »
At systems programming level, I would actually tweak that interface quite a bit.  I'll try to explain exactly why.

    int icsp_configure(uint32_t version, uint32_t options); // Configure interface
    int icsp_get_device_info(struct icsp_device_info *devinfo); // Identify device, Flash page size, etc.
    int icsp_erase_flash_page(uint32_t address, uint32_t options);
    int icsp_write_flash_page(uint32_t address, const unsigned char *data, uint32_t options);
    int icsp_read_flash_page(uint32_t address, unsigned char *data, uint32_t options);

Every one of these returns either success (0) or an error code indicating why the operation failed or cannot be performed right now.  We must not assume that an operation succeeds; we must always be ready to detect an error and report it to the human user.  In Linux syscalls, the failure codes are negated errno values (errnos are all positive; failure codes always negative), as listed in errno-base.h and errno.h.

The icsp_configure(0x0010000, 0) call does not communicate with the attached device at all, only prepares for such communications.  Instead of version, you could use family and the expected MCU family identifier.  The purpose of this function is to make sure both the application and the driver agree as to what they are trying to do; what the protocol is, and so on.  The options parameter is usually a bit mask, and the call should fail if any of the unsupported option bits are set, so that an application using a too-old driver will fail rather than silently fail and produce garbage.  While this function may feel superfluous, it is near impossible to add one afterwards, when you already have written application(s) that do not do such a call.  In particular, if you ever end up doing something similar for another MCU family, this is the function call that differentiates the two.  You might not even have to change the application much to support both families, for example by requiring the user specifies the MCU family with the firmware files to be uploaded.

The icsp_get_device_info(&devinfo) connects to the target MCU, and obtains the device information.  The driver will also fill in the Flash page size field in the structure.  Even if you believe it will stay the same, having it explicitly specified in the structure, and having the application use that instead of a fixed value (or, if using a fixed value, fail if the field value is not the same) adds both robustness and flexibility.  (The background story here is the POSIX virtual memory page size, as virtual memory mappings have a similar fixed granularity as Flash pages.  Initially, it too was thought to be a compile-time constant, until it wasn't.  So, nowadays we need to use sysconf(_SC_PAGESIZE) to obtain it.  However, some systems have both "normal" and "huge pages", some several different possible sizes.  Instead of properly expanding the interfaces, the silly developers gave up and created userspace library, libhugetlbfs, which is now in development limbo.  I can do it myself, but for anybody not familiar with how these came to be, and how to use mmap() and madvise() and perhaps even /proc/meminfo to discover the support, it can be frustratingly difficult –– and only because they didn't correctly design the interface from the get go.  Let's not repeat that mistake.)

I would suggest having Flash address range in the devinfo structure also, for error checking against the firmware files.

The icsp_erase_flash_page(address, options) erases the given Flash page, using the specified options (which would usually be zero).  Again, if any unsupported option bits are set, the call must fail, so that you can safely add new option bits.  Similarly, if address is not page aligned, the call must fail.

Instead of having a separate configuration call to affect subsequent write operations, it is better to keep such options with the read/write operations instead.  As usual, the options should be zero, and if any unsupported options bits are set, the function call should fail.  That way, you can safely add new option bits later on, and not risk silent failures or garbling data when mixing new applications and old drivers or vice versa.

If you consider how firmware data is typically read in an userspace application, you'll realize that having the data in a separate structure/object than the other parameters to the read/write call makes lots of practical sense.  (As a practical example, consider the POSIX sendto() and recvfrom() functions, that allows an application to send data and specify the recipient separately, and to receive data and the sender address.  If the address was in a structure with the data, you'd need to move the data or address part in memory, when the sent/received data was a continuous block in memory.)

As a Linux kernel character device driver, read() and write() do not support extra options flags, so to set the options, you'd use an ioctl() call instead.  Similarly, the icsp_configure() and icsp_get_device_info() would need to be ioctl()s too.  For erasure, I might prefer having it part of the write operation instead of as a separate ioctl(), because I don't immediately see a benefit from keeping them separate.  In my opinion, they should definitely fail if the offset and/or size are not page aligned.



Regardless of whether the ICSP functionality is provided by a custom kernel driver, done via kernel SPI (spidev), or via separate MCU using USB serial, I would wrap it in a C interface similar to the following:

    icsp_t *icsp_open(const char *scheme);
    int  icsp_set_clockfreq(icsp_t *, uint32_t hz);
    int  icsp_close(icsp_t *icsp);
    int  icsp_erase_page(icsp_t *icsp, uint32_t address, uint32_t options);
    int  icsp_write_page(icsp_t *icsp, uint32_t address, const unsigned char *data, uint32_t options);
    int  icsp_read_page(icsp_t *icsp, uint32_t address, unsigned char *data, uint32_t options);

plus some useful accessor functions, like

    uint32_t  icsp_get_clockfreq(const icsp_t *);
    uint32_t  icsp_get_pagesize(const icsp_t *);
    int  icsp_get_flash_regions(const icsp_t *);
    uint32_t  icsp_get_flash_region_address(const icsp_t *, int region);
    uint32_t  icsp_get_flash_region_length(const icsp_t *, int region);
    uint32_t  icsp_get_flash_region_type(const icsp_t *, int region);

I would explicitly allow scheme == NULL for "whatever there is", but plan for supporting specific schemes like usb://, spi://, icspdev://, with the user specifying the exact uri to the application.  icsp_t would be an opaque cookie, and refer to dynamically allocated memory that would be released when icsp_close() is called.  The icsp_open() call performs both configure and get-device-info steps.

This is a lightweight shim layer between the application and the low-level implementation, and essentially allows you to change the low-level implementation without changing the application.  And, if you find you want to support different schemes or low-level implementations, maybe expand to other MCU families, then this interface should allow that transparently, without changes to the application, basically – becoming a proper library then.

I am not 100% certain that I'd end up with these particular function prototypes, though; I have not done this for PIC microcontrollers, and I've found that creating a fully-featured testcase usually reveals details I haven't thought of beforehand.  So, this particular application interface hasn't been properly designed for this use case, and is just my best guess for what it would be, after I tested the thing in practice.  I suspect I would have two additional functions, one usable before opening to scan for possible devices, and the other to send queries or commands to the device, optionally receiving a response.
 

Offline JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3523
  • Country: it
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #8 on: September 06, 2024, 06:20:16 pm »
thank you for putting the time into it. truly appreciated  :-+
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4306
  • Country: us
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #9 on: September 27, 2024, 07:07:37 am »
Quote
I already written in the past the code to implement ICSP in a microcontroller, so it could program a secondary controller on board, it's almost trivial on todays devices, as you can just use SPI and logic level signals.
But i wouldn't know where to start on linux
Why do you think this would be difficult?  Assuming some sort of access to some sort of GPIO pins on the linux system, you can just bit-bang SPI.  ISP/SPI has a maximum speed, but AFAIK, no minimum speed.

The early hobbyist AVR programmers worked by bit-banging the modem control signals on a COM port, and I think avrdude still supports that using the signals at the far end of an FT232R USB/Serial converter (though I'm not sure whether it uses the standard COM driver, or FTDI's driver.)
 

Offline JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3523
  • Country: it
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #10 on: September 27, 2024, 07:36:45 am »
Why do you think this would be difficult?
difficult part would be doing it in linux, i guess.
I currently shelved the project after a couple of nights of frustating attempt to get things going with a beaglebone black i had laying around, which for the life of me could not get to work (trying to reproduce any example i found on the internet, using old distros or current one, all failed. It's posponed until i get another SBC, one with better community support.)
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6851
  • Country: fi
    • My home page and email address
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #11 on: September 27, 2024, 09:12:06 am »
BeagleBone Black has an eMMC issue (Kingston MK2704 4GB eMMC) with 6.x kernels that is apparently being investigated.  The eMMC dies a very early death, making the board unusable.  I recommend starting with a Debian image with a 5.10.168-ti-r71 kernel and v2022.04 UBoot for your exact BBB variant, here.

Other than using Discourse, forum.beagleboard.org is quite active.

BBB (TI AM335x "Sitara" SoC) is a valid choice, because of the two PRUs, programmable realtime units, that can handle the actual low-level I/O details, with an userspace program managing the bulk data and control.  The AM335x PRU-ICSS Reference Guide (rev. A, PDF) should be very useful.  For examples, I do recommend you start with the BeagleBoard ones at github.com/beagleboard/am335x_pru_package and Derek Molloy examples from his book Exploring Beaglebone at github.  First, however, do look at the diagrams in the AM335x PRU Linux Application Loader (PDF) to remind you of the structure of the system at hand.

Instead of PRU, you can also use the "raw" SPI on BBB, follow the Element14 SPI example, using Derek Molloy's spidev_test code.

I have never gotten a BeagleBone Black (or any other BB), because they're relatively expensive for what they are, being over a decade old design.  The cheapest I've found is from Mouser, which comes to about 60€ including Finnish VAT 25.5%.  (Right now, I'm ogling at Odroid M2, with a PCIe M.2 2280 SSD.)  In the same price range as BBB would be Odroid M1S, or a Radxa Rock 5C (2GB) plus a 16 GB eMMC module.

For a tiny ICSP SBC, I would consider Radxa Rock Pi S 512MB+8GB+WiFi (Wiki; get the PoE module too if you intend to use it over Ethernet and have PoE capability), or Odroid M1S (Wiki).

Note, however, that my suggestions have a HUGE bias: I focus on hardware capabilities and kernel support versus price.  I have time and the skills needed to put together my own distro if I want or need, but not much money to spend.  I do prefer Debian basis, for a number of reasons; and avoid fork-based "SDK" vendors like a plague (because they get stale faster than wheat rolls).  I need examples to understand how the integrators/designers intended various features to be used/configured, but I can extrapolate from there.  To me, the way e.g. Fuzhou Rockchip Electronics Co., Ltd. has long-term developers actively contributing support into upstream Linux kernels, as opposed to say a certain United Kingdom foundation with millions of users, is much more significant (due to its long-term implications) than the community or community support.
 

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 4229
  • Country: gb
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #12 on: September 27, 2024, 01:20:43 pm »
I'm not sure whether it uses the standard COM driver, or FTDI's driver.

FTDI's driver  :D

The serial part of the kernel has a lot of problems, like the fact that - by default (Vanilla code) - as soon as you open the "/dev/ttyS0" port with e.g. { picocome, minicom, kermit, ... }, there is a driver part of the kernel, related to "ttyS0.open()", that moves the CTS pin, which personally bothers me a lot because it resets my devices.

It can be solved in a couple of minutes by hacking the driver, but it's very annoying as the driver is a mess of lines that are supposed to support EOL modem stuff.

It was my 50 cents just to say that - for me - the extra serial pins are a formidable mess and it is much better not to touch them because otherwise you have to dedicate extra time to them.

On the contrary FTDI USB-serial drivers have the idea of ​​GPIO as "extra pins", which can be used to implement things like jtag and it's not even too bad to work with them.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3246
  • Country: ca
Re: implementing ICSP (program Microchip MCU) on a linux SBC
« Reply #13 on: October 02, 2024, 03:42:14 am »
Linux driver is just the other end of the file. You write to a file, the driver gets called and does whatever you want it to do. Driver can write to hardware registers or serve interrupts. These registers and interrupts must be provided by some sort of device. Usually, such devices use one of the system buses, such as PCIe or USB, but there could be other ways of interfacing CPU. But at any rate you need some sort of hardware to toggle pins for you.

RPi has bunch of pins, which can do UART, SPI etc. They should already have drivers. I've never used them, but it must be very easy as there are hoards of housewives using RPi. I don't know how good these drivers are. Can they produce metered delays, for example? How fast is the feedback loop - i.e. what is the delay between writing an SPI byte and receiving the response? Are the RPi pins suitable for programming? I don't know. But there are many people who use RPi for these purposes, they must know. There must be RPi groups somewhere.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf