2017年8月20日 星期日

nRF5x : how to control 4 channel pwm in real-time with BLE

This article is to help someone who wants to manipulate 4 channels PWM without lossing BLE.


The PWM example in nRF5x  SDK
(located in nRF5x_SDK_11.0.0\examples\peripheral\pwm_library)
has 2 insufficiency that if you follow SDK example totally:
1.the nRF51 is only able to control two channel PWM  with BLE, or the BLE and PWM would be disorderd.
2.the example is not enable to  start  BLE at the same time

Board : nRF51822 / pca10028

the following example is how to control 4 channel PWM with UART and enable BLE

STEP 0.  initial UART

Please follow this article:
http://gaiger-programming.blogspot.tw/2016/12/nrf51-make-printf-works-well.html?m=0


STEP 1. initial PWM
add this in Keil project setting



























and include these files


add this in main.c

#include "app_pwm.h"

and change your BLE device name

#define DEVICE_NAME                      "PWM Peripheral"


STEP 3. enable PWM timers

one timer can only use 2 channel PWM as the SDK's restriction , besides,  TIMER0 has been used by Softdevice to maintain scheduler.

so you have to enable TIMER1 and TIMER2
if  I use TIMER0, the Softdevice would not work normally.

if the PWM channel number is over 2,  the BLE and PWM would be disorderd

in "nrf_drv_config.h"

you will see about line 60

#define TIMER1_ENABLED 0

and about line 70

#define TIMER2_ENABLED 0

change as

//for pwm
//#define TIMER1_ENABLED 0 
#define TIMER1_ENABLED 1

//#define TIMER2_ENABLED 0
#define TIMER2_ENABLED 1


STEP 4. add PWM initial functions in main.c

about line 100

APP_PWM_INSTANCE(PWM1,1); 
APP_PWM_INSTANCE(PWM2,2);

APP_TIMER_DEF(m_pwm1_timer_id);
#define PWM1_TIMEOUT_INTERVAL     APP_TIMER_TICKS(50, APP_TIMER_PRESCALER)

APP_TIMER_DEF(m_pwm2_timer_id);
#define PWM2_TIMEOUT_INTERVAL     APP_TIMER_TICKS(50, APP_TIMER_PRESCALER)


static uint8_t led_1_value = 0;
static uint8_t led_2_value = 0;
static uint8_t led_3_value = 0;
static uint8_t led_4_value = 0;


static volatile bool ready_flag; 


void pwm_ready_callback(uint32_t pwm_id)    
{
    ready_flag = true;
}


in main function

app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(2000L, LED_1, LED_2);
pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_LOW;
err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
APP_ERROR_CHECK(err_code);
app_pwm_enable(&PWM1);
  
app_pwm_config_t pwm2_cfg =  APP_PWM_DEFAULT_CONFIG_2CH(2000L, LED_3, LED_4);                  
pwm2_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_LOW;
err_code = app_pwm_init(&PWM2,&pwm2_cfg,pwm_ready_callback);
APP_ERROR_CHECK(err_code);
app_pwm_enable(&PWM2);



STEP 5. add app_timer for pwm



static void pwm1_timeout_handler(void * p_context)
{ 
    UNUSED_PARAMETER(p_context);

    ready_flag = false;
 
    while (app_pwm_channel_duty_set(&PWM1, 0, led_1_value) == NRF_ERROR_BUSY);  
    while (app_pwm_channel_duty_set(&PWM1, 1, led_2_value) == NRF_ERROR_BUSY); 
 
}


static void pwm2_timeout_handler(void * p_context)
{ 
    UNUSED_PARAMETER(p_context);

    ready_flag = false;
 
    while (app_pwm_channel_duty_set(&PWM2, 0, led_3_value) == NRF_ERROR_BUSY);  
    while (app_pwm_channel_duty_set(&PWM2, 1, led_4_value) == NRF_ERROR_BUSY); 
 
}

static void timers_init(void)
{
    // Initialize timer module.
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);

    /*agatha : add from here*/
    uint32_t err_code;
    err_code = app_timer_create(&m_pwm1_timer_id, 
                                APP_TIMER_MODE_SINGLE_SHOT, 
                                pwm1_timeout_handler);
    APP_ERROR_CHECK(err_code);
 
    err_code = app_timer_create(&m_pwm2_timer_id, 
                                APP_TIMER_MODE_SINGLE_SHOT, 
                                pwm2_timeout_handler);
    APP_ERROR_CHECK(err_code);
  
}


STEP 6. add command analyzed function in uart_event_handle

here is my command format (in hex)

0x40 0x01 value1 0x02 value2 0x03 value3 0x04 value4 0x23

for example,if I send :

40 01 ff 02 ff 03 ff 04 ff 23

than 4 channel PWM will be 100% (the max value of PWM)


add above uart_event_handle

static uint8_t  data_array[255];
static uint8_t  index;

and this is my uart_event_handle

void uart_event_handle(app_uart_evt_t * p_event)
{
    uint32_t        err_code; 
 
  
    switch (p_event->evt_type)
  {
   case APP_UART_DATA_READY:
    UNUSED_VARIABLE(app_uart_get(&data_array[index]));
    index++; 
   
    printf("uart event\r\n");
    if (((0x40 == data_array[0] && 0x23 == data_array[index-1]))
    { 
     
     for (uint8_t i = 0;i < index;i++)        
         app_uart_put(data_array[i]);
    
     led_1_value = data_array[2]*0x64/0xff;
     led_2_value = data_array[4]*0x64/0xff;
     
     err_code = app_timer_start(m_pwm1_timer_id,PWM1_TIMEOUT_INTERVAL,NULL);
     APP_ERROR_CHECK(err_code);
     
     led_3_value = data_array[6]*0x64/0xff;
     led_4_value = data_array[8]*0x64/0xff;
     
     err_code = app_timer_start(m_pwm2_timer_id,PWM2_TIMEOUT_INTERVAL,NULL);
     APP_ERROR_CHECK(err_code);
   
     memset(&data_array,0,sizeof(data_array));       
     index = 0;
    } 
    
    
    break;
   
   case APP_UART_COMMUNICATION_ERROR:
    APP_ERROR_HANDLER(p_event->data.error_communication);
    break;

   case APP_UART_FIFO_ERROR:
     APP_ERROR_HANDLER(p_event->data.error_code);
     break;

   default:
     break;
   
  } 
 
} 


STEP 7. test device BLE advertisement by your smart phone




STEP 8. Test PWM function

open your serial tool ( I use sscom)

first command I send in hex



and the second command



and you can see the different PWM of your LEDs


reference:

https://github.com/electronut/nRF51-RGB-LED-test































沒有留言:

張貼留言