Pull to refresh

Load-Detect для Проверки Качества Пайки

Level of difficultyEasy
Reading time12 min
Views3.9K

Во всех современных микроконтроллерах уже давно как (больше 11 лет) есть подтяжки напряжения на пинах GPIO. Как вы думаете зачем в микроконтроллерах есть функция pull-up/pull-down, если можно просто воспользоваться установкой логического уровня push-pull?

Вы наверное скажете, что подтяжки к питанию нужны для конфигурации пинов шины I2C/1-Wire, нужны для кнопок. Верно! Но это не единственная причина.

Вот типичная ситуация. Вам принесли 6ти слойную электронную плату прямо с производства. Её ещё ни разу не включали. Обычно в таких случаях 90% вероятность, что в PCB есть какие-то аппаратные баги: короткие замыкания на GND, короткие замыкания на VCC или вовсе непропай пинов MCU. Как выявить эти бракованные пины?

Вот тут-то нам и помогут подтяжки к питанию и земле на пинах MCU. Называется эта тема load-detect (LD). У меня уже был текст про load-detect для тестирования силовых высоковольтных H-мостов перед запуском. Вот он: «H‑мост: Load Detect (или как выявлять вандализм)».

Однако load-detect можно реализовать не только на специализированных для этого ASIC(ах), а прямо на пинах микроконтроллера!

LD может выявить короткое замыкание (КЗ) пинов прямо в BGA корпусах, где даже щупом осциллографа к пинам не подлезть! LD это часть прошивки, чисто программный компонент.

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

Обычно LD оформляют как конечный автомат на три состояния. Разработка же конечных автоматов это хорошо формализованный процесс, состоящий из 7 фаз.

Фаза 1. Перечислить возможные состояния конечного автомата

#

Пояснение

Состояние

1

На пине нет подтяжек напряжения

Pull air

2

На пине подтяжка к GND

Pull GND

3

На пине подтяжка к VCC

Pull VCC

Фаза 2. Определить входы конечного автомата

В данном случае у конечного автомата будет только один вход. Это сигнал переполнения таймера. TimeOut. Дело в том что при установке подтяжки напряжения надо подождать окончания переходного процесса и только потом измерять состояние логического уровня на пине микроконтроллера. Обычно это время порядка единиц миллисекунд.

В частности же время переходного процесса на пине не превышает даже 3 миллисекунд. Получается, что можно обновлять состояние диагностики пина с периодом 4ms*3 = 12 ms или с частотой 83 Hz!

Фаза 3. Определить действия конечного автомата

Конечный автомат контроля пайки может делать только следующие легальные действия

#

Пояснение действия

Действие

1

Измерить логический уровень на пине

Read GPIO

2

Установить на пине подтяжку к питанию

Set pull Up

3

Установить на пине подтяжку к заземлению

Set pull Down

4

Отключить на пине какие - либо подтяжки

Set pull air

5

Вычислить решение о состоянии пина на основе накопленных измерений

calculate solution

Тут сразу надо отметить что такое вычисление решения. Вот Look Up таблица принятия решения по измерениям автомата Load detect. Как видно после одного цикла измерений согласно комбинаторному правилу перемножения может быть максимум 8 различных вариантов (2*2*2 =2**3=8). В ячейках таблицы измеренные логические уровни GPIO пина на котором работал LoadDetect.

Open load означает, что пин ни к чему не подключен или, если формально, подключен к резистору с бесконечным сопротивлением одним концом и на GND другим. Еще говорят Z-состояние. И то же самое с VCC.

Фаза 4. Составить таблицу переходов для состояний конечного автомата

Как работать с этой таблицей. Если автомат был с состоянии pull-air и сработало прерывание по переполнению таймера, то автомат переходит в состояние pull-down. Если автомат был с состоянии pull-down и сработало прерывание по переполнению таймера, то автомат переходит в состояние pull-up. Если автомат был с состоянии pull-up и сработало прерывание по переполнению таймера, то автомат переходит в состояние pull-air. Это один цикл измерений. Дальше автомат продолжает работать непрерывно новые и новые циклы.

Фаза 5. Нарисовать граф переходов конечного автомата

На самом деле всё, что я тут написал можно объяснить только одной вот этой картинкой графа конечного автомата. Надо пробежаться по всем трём подтяжкам, в каждой подтяжке прочитать состояние пина и найти в подсказке строчку, которая соответствует этим измерениям и покажет в соседней колонке, что, собственно, подключено к этому конкретному пину со стороны улицы. Easy!

Это классический пример конечного автомата Мили так как выходы генерируются на основе входа и состояния.

Фаза 6. написать программный Си-код

Прежде всего LD надо сконфигурировать. Указать с какими пинами ему надо работать а с какими не надо.

#include "load_detect_config.h"

#ifndef HAS_LOAD_DETECT
#error "Add HAS_LOAD_DETECT"
#endif /*HAS_LOAD_DETECT*/

#include "data_utils.h"
#include "gpio_drv.h"
#include "log.h"


const LoadDetectPinConfig_t LoadDetectPinConfig[] = {
    {.num = 1, .pin_num = 1, .pad={.port=0, .pin=8}, .valid=true,},
    {.num = 1, .pin_num = 2, .pad={.port=0, .pin=16}, .valid=true,},
   ....
    {.num = 1, .pin_num = 22, .pad={.port=1, .pin=12}, .valid=true,},

};

LoadDetectPinInfo_t LoadDetectPinInstance[] = {
        {.num = 1, .pin_num = 1,  .valid=true,},
        {.num = 1, .pin_num = 2,  .valid=true,},
...
        {.num = 1, .pin_num = 22,  .valid=true,},
};

const LoadDetectConfig_t LoadDetectConfig[] = {
    {.num = 1, .name="MCUgpio", .valid=true,  .gpio_class=GPIO_CLASS_MCU, },
};

LoadDetectHandle_t LoadDetectInstance[] = {
    {.num = 1, .valid=true, },
};

uint32_t load_detect_get_cnt(void) {
    uint32_t cnt = 0;
    uint32_t cnt_conf = ARRAY_SIZE(LoadDetectConfig);
    uint32_t cnt_ints = ARRAY_SIZE(LoadDetectInstance);
    if(cnt_conf == cnt_ints) {
        cnt = cnt_ints;
    }
    return cnt;
}

uint32_t load_detect_get_pin_cnt(void) {
    uint32_t cnt = 0;
    uint32_t cnt_conf = ARRAY_SIZE(LoadDetectPinConfig);
    uint32_t cnt_ints = ARRAY_SIZE(LoadDetectPinInstance);
    if(cnt_conf == cnt_ints) {
        cnt = cnt_ints;
    }else{
    	LOG_ERROR(LOAD_DETECT,"PinConfigMisMatch ConfPins%u!=RamPins%u",cnt_conf,cnt_ints);
    }
    return cnt;
}

Вот API. Как и любой программный компонент его надо проинициализировать load_detect_init() , затем прокручивать load_detect_proc() где-то с супер цикле.

#ifndef LOAD_DETECT_DRIVER_H
#define LOAD_DETECT_DRIVER_H

#include <stdbool.h>
#include <stdint.h>

#include "load_detect_config.h"
#include "load_detect_types.h"

bool load_detect_init(void);
bool load_detect_proc(void);

#endif /* LOAD_DETECT_DRIVER_H  */

А это код самого драйвера load-detect. Как видите все функции тривиальные и помещаются на один экран.

#include "load_detect_drv.h"

#include <stdint.h>

#include "gpio_drv.h"
#include "log.h"
#include "time_utils.h"

LoadDetectHandle_t* LoadDetectGetNode(uint8_t num) {
    LoadDetectHandle_t *LdNode = NULL;
    uint32_t i = 0;
    uint32_t cnt = load_detect_get_cnt();
    for (i = 0; i < cnt; i++) {
        if (num == LoadDetectInstance[i].num) {
            if (LoadDetectInstance[i].valid) {
                LdNode = &LoadDetectInstance[i];
                break;
            }
        }
    }
    return LdNode;
}

const LoadDetectConfig_t* LoadDetectGetConfNode(uint8_t num) {
    const LoadDetectConfig_t *LDConfig = NULL;
    uint32_t i = 0;
    uint32_t cnt = load_detect_get_cnt();
    for (i = 0; i < cnt; i++) {
        if (num == LoadDetectConfig[i].num) {
            if (LoadDetectConfig[i].valid) {
                LDConfig = &LoadDetectConfig[i];
                break;
            }
        }
    }
    return LDConfig;
}

const LoadDetectPinConfig_t* LoadDetectGetPinConfNode(uint8_t pin_num) {
    const LoadDetectPinConfig_t *PinConfig = NULL;
    uint32_t i = 0;
    uint32_t cnt = load_detect_get_pin_cnt();
    for (i = 0; i < cnt; i++) {
        if (pin_num == LoadDetectPinConfig[i].pin_num) {
            if (LoadDetectPinConfig[i].valid) {
                PinConfig = &LoadDetectPinConfig[i];
                break;
            }
        }
    }
    return PinConfig;
}

LoadDetectPinInfo_t* LoadDetectGetPinNode(uint8_t pin_num) {
    LoadDetectPinInfo_t *PinNode = NULL;
    uint32_t i = 0;
    uint32_t pin_cnt = load_detect_get_pin_cnt();
    for (i = 0; i < pin_cnt; i++) {
        if (pin_num == LoadDetectPinInstance[i].pin_num) {
            if (LoadDetectPinInstance[i].valid) {
                PinNode = &LoadDetectPinInstance[i];
                break;
            }
        }
    }
    return PinNode;
}

static bool load_detect_init_pin(const LoadDetectPinConfig_t* const PinConfig,LoadDetectPinInfo_t* const  PinNode) {
    bool res = false;
    if(PinConfig) {
        if(PinNode) {
            uint32_t ok = 0 ;
            LOG_WARNING(LOAD_DETECT, "InitPad: %s In PullAir", GpioPad2Str(PinConfig->pad.byte));
            PinNode->num = PinConfig->num;
            PinNode->valid = PinConfig->valid;
            PinNode->pad = PinConfig->pad;
            PinNode->pin_num = PinConfig->pin_num;
            PinNode->on_off = true;

            PinNode->state = LOAD_DETECT_OUT_UNDEF;
            PinNode->prev_state = LOAD_DETECT_OUT_UNDEF;
            PinNode->llevel_at_pullair = GPIO_LVL_UNDEF;
            PinNode->llevel_at_pulldown = GPIO_LVL_UNDEF;
            PinNode->llevel_at_pullup = GPIO_LVL_UNDEF;

            res = gpio_set_dir(  PinConfig->pad.byte, GPIO_DIR_IN) ;
            if(res) {
                ok++;
            } else {
                LOG_ERROR(LOAD_DETECT, "Pad: %s SetDirIn Err", GpioPad2Str(PinConfig->pad.byte));
            }

            res = gpio_set_pull(  PinConfig->pad.byte, GPIO__PULL_AIR  );
            if(res){
                ok++;
            }else {
                LOG_ERROR(LOAD_DETECT, "Pad: %s SetPullAir Err", GpioPad2Str(PinConfig->pad.byte));
            }

            if(3==ok){
                res = true;
            }else{
                res = false;
            }
        }
    }

    return res;
}

bool load_detect_init_pins(uint8_t num) {
    bool res = false;
    uint32_t pin_cnt = load_detect_get_pin_cnt();
    LOG_WARNING(LOAD_DETECT, "LD%u Init %u Pins",num, pin_cnt);
    uint32_t i;
    uint32_t ok=0;
    for (i = 0; i < pin_cnt; i++) {
        if(num == LoadDetectPinConfig[i].num) {
            res= load_detect_init_pin(&LoadDetectPinConfig[i],&LoadDetectPinInstance[i]);
            if (res) {
                ok++;
                LOG_DEBUG(LOAD_DETECT, "InitPin %s Ok", GpioPad2Str(LoadDetectPinInstance[i].pad.byte));
            } else {
                LOG_ERROR(LOAD_DETECT, "InitPinErr %d", num);
            }
        }
    }
    if(0<ok){
        res = true;
    }
    return res;
}

bool load_detect_init_one(uint8_t num) {
    bool res = false;
    LOG_WARNING(LOAD_DETECT, "Init %d", num);
    const LoadDetectConfig_t *Config = LoadDetectGetConfNode(num);
    if (Config) {
        LoadDetectHandle_t *Node = LoadDetectGetNode(num);
        if (Node) {
            Node->gpio_class = Config->gpio_class;
            Node->init_done = true;
            Node->on_off = true;
            Node->valid = true;
            Node->state =  GPIO__PULL_AIR;
            Node->spin_cnt = 0;
            res = load_detect_init_pins(num);
            if(res){
                LOG_INFO(LOAD_DETECT, "%u InitPinsOk",num);
            }else{
                LOG_ERROR(LOAD_DETECT, "%u InitPinsErr",num);
            }
        } else {
            LOG_ERROR(LOAD_DETECT, "%u NodeErr",num);
        }
    } else {
        LOG_ERROR(LOAD_DETECT, "%u ConfErr",num);
    }
    return res;
}

bool load_detect_init(void) {
    bool res = false;
    log_level_set(LOAD_DETECT, LOG_LEVEL_DEBUG);
    uint32_t cnt = load_detect_get_cnt();
    uint32_t ok = 0;
    LOG_WARNING(LOAD_DETECT, "Init Cnt %d", cnt);

    uint32_t i = 0;
    for (i = 1; i <= cnt; i++) {
        res = load_detect_init_one(i);
        if (res) {
            ok++;
            LOG_INFO(LOAD_DETECT, "LD%u InitOk",i);
        }else{
            LOG_ERROR(LOAD_DETECT, "LD%u InitErr",i);
        }
    }

    if (ok) {
        res = true;
        LOG_INFO(LOAD_DETECT, "Init %u Ok",ok);
    } else {
        res = false;
        LOG_ERROR(LOAD_DETECT, "InitErr");
    }

    log_level_set(LOAD_DETECT, LOG_LEVEL_INFO);
    return res;
}


static bool load_detect_set_mcu_ll(LoadDetectHandle_t *Node, GpioPullMode_t pull_mode) {
    bool res = false;
    uint32_t i = 0;
    uint32_t ok = 0;
    uint32_t cnt = load_detect_get_pin_cnt();
    for (i = 1; i <= cnt; i++) {
        LoadDetectPinInfo_t *PinNode = LoadDetectGetPinNode(i);
        if (PinNode) {
            if (PinNode->num == Node->num) {
                res = gpio_set_pull(PinNode->pad.byte, pull_mode);
                if (res) {
                    ok++;
                }
            }
        }
    }

    res = (ok == cnt) ? true : false;

    return res;
}

static bool load_detect_pin_update(LoadDetectHandle_t *Node,
        LoadDetectPinInfo_t *PinNode, GpioLogicLevel_t logic_level) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "Update: %u %s %s %s" , Node->num,GpioPad2Str(PinNode->pad.byte), GpioPull2Str(Node->state),GpioLevel2Str(logic_level));
    switch (Node->state) {
    case GPIO__PULL_AIR: {
        PinNode->llevel_at_pullair = logic_level;
        res = true;
    }
        break;
    case GPIO__PULL_DOWN: {
        PinNode->llevel_at_pulldown = logic_level;
        res = true;
    }
        break;
    case GPIO__PULL_UP: {
        PinNode->llevel_at_pullup = logic_level;
        res = true;
    }
        break;
    default:
        break;
    }
    return res;
}

static bool load_detect_measure_mcu_ll(LoadDetectHandle_t *Node) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "ProcMeasureMcu:%u", Node->num);
    uint32_t i = 0;
    uint32_t cnt = load_detect_get_pin_cnt();
    for (i = 1; i <= cnt; i++) {
        LoadDetectPinInfo_t *PinNode = LoadDetectGetPinNode(i);
        if (PinNode) {
            if (PinNode->num == Node->num) {
                GpioLogicLevel_t logic_level = GPIO_LVL_UNDEF;
                res = gpio_get_state(PinNode->pad.byte, &logic_level);
                if (res) {
                    res = load_detect_pin_update(Node, PinNode, logic_level);
                }
            }
        }
    }

    return res;
}

static bool load_detect_set_pull_ll(LoadDetectHandle_t *Node, GpioPullMode_t pull_mode) {
    bool res = false;
    switch (Node->gpio_class) {
    case GPIO_CLASS_MCU:
        res = load_detect_set_mcu_ll(Node, pull_mode);
        break;
    case GPIO_CLASS_DW1000:
        res = false;
        break;
    case GPIO_CLASS_DW3000:
        res = false;
        break;
    default:
        LOG_ERROR(LOAD_DETECT, "UndefGPIO");
        break;
    }
    return res;
}

static bool load_detect_measure(LoadDetectHandle_t *Node) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "ProcMeasure:%u", Node->num);
    switch (Node->gpio_class) {
    case GPIO_CLASS_MCU:
        res = load_detect_measure_mcu_ll(Node);
        break;
    case GPIO_CLASS_DW1000:
        res = false;
        break;
    case GPIO_CLASS_DW3000:
        res = false;
        break;
    default:
        LOG_ERROR(LOAD_DETECT, "UndefGPIOclass");
        break;
    }
    return res;
}

static bool load_detect_calc_pin_solution(LoadDetectHandle_t *Node, LoadDetectPinInfo_t* PinNode){
    bool res = false;
    if(Node){
        LOG_DEBUG(LOAD_DETECT, "CalcSolution:%u", Node->num);
        if(PinNode) {
            if(PinNode->num == Node->num){
                switch((uint8_t)PinNode->llevel_at_pullup){
                    case GPIO_LVL_LOW: {
                        PinNode->state = LOAD_DETECT_OUT_SHORT_GND;
                        res = true;
                    }break;
                    case GPIO_LVL_HI: {
                        res = true;

                    }break;
                }

                switch((uint8_t)PinNode->llevel_at_pulldown){
                    case GPIO_LVL_LOW: {
                        res = true;
                    }break;
                    case GPIO_LVL_HI: {
                        PinNode->state = LOAD_DETECT_OUT_SHORT_VCC;
                        res = true;

                    }break;
                }

                if(GPIO_LVL_LOW==PinNode->llevel_at_pulldown) {
                    if(GPIO_LVL_HI==PinNode->llevel_at_pullup){
                        PinNode->state =  LOAD_DETECT_OUT_OPEN;
                        res = true;
                    }
                }

                if(PinNode->prev_state!=PinNode->state){
                    LOG_WARNING(LOAD_DETECT,"Pad %s NewState %s->%s",GpioPad2Str(PinNode->pad.byte),LoadDetectOut2Str(PinNode->prev_state),LoadDetectOut2Str(PinNode->state));
                }
                PinNode->prev_state = PinNode->state;
            }
        }
    }
    return res;
}

static bool load_detect_calc_solution(LoadDetectHandle_t *Node){
    bool res = false;
    uint32_t pin_cnt = load_detect_get_pin_cnt();
    LOG_DEBUG(LOAD_DETECT, "CalcSolution:%u for %u pins", Node->num, pin_cnt);
    Node->spin_cnt++;
    uint32_t i = 0 ;
    uint32_t ok = 0 ;
    for(i=0; i<pin_cnt; i++) {
        res = load_detect_calc_pin_solution(Node,&LoadDetectPinInstance[i]);
        if(res){
            ok++;
        }
    }

    if(pin_cnt==ok){
        res = true;
    }else{
        res = false;
    }
    return res;
}

static bool load_detect_proc_air_ll(LoadDetectHandle_t *const Node) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "ProcAir:%u", Node->num);
    if (ONE_STATE_TIME_OUT_MS < Node->pause_ms) {
        load_detect_measure(Node);
        Node->state = GPIO__PULL_DOWN;
        Node->time_start = time_get_ms();
        LOG_DEBUG(LOAD_DETECT, "SwitchState Air->Down");
        res = load_detect_set_pull_ll(Node, GPIO__PULL_DOWN);
    }

    return res;
}

static bool load_detect_proc_down_ll(LoadDetectHandle_t *Node) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "ProcDown:%u", Node->num);
    if (ONE_STATE_TIME_OUT_MS < Node->pause_ms) {
        load_detect_measure(Node);
        Node->state = GPIO__PULL_UP;
        Node->time_start = time_get_ms();
        LOG_DEBUG(LOAD_DETECT, "SwitchState Down->Up");
        res = load_detect_set_pull_ll(Node, GPIO__PULL_UP);
    }

    return res;
}

static bool load_detect_proc_up_ll(LoadDetectHandle_t *Node) {
    bool res = false;
    LOG_DEBUG(LOAD_DETECT, "ProcUp:%u", Node->num);
    if (ONE_STATE_TIME_OUT_MS < Node->pause_ms) {
        load_detect_measure(Node);
        Node->state = GPIO__PULL_AIR;
        Node->time_start = time_get_ms();
        LOG_DEBUG(LOAD_DETECT, "PullState:Up->Air");
        res = load_detect_set_pull_ll(Node, GPIO__PULL_AIR);

        res=load_detect_calc_solution(Node);
    }

    return res;
}

/*UP->AIR->Down->Up*/
bool load_detect_proc_one(uint8_t num) {
    bool res = false;
    uint32_t up_time = time_get_ms();
    LOG_DEBUG(LOAD_DETECT, "Proc:%u UpTime %u ms", num,up_time);
    LoadDetectHandle_t *Node = LoadDetectGetNode(num);
    if (Node) {
        if (Node->on_off) {
            Node->pause_ms = up_time - Node->time_start;
            LOG_DEBUG(LOAD_DETECT, "Proc Cnt:%u Pause %u ms", num, Node->pause_ms);
            switch (Node->state) {
            case GPIO__PULL_AIR:
                res = load_detect_proc_air_ll(Node);
                break;
            case GPIO__PULL_DOWN:
                res = load_detect_proc_down_ll(Node);
                break;
            case GPIO__PULL_UP:
                res = load_detect_proc_up_ll(Node);
                break;
            default:
                Node->state = GPIO__PULL_AIR;
                res = false;
                break;
            }
        }
    } else {
        LOG_ERROR(LOAD_DETECT, "NodeErr %u",num);
    }
    return res;
}

bool load_detect_proc(void) {
    bool res = false;
    uint8_t ok = 0;
    uint8_t cnt = load_detect_get_cnt();
    LOG_DEBUG(LOAD_DETECT, "Proc Cnt:%u", cnt);
    for (uint32_t i = 1; i <= cnt; i++) {
        res = load_detect_proc_one(i);
        if (res) {
            ok++;
        }
    }

    if (ok) {
        res = true;
    } else {
        res = false;
    }

    return res;
}

Как видите драйвер load-detect разом командует сразу всеми N пинами из конфига, подобно тому как в армии лейтенант командует сразу всем взводом (ровняясь! смирно! Напра-аааа-во !). Поэтому все N пинов переключают свои подтяжки синхронно.

Фаза 7. отладить конечный автомат

Для того чтобы просмотреть отчет работы LD вам в прошивку надо добавить интерфейс командной строки поверх UART-CLI. Что это и зачем есть отдельный текст
https://habr.com/ru/articles/694408/
Иначе без UART-CLI Вы просто никогда не узнаете, где, собственно, обнаружились короткие замыкания. В прошивке отчет LD выглядит как ASCII табличка, где каждому GPIO пину поставлено в соответствие состояние его нагрузки: short GND/VCC или оpen-load.

По-хорошему для верификации микроконтроллерных плат с производства должна быть собрана отдельная прошивка (сборка), которая прокручивает шестерни механизма load-detect. В этой сборке должны быть такие компоненты как TIMER, GPIO, UART, CLI, LD, LED. Эту сборку обычно называют BoardName_IO_Bang. Получается так, что плата как бы изнутри тестирует сама себя.

Лично мне load-detect однажды очень помог найти один чрезвычайно красивый аппаратный баг в первой ревизии одной новой платы. Схемотехники для мультиплексора RS2058 при трассировке взяли для схемотехники распиновку от корпуса MSOP10, а PCB поставили корпус QFN. Пины для MSOP10 и QFN, естественно, не совпадают по номерам. В результате мультиплексор RS2058 не пропускал сигнал и вообще не управлялся.

Вывод

При помощи манипуляции подтяжками напряжений на пинах микроконтроллера и измерений в нужный момент логического уровня на GPIO пине можно запросто определять такие высокоуровневые события как короткое замыкание на GND/VCC или отсутствие нагрузки (подключено бесконечное сопротивление).

Добавляйте в свои прошивки компонент load-detect. Это позволит Вам делать bring-up новых электронных плат легко и эффективно.

Акроним

Расшифровка

GPIO

general-purpose input/output

LD

Load Detect

GND

заземление (Ground)

MCU

MicroController Unit

API

Application programming interface

VCC

Voltage at the Common Collector (Supply voltage )

I2C

Inter-Integrated Circuit

FSM

finite-state machine

PCB

printed circuit board

КЗ

Короткое замыкание

BGA

Ball grid array

ASIC

Application-Specific Integrated Circuit

Links

load detect one pin look up table

H-мост: Load Detect (или как выявлять вандализм)

Почему Нам Нужен UART-Shell? (или Добавьте в Прошивку Гласность)

https://habr.com/ru/articles/681280/

https://en.wikipedia.org/wiki/Mealy_machine

https://habr.com/ru/articles/762142/

Контрольные вопросы:

— Как делать тест PCB на пропай?

-- Что такое конечный автомат Мили?

-- Сколько времени длится переходной процесс установки подтяжки напряжения?

Only registered users can participate in poll. Log in, please.
Вы делаете load-detect для контроля пайки в новых электронных платах?
3.13% да1
96.88% нет31
32 users voted. 7 users abstained.
Only registered users can participate in poll. Log in, please.
Вы делаете отдельные тестировочные прошивки для контроля качества изготовления электронных плат?
42.86% да15
57.14% нет20
35 users voted. 7 users abstained.
Only registered users can participate in poll. Log in, please.
Вы поняли из этого текста механизм работы load-detect?
73.68% да28
26.32% нет10
38 users voted. 4 users abstained.
Tags:
Hubs:
Total votes 9: ↑6 and ↓3+5
Comments36

Articles