STM32 Первая осмысленная конструкция I/O модуль на MODBUS (Modbus часть 2.)

Вобщем несколько дней назад сделал первую целостную конструкцию на STM32 — I/O модуль на MODBUS c 4DI/4DO.Немного поскрипев и при помощи конденсатора сдался SWD. Немного допилив код из предыдущей статьи добавив EEPROM и ноги входов выходов мы получаем конструкцию которая может быть полезна как в изучении STM32, протоколов обмена данными, так и просто при использовании по назначению.
Сразу скажу схему не делал ибо лень да и ненадо для такой простой железяки- в проекте на CooCox написано какие пины, что значат. Видео для затравки.




Процессор был выбран из принципа что есть за разумные деньги с количеством флеша про запас STM32F100C8T6
(все компоненты есть в ЧИП-НН там и бралось). Конструкция как мне кажется достаточно стандартна для подобных вещей и состоит из:
1. ИБП на 34063
2. EEPROM 24C64
3. Трансивер RS-485 на ADM3485
4. Массив дарлингтонов ULN2003 — для управления мощной нагрузкой
5. И процессор STM32F100C8T6
6. Резисторы конденсаторы индуктивности итп.


Для программирования и отладки по SWD нога NRST не нужна, но может понадобиться емкость ~100pf на ноге SWCLK (на всякий случай по SWDIO тоже поставил) we.easyelectronics.ru/GYUR22/stm32-swd-misteriya.html.
При сборке нашлось несколько неудобных мест на плате поэтому я их поправил в варианте который выкладываю, проект для CooCox IDE прилагается. Что делает данная конструкция? — она умеет принимать сухой контакт в качестве 4 входов и коммутировать реле и светодиодики в качестве 4-х выходов питать можно +9-24V и естественно все отдается по RS-485 на Modbus. На видео все сделано с тач скрином от Weintek MT6070iH он стоит достаточно разумных денег ~340USD и может быть применен для всяких умных домов и тп. Аппликация для тачскрин делается в Easy Builder 8000 — оно бесплатное можно поискать скачать и потренироваться ибо там есть симулятор.Есть кстати его более дешевый младший брат MT6050I с дисплеем 4,3" за ~264USD.
В прошлый раз мы остановились на том что есть некоторая карта регистров, а что это ни сном ни духом. Итак занавес -карта регистров это всего лишь описание того как вы расположите свои переменные в пространстве памяти контроллера с учетом того что они эти регистры 16-ти битные. Но никто не запрещает располагать там например и флоат занимая 2 регистра или паковать 16 bool переменных в 16 битов регистра. Ок нам нужен массив для хранения переменных и желательно чтобы его можно было слить в еепром и считать оттуда (хотя в основном это на будущее). Мне нравится вариант с union и сделаем с ними:


//OBJECT ARRAY WHERE READING AND WRITING OCCURS
union {
int16_t regs[OBJ_SZ];
int8_t bytes[OBJ_SZ*2];
}res_table;


казалось бы зачем union — им удобно писать и читать в eeprom любой тип данных по байтам. res_table.regs[] — это наши 16 ти битные регистры скоими мы и работаем основную часть времени.Заглянем в файлик modbus_slave.c в функцию MODBUS_SLAVE — в ней происходит первичная обработка и проверка пакета на адрес, СRC16 и тип запроса


void MODBUS_SLAVE(UART_DATA *MODBUS)
{
  unsigned int tmp;


     //recive and checking rx query
	if((MODBUS->buffer[0]!=0)&(MODBUS->rxcnt>5)& ((MODBUS->buffer[0]==SET_PAR[0])|(MODBUS->buffer[0]==255)))
 {
	  tmp=Crc16(MODBUS->buffer,MODBUS->rxcnt-2);

     if((MODBUS->buffer[MODBUS->rxcnt-2]==(tmp&0x00FF)) & (MODBUS->buffer[MODBUS->rxcnt-1]==(tmp>>8)))
    {
     //если мы сюда попали значит пакет наш и crc совпало - надо проверить поддерживаем ли мы такой запрос
	 
         //choosing function
	     switch(MODBUS->buffer[1])
       {
       case 3:
       TX_03_04(MODBUS);
       break;

       case 4:
       TX_03_04(MODBUS);
       break;

       case 6:
       TX_06(MODBUS);
       break;

       default://если нет то выдаем ошибку
       //illegal operation
       TX_EXCEPTION(MODBUS,0x01);
	   }
              //добавляем CRC и готовим к отсылке
	     //adding CRC16 to reply
   	     tmp=Crc16(MODBUS->buffer,MODBUS->txlen-2);
         MODBUS->buffer[MODBUS->txlen-2]=tmp;
         MODBUS->buffer[MODBUS->txlen-1]=tmp>>8;
         MODBUS->txcnt=0;

	}
 
 }
          //сброс индикаторов окончания посылки
	  MODBUS->rxgap=0;
	  MODBUS->rxcnt=0;
	  MODBUS->rxtimer=0xFFFF;

}


Если мы правильно сформировали запрос и попали в функцию TX_03_04(MODBUS); то при запросе здесь валидны запросы 3 и 4 с количеством перменных не более чем у нас есть мы можем сформировать ответ если же нет и количество переменных велико или выходит за границы наших переменных то выдадим сообщение об ошибке.

void TX_03_04(UART_DATA *MODBUS)
{
unsigned int tmp,tmp1;
unsigned int m=0,n=0;
int tmp_val,tmp_val_pos;
  
   //MODBUS->buffer[0] =SET_PAR[0]; // adress - stays a same as in received
   //MODBUS->buffer[1] = 3; //query type - - stay a same as in recived
   //MODBUS->buffer[2] = data byte count

    //2-3  - starting address
   tmp=((MODBUS->buffer[2]<<8)+MODBUS->buffer[3]); //стратовый адрес для чтения
   
   //4-5 - number of registers
   tmp1=((MODBUS->buffer[4]<<8)+MODBUS->buffer[5]);//количество регистров для чтения

   //default answer length if error
   n=3; 

  //если нас устраивает длина запроса и и стартовый адрес
  if((((tmp+tmp1)<OBJ_SZ)&(tmp1<MODBUS_WRD_SZ+1)))
   {

    for(m=0;m<tmp1;m++)
    {
      tmp_val=res_table.regs[m+tmp];//читаем текущее значение 

    if(tmp_val<0)
    {
    //пакуем отрицательное
    tmp_val_pos=tmp_val;
    MODBUS->buffer[n]=(tmp_val_pos>>8)|0b10000000;
    MODBUS->buffer[n+1]=tmp_val_pos;
    }
    else
    {
     //пакуем положительное
     MODBUS->buffer[n]=tmp_val>>8;
     MODBUS->buffer[n+1]=tmp_val;
    }
     n=n+2;
    }
     
     //запишем длину переменных пакета в байтах и вставим всообщение
     MODBUS->buffer[2]=m*2; //byte count
     //подготовим к отправке
     MODBUS->txlen=m*2+5; //responce length
    
   }
  else 
  {
   //exception illegal data adress 0x02
   TX_EXCEPTION(MODBUS,0x02);
  }
 
}


Функция записи похожа на чтение с тем лишь условием что мы используем запись с функцией 6 — это запись одного регистра. И еще мы в ней пишем адрес устройства в 0 регистр и в EEPROM т.к. никаких дип свитчей я не ставил.
В основном цикле мы циклически читаем и пишем наши переменные согласно карте регистров и делаем мы это по таймеру 1 раз в секунду + моргая светодиодом на всякий случай

//i/o
    	 if(run_loop)
      {
    	 i=GPIO_ReadInputData  ( GPIOA);
    	 res_table.regs[1]=!(i&1);//a0
    	 res_table.regs[2]=!((i>>1)&1);//a1
    	 res_table.regs[3]=!((i>>2)&1);//a2
    	 res_table.regs[4]=!((i>>3)&1);//a3

    	 if(res_table.regs[5]) //a8
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_8,Bit_SET);
    	 else
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_8,Bit_RESET);

    	 if(res_table.regs[6])//a9
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_9,Bit_SET);
    	 else
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_9,Bit_RESET);

    	 if(res_table.regs[7])//a10
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_10,Bit_SET);
    	 else
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_10,Bit_RESET);

    	 if(res_table.regs[8])//a11
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_11,Bit_SET);
    	 else
    	 GPIO_WriteBit  ( GPIOA,GPIO_Pin_11,Bit_RESET);

    	 run_loop=0;
      }


И у нас получилась следующая карта регистров:

0. Адрес устройства — RW- с записью в eeprom и чтением при старте
1.DI Вход1 -R
2.DI Вход2 -R
3.DI Вход3 -R
4.DI Вход4 -R
5.DO Выход1 — RW
6.DO Выход2 — RW
7.DO Выход3 — RW
8.DO Выход4 — RW

Пишите ежели что…
ps апгрейд до аналоговых входов we.easyelectronics.ru/STM32/stm32-i-o-modul-na-modbus-i-nemnogo-acp-prevraschenie-bryuk.html
  • +3
  • 01 сентября 2011, 17:36
  • GYUR22
  • 2
Файлы в топике: io_board.zip, stm32_pico_plc2.zip

Комментарии (56)

RSS свернуть / развернуть
Отлично!
Мы на АВР-ках такое делали на работе.
Осталось заюзать АЦП, тогда будет полный фарш.
0
и PWM…
0
ну ШИМ нам только недавно понадобился, ибо не было особой необходимости… Можно прикрутить на DO в Вашем случае, получится универсальный выход. + если опторазвязку бы сделать…
0
так оно и планировалося — легким движением руки модуль превращается…
0
в брюки? :-)
+1
А зачем ЕЕПРОМ? Для настроек вполне ведь можно задействовать сектор флеша.
0
  • avatar
  • Vga
  • 01 сентября 2011, 20:29
Воистину радиолюбительский вопрос, к чему надежность…
0
Они настолько часто записываютя? На крайний случай, можно выделить несколько секторов и переключаться между ними по мере износа. EEPROM тоже не вечный, длиный рассказывал как угробил его где-то недельку.
0
  • avatar
  • Vga
  • 02 сентября 2011, 11:00
но он надежней по циклам и легко выпаивается если что :)
0
Еще было бы неплохо поставить переключатель для установки modbus-адреса, чтобы программу не менять каждый раз
0
Зачем? Адрес модуля хранится в нулевом регистре.

Вопрос автору: какой адрес имеет модуль при первоначальном включении?
0
Скажем так — протокол написан так что устройство откликается еще и на 255 адрес при любом установленном
0
Стоп, а как их тогда использовать в сети?
0
без проблем 255 устройств врядли на одну сеть в здравом уме кто то повесит.
0
В том смысле, если на шине висит более одного такого модуля.
0
этот адрес нужен для первоначальной конфигурации модуля — на сети используются адреса от 1 до 254
считаем что 255 зарезервирован
0
Не, всё равно не понимаю. Ведь все модули на адрес 255 будут откликаться одновременно. Кстати, разрешенные адреса от 1 до 247.
0
сконфигурировали -выставили адрес и все 255 не используем.
0
в смысле одтельно от сети скофигурировали
0
А, отдельно, теперь понятно. Вообще, видел, делают так: первоначальный адрес 247, вешаешь модуль на шину, в которой уже имеющиеся модули имеют адреса от 1 и далее, связываешься с ним по адресу 247 и задаешь ему нужный адрес.
0
это просто от предыдущей железяки осталось и заодно так удобнее
0
а 255 разве не широковещательный?.. что-то мне помнится такое…
0
типа да,
но тут каждый сам себе хозяин
//recive and checking rx query
if((MODBUS->buffer[0]!=0)&(MODBUS->rxcnt>5)& ((MODBUS->buffer[0]==SET_PAR[0])|(MODBUS->buffer[0]==255)))
поменяйте 255 на любое число и будет вам благо :)
0
Широковещательный — 0.
0
если не ошибаюсь, то — оба..)
0
на самом деле скажу по секрету… широковещательный обмен обычно не делают ибо не нужен он, обмен всегда идет адресно
0
лично я без претензий) респект за проект, жаль почему-то не могу плюсануть :(
думаю даже повторить, есть контролер ОВЕН ПЛК100, хочется побаловатся :)))
0
сделайте, чтобы это было конечное устройство и оно работало с mach3 для чпу станков
0
Немного в теме про ЧПУ и Mach3 но не совсем понимаю что надо сделать -поясните
0
сделать побольше входов для концевиков
штук 5 выходов
а еще можно ли сделать вывод инфы на индикаторы и положении осей?
китайцы продают плату с индикаторами на 4 оси по 25 баксов и она напрямую подключается к сигналам движков
я хтел было уже такое делать, но все украли до нас

где-то видел в продаже модбас пик, так он много чего может
и еще какой-то мк с реализацией модбас с еще большей кучей фишек и индикатор в нем тоже есть
правда свой плагин там идет

хочу просто иметь ручное управление (клавиатурка усб или специальная версия у китайцев)
а вот выход на индикаторы через модбас нигде особо не видел
0
все равно не догоняю чего конкретно хотите.
Добавить пару входов не проблема — грохнуть сетодиоды и ли пренести и все разведется
Индикаторы положения осей… она имульсы что ли считает?
дайте ссылочку?
0
www.aliexpress.com/product-gs/434728580-4-axis-stepper-motor-driver-Digital-Display-Panel-Product-for-4-axis-TB6560-CNC-stepper-motor-wholesalers.html

эта считает импульсы, но ведь модбас в мах3 так же должен слать какую-то инфу для внешних контроллеров

например вот простой контроллер входов и выходов
www.hobbycnc.hu/CNC/MBIO/MBIO_en.html
в нем даже железный усб в пике есть

kristof.dhulster.nl/cccal/Modbus_cccal.html
вот тут подключают штангенциркуль к мах3 по модбас

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

придется вешать тот китайский тогда, но думаю по моей оси х в 1000мм не хватит разрядов
0
посмотрел на венгерскую платку и примерно понял…
но честно говоря насчет положения осей не ясно что считаем и сколько счетных входов надо?
Расширить эту плату можно — 3 входа и 3 выхода можно без проблем добавить все ровно разведется на одном слое.
Pwm выход — это тоже не проблема все текущие выходы висят на таймерных ногах
аналоговые входы я немного рассмотрел в следующей статье.
Напишите конкретно сколько каких i/o надо
0
да конкретно я не хочу заказывать тут
просто я не разобрался с протоколом даже на уровне приложений

по поводу платы с индикаторами — она ничего не должна считать
вся инфа идет через сам протокол — в этом все и дело

а та китайская плата просто тупо считает импульсы

по поводу входов
хороший контроллер обеспечивает такой конфиг
концевики + и -, а так же «домой» по 3 оси
итого 9 штук
вход для установления нуля по оси з, когда инструмент касается пластины с контактом

вход для кнопки аварийного останова
итого 11 входов

выходы
включение шпинделя
подача воздуха или жидкости
отсос стружки
освещение для камеры
0
можно и 11 входов — но уже с перемычками
0
Респект за то, что нашел время и написал цикл постов по modbus. Я тоже реализовал для STM32F1xx modbus, чуток по другому, подсматривал в FreeModBus, но все-равно по своему сделал. Например, у меня нет switch case, а делаю поиск обработчика запроса среди поддерживаемых и при нахождении такового перехожу по указателю на этот обработчик. Хотел выложить работу, поделиться так сказать, но руки не доходят. Кстати последний девайс, в который вживился modbus, тоже управляется панелью MT6070iH. Как думаете, стоит выложить?
И вопрос автору по поводу интервала тишины 3.5. Между чем и чем ты его держишь?
0
  • avatar
  • ISK
  • 02 сентября 2011, 10:44
между приемами уарта
имхо выкладывать думаю стоит все что связано с автоматизацией и может помочь в освоении этого дела другим людям
0
) я тоже сначала между приемами делал. Но по спецификации Modbus интервал «3.5» должен быть именно интервалом тишины, т.е. когда ничего уже и еще не передается. Т.е. между концом предыдущего и началом нового байта. И если этот интервал привести к промежутку времени между приемами, то получается, что выходит интервал между приемами 4.5, на него и нужно настраивать. Аналогично с интервалом «1.5». Если мастер не сильный тормоз и не делает пауз между байтами запроса доходящих по времени > 0.5 символа, то все хорошо, и моя поправка не важна.

А для MT6070 используешь импорт адресных меток из екселя? Хочу сделать скриптик, который из текста объявления типа структуры (данных, которые лежат на внутренних регистрах подчиненного устройства) делал экселевскую табличку с именами и смещениями меток.
0
  • avatar
  • ISK
  • 02 сентября 2011, 11:33
на самом деле можно больще можно меньше — обычно все равно таймоут ~100ms
я с этим дисплеем относительный новичок и чесно говоря местами слабо понимаю (просто у основного нашего оборудования другая парадигма и протоколы) — что такое recipe например можете пояснить?
и как делать импорт экспорт точек в ексель — очень интересная тема
0
Тут с обнародованием и выкладыванием еще есть одна проблема. Проект большой, контроллер на плате не один, сам девайс, который могу зафотать, это две стойки. Т.е. результат работы общий. И есть часть интеллектуальной собственности и коммерческой тайны. Поэтому сижу и думаю, что можно а что нельзя выкладывать.
0
  • avatar
  • ISK
  • 02 сентября 2011, 11:50
у меня это тоже была некоторая часть софта из коммерческого проекта, но на самом деле это секрет Полишинеля поэтому я решил поделиться в контексте чего то малого так сказать в образовательных целях.
а про дисплейчик вы бы поделились знаниями, а то чето не укого спросить :)
0
«рецепт» для меня тоже чуждое понятие. Сам до этого не занимался такими панелями и автоматизацией, был вынужден, т.к. «больше некому». Винтековский редактор рецептов поковырял и не понял на кой он мне), а из описания: «Рецепт – это набор параметров, который хранит настойки для определённого режима работы. Рецепты можно формировать и сохранять в процессе работы.» И вправду там можно создать что-то вроде структуры из нужного набора полей и создать массив этих структур. Сейчас панель используется для управления, выбора режимов работы, снятия показаний и ведения логов экспериментов. Графики панелька рисует хорошо, особенно после того как макросами на нее БИХ фильтр первого порядка натянул. А выборки данных, т.е. тот самый лог, на USB флашку сохраняю.
Минус у нее — тормозит при переходе в подменю с графиком, связано с чтением всего(!) лога с флешки, и только потом отрисовки видимой части.
0
  • avatar
  • ISK
  • 02 сентября 2011, 12:33
у меня похожий модуль на STM8F103F3:



Вход в режим настройки происходит замыканием джампера XT2.
0
отличный пост! повторил проект на F103VCT, но не просто так, а встроил в RT Chibios. Понравилась крайняя простота в отличии от FreeModbus
0
Не могли бы Вы поделиться примером с реализацией MODBUS в ChibiOS?
0
как обычно… компилируется с ошибками…

GCC HOME: C:\Program Files\GNU Tools ARM Embedded\4.7 2012q4\bin
compile:
[mkdir] Created dir: C:\Temp\Discovery\io_board\io_board\Debug\bin
[mkdir] Created dir: C:\Temp\Discovery\io_board\io_board\Debug\obj
[cc] 12 total files to be compiled.
[cc] arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -g -Os -c -DSTM32F100C8 -DSTM32F10X_MD_VL -DUSE_STDPERIPH_DRIVER -D__ASSEMBLY__ -IC:\Temp -IC:\Temp\Discovery\io_board\stm_lib -IC:\Temp\Discovery -IC:\Temp\Discovery\io_board\cmsis_boot -IC:\Temp\Discovery\io_board\cmsis -IC:\Temp\Discovery\io_board -IC:\Temp\Discovery\io_board\stm_lib\inc C:\Temp\Discovery\io_board\stm_lib\src\stm32f10x_tim.c C:\Temp\Discovery\io_board\i2c.c C:\Temp\Discovery\io_board\cmsis_boot\startup\startup_stm32f10x_md_vl.c C:\Temp\Discovery\io_board\cmsis\core_cm3.c C:\Temp\Discovery\io_board\cmsis_boot\system_stm32f10x.c C:\Temp\Discovery\io_board\stm_lib\src\stm32f10x_gpio.c C:\Temp\Discovery\io_board\main.c C:\Temp\Discovery\io_board\modbus_slave.c C:\Temp\Discovery\io_board\stm_lib\src\stm32f10x_rcc.c C:\Temp\Discovery\io_board\stm_lib\src\stm32f10x_usart.c C:\Temp\Discovery\io_board\stm_lib\src\misc.c C:\Temp\Discovery\io_board\stm_lib\src\stm32f10x_i2c.c
[cc] 200
[cc] z:\1\ccPufXUk.s: Assembler messages:
[cc] z:\1\ccPufXUk.s:508: Error: registers may not be the same — `strexb r0,r0,[r1]'
[cc] z:\1\ccPufXUk.s:533: Error: registers may not be the same — `strexh r0,r0,[r1]'

BUILD FAILED
Total time: 1 second
0
Как исправляется эта ошибка — тут в сообществе уже где-то писалось.
0
это все равно что написать что на все вопросы есть ответы…
вообщем спасибо успокоили…
а вот если поможете то спасибо
0
На самом деле, в данном случае достаточно знать, что решение этой ошибки тут уже описывалось.
google.com "Error: registers may not be the same" site:we.easyelectronics.ru
0
как обычно отвечу сам на поставленный вопрос… лечится установкой опримизации компилятора в соосох в none
0
Не слишком хорошее решение. Правильное — исправить код.
0
Первая же ссылка. Любите гугл, мать вашу!
0
0
Напишем по русски: проект был создан в старом кокосе со старым компилятором и cmsis, всего то и делов — убить старые папки и заново поставить галочки в репозитарии кокоса.
0
К ПЛК подключать пробовали? Если да, то к каким?
0
Я не работаю с ПЛК — только с DDC(контроллерами) и, поэтому не пробовал.
Но все должно работать ибо со всякими modbus тулами, OPC, панелькой все работало.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.