2018年4月16日 星期一

Nordic nRF52832 DFU介紹

什麼是DFU?就是Device Firmware Update 。Nordic提供nRF Toolbox APP可以在Play商店下載。可以經由over-the-air(OTA)方式來更新程式。
Screenshot_20180222-131341
如何讓你裝置有DFU功能?可以分成三個部份。
1.先修改Bootloader程式,因為有專屬private key
2.燒錄檔.hex轉成.zip
3.利用nRF Toolbox的DFU來更新程式
Bootloader程式,請參考nRF5-SDK-zip 的examples/duf/bootloader_secure_ble的範例。我是用Keil去編譯,出現錯誤,如下。
dfu-1
顯示錯誤訊息如下
#error “Debug public key not valid for production. Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate it"
點選以上網址,根據網頁內容步驟去執行,步驟如下。
1.下載pc-nrfutil 的檔案。
3.安裝pip
4.在Windows系統,新增環境變數內系統變數的Path路徑如下。
C:\Python27\Scripts;C:\Python27\;
5.接下來的指令都是在DOS畫面下執行,請在開始的搜尋檔案處,打上cmd,就會出現如下圖示。
dos
6.Installing from PyPi ,請在DOS畫面輸入以下指令
pip install nrfutil
7.PyInstall,請在DOS畫面輸入以下指令
pip install pyinstaller
8.在DOS畫面,跳到下載pc-nrfutil檔案的pc-nrfutil-master資料夾下,執行以下的指令
pip install -r requirements.txt
python nordicsemi/__main__.py
python setup.py install
pyinstaller nrfutil.spec
9.產生key.pem,請在DOS畫面輸入以下指令,請把key.pem存到pc-nrfutil-master\nordicsemi\dfu\tests下。
nrfutil keys generate key.pem
10.把key.pem轉換成code ,請在DOS畫面輸入以下指令。把code如下圖紅色框框,複蓋到SDK的dfu_public_key.c
nrfutil keys display --key pk --format code key.pem
keypem
11.Keil編譯應該就可以成功了。
12.利用nRFgo Studio軟體把.hex燒錄到板子上,先燒錄SoftDevice,再燒錄Application,最後才燒錄Bootlader。這樣機板就有DFU功能。
13.把.hex轉成.zip檔案,請在DOS畫面輸入以下指令,選用nRF52xxx系列–hw-version 就填52,--sd-req 請參考下圖,--application填寫要燒錄的檔案名稱,--key-file key.pem就是dfu_public_key.c,不同key.pem就無法DFU,這一點要特別注意。app_dfu_package.zip是你要轉出的.zip檔案,名稱可以自訂。
nrfutil pkg generate --hw-version 52 --sd-req 0x9D --application-version 4 --application app.hex --key-file key.pem app_dfu_package.zip
SoftDevice
14.打開手機的nRF Toolbox APP,選擇DFU,把app_dfu_package.zip上傳到手機,選擇藍芽裝置,就可以更新程式了。
PS.  藍芽裝置要先進入Bootloader才可以DFU,藍芽裝置按住按鍵再上電就可以進入Bootloader。
#CSR #CSR8635 #藍芽 #藍芽立體聲喇叭 #電子外包 #PCB LAYOUT #電子設計 #學生專題製作#電路設計 #數位線路 #類比線路 #單晶片程式設計

2018年4月13日 星期五

Nordic nRF52832 Timer程式介紹

#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_drv_timer.h"
#include "bsp.h"
#include "app_error.h"

const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0);

/**
 * @brief Handler for timer events.
 */
void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
{
    static uint32_t i;
    uint32_t led_to_invert = ((i++) % LEDS_NUMBER);

    switch (event_type)
    {
        case NRF_TIMER_EVENT_COMPARE0:
            bsp_board_led_invert(led_to_invert);  
            break;

        default:
            //Do nothing.
            break;
    }
}

/**
 * @brief Function for main application entry.
 */
int main(void)
{
    uint32_t time_ms = 500; //Time(in miliseconds) between consecutive compare events.
    uint32_t time_ticks;
    uint32_t err_code = NRF_SUCCESS;

    //Configure all leds on board.
    bsp_board_leds_init();
 
    //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);
    APP_ERROR_CHECK(err_code);

    time_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);

    nrf_drv_timer_extended_compare(
         &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    nrf_drv_timer_enable(&TIMER_LED);

    while (1)
    {
        __WFI();
    }
}
const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0);設定為Timer0。
void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context);Timer中斷副程式。
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;設定為default config。
nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);Timer初時化設定及中斷副程式。
nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);中斷時間轉為ticks.
nrf_drv_timer_extended_compare( &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
nrf_drv_timer_enable(&TIMER_LED);Timer啟動
此程式每500ms產生中斷,程式會跳到void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)去執行LED控制。




#CSR #CSR8635 #藍芽 #藍芽立體聲喇叭 #電子外包 #PCB LAYOUT #電子設計 #學生專題製作#電路設計

2018年4月11日 星期三

Nordic nRF52832 PWM Library介紹

#include "nrf.h"
#include "app_error.h"
#include "bsp.h"
#include "app_pwm.h"
#include "nrf_delay.h"

APP_PWM_INSTANCE(PWM0,0); // Create the instance "PWM0" using TIMER0.
void pwm_ready_callback(uint32_t pwm_id)    // PWM callback function
{
    ready_flag = true;
}
int main(void)
{
 ret_code_t err_code;
 
 /* 2-channel PWM, 1000Hz, output on p0.17 & p0.18 */
        app_pwm_config_t pwm0_cfg = APP_PWM_DEFAULT_CONFIG_2CH(1000L, 17, 18);
 
 /* Switch the polarity of the second channel. */
 pwm0_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_HIGH;
 pwm0_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_LOW;
 
 app_pwm_init(&PWM0,&pwm0_cfg,pwm_ready_callback);
 app_pwm_enable(&PWM0);
 
 ready_flag = false;
 while (app_pwm_channel_duty_set(&PWM0, 0, 0) == NRF_ERROR_BUSY);
 while (!ready_flag);
 ready_flag = false;
 while (app_pwm_channel_duty_set(&PWM0, 1, 0) == NRF_ERROR_BUSY);
 while (!ready_flag);
 
 uint32_t value;
        while (true)
        {
            for (uint8_t i = 0; i < 40; ++i)
            {
            value = (i < 20) ? (i * 5) : (100 - (i - 20) * 5);

            ready_flag = false;
            // Set the duty cycle - keep trying until PWM is ready... 
            while (app_pwm_channel_duty_set(&PWM0, 0, value) == NRF_ERROR_BUSY);

            // ... or wait for callback. 
            while (!ready_flag);
            APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM0, 1, value));
            nrf_delay_ms(25);
            }
        }
}
函式APP_PWM_INSTANCE(PWM0,0) ;是指PWM0模組時序是TIMER0。
函式APP_PWM_DEFAULT_CONFIG_2CH(1000L, 17, 18);PWM模組有兩個頻道輸出,為channel0為P0.17,channel1為P0.18,頻率為1000Hz。
pwm0_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_HIGH;
channel0 的PWM POLARITY為High。
pwm0_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_LOW;
channel1 的PWM POLARITY為Low。
函式app_pwm_init(&PWM0,&pwm0_cfg,pwm_ready_callback);PWM0初時化設定及設定callback函式。
函式app_pwm_enable(&PWM0);啟動PWM0
函式app_pwm_channel_duty_set(&PWM0, 0, value);channel0 pwm duty設定,value為0~100。

2018年4月9日 星期一

Nordic nRF52832 PWM driver程式介紹

nRF52832 有三個PWM模組,每個模組有4各channels,所以總共有12各channels可以使用,每支GPIO都可以設定為PWM。以下圖是PWM模組的方塊圖。
PWM 方塊圖
Wave Counter有兩種模式,分別為PWM up counter與PWM up and down counter。主要是產生duty-cycle pulses,如下兩圖示。
PWM up counter
PWM up and down counter
Decoder with EasyDMA,分成四種模式Common、Grouped、Single、WaveForm,如下圖示。
Decoder
以下是SDK的PWM範例程式
static void demo5(void)
{
    NRF_LOG_INFO("Demo 5");

    /*
     * This demo, similarly to demo1, plays back a sequence with different
     * values for individual channels. Unlike demo 1, however, it does not use
     * an event handler. Therefore, the PWM peripheral does not use interrupts
     * and the CPU can stay in sleep mode.
     * The LEDs (1-4) blink separately. They are turned on for 125 ms each,
     * in counterclockwise order (looking at the board).
     */

    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
            BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED, // channel 1
            BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
            BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED  // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = 15625,
        .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    m_used |= USED_PWM(0);

    // This array cannot be allocated on stack (hence "static") and it must
    // be in RAM (hence no "const", though its content is not changed).
    static nrf_pwm_values_individual_t /*const*/ seq_values[] =
    {
        { 0x8000,      0,      0,      0 },
        {      0, 0x8000,      0,      0 },
        {      0,      0, 0x8000,      0 },
        {      0,      0,      0, 0x8000 }
    };
    nrf_pwm_sequence_t const seq =
    {
        .values.p_individual = seq_values,
        .length              = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats             = 0,
        .end_delay           = 0
    };

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
}
PWM範例程式參數設定介紹
.output_pins 設定4組PWM channel的輸出PIN。
.base_clock設定基頻,NRF_PWM_CLK_16MHz、NRF_PWM_CLK_8MHz、NRF_PWM_CLK_4MHz、NRF_PWM_CLK_2MHz、NRF_PWM_CLK_1MHz、NRF_PWM_CLK_500kHz、NRF_PWM_CLK_250kHz、NRF_PWM_CLK_125kHz。可設定這8種頻率。
.count_mode設定Wave Counter模式,NRF_PWM_MODE_UP、NRF_PWM_MODE_UP_AND_DOWN。

2018年4月2日 星期一

CSR8635 藍芽立體聲喇叭設計

CSR8635是ROM版的藍芽立體聲 BT4.0模組,如下圖。
CSR8635.jpg
利用這顆模組來設計立體聲藍芽喇叭,這顆模組功能強大,只要設定一些參數,燒錄到EEPROM,就可以動作了。藍芽喇叭的方塊圖,如下。
方塊圖_
做好的機板如下圖。
BTA-01_
藍芽模組的電路圖,如下。
BT-schematic_
CSR8635是利用SPI界面來燒錄設定值,請注意藍芽模組的PIN16(SPI)需要接1V8 HIGH電位,才可以進入SPI模式,不然無法燒錄。
CSR8635類比輸出是差動訊號,經由DRV602轉成類比聲音訊號,再到PAM8403經由喇叭放大出來,如下電路圖。
Audio-SCH_



#CSR #CSR8635 #藍芽 #藍芽立體聲喇叭 #電子外包 #PCB LAYOUT #電子設計 #學生專題製作#電路設計

2016年10月6日 星期四

NUCLEO-F030R8開發板利用STM32CubeMX來設定Timer中斷副程式

利用STM32F030R8的開發板NUCLEO-F030R8來設計Timer中斷副程式,是利用STM32CubeMx的軟體來產生程式Project。

1.打開STM32CubeMX軟體,點選New Project。

2.點選Board Selector,選擇NUCLEO-F0308,按OK鍵。

3.選擇TIM1,Clock Source 選擇Internal Clock。 

4.設定內部Clock,因TIM1使用APB1作為clock source,所以可以確認Clock為48MHz。 

 5.點選Configuration的TIM1。 

6.STM32F030R8的CPU為48MHz,TIM1CLK為48MHz。若要將Counter Clock設定為10KHz,
則Prescaler應該設定為(System Core Clk / 10KHz)-1 = 4799。 
若要產生每一秒中斷一次,頻率也就是1Hz。Update Rate為1Hz,依照公式可得出:
Update Rate = 1Hz = Counter Clock / (ARR+1) = 10KHz / (ARR+1)  。
可算出ARR = 9999 ,並把Prescaler填入4799,Counter Period填入9999。

7.NVIC Settings 選項內,TIM1interrupts Enabled打勾。


8.在Project Settings選擇程式編輯的軟體。這裡是選擇MDK-ARM V5(Keil)來編輯。


9.Project / Generate Code來產生Project Code,打開Project,在main.c內的/* USER CODE BEGIN 2 */後面加上HAL_TIM_Base_Start_IT(&htim1);來啟動TIM1。 


10.在Main.c內,/* USER CODE BEGIN 4 */與/* USER CODE END 4 */之間,
填入void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)函式 ;如下
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
          HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin); 
}
/* USER CODE END 4 */
程式動作是LED每1秒閃爍一次。

STM32CubeMX Timer中斷教學錄影

2016年9月29日 星期四

8x8矩陣LED驅動電路圖與source code

8x8矩陣LED驅動電路設計架構,主要由一顆STM32L031K6 ST MCU來控制,以掃描方式來一顆顆點亮LED,用一顆CR2032鈕扣電池就可以驅動,可以連續動作20個小時,下面圖式是8x8矩陣LED電路圖,電路架構很單純,就是一般的GPIO來控制矩陣LED。

接下來就是介紹8x8矩陣LED驅動的Source code介紹。

void All_ROW_H(void)
{
LED_ROW_A_H();
LED_ROW_B_H();
LED_ROW_C_H();
LED_ROW_D_H();
LED_ROW_E_H();
LED_ROW_F_H();
LED_ROW_G_H();
LED_ROW_H_H();
}
void All_ROW_L(void)
{
LED_ROW_A_L();
LED_ROW_B_L();
LED_ROW_C_L();
LED_ROW_D_L();
LED_ROW_E_L();
LED_ROW_F_L();
LED_ROW_G_L();
LED_ROW_H_L();
}
void All_COL_H(void)
{
LED_COL_A_H();
LED_COL_B_H();
LED_COL_C_H();
LED_COL_D_H();
LED_COL_E_H();
LED_COL_F_H();
LED_COL_G_H();
LED_COL_H_H();
}
void All_COL_L(void)
{
LED_COL_A_L();
LED_COL_B_L();
LED_COL_C_L();
LED_COL_D_L();
LED_COL_E_L();
LED_COL_F_L();
LED_COL_G_L();
LED_COL_H_L();
}

void Select_Turn_On_LED(uint8_t ROW,uint8_t COL)
{
All_ROW_L();
All_COL_H();
switch(ROW)
{
case 1 :
LED_ROW_A_H();
break;

case 2 :
LED_ROW_B_H();
break;

case 3 :
LED_ROW_C_H();
break;

case 4 :
LED_ROW_D_H();
break;

case 5 :
LED_ROW_E_H();
break;

case 6:
LED_ROW_F_H();
break;

case 7 :
LED_ROW_G_H();
break;

case 8 :
LED_ROW_H_H();
break;

default :
break;
}
switch(COL)
{
case 1 :
LED_COL_A_L();
break;

case 2 :
LED_COL_B_L();
break;

case 3 :
LED_COL_C_L();
break;

case 4 :
LED_COL_D_L();
break;

case 5 :
LED_COL_E_L();
break;

case 6:
LED_COL_F_L();
break;

case 7 :
LED_COL_G_L();
break;

case 8 :
LED_COL_H_L();
break;

default :
break;
}

Wait_delay(100);
}

void Smile_Face(void)
{
Select_Turn_On_LED(1,4);
Select_Turn_On_LED(1,5);
Select_Turn_On_LED(2,1);
Select_Turn_On_LED(2,2);
Select_Turn_On_LED(2,5);
Select_Turn_On_LED(2,6);
Select_Turn_On_LED(3,1);
Select_Turn_On_LED(3,2);
Select_Turn_On_LED(3,6);
Select_Turn_On_LED(3,7);
Select_Turn_On_LED(4,6);
Select_Turn_On_LED(4,7);
Select_Turn_On_LED(5,6);
Select_Turn_On_LED(5,7);
Select_Turn_On_LED(6,1);
Select_Turn_On_LED(6,2);
Select_Turn_On_LED(6,6);
Select_Turn_On_LED(6,7);
Select_Turn_On_LED(7,1);
Select_Turn_On_LED(7,2);
Select_Turn_On_LED(7,5);
Select_Turn_On_LED(7,6);
Select_Turn_On_LED(8,4);
Select_Turn_On_LED(8,5);
}
void main(void)
{
     HAL_Init();

     /* Configure the system clock */
     SystemClock_Config();

     /* Initialize all configured peripherals */
     MX_GPIO_Init();
     MX_TIM2_Init();
     while(1)
    {
           Smile_Face();
     }
}