Skip to content

Instantly share code, notes, and snippets.

@vbenso
Created June 25, 2023 17:34
Show Gist options
  • Save vbenso/798c2b807821201e59966cae3da13d53 to your computer and use it in GitHub Desktop.
Save vbenso/798c2b807821201e59966cae3da13d53 to your computer and use it in GitHub Desktop.
Instructions for @cederom
Hi cederom,
I've finished the implementation of the RMT driver (at least for writing to an output pin).
Something went wrong on my rebase of the nuttX repo, and I'm going to be traveling next week.
So until I fix this issue with the pull request, you can have access to the driver in the
following branch: https://github.com/vbenso/incubator-nuttx-rmt/tree/for-cederom
I'll include the app that is used to drive the leds as well.
In my board, I'm using GPIO 4 as output pin (#define RMT_OUTPUT_PIN 4) you can change this
to whichever pin suits your case.
I've noticed an issue with wifi, if you use wifi, something the led refreshing can have an hiccup.
In that case, just increase the mem_blocks value inside the rmt_open function. The value can be as
high as 8, in my case (768 leds) mem_blocks=4 fixed all the refreshing glitches.
This is all about the rate at which the buffering IRQ is called.
Bit Period (ns) BitF(Hz) RMT Memblocks Words/IRQ s/Buffer IRQ Rate (Hz)
1250 0,00000125 800000 1 32 0,00004 25.000 FLICKERS WITH WIFI
1250 0,00000125 800000 2 64 0,00008 12.500 FLICKERS WITH WIFI
1250 0,00000125 800000 3 96 0,00012 8.333 FLICKERS WITH WIFI
1250 0,00000125 800000 4 128 0,00016 6.250 SMOOTH
@vbenso
Copy link
Author

vbenso commented Jun 25, 2023

Hi @cederom
You can paste this over apps/examples/ws2812/ws2812_main.c and test with your leds.

/****************************************************************************
 * apps/examples/ws2812/ws2812_main.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define APB_PERIOD (12.5)

#define T0H ((uint16_t)(350 / APB_PERIOD))   // ns
#define T0L ((uint16_t)(900 / APB_PERIOD))   // ns
#define T1H ((uint16_t)(900 / APB_PERIOD))   // ns
#define T1L ((uint16_t)(350 / APB_PERIOD))   // ns
#define RES ((uint16_t)(60000 / APB_PERIOD)) // ns

/****************************************************************************
 * Public Types
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/
struct led_s
  {
    uint8_t g;
    uint8_t r;
    uint8_t b;
  };

  
/****************************************************************************
 * Private Data
 ****************************************************************************/
static int fd;

//static uint32_t* rmt_buffer;
static uint32_t* rmt_buffer;
static uint32_t rmt_buffer_len_in_words;

static uint32_t leds_buffer_len_in_bytes;
static uint8_t* leds_buffer;



/****************************************************************************
 * Private Functions
 ****************************************************************************/

static uint32_t map_byte_to_words(uint8_t byte, uint32_t* dst)
{
  uint32_t ret = 0;
  uint8_t mask = 0x80;
  for(int i = 0; i<8;i++)
    {
      uint8_t bit = byte&mask;
      mask>>=1;
      uint32_t word;
      if(bit)
        {
          word = (T1L<<16) | (0x8000|T1H);
        }
        else
        {
          word = (T0L<<16) | (0x8000|T0H);
        }
      *dst = word;
      dst++;
      ret++;
    }

  return ret;
}

static int map_leds_to_words(struct led_s* leds, uint32_t n_leds, uint32_t *dst, uint32_t dst_len_in_words)
{
  int ret = OK;

  if(!dst ||!leds)
    return -1;

  uint32_t required_words = n_leds*3*8;

  if(required_words>=dst_len_in_words)
  {
    _err("Required %d words, but buffer has only:%d", required_words, dst_len_in_words);
    return -1;
  }
    

  uint32_t dst_offset = 0;
  for(uint32_t led_idx = 0; led_idx < n_leds; led_idx++)
    {
      dst_offset+= map_byte_to_words(leds[led_idx].r, dst+dst_offset);
      dst_offset+= map_byte_to_words(leds[led_idx].g, dst+dst_offset);
      dst_offset+= map_byte_to_words(leds[led_idx].b, dst+dst_offset);
    }

 
  return ret;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * ws2812_main
 ****************************************************************************/

int main(int argc, FAR char *argv[])
{
  ssize_t       result;

  if(argc<2)
    {
      printf("usage: %s channel led_count\n", argv[0]);
      exit(1);
    }

  int ch_idx = atoi(argv[1]);
  if(ch_idx>=8)
  {
    printf("channel must be int the range 0~7\n");
    exit(1);
  }

  uint32_t n_leds = atoi(argv[2]);

  /* Run the display loop */
  char dev_name[50] = {};
  snprintf(dev_name, 50, "/dev/rmt%d", ch_idx);
  fd = open(dev_name, O_WRONLY);
  if (fd < 0)
    {
      fprintf(stderr,
              "ws2812_main: open %s failed: %d\n",
              dev_name,
              errno);
      goto errout_with_dev;
    }
  
  _info("size of (struct led_s) is:%d", sizeof(struct led_s));  

  leds_buffer_len_in_bytes = sizeof(struct led_s)*n_leds;
  leds_buffer = malloc(leds_buffer_len_in_bytes);
  _info("leds:%d - leds_buffer %p len:%d\n", n_leds, leds_buffer, leds_buffer_len_in_bytes);
  if(!leds_buffer)
  {
    _err("failure allocating %d bytes for LED buffer", leds_buffer_len_in_bytes);
    goto errout;
  }

  
  
  rmt_buffer_len_in_words = (leds_buffer_len_in_bytes*8+1);
  rmt_buffer = (uint32_t*)malloc(rmt_buffer_len_in_words*4);
  _info("words:%d - rmt_buffer %p rmt_buffer_len_in_words:%d\n", rmt_buffer_len_in_words, rmt_buffer, rmt_buffer_len_in_words);
  if(!rmt_buffer)
  {
    _err("failure allocating %d words for RMT word buffer", rmt_buffer_len_in_words);
    goto errout;
  }

  int pos = 0;
  int direction = 0;
  while(1)
  {
    int n_loops = 30;
    for (int i = 0; i < n_loops; ++i)
    {
      for(int l = 0; l<n_leds; l++)
      {
        if(!direction)
          leds_buffer[l*sizeof(struct led_s)+0] = i;
        else
          leds_buffer[l*sizeof(struct led_s)+0] = 30-i;

        leds_buffer[l*sizeof(struct led_s)+1] = 0x00;
        leds_buffer[l*sizeof(struct led_s)+2] = 0x00;
        
        if(l == pos)
        {
          leds_buffer[l*sizeof(struct led_s)+0] = 0x00;
          leds_buffer[l*sizeof(struct led_s)+1] = 0xFF;
          leds_buffer[l*sizeof(struct led_s)+2] = 0x00;
          
        }
        
      }
      if(pos++>=n_leds)
          pos = 0;
      int ret;
      ret = map_leds_to_words(leds_buffer, n_leds, rmt_buffer, rmt_buffer_len_in_words);
      if(ret)
      {
        _err("error mapping leds to words");
        goto errout;
      }

      ret = write(fd, rmt_buffer, rmt_buffer_len_in_words*4);
      
      if(ret<0)
        {
          _err("error writing to device, %d errno:%d", ret, errno);
          goto errout_with_dev;
        }
      
      /* it's possible to sleep for less time, however things can get unstable */
      
      usleep(50000); 
    }
    direction = !direction;
  }

  free(leds_buffer);
  free(rmt_buffer);;
  close(fd);
  fflush(stdout);
  return OK;

errout_with_dev:
  close(fd);

errout:
  fflush(stdout);
  return ERROR;
}

@cederom
Copy link

cederom commented Jun 26, 2023

TANK U @vbenso !! I am on a trip too right now.. will verify and let you know.. thanks again!! :-)

@cederom
Copy link

cederom commented Jun 30, 2023

Thanks @vbenso I also came back from my trip :-)

I cannot see the RMT driver in peripheral selection neither in for-cederom nor ws2812-rmt-driver branch :-(

Al so in apache/nuttx#6992 I can see lots of unaossociated files change :-P

I will finish my qencoder driver in the first place as its the priority right now then help you out with the RMT driver :-)

Thank you for your time and work!! :-)

@vbenso
Copy link
Author

vbenso commented Jul 13, 2023

All those bad references are from an ill made rebase. That PR will be fixed, and everything will look as expected soon. Sorry for the delay. Lot's of stuff going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment