Pull to refresh

Зачем Программисту Микроконтроллеров Диофантовы Уравнения

Level of difficultyEasy
Reading time4 min
Views12K

Пролог

В математике есть такая тема как Диофантовы уравнения. В самом простом виде выглядят они так.

ax+by=d \qquad \qquad\qquad (1)

Тут одно уравнение и две неизвестные: x,y. При этом a, b, d - это известные константы.

На первый взгляд может показаться как бесполезная вещь для программирования Micro Controller Unit (MCU), но не тут-то было.

Когда программируешь микроконтроллер (MCU), то первое, что приходится делать - это конфигурировать тактирование процессорного ядра. Тактированием заправляет устройство с названием phase-locked loop (PLL/ФАПЧ). Это подсистема SoC(а).

И там надо подобрать три константы (натуральных числа): M, N и F. В микроконтроллере Artery, абстрактно изображая, эта электрическая цепь выглядит вот так. Умножители и делители частот.

По формуле (2)

\frac{(N\frac{F_{quartz}}{M})   }  {F} = F_{sys}    \qquad  \qquad  (2)

Вычисляется системная частота F_sys (sclk). Это уравнение (2) преобразуется в уравнение (3)

F_{quartz}   \frac{N}{M} -F_{sys}F     = 0   \qquad  \qquad  (3)

А уравнение (3), в свою очередь, это самое настоящее Диофантово уравнение! Да.. Тут F_quartz - это частота кварцевого резонатора, который задается схемотехникой электронной платы.
F_sys задается программистом для достижения желаемой производительности прошивки. Остаются целочисленные неизвестные N, M и F.

Далее накладываются ещё ограничения уже производителем микроконтроллера. Для Artery это

Переменная

Минимальное

Максимальное

M

1

15

N

31

500

FR (степени двойки)

1

32

Это ограничение даже прописано комментариями в исходных кодах на Cи, которые бесплатно предоставляет производитель Artery Tech.


/**
  *    config crm pll
  *                        pll_rcs_freq * pll_ns
  *         pll clock = --------------------------------
  *                           pll_ms * pll_fr_n
  *         attemtion:
  *                  31 <= pll_ns <= 500
  *                  1  <= pll_ms <= 15
  *
  *                       pll_rcs_freq
  *         2mhz <=  ---------------------- <= 16mhz
  *                          pll_ms
  *
  *                       pll_rcs_freq * pll_ns
  *         500mhz <=  -------------------------------- <= 1200mhz
  *                               pll_ms
  * @param  clock_source
  *         this parameter can be one of the following values:
  *         - CRM_PLL_SOURCE_HICK
  *         - CRM_PLL_SOURCE_HEXT
  * @param  pll_ns (31~500)
  * @param  pll_ms (1~15)
  * @param  pll_fr
  *         this parameter can be one of the following values:
  *         - CRM_PLL_FR_1
  *         - CRM_PLL_FR_2
  *         - CRM_PLL_FR_4
  *         - CRM_PLL_FR_8
  *         - CRM_PLL_FR_16
  *         - CRM_PLL_FR_32
  * @retval none
  */
void crm_pll_config(crm_pll_clock_source_type clock_source, 
                    uint16_t pll_ns, 
                    uint16_t pll_ms,
                    crm_pll_fr_type pll_fr)
  

Итак, постановка задачи:

Есть электронная плата с Artery MCU на борту. На PCB также припаян кварцевый резонатор c частотой 8 MHz и подключен к микроконтроллеру. Необходимо программно сконфигурировать PLL так чтобы базовая системная частота ядра стала ровно на 100 MHz.

Какими при этом должны быть коэффициенты PLL: N, M и RF?

Решение

По сути, задача свелась к тому чтобы решить Диофантово уравнение (4).

8000000 \frac{N}{M} -100000000F     = 0   \qquad  \qquad  (4)

Можно попробовать прибегнуть к помощи пресловутого Artificial intelligence (AI) на сайте Wolfram Alfa

Однако это не решение в частном виде. На практике же нужно именно численное решение, чтобы проинициализировать им аргументы функции crm_pll_config().

Однако нам повезло. В частности, тут область значений функции (2) достаточно маленькая, поэтому можно найти решение уравнения (3) обыкновенным перебором.

Для этого я написал свой простой численный решатель Диофантова уравнения для PLL прямо на Си.

uint64_t ipow(uint32_t base, uint32_t exponenta) {
	uint64_t ret = 1, i = 0;
    if(0 != exponenta) {
        for(i = 1; i <= exponenta; i++) {
            ret *= base;
        }
    }
    return ret;
}

typedef struct{
    uint32_t ms;
    uint32_t ns;
    uint32_t fr;
}PllArtety_t;

bool pll_calc_artery(uint32_t freq_xtal_hz, 
                     uint32_t freq_sys_hz, 
                     PllArtety_t* const PllArtety) {
    bool res = false;

    LOG_INFO(PLL_CALC, "FreqXtal:%u Hz,FreqSys:%u  Hz", 
             freq_xtal_hz, freq_sys_hz);
    cli_printf("{ [ (  {Xtal:%uHz} /M )*N ]/FR  }= Sys:%u Hz" CRLF, 
               freq_xtal_hz, freq_sys_hz);
    uint32_t solution_cnt = 0;
    if(PllArtety) {
        uint32_t m = 0;
        uint32_t temp_hz = 0;
        uint32_t temp_m_hz = 0;
        uint32_t cur_freq_sys_hz = 0;
        for(m = 1; m <= 15; m++) {
            uint32_t n = 0;
            temp_m_hz = freq_xtal_hz/m;
            if(2000000<=temp_m_hz) {
                if(temp_m_hz<=16000000){
                    for(n = 31; n <= 500; n++) {
                        uint32_t f = 0;
                        for(f = 0; f <= 5; f++) {
                            uint32_t fr = ipow(2, f);
                            cur_freq_sys_hz = ((n * freq_xtal_hz) / (m * fr));
                            if(freq_sys_hz == cur_freq_sys_hz) {
                                temp_hz = freq_xtal_hz * n / m;
                               /*condition from Artery New Clock Config*/
                                if(500000000 <= temp_hz) {
                                    if(temp_hz <= 1200000000) {
                                        solution_cnt++;
                                        cli_printf("%u: MS:%2u,NS:%3u,FR:%2u" CRLF,
                                                   solution_cnt, m, n, fr);
                                        PllArtety->ms = m;
                                        PllArtety->ns = n;
                                        PllArtety->fr = fr;
                                        res = true;
                                    }
                                }
                            }
                        }
                    }
            	}
            }
        }
    }
    if(res) {
        LOG_INFO(PLL_CALC, "SpotPllVals! %u Solutions",solution_cnt);
    } else {
        LOG_ERROR(PLL_CALC, "NoPllVals!");
    }
    return res;
}

Отладка

Вот так, в UART-CLI прошивки я исполнил команду обсчета PLL и получил аж 4 решения на выбор.

Вот эти решения:

1: MS:1, NS:100, FR:8
2: MS:2, NS:200, FR:8
3: MS:3, NS:300, FR:8
4: MS:4, NS:400, FR:8

Далее подставив четвёртое решение в утилиту проверки PLL я увидел, что конфиг валидный!

Успех!

Итоги

Как видите, программирование микроконтроллеров требует математической подготовки. А как Вы хотели?

Благодаря способности решать Диофантовы уравнения, Вы можете переконфигурировать частоту процессора далеко в run-time.

Можно, например, перейти в режим энергосбережения специально понизив частоту ядра. Или увеличить для ускорения расчета какой-то сложной математической процедуры.

Вот так..

Links/URLs

https://www.wolframalpha.com/input?i=8000000x-100000000y%3D0

http://latex.codecogs.com/eqneditor/editor.php

https://ru.wikipedia.org/wiki/Диофантово_уравнение

Only registered users can participate in poll. Log in, please.
Вам приходилось на работе решать Диофантовы уравнения?
34.09% да30
65.91% нет58
88 users voted. 10 users abstained.
Tags:
Hubs:
Total votes 21: ↑20 and ↓1+23
Comments60

Articles