Avatar for jugglingcats


Member since Feb 2018 • Last active May 2019
  • 6 conversations

Most recent activity

  • in Interfacing
    Avatar for jugglingcats

    Hi, we are interested in supporting wired networking (ethernet) on STM32 using the low-level ST ethernet driver package plus lwip (or similar). For example this might be on a nucleo board with built-in ethernet. Less interested in SPI or other add-on board solutions such as WIZnet.

    Has anyone achieved this, or have any helpful pointers on steps required to enable?

    Many thanks!

  • in Interfacing
    Avatar for jugglingcats

    After a lot of messing about and reading the spec, I don't think it's possible to have the counter DIR bit change according to a simple high/low on another GPIO as required. The encoder mode of the timer is designed for a quadrature encoder.

    I ended up with a little inline C to switch this bit according to GPIO input edge change like this:

    const native=E.compiledC(`
      // void dir(bool)
      unsigned char *addr=(unsigned char *) 0x40010000;
      void dir(bool state) {
        *addr = (*addr & ~0b00010000) | (state * 0b00010000);
    setWatch(native.dir, B7, {repeat: true, edge: "both", irq: true});
  • in Interfacing
    Avatar for jugglingcats

    I'd now like to go further and handle step and direction (with direction being high/low signal on another gpio). Having trouble understanding how to translate the example given below into Espruino equivalent!


  • in Interfacing
    Avatar for jugglingcats

    It just seemed to be easier to use 3 timers in the end because the register offsets are all the same, so I could write some generic code to initialise and read them etc.

    I've put all the code on Github...


  • in Interfacing
    Avatar for jugglingcats

    Nearly done...

  • in Interfacing
    Avatar for jugglingcats

    I got it all working - thanks!

    Here is my code in case it's useful to anyone...

    var DEFS={
      TIM1: {
        base: 0x40010000,
        pin: A8
      TIM3: {
        base: 0x40000400,
        pin: B4
      TIM4: {
        base: 0x40000800,
        pin: B6
    function init(tim) {
      const def=DEFS[tim];
      if ( !def ) {
        throw "Invalid timer!";
      const BASE=def.base;
      // slave mode control register
      var SMCR = BASE+0x0008;
      // event generation register
      var EGR = BASE+0x0014;
      // Capture compare mode register
      var CCMR1 = BASE+0x0018;
      // Capture/compare enable register
      var CCER = BASE+0x0020;
      // counter
      var CNT = BASE+0x0024;
      // prescaler
      var PSC = BASE+0x0028;
      // auto reload register
      var ARR = BASE+0x002C;
      // enable PWM on A8 (TIM1 CH1)
      // CC1E = 0 (Turn channel 1 off)
      poke16(CCER, peek16(CCER) & ~1);
      // CC1S[1:0]=01 (rising edge), IC1F[7:4]=0 (no filter)
      poke16(CCMR1, (peek16(CCMR1) & ~0b11110011) | (0b00000001));
      // CC1P=0, CC1NP=0 (detect rising edge), CC1E[0] = 1 (Turn channel 1 on)
      poke16(CCER, peek16(CCER) & ~(0b1011) | (0b0001));
      // SMS[2:0]=111 (ext clock), TS[6:4]=101 (CH1 as trigger)
      poke16(SMCR, (peek16(SMCR) & ~0b1110111) | 0b1010111);
      // Prescaler to 0 - use every transition
      poke16(PSC, 0);
      // auto-reload with the full range of values
      poke16(ARR, 65535);
      // poke the UG[0] bit to reset the counter and update the prescaler
      poke16(EGR, 1);
      return {
        get: function() {
          return peek16(CNT);
        reset: function() {
          poke16(CNT, 0);
    const TIM1=init("TIM1");
    const TIM4=init("TIM4");
    const TIM3=init("TIM3");
    function update(){
      g.drawString("Steps X: "+TIM1.get(), 2, 2);
      g.drawString("Steps Y: "+TIM4.get(), 2, 20);
      g.drawString("Steps Z: "+TIM3.get(), 2, 38);
    // SPI
    var s = new SPI();
    s.setup({mosi: B15 /* D1 */, sck:B13 /* D0 */});
    var g = require("SH1106").connectSPI(s, B14 /* DC */, B10 /* RST - can leave as undefined */, function() {
      setInterval(update, 100);
    setWatch(function(e) {
    }, BTN, { repeat: true });
  • in Interfacing
    Avatar for jugglingcats

    That's helpful Gordon thanks. Yes I am using the Pico.

    So you think if I pick, say B6 and B7 (TIM4, CH1 and CH2 respectively), I should be able to do the same trick as you did with TIM1, just by looking up the relevant register addresses/offsets?