Migrating to the STM32F405ZGT6

Posted on
  • Hi,

    I'm trying to utilize Espruino on the larger STM32F405 package, and having some trouble with getting it to even boot. I've been able to successfully compile and load espruino to an STM32F4DISOVERY on a ubuntu box, and I've also got an Espruino board coming from adafruit to play with directly (I want to see how it does as a nice little protocol tester for some of my devices), but the migration to the larger package for a design already in place is just leaving me puzzled.

    I believe I've changed the basic required information to what's needed, and didn't see anything that was obvious in the target libraries (although I could be overlooking an HSE adjustment there?).
    Main differences:

    1. The larger package.
    2. The board uses a 16Mhz oscillator.
    3. Some things like the SD card have different connections so I've left them undefined for now.

    I've taken a few bits and pieces from the smartwatch board configuration to account for the speed change, but it looks to need some heavy updating, so I don't know if that was the best move.

    T2_STM32F4.py pertinent info

    import pinutils;
    info = {
     'name' : "T2 STM32F4",
     'link' :  [ "http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1577/LN1035/PF252138" ],
     'default_console' : "EV_SERIAL3", # USART3 by default. USART6 goes to the embedded modem
     'default_console_tx' : "B10", # USART3_TX on PB10
     'default_console_rx' : "B11", # USART3_RX on PB11
     'default_console_baudrate' : "115200", # Baudrate default
     'variables' : 5450, [#Leaving](https://forum.espruino.com/search/?q=%23Leaving) it same as the discovery.
     'binary_name' : 'espruino_%v_T2stm32f4.bin',
    };
    chip = {
      'part' : "STM32F405ZGT6",
      'family' : "STM32F4",
      'package' : "LQFP144",
      'hse' : 16000000, # oscillator
      'ram' : 192,
      'flash' : 1024,
      'speed' : 168,
      'usart' : 6,
      'spi' : 3,
      'i2c' : 3,
      'adc' : 3,
      'dac' : 2,
    };
    # left-right, or top-bottom order
    board = {
    };
    devices = {
      'OSC' : { 'pin_1' : 'H0',
                'pin_2' : 'H1' },
      'OSC_RTC' : { 'pin_1' : 'C14',
                    'pin_2' : 'C15' },
      'LED1' : { 'pin' : 'E12' },
      'LED2' : { 'pin' : 'E13' },
      'LED3' : { 'pin' : 'E14' },
      #'LED4' : { 'pin' : 'D15' },
      #'BTN1' : { 'pin' : 'A0' },
      'USB' : { #'pin_otg_pwr' : 'E14',
                'pin_dm' : 'A11',
                'pin_dp' : 'A12',
                'pin_vbus' : 'A9',
                'pin_id' : 'A10', },
      #'SD' :  { 'pin_cs' :  'D2',
      #          'pin_di' :  'B15',
      #          'pin_do' :  'B14',
      #          'pin_clk' :  'B13' }, 
      #'MEMS' :  {  'device' : 'LIS302DL',
      #          'pin_cs' :  'E3',
      #          'pin_int1' :  'E0',
      #          'pin_int2' :  'E1',
      #          'pin_mosi' :  'A7',
      #          'pin_miso' :  'A6',
      #          'pin_sck' :  'A5' },
      #'MIC' :  { 'device' : 'MP45DT02',
      #           'pin_clk' :  'C3',
      #           'pin_dout' :  'B10', },
      #'AUDIO' :  { 'device' : 'CS43L22',
      #             'pin_sda' :  'B9',
      #             'pin_scl' :  'B6',
      #             'pin_mclk' :  'C7',
      #             'pin_sclk' :  'C10',   
      #             'pin_sdin' :  'C12', 
      #             'pin_lrck' :  'A4',
      #             'pin_nrst' :  'D4',   
      #              },
    };
    
    
    board_css = """
    """;
    
    def get_pins():
      pins = pinutils.scan_pin_file([], 'stm32f40x.csv', 6, 9, 10)
      return pinutils.only_from_package(pinutils.fill_gaps_in_pin_list(pins), chip["package"])
    

    Makefile pertinent info

    else ifdef T2_STM32F4
    EMBEDDED=1
    DEFINES += -DUSE_USB_OTG_FS=1
    DEFINES+=-DHSE_VALUE=16000000UL
    BOARD=T2_STM32F4
    STLIB=STM32F40_41xxx
    PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f4/lib/startup_stm32f40_41xxx.o
    OPTIMIZEFLAGS+=-O3
    

    I checked the generated pin information and it appears to be okay. Some oddities are the pin labels like below though... although below it shows the define as pin 27 and 27? PB10/PB11 are pins 69 and 70. I see the LED defines are labeled as INDEX, so I'm guessing I'm just reading this wrong and these are offsets, not the actual pin numbers. I did a sanity check with the discovery board it's similar, but just in case her's the info:

    DEFAULT_CONSOLE_DEVICE              EV_SERIAL3
    DEFAULT_CONSOLE_TX_PIN 26/* B10 */
    DEFAULT_CONSOLE_RX_PIN 27/* B11 */
    DEFAULT_CONSOLE_BAUDRATE 115200
    

    Am I missing something obvious? After a reset I get nothing out of the USB at all. The USB connections are identical to the discovery. I was going to try and remove the 16Mhz oscillator and bump it down to an 8Mhz one just in case but I figured the above should cover that.

  • In platform_config.h, the pin numbers are references into the array in jspininfo.c - so they don't actually refer to pin numbers on the chip. That bit looks ok.

    Does an LED flash when you reset it?

    It could be worth ignoring USB for now and just connecting to B10/B11 by serial. Maybe put a scope on it in case the baud rate is wrong (in which case you'll know it's the oscillator)?

    If it is a speed problem, the oscillator config is actually part of ST's code. I think: targetlibs/stm32f4/lib/stm32f4...c. It would be worth checking in there and seeing if it actually does the right divisors for 16Mhz.

  • HI Gordon, thanks for the response.

    Okay, so after playing with the ST libraries, I still couldn't get it to function. No LED flashing, no nothing.

    I did the drastic thing and just cut the traces, hacking in an 8Mhz crystal. After putting things back to normal I'm happy to say that I see the nice greeting on the terminal.

    Now that I have life I need to figure out what I didn't set to get the higher speed working, as well as start playing with the I/O and writing a library.

  • Ok, great - glad it's working! What's the board you're porting it to?

  • Hi Gordon,

    I'm porting it over to this guy: http://www.janus-rc.com/T2Terminus.html

    I wanted to see how Espruino would work for this device, since currently it's a pretty open platform, but it's certified for network usage.

    I've been away from it for a few days, but back at it now and working on exploring Espruino and writing some modem controlling functions to start with.

    Quick question since I can't seem to find it anywhere. Is there a way to grab data from the console itself or is that not possible? It seems like I need to have console as a dedicated port such as USB, and then do serial.read/serial.writes to the other ports if I want to manipulate information.

  • Ahh - so you want to implement the terminal, but also interpret AT commands?

    You can't access the port the terminal is on, but you can move the terminal to a 'loopback' port where you can send data to it - so for example:

    LoopbackA.setConsole();
    USB.on('data', function(s) {
      if (isAnATCommand) doStuff(s);
      else LoopbackB.write(s);
    });
    LoopbackB.on('data', function(s) {
      USB.write(s);
    });
    
  • Yeah I'd like to have a few options.

    1. A direct cross terminal, which is easily accomplished with the Serialx.on with a write to the oppositng port. I could also put them to a loopback too, I suppose. Right now I've got the xterminal going with USB as my kind of watch port.
    2. What you're suggesting, which is pretty great actually.
    3. A little more autonomous functionality that I could call a send so that higher level functions could be implemented for bigger things such as socket dials.

    Having trouble with #3 right now. Any particular reason this wouldn't work? Right now it's like if I sent anything to the port I can't loop and read or check availability from that port in waiting, it needs to come back to the terminal or I need to utilize setTimeout.

    function Test(inSTR, timeOut) {
      var TO=getTime() + timeOut; //set timeout in seconds
    
      Serial6.println(inSTR);
    
      while (getTime() < TO) {
        if (Serial6.available() > 0) {
          console.log("Success.");
          return 1;
          }
      }
      return -1;
    }
    

    So Test('AT+CREG?',2) simply comes back with a timeout in this case.

    This is probably something silly that I'm not taking into account.

    I've tried implementing other options such as using a Serial6.on to just parse and put to a buffer, but I'm having trouble with what appears to be scoping (my JS is awful, kind of learning as I go).

  • Ahh - the interpreter isn't multi-threaded so it relies on having short functions that finish relatively quickly. When you get data from serial it goes into a Queue, and that queue is handled in the 'idle' loop - so it's expected that what you're doing there won't work.

    The best bet is to use on('data' as you say. If you post up details of what you've got then I can try and see why it's not working?

    As a start, you could try:

    var dataLine = "";
    Serial6.on('data',function(d) {
      dataLine += d;
      var idx = dataLine.indexOf("\n");
      while (idx>=0) {
        var command = dataLine.substr(0,idx);
        soStuffWith(command);
        dataLine = dataLine.substr(idx+1);
        idx = dataLine.indexOf("\n");
      }
    });
    
  • Okay, so I kinda followed what you have there, expounding on what I had started previously. Now I've written a small function to grab the incoming data and pop the information to a buffer that I can check at any time.

    var mdmBuffer = "";       //Main storage buffer
    var mdmURCBuffer = "";    //URC Buffer
    var mdmParsedBuffer = ""; //AT Response buffer
    
    Serial6.on('data', function (data) {
      //This event grabs incoming data and concatenates it into a buffer
      //The buffer is scanned for identifiable modem AT responses and handles URCs.
      //This assumes a regular cleanup of the main buffer in your flow.
      var idx1 = -1;
      var idx2 = -1;
    
      mdmBuffer += data;
      //Scan for common AT responses, need to search for these as 
      //sometimes you can get multiple CRLF and throw off other checks
      if (mdmBuffer.indexOf("#")>=0 ||
          mdmBuffer.indexOf("+")>=0 ||
          mdmBuffer.indexOf("$")>=0 ||
          mdmBuffer.indexOf(">")>=0 ||
          mdmBuffer.indexOf("ERROR")>=0 ||
          mdmBuffer.indexOf("OK")>=0) {
          //Found one of these
    
        if (mdmBuffer === "\r\nOK\r\n") {
          //Straight up OK response, no other return
          mdmParsedBuffer = "OK";
        }
        if (mdmBuffer === "\r\nERROR\r\n") {
          //Straight up ERROR response, no other return
          mdmParsedBuffer = "ERROR";
        }
    
        if (mdmBuffer.indexOf("#")>=0 ||
            mdmBuffer.indexOf("+")>=0 ||
            mdmBuffer.indexOf("$")>=0) {
          //Found one of these
          //Could be a URC or an AT response
    
          //Put these to a buffer we can call upon at runtime or service through 
          //a set interval. If we print these immediately they may not be read/full.
          if (mdmBuffer.indexOf("OK")>=0) {
            //If there's an OK in here, it's not a URC
            mdmParsedBuffer = mdmParseResponse(mdmBuffer);
    
          }
          else mdmURCBuffer = mdmParseResponse(mdmBuffer);
    
        }
      }
    });
    
    function mdmParseResponse(inSTR) {
      //This function parses out the responses to readable format
      //It recognizes non-AT style responses by the \r\n and just returns them.
    
      if (inSTR !== -1 && inSTR.length > 0) {
        rtnSTR= inSTR;
    
        if (inSTR.indexOf('\r\n',0) !== -1) {
          splitList = inSTR.split('\r\n');
          rtnSTR = splitList[1];
        }
    
      }
    return rtnSTR;
    }
    

    and some little helper functions

    function mdmClearBuffer() {
      //Clears the main buffer
      mdmBuffer = "";
    }
    
    function mdmPrintBuffers() {
      //Prints out the main buffers to console
      console.log("mdmBuffer = " + mdmBuffer);
      console.log("mdmURCBuffer = " + mdmURCBuffer);
      console.log("mdmParsedBuffer = " + mdmParsedBuffer);
    }
    
    function mdmSendAT(data) {
      Serial6.println(data);
    }
    
    

    Output

    mdmSendAT('AT+CREG?')
    =undefined
    mdmPrintBuffers()
    mdmBuffer =
    +CREG: 0,2
    OK
    mdmURCBuffer = +CRE
    mdmParsedBuffer = +CREG: 0,2
    =undefined

    So overall it's similar to what I had before, just done more automatic and doesn't require calling a receive/parsing function separately, which is nice.

    Now the question is how do I actually take this further and if it's even possible, as in, is it possible to automate something that I want done. I'm trying to wrap my head around what normally gets done for this and it's getting a little muddy.

    An example would be something simple like reading an I/O and sending an SMS upon a desired state. I feel like I'm going to be creating a bunch of little functions, and a larger function that gets an interval set and passed the desired small function to run, so that it can run the network checking and whatnot when required.

    Hmm, I feel like this should be moved to the projects/JS area so it's not muddying up the General area.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Migrating to the STM32F405ZGT6

Posted by Avatar for CKnight @CKnight

Actions