Deek Robot BL-02 için alternatif firmware

taydin

Timur Aydın
Staff member
Katılım
24 Şubat 2018
Mesajlar
24,143
Elimde birkaç yıl önce aldığım Deek Robot BL-02 voltmetre/ampermetre paneli var. 0 - 100V ve 0 - 10A arası ölçüm yapabildiği ve hata oranının da %1 olduğu belirtiliyor. Ama benim yaptığım ölçümlerde hata oranının daha yüksek olduğunu gözlemledim. Üzerinde Nuvoton N76E003AT20 MCU var ve bu MCU ile ilgili bazı çalışmalar da yapmak istediğimden, bu panel için sıfırdan yeni firmware yazacağım. Bakalım nasıl bir ölçüm performansı elde edebileceğim göreceğiz.

Mevcut devre şeması şöyle

1732381776756.png
 
N76E003AT20 yi programlamak için Nuvoton'un kendi programlayıcılarının kullanılması gerekiyor. Bende NuLink Pro var. Windows için bedava programlayıcı yazılımını Nuvoton sağlıyor. Derleyici olarak Nuvoton Keil veya IAR'ı öne çıkarıyor, ama bunlar çok pahalı ve bedava versiyonlarının de kod üretme üst limiti var. Keil için bu 2 KByte. Bu ücretli IDE'ler yerine ben açık kaynak SDCC yi kullanıyorum.

 
Debug etmek için OpenOCD ile ilgili bazı çalışmalar yapılması gerekiyor, Nuvoton'un sağladığı özel versiyonu düzgün bir şekilde çalıştıramadım. O yüzden yazacağımız firmware'i bir debugger altında çalıştıramayacağız. Bu durumda da firmware'de neler olup bittiğini anlamak için ilk işimiz seri port üzerinden bir terminal oluşturmak olmalı.

Datasheet'i inceleyerek UART0 portunu uygun şekilde yapılandırdım. Baudrate olarak da şimdilik düşük bir hız seçtim (10000 baud). Sonra gerektiğinde arttırırız.

C:
#include "numicro_8051.h"

/***********************************************************************
 ***********************************************************************/
static void console_init(unsigned long baudrate)
{
    /* UART0 TX */
    P06_PUSHPULL_MODE;

    /* UART0 RX */
    P07_INPUT_MODE;

    /* UART0 mode 1, don't check stop bit, enable receive */
    SCON = 0x50;

    /* timer 1, mode 2 */
    TMOD = 0x20;

    /* UART0 double baud rate */
    set_PCON_SMOD;

    /* timer 1 is driven directly by the system clock */
    set_CKCON_T1M;

    /* UART0 clock source is timer 1 */
    clr_T3CON_BRCK;

    /* configure baud rate */
    TH1 = 256 - 1000000 / baudrate;

    /* enable timer 1 */
    set_TCON_TR1;

    /* initiate transmit operations */
    set_SCON_TI;
}

/***********************************************************************
 ***********************************************************************/
void main(void)
{
    console_init(10000);
}
 
Sonra da MCU kütüphanelerin içinde yer alan ve standart C kütüphanesindeki getchar ve putchar fonksiyonlarını UART haberleşmesi şeklinde gerçekleştiren kaynak kodunu projeye dahil ettim.

C:
/*---------------------------------------------------------------------------------------------------------*/
/*                                                                                                         */
/* SPDX-License-Identifier: Apache-2.0                                                                     */
/* Copyright(c) 2023 Nuvoton Technology Corp. All rights reserved.                                         */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------------------------------------*/
/* Program Description:                                                                                    */
/*                                                                                                         */
/* This set of functions includes putchar (), getchar ()                                                   */
/* functionality for the SDCC compiler.                                                                    */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/
#include "numicro_8051.h"


//-----------------------------------------------------------------------------
// putchar
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : character to send to UART
//
// This function outputs a character to the UART.
//-----------------------------------------------------------------------------
#if 0
char putchar (char c)
{
    while (!TI_1);  /* wait until transmitter ready */
    TI_1 = 0;
    SBUF_1 = c;      /* output character */
    return (c);
}
#else
int putchar (int c)  {
  while (!TI);
  TI = 0;
  return (SBUF = c);
}
#endif

//-----------------------------------------------------------------------------
// getchar
//-----------------------------------------------------------------------------
//
// Return Value : character received from UART
// Parameters   : None
//
// This function returns a character from the UART.
//-----------------------------------------------------------------------------
int getchar (void)
{
   char c;

   while (!RI);
   c = SBUF;
   RI = 0;

   return c;
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------
 
Bunu yapınca da artık linux'taki bir terminal emülatör ile MCU ya komut göndermek ve MCU nun da mesaj göndermesi mümkün hale geldi.

C:
#include "numicro_8051.h"

/***********************************************************************
 ***********************************************************************/
static void led_init(void)
{
    P15_PUSHPULL_MODE;

    P15 = 1;
}

/***********************************************************************
 ***********************************************************************/
static void console_init(unsigned long baudrate)
{
    /* UART0 TX */
    P06_PUSHPULL_MODE;

    /* UART0 RX */
    P07_INPUT_MODE;

    /* UART0 mode 1, don't check stop bit, enable receive */
    SCON = 0x50;

    /* timer 1, mode 2 */
    TMOD = 0x20;

    /* UART0 double baud rate */
    set_PCON_SMOD;

    /* timer 1 is driven directly by the system clock */
    set_CKCON_T1M;

    /* UART0 clock source is timer 1 */
    clr_T3CON_BRCK;

    /* configure baud rate */
    TH1 = 256 - 1000000 / baudrate;

    /* enable timer 1 */
    set_TCON_TR1;

    /* initiate transmit operations */
    set_SCON_TI;
}

/***********************************************************************
 ***********************************************************************/
void main(void)
{
    led_init();

    console_init(10000);

    while (1)
    {
        if (RI)
        {
            char c = SBUF;
            RI = 0;

            printf("c = 0x%02X\n", c);

            switch (c)
            {
                default:
                    break;
            }
        }
    }
}
 
Artık debug etme olanağımız olduğuna göre, firmware'in ne yapması gerektiğine odaklanabiliriz. Temelde bu firmware'in sürekli olarak voltaj ve akımı okuyup ekrana yazması lazım. Ama bunun içindeki ADC nin kalitesi ve lineerliği nedir bilmiyoruz. Muhtemelen çok iyi değildir. O yüzden özel bir kalibrasyon prosedürü ile lineerlik hatalarını dengeleme özelliği de eklemek istiyorum. Yani mesela bir GPIO pini şaseye çekilirse, firmware UART terminali kullanarak değişik voltajların verilmesini isteyecek, sonra da o voltaj için ne okuduğunu bir kenara kaydedecek. Sonra da bu tabloyu kullanarak interpolasyon yolu lile lineerlik hatalarını azaltacağız.

İlk önce ADC yi yapılandırdım ve giriş voltajı değişimlerinde ona göre değişen değerler okuduğumu teyit ettim. Bu kod, sürekli olarak ADC yi okuyor ve geçmişe dönük 40 tane değerin ortalamasını tutuyor. Sonra da bu ortalamayı peryodik olarak ekrana yazıyor.

Kod içinde bir de "bandgap reference" ile ilgili şeyler var. Bunlara daha sonra daha ayrıntılı değineceğim, ama bu kodların genel amacı şu: Bu kartta harici, yüksek kaliteli bir voltaj referansı yok. Referans, +5V Vcc oluyor. Bu değer de her ünitede değişebilecektir. Ayrıca ortam sıcaklığına göre ve yaşlanmaya göre de değişecektir. O yüzden bu bandgap reference kodunu kullanarak Vcc değerinin tam olarak belirliyoruz. Bu sayede de arka planda sürekli olarak Vcc referansındaki değişimleri hesaba katmış oluyoruz.

C:
#include "numicro_8051.h"

#define debug_log(format, args...) \
    printf("%s - " format, __func__, ## args)

static float adc_vdd;

/***********************************************************************
 ***********************************************************************/
static void led_init(void)
{
    P15_PUSHPULL_MODE;

    P15 = 1;
}

/***********************************************************************
 ***********************************************************************/
static void console_init(unsigned long baudrate)
{
    /* UART0 TX */
    P06_PUSHPULL_MODE;

    /* UART0 RX */
    P07_INPUT_MODE;

    /* UART0 mode 1, don't check stop bit, enable receive */
    SCON = 0x50;

    /* timer 1, mode 2 */
    TMOD = 0x20;

    /* UART0 double baud rate */
    set_PCON_SMOD;

    /* timer 1 is driven directly by the system clock */
    set_CKCON_T1M;

    /* UART0 clock source is timer 1 */
    clr_T3CON_BRCK;

    /* configure baud rate */
    TH1 = 256 - 1000000 / baudrate;

    /* enable timer 1 */
    set_TCON_TR1;

    /* initiate transmit operations */
    set_SCON_TI;
}

#define RAVG_BUFCNT 40

typedef struct
{
    unsigned int buffer[RAVG_BUFCNT];
    unsigned int index;
    unsigned long sum;

} RAVG;

static RAVG ravg_voltage;
static RAVG ravg_current;

#define SCALE_VOLTAGE 1.0
#define SCALE_CURRENT 1.0


/***********************************************************************
 ***********************************************************************/
static void ravg_run(RAVG* ravg, unsigned int data)
{
    ravg->sum -= ravg->buffer[ravg->index];
    ravg->sum += data;
    ravg->buffer[ravg->index] = data;
    ravg->index = (ravg->index + 1) % RAVG_BUFCNT;
}

/***********************************************************************
 ***********************************************************************/
static float ravg_average(RAVG* ravg)
{
    return (float)ravg->sum / RAVG_BUFCNT;
}

/***********************************************************************
 ***********************************************************************/
static unsigned int adc_read_data(void)
{
    clr_ADCCON0_ADCF;
    set_ADCCON0_ADCS;
    while ((ADCCON0 & SET_BIT7) == 0)
    {
        ;
    }

    unsigned int data = (ADCRH << 4) | (ADCRL & 0x0f);
    return data;
}

/***********************************************************************
 ***********************************************************************/
static unsigned int adc_bandgap_stored(void)
{
    unsigned int data;

    set_IAPEN;
    IAPAL = 0x0C;
    IAPAH = 0x00;
    IAPCN = 0x04;
    set_IAPGO;
    data = IAPFD;
    IAPAL = 0x0d;
    IAPAH = 0x00;
    IAPCN = 0x04;
    set_IAPGO;
    data = (data << 4) | (IAPFD & 0x0f);
    clr_IAPEN;

    debug_log("data = %u\n", data);
    return data;
}

/***********************************************************************
 ***********************************************************************/
static unsigned int adc_bandgap_actual(void)
{
    ENABLE_ADC_BANDGAP;

    adc_read_data();
    adc_read_data();
    adc_read_data();

    unsigned int data = adc_read_data();

    debug_log("data = %u\n", data);
    return data;
}

/***********************************************************************
 ***********************************************************************/
static void adc_read(RAVG* ravg)
{
    unsigned int data = adc_read_data();
    ravg_run(ravg, data);
}

/***********************************************************************
 ***********************************************************************/
static float adc_calc_value(RAVG* ravg, float scale)
{
    float value = ravg_average(ravg) * adc_vdd / 4095.0 * scale;
    return value;
}

/***********************************************************************
 ***********************************************************************/
static void adc_init(void)
{
    unsigned int bg_stored = adc_bandgap_stored();
    unsigned int bg_actual = adc_bandgap_actual();

    float vbg = 3072.0 * bg_stored / 4096.0;
    adc_vdd = 4095.0 / bg_actual * vbg;

    debug_log("bg_stored = %u, bg_actual = %u, vbg = %f mV, adc_vdd = %f mV\n",
              bg_stored, bg_actual, vbg, adc_vdd);
}

/***********************************************************************
 ***********************************************************************/
void main(void)
{
    led_init();

    console_init(10000);

    adc_init();

    unsigned long count = 0;
    while (1)
    {
        if (RI)
        {
            char c = SBUF;
            RI = 0;

            printf("c = 0x%02X\n", c);

            switch (c)
            {
                default:
                    break;
            }
        }

        ENABLE_ADC_AIN6;
        adc_read(&ravg_voltage);

        ENABLE_ADC_AIN5;
        adc_read(&ravg_current);

        ++count;
        if (count == 1000)
        {
            count = 0;
            float value = adc_calc_value(&ravg_voltage, SCALE_VOLTAGE);
            debug_log("voltage = %f mV\n", value);
        }
    }
}
 
Şu anda girişe tam olarak 5.000V veriyorum ve terminalde de kararlı bir değer okuyorum. Bandgap reference kodu sayesinde Vcc yi de 5.198V olarak ölçüyoruz. Aslında bu tam doğru bir değer değil. Gerçek Vcc 5.034V. Ama önemli de değil. Neticede bizim değişimi algılamamız ve ona göre ölçekleme katsayısını ayarlamamız gerekir.

1732227714157.png
 
MCU içerisinde 18 KByte bellek var, mevcut printf desteği ve floating point aritmetik desteği ile 10.2 KByte yer kullanmış oldum. Geri kalan yer rahatlıkla yeter, hatta kalibrasyon katsayılarını saklamak için de bir yer ayırabileceğim.

Keil de beklesin dursun ben 3500 dolar vereyim de 2 KByte kod limitini aşayım diye :temizlik1:
 
Float derinliğini siz belirleyin ve bunu integer olarak dönüştürün. Alttaki linkteki itoa ile tam değeri ile birlikte dönüştürün. Epeyce yer kazanılıyor.

 
Yer kazanmaya ihtiyacım olmayacak. İlgili kütüphanelerin hepsi yüklü şu anda ve yeterince yerim var.
 

Çevrimiçi personel

Forum istatistikleri

Konular
6,955
Mesajlar
118,794
Üyeler
2,824
Son üye
selocan32

Son kaynaklar

Son profil mesajları

hakan8470 wrote on Dede's profile.
1717172721760.png
Dedecim bu gul mu karanfil mi? Gerci ne farkeder onu da anlamam. Gerci bunun anlamini da bilmem :gulus2:
Lyewor_ wrote on hakan8470's profile.
Takip edilmeye başlanmışım :D ❤️
Merhaba elektronik tutsakları...
Lyewor_ wrote on taydin's profile.
Merhabalar. Elektrik laboratuvarınız varsa bunun hakkında bir konunuz var mı acaba? Sizin laboratuvarınızı merak ettim de :)
Lyewor_ wrote on taydin's profile.
Merhabalar forumda yeniyim! Bir sorum olacaktı lcr meterler hakkında. Hem bobini ölçen hemde bobin direnci ölçen bir lcr meter var mı acaba?
Back
Top