Объявление

США начнут предпринимать меры экономического и военного характера, если Россия не прекратит нарушать Договор о ракетах средней и меньшей дальности (ДРСМД)

Решение главы Национального Объединения "Новая Россия - Великая Россия" Rommstaina: http://russianew.ru/viewtopic.php?f=120&t=4039

STM32 и DS18B20

Правила форума
Внимание! Любой спам на нашем форуме запрещён!
Коммерческая реклама сайтов, ссылки, спам запрещены. Так же запрещены ссылки на сайты в профилях новичков.
Бан без предупреждений.

STM32 и DS18B20

Сообщение admin » 18 дек 2017, 15:42

Решил подцепить DS18B20 к STM32 по двухпроводной шине. Задача несложная, благо и 1-wire уже не раз ковырял (первый раз лет 15 назад, еще на PIC, цеплял и ключи-идентификаторы и термометр), да и на STM32 тема сейчас избитая, примеров навалом. Прицепил термометр к известной плате STM32 Mini, и, хоть и не люблю копипастить, но все же взял готовую библиотеку от steel_ne из его статьи «Stm32 + 1-wire + DMA» и ее продолжения — мне понравилось это решение. Подправил чуть-чуть на свободный у меня USART3 и, после устранения ВСЕХ моих ошибок )), все заработало, за что автору большое спасибо.
К чему это я? А вот — главной моей ошибкой было то, что цеплял я термометр по двухпроводной схеме. И температура не измерялась, +85 градусов, хоть удавись… У меня термометры уже работают по двухпроводной схеме на AT91SAM7SE512 и раньше на PIC16F84 работали, поэтому вопрос для меня о схеме подключения особо не стоял, в даташиты я на эту тему давно не смотрел и схему автора вышеупомянутой статьи тоже глянул одним глазом. Посмотрел повнимательнее на схему в статье, а там термометр подключен по трем проводам. Подключил и я питание – работает. Проверил на Атмеле – но там несколько тех же DS18B20 на двух проводах стоят и подцеплены резистором 4,7 К к питанию, но правда там к 5 В. Ага, понятно, надо разбираться, ведь работает же оно на 5 вольтах… Вот тут-то и пригодилось свойство альтернативности выводов в STM32.

Полез осциллографом туда и сюда, вижу — разница налицо. На 5-вольтовой шине во время измерения есть просад напряжения шины, но небольшой, где-то до 4,5 В, не менее, измерения идут нормально:

Изображение

А на шине 3,3 В во время измерения, после выдачи команды Convert T ( 0х44) напряжение на шине просаживается до 1,6 — 1,7 Вольта, видимо на меньших напряжениях ток потребляемый побольше, напряжение на шине становится недостаточным и ничего уже не измеряется, читаются показания +85 С:

Изображение

Собственно, для вас это наверняка не откровенность, посмотрел по форумам – вопрос с подключением обсосан не раз. Решения предлагается два – включить по трехпроводной схеме, с питанием, или по двухпроводной схеме, подключая питание к шине на время измерения с помощью ключа. Вот предложенная в даташитах схема:

Изображение

Также ставят полевые транзисторы еще и на выход порта

Изображение

А ведь токи на 1-wire маленькие, тут любой выход потянет.
Вот тут-то и есть мой ответ на вопрос, зачем написал все это. Проблема подключения термометров по двухпроводной схеме, а для меня это очень актуально, ибо я подключаю их несколько (да и не только их, поэтому хотелось бы тянуть двух, а не трехпроводную линию), решается очень просто. Достаточно:
1. Убрать диод из цепи передатчика UART, а для этого нужно выход передатчика программировать как альтернативный open-drain.
(кстати — здесь и далее на некоторых рисунках перепутаны ножки 1 и 3, по даташиту 1 = GND, 3 = Vdd)

Изображение

2. На время измерения переводить этот пин в режим GPIO push-pull c подачей на него высокого уровня.
Вот что увидим:

Изображение

В кодах это не очень напряжно, просто добавить несколько строк — например в вышеупомянутые коды (если автор не против):
В библиотечном файле onewire.c изменить строки инициализации ножек передатчика USARTов c:
Код: выделить все
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

на
Код: выделить все
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;


Ну и, соответственно, можно добавить несколько строк для переназначения ножки на время измерения, куда хотите, можно например в вышеупомянутую библиотеку (если автор опять же не против), что-то типа того (не пинайте — я по образованию старый железячник) — в файл onewire.h:
Код: выделить все
void OW_out_set_as_Power_pin(void);
void OW_out_set_as_TX_pin(void);
 И в onewire.c (не забывая установить на выходе единицу для подачи питания на шину!)
void OW_out_set_as_TX_pin(void){
        GPIO_InitTypeDef GPIO_InitStruct; 
        if (OW_USART == USART1) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
                // USART TX
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStruct);
        }
        if (OW_USART == USART2) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

                GPIO_Init(GPIOA, &GPIO_InitStruct);
        }       
        if (OW_USART == USART3) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOB, &GPIO_InitStruct);
        } 
}

void OW_out_set_as_Power_pin(void){
        GPIO_InitTypeDef GPIO_InitStruct; 
        if (OW_USART == USART1) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
                // GPIO
                GPIO_SetBits(GPIOA, GPIO_Pin_9);
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStruct);               
        }
        if (OW_USART == USART2) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
                GPIO_SetBits(GPIOA , GPIO_Pin_2);
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStruct);
        }       
        if (OW_USART == USART3) {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
                GPIO_SetBits(GPIOB , GPIO_Pin_10);
                GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
                GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
                GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOB, &GPIO_InitStruct);
        } 
}


В рабочем коде надо просто после подачи команды на измерение добавить назначение выхода, как питающего шину, а по истечении времени измерения вернуть выход в исходное состояние – как передатчик UART:

Код: выделить все
OW_Init();
    OW_Send(OW_SEND_RESET, "\xcc\x44", 2, NULL, NULL, OW_NO_READ);
   
    // назначаем функцию двухтактного выхода - подаем "питание" на шину
    OW_out_set_as_Power_pin();
   
    // выдерживаем время измерения (например 750 мс для 12-битного измерения)
   for (i=0; i<1000000; i++);


// восстанавливаем функцию передатчика UART
OW_out_set_as_TX_pin();

uint8_t buf[2];
OW_Send(OW_SEND_RESET, "\xcc\xbe\xff\xff", 4, buf,2, 2);[/code]

Вот и все, надеюсь, сильно не запинают. Хотя за что — просто поделился своим опытом, авось кому-нить и пригодится. (Табличку из моего первого опыта с публикацией, хоть и потоптали, но всеж кто-то скачал, а значит не зря публиковал.) Особо не претендую ни на что, возможно это давно решено таким же способом — я глубоко не копал, просто посмотрел немножко на форумах, в том числе и здесь, не нашел. Кстати, это решение достаточно универсально и, если не использовать UART, а только GPIO, формируя сигналы интерфейса 1-wire программно, все можно сделать на одном выводе. Как — достаточно понятно, думаю — назначая вывод то входом, то выходом с открытым стоком, то двухтактным выходом. Такой подход может и еще где-нибудь пригодиться, если пользоваться «дерущейся за выводы» периферией не одновременно, а последовательно.
Еще раз спасибо steel_ne.
PS немножко подправил по тексту, вчера спать хотелось, пару строк пропустил.

PPS. Сегодня наконец опять добрался до термометров. Не буду рассусоливать, естественно наш коллега Gaaaaaad был абсолютно точен и прав, хватит копипастить избыточное решение с использованием двух ножек процессора для нашего случая.
Можно просто объявить полудуплексный режим для USART и для работы с 1-Wire вполне хватит одного вывода USART_TX, работающего в режиме Alternate_Open_Drain во время приема-передачи и в режиме GPIO_Out_Push_Pull во время проведения измерения или перезаписи. Таким образом схема становится минимальной и для связи с устройствами 1-wire используются всего два провода, к чему и стремились:

Изображение

Итожим все написанное:
В библиотеке из вышеупомянутой статьи steel_ne нужно лишь немножко подправить код в OW_Init(), вот так:

1. Изменить строки инициализации ножек передатчика USARTов c:
Код: выделить все
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

на
Код: выделить все
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;


2. Везде убрать инициализацию USART_RX:

Код: выделить все
 // USART RX
//              GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
//              GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//              GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

//              GPIO_Init(GPIOA, &GPIO_InitStruct);


3. В конце, перед выходом из OW_Init(), добавить одну-единственную строку:

Код: выделить все
USART_Init(OW_USART, &USART_InitStructure);
        USART_Cmd(OW_USART, ENABLE);
       
        // Здесь вставим разрешение работы USART в полудуплексном режиме
        USART_HalfDuplexCmd(OW_USART, ENABLE);
       
        return OW_OK;


Необходимо также добавить в библиотеку вышеописанные функции
Код: выделить все
OW_out_set_as_TX_pin()
и
Код: выделить все
OW_out_set_as_Power_pin()
.

Будем пользоваться библиотекой также, как и приводилось выше. Вот рабочий пример для измерения температуры тремя термометрами DS18B20, проверен на STM32 Mini. Здесь используются заранее определенные ID термометров, соответственно {0x28, 0xF8, 0xFB, 0xE1, 0x01, 0x00, 0x00, 0x76}, {0x28, 0x3C, 0xED, 0xDF, 0x01, 0x00, 0x00, 0x88} и {0x28, 0xCA, 0xED, 0xE1, 0x01, 0x00, 0x00, 0x15}:

Код: выделить все
OW_Init();
    // Заставляем ВСЕ термометры одновременно провести измерение температуры   
    OW_Send(OW_SEND_RESET, "\xcc\x44", 2, NULL, NULL, OW_NO_READ);
   
    // назначаем функцию двухтактного выхода - подаем "питание" на шину
    OW_out_set_as_Power_pin();
   
    // выдерживаем время измерения (например 750 мс для 12-битного измерения)
    for (i=0; i<1000000; i++);

    // восстанавливаем функцию передатчика UART
    OW_out_set_as_TX_pin();

    uint8_t buf[2];

    // читаем показания первого термометра     
    OW_Send(OW_SEND_RESET, "\x55\28\xF8\xFB\xE1\x01\x00\x00\x76\xbe\xff\xff", 12, buf, 2, 10);

    // используем их по Вашему разумению, например выводим на экран
    Out_buf_as_Temperature_on_TFT (buf, xy_1);

    // второго термометра       
    OW_Send(OW_SEND_RESET, "\x55\x28\x3C\xED\xDF\x01\x00\x00\x88\xbe\xff\xff", 12, buf, 2, 10);
    Out_buf_as_Temperature_on_TFT (buf, xy_2);

    //и третьего термометра     
    OW_Send(OW_SEND_RESET, "\x55\x28\xCA\xED\xE1\x01\x00\x00\x15\xbe\xff\xff", 12, buf, 2, 10);
    Out_buf_as_Temperature_on_TFT (buf, xy_3);



Еще раз спасибо steel_ne и Gaaaaaad. Библиотеку я не прилагаю, если steel_ne захочет – можно внести соответствующие коррективы в приложении к его статье, я буду только рад.

Оригинал статьи: http://we.easyelectronics.ru/blog/STM32/3276.html
admin
Администратор
 
Сообщений: 79
Зарегистрирован: 27 ноя 2016, 18:21
ТегиSTM32, DS18B20, OLED, i2c

Вернуться в Для новичков. Азы.

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 0

/