mbcontroller.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // mbcontroller.c
  15. // Implementation of the modbus controller
  16. // The modbus controller is responsible for processing of modbus packet and transfer data
  17. // into parameter instance.
  18. #include <sys/time.h> // for calculation of time stamp in milliseconds
  19. #include "esp_log.h" // for log_write
  20. #include "freertos/FreeRTOS.h" // for task creation and queue access
  21. #include "freertos/task.h" // for task api access
  22. #include "freertos/event_groups.h" // for event groups
  23. #include "freertos/queue.h" // for queue api access
  24. #include "mb.h" // for mb types definition
  25. #include "mbutils.h" // for mbutils functions definition for stack callback
  26. #include "sdkconfig.h" // for KConfig values
  27. #include "mbcontroller.h"
  28. static const char* TAG = "MB_CONTROLLER";
  29. #define MB_CHECK(a, ret_val, str, ...) \
  30. if (!(a)) { \
  31. ESP_LOGE(TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
  32. return (ret_val); \
  33. }
  34. // The Macros below handle the endianness while transfer N byte data into buffer
  35. #define _XFER_4_RD(dst, src) { \
  36. *(uint8_t *)(dst)++ = *(uint8_t*)(src + 1); \
  37. *(uint8_t *)(dst)++ = *(uint8_t*)(src + 0); \
  38. *(uint8_t *)(dst)++ = *(uint8_t*)(src + 3); \
  39. *(uint8_t *)(dst)++ = *(uint8_t*)(src + 2); \
  40. (src) += 4; \
  41. }
  42. #define _XFER_2_RD(dst, src) { \
  43. *(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
  44. *(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
  45. (src) += 2; \
  46. }
  47. #define _XFER_4_WR(dst, src) { \
  48. *(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
  49. *(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
  50. *(uint8_t *)(dst + 3) = *(uint8_t *)(src)++; \
  51. *(uint8_t *)(dst + 2) = *(uint8_t *)(src)++ ; \
  52. }
  53. #define _XFER_2_WR(dst, src) { \
  54. *(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
  55. *(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
  56. }
  57. #ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
  58. #define MB_ID_BYTE0(id) ((uint8_t)(id))
  59. #define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
  60. #define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
  61. #define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
  62. #define MB_CONTROLLER_SLAVE_ID (CONFIG_MB_CONTROLLER_SLAVE_ID)
  63. #define MB_SLAVE_ID_SHORT (MB_ID_BYTE3(MB_CONTROLLER_SLAVE_ID))
  64. // Slave ID constant
  65. static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
  66. MB_ID_BYTE1(MB_CONTROLLER_SLAVE_ID),
  67. MB_ID_BYTE2(MB_CONTROLLER_SLAVE_ID) };
  68. #endif
  69. // Event group parameters
  70. static TaskHandle_t mb_controller_task_handle = NULL;
  71. static EventGroupHandle_t mb_controller_event_group = NULL;
  72. static QueueHandle_t mb_controller_notification_queue_handle = NULL;
  73. static uint8_t mb_type = 0;
  74. static uint8_t mb_address = 0;
  75. static uint8_t mb_port = 0;
  76. static uint32_t mb_speed = 0;
  77. static uint16_t mb_parity = 0;
  78. // This is array of Modbus address area descriptors
  79. static mb_register_area_descriptor_t mb_area_descriptors[MB_PARAM_COUNT] = { 0 };
  80. // The helper function to get time stamp in microseconds
  81. static uint64_t get_time_stamp()
  82. {
  83. uint64_t time_stamp = esp_timer_get_time();
  84. return time_stamp;
  85. }
  86. // Helper function to send parameter information to application task
  87. static esp_err_t send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
  88. uint8_t* par_address, uint16_t par_size)
  89. {
  90. esp_err_t error = ESP_FAIL;
  91. mb_param_info_t par_info;
  92. // Check if queue is not full the send parameter information
  93. par_info.type = par_type;
  94. par_info.size = par_size;
  95. par_info.address = par_address;
  96. par_info.time_stamp = get_time_stamp();
  97. par_info.mb_offset = mb_offset;
  98. BaseType_t status = xQueueSend(mb_controller_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
  99. if (pdTRUE == status) {
  100. ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
  101. par_type, (uint32_t)par_address, par_size);
  102. error = ESP_OK;
  103. } else if (errQUEUE_FULL == status) {
  104. ESP_LOGD(TAG, "Parameter queue is overflowed.");
  105. }
  106. return error;
  107. }
  108. static esp_err_t send_param_access_notification(mb_event_group_t event)
  109. {
  110. esp_err_t err = ESP_FAIL;
  111. mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mb_controller_event_group, (EventBits_t)event);
  112. if (bits & event) {
  113. ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
  114. err = ESP_OK;
  115. }
  116. return err;
  117. }
  118. // Modbus task function
  119. static void modbus_task(void *pvParameters) {
  120. // Main Modbus stack processing cycle
  121. for (;;) {
  122. BaseType_t status = xEventGroupWaitBits(mb_controller_event_group,
  123. (BaseType_t)(MB_EVENT_STACK_STARTED),
  124. pdFALSE, // do not clear bits
  125. pdFALSE,
  126. portMAX_DELAY);
  127. // Check if stack started then poll for data
  128. if (status & MB_EVENT_STACK_STARTED) {
  129. (void)eMBPoll(); // allow stack to process data
  130. (void)xMBPortSerialTxPoll(); // Send response buffer if ready
  131. }
  132. }
  133. }
  134. // Blocking function to get event on parameter group change for application task
  135. mb_event_group_t mbcontroller_check_event(mb_event_group_t group)
  136. {
  137. assert(mb_controller_event_group != NULL);
  138. BaseType_t status = xEventGroupWaitBits(mb_controller_event_group, (BaseType_t)group,
  139. pdTRUE , pdFALSE, portMAX_DELAY);
  140. return (mb_event_group_t)status;
  141. }
  142. esp_err_t mbcontroller_set_descriptor(const mb_register_area_descriptor_t descr_info)
  143. {
  144. MB_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
  145. ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
  146. (uint32_t)descr_info.type);
  147. MB_CHECK((descr_info.address != NULL),
  148. ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
  149. MB_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
  150. ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
  151. (uint32_t)descr_info.size);
  152. mb_area_descriptors[descr_info.type].type = descr_info.type;
  153. mb_area_descriptors[descr_info.type].start_offset = descr_info.start_offset;
  154. mb_area_descriptors[descr_info.type].address = (uint8_t*)descr_info.address;
  155. mb_area_descriptors[descr_info.type].size = descr_info.size;
  156. return ESP_OK;
  157. }
  158. // Initialization of Modbus controller
  159. esp_err_t mbcontroller_init(void) {
  160. mb_type = MB_MODE_RTU;
  161. mb_address = MB_DEVICE_ADDRESS;
  162. mb_port = MB_UART_PORT;
  163. mb_speed = MB_DEVICE_SPEED;
  164. mb_parity = MB_PARITY_NONE;
  165. // Initialization of active context of the modbus controller
  166. BaseType_t status = 0;
  167. // Parameter change notification queue
  168. mb_controller_event_group = xEventGroupCreate();
  169. MB_CHECK((mb_controller_event_group != NULL),
  170. ESP_ERR_NO_MEM, "mb event group error.");
  171. // Parameter change notification queue
  172. mb_controller_notification_queue_handle = xQueueCreate(
  173. MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
  174. sizeof(mb_param_info_t));
  175. MB_CHECK((mb_controller_notification_queue_handle != NULL),
  176. ESP_ERR_NO_MEM, "mb notify queue creation error.");
  177. // Create modbus controller task
  178. status = xTaskCreate((void*)&modbus_task,
  179. "modbus_task",
  180. MB_CONTROLLER_STACK_SIZE,
  181. NULL,
  182. MB_CONTROLLER_PRIORITY,
  183. &mb_controller_task_handle);
  184. if (status != pdPASS) {
  185. vTaskDelete(mb_controller_task_handle);
  186. MB_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
  187. "mb controller task creation error, xTaskCreate() returns (0x%x).",
  188. (uint32_t)status);
  189. }
  190. assert(mb_controller_task_handle != NULL); // The task is created but handle is incorrect
  191. return ESP_OK;
  192. }
  193. // Function to get notification about parameter change from application task
  194. esp_err_t mbcontroller_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
  195. {
  196. esp_err_t err = ESP_ERR_TIMEOUT;
  197. MB_CHECK((mb_controller_notification_queue_handle != NULL),
  198. ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
  199. MB_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
  200. BaseType_t status = xQueueReceive(mb_controller_notification_queue_handle,
  201. reg_info, pdMS_TO_TICKS(timeout));
  202. if (status == pdTRUE) {
  203. err = ESP_OK;
  204. }
  205. return err;
  206. }
  207. // Start Modbus controller start function
  208. esp_err_t mbcontroller_start(void)
  209. {
  210. eMBErrorCode status = MB_EIO;
  211. // Initialize Modbus stack using mbcontroller parameters
  212. status = eMBInit((eMBMode)mb_type, (UCHAR)mb_address, (UCHAR)mb_port,
  213. (ULONG)mb_speed, (eMBParity)mb_parity);
  214. MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
  215. "mb stack initialization failure, eMBInit() returns (0x%x).", status);
  216. #ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
  217. status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
  218. MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
  219. #endif
  220. status = eMBEnable();
  221. MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
  222. "mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
  223. // Set the mbcontroller start flag
  224. EventBits_t flag = xEventGroupSetBits(mb_controller_event_group,
  225. (EventBits_t)MB_EVENT_STACK_STARTED);
  226. MB_CHECK((flag & MB_EVENT_STACK_STARTED),
  227. ESP_ERR_INVALID_STATE, "mb stack start event set error.");
  228. return ESP_OK;
  229. }
  230. // Modbus controller destroy function
  231. esp_err_t mbcontroller_destroy(void)
  232. {
  233. eMBErrorCode mb_error = MB_ENOERR;
  234. // Stop polling by clearing correspondent bit in the event group
  235. EventBits_t flag = xEventGroupClearBits(mb_controller_event_group,
  236. (EventBits_t)MB_EVENT_STACK_STARTED);
  237. MB_CHECK((flag & MB_EVENT_STACK_STARTED),
  238. ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
  239. // Desable and then destroy the Modbus stack
  240. mb_error = eMBDisable();
  241. MB_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
  242. (void)vTaskDelete(mb_controller_task_handle);
  243. (void)vQueueDelete(mb_controller_notification_queue_handle);
  244. (void)vEventGroupDelete(mb_controller_event_group);
  245. mb_error = eMBClose();
  246. MB_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
  247. "mb stack close failure returned (0x%x).", (uint32_t)mb_error);
  248. return ESP_OK;
  249. }
  250. // Setup modbus controller parameters
  251. esp_err_t mbcontroller_setup(const mb_communication_info_t comm_info)
  252. {
  253. MB_CHECK(((comm_info.mode == MB_MODE_RTU) || (comm_info.mode == MB_MODE_ASCII)),
  254. ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
  255. (uint32_t)comm_info.mode);
  256. MB_CHECK((comm_info.slave_addr <= MB_ADDRESS_MAX),
  257. ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).",
  258. (uint32_t)comm_info.slave_addr);
  259. MB_CHECK((comm_info.port <= UART_NUM_2), ESP_ERR_INVALID_ARG,
  260. "mb wrong port to set = (0x%x).", (uint32_t)comm_info.port);
  261. MB_CHECK((comm_info.parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
  262. "mb wrong parity option = (0x%x).", (uint32_t)comm_info.parity);
  263. mb_type = (uint8_t)comm_info.mode;
  264. mb_address = (uint8_t)comm_info.slave_addr;
  265. mb_port = (uint8_t)comm_info.port;
  266. mb_speed = (uint32_t)comm_info.baudrate;
  267. mb_parity = (uint8_t)comm_info.parity;
  268. return ESP_OK;
  269. }
  270. /* ----------------------- Callback functions for Modbus stack ---------------------------------*/
  271. // These are executed by modbus stack to read appropriate type of registers.
  272. // This is required to suppress warning when register start address is zero
  273. #pragma GCC diagnostic ignored "-Wtype-limits"
  274. // Callback function for reading of MB Input Registers
  275. eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
  276. USHORT usNRegs)
  277. {
  278. assert(pucRegBuffer != NULL);
  279. USHORT usRegInputNregs = (USHORT)(mb_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
  280. USHORT usInputRegStart = (USHORT)mb_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
  281. UCHAR* pucInputBuffer = (UCHAR*)mb_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
  282. USHORT usRegs = usNRegs;
  283. eMBErrorCode eStatus = MB_ENOERR;
  284. USHORT iRegIndex;
  285. // If input or configuration parameters are incorrect then return an error to stack layer
  286. if ((usAddress >= usInputRegStart)
  287. && (pucInputBuffer != NULL)
  288. && (usNRegs >= 1)
  289. && ((usAddress + usRegs) <= (usInputRegStart + usRegInputNregs + 1))
  290. && (usRegInputNregs >= 1)) {
  291. iRegIndex = (USHORT)(usAddress - usInputRegStart - 1);
  292. iRegIndex <<= 1; // register Address to byte address
  293. pucInputBuffer += iRegIndex;
  294. UCHAR* pucBufferStart = pucInputBuffer;
  295. while (usRegs > 0) {
  296. _XFER_2_RD(pucRegBuffer, pucInputBuffer);
  297. iRegIndex += 2;
  298. usRegs -= 1;
  299. }
  300. // Send access notification
  301. (void)send_param_access_notification(MB_EVENT_INPUT_REG_RD);
  302. // Send parameter info to application task
  303. (void)send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)usAddress,
  304. (uint8_t*)pucBufferStart, (uint16_t)usNRegs);
  305. } else {
  306. eStatus = MB_ENOREG;
  307. }
  308. return eStatus;
  309. }
  310. // Callback function for reading of MB Holding Registers
  311. // Executed by stack when request to read/write holding registers is received
  312. eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
  313. USHORT usNRegs, eMBRegisterMode eMode)
  314. {
  315. assert(pucRegBuffer != NULL);
  316. USHORT usRegHoldingNregs = (USHORT)(mb_area_descriptors[MB_PARAM_HOLDING].size >> 1);
  317. USHORT usRegHoldingStart = (USHORT)mb_area_descriptors[MB_PARAM_HOLDING].start_offset;
  318. UCHAR* pucHoldingBuffer = (UCHAR*)mb_area_descriptors[MB_PARAM_HOLDING].address;
  319. eMBErrorCode eStatus = MB_ENOERR;
  320. USHORT iRegIndex;
  321. USHORT usRegs = usNRegs;
  322. // Check input and configuration parameters for correctness
  323. if ((usAddress >= usRegHoldingStart)
  324. && (pucHoldingBuffer != NULL)
  325. && ((usAddress + usRegs) <= (usRegHoldingStart + usRegHoldingNregs + 1))
  326. && (usRegHoldingNregs >= 1)
  327. && (usNRegs >= 1)) {
  328. iRegIndex = (USHORT) (usAddress - usRegHoldingStart - 1);
  329. iRegIndex <<= 1; // register Address to byte address
  330. pucHoldingBuffer += iRegIndex;
  331. UCHAR* pucBufferStart = pucHoldingBuffer;
  332. switch (eMode) {
  333. case MB_REG_READ:
  334. while (usRegs > 0) {
  335. _XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
  336. iRegIndex += 2;
  337. usRegs -= 1;
  338. };
  339. // Send access notification
  340. (void)send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
  341. // Send parameter info
  342. (void)send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)usAddress,
  343. (uint8_t*)pucBufferStart, (uint16_t)usNRegs);
  344. break;
  345. case MB_REG_WRITE:
  346. while (usRegs > 0) {
  347. _XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
  348. pucHoldingBuffer += 2;
  349. iRegIndex += 2;
  350. usRegs -= 1;
  351. };
  352. // Send access notification
  353. (void)send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
  354. // Send parameter info
  355. (void)send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)usAddress,
  356. (uint8_t*)pucBufferStart, (uint16_t)usNRegs);
  357. break;
  358. }
  359. } else {
  360. eStatus = MB_ENOREG;
  361. }
  362. return eStatus;
  363. }
  364. // Callback function for reading of MB Coils Registers
  365. eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
  366. USHORT usNCoils, eMBRegisterMode eMode)
  367. {
  368. assert(NULL != pucRegBuffer);
  369. USHORT usRegCoilNregs = (USHORT)(mb_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
  370. USHORT usRegCoilsStart = (USHORT)mb_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
  371. UCHAR* pucRegCoilsBuf = (UCHAR*)mb_area_descriptors[MB_PARAM_COIL].address;
  372. eMBErrorCode eStatus = MB_ENOERR;
  373. USHORT iRegIndex;
  374. USHORT usCoils = usNCoils;
  375. usAddress--; // The address is already +1
  376. if ((usAddress >= usRegCoilsStart)
  377. && (usRegCoilNregs >= 1)
  378. && ((usAddress + usCoils) <= (usRegCoilsStart + (usRegCoilNregs << 4) + 1))
  379. && (pucRegCoilsBuf != NULL)
  380. && (usNCoils >= 1)) {
  381. iRegIndex = (USHORT) (usAddress - usRegCoilsStart);
  382. CHAR* pucCoilsDataBuf = (CHAR*)(pucRegCoilsBuf + (iRegIndex >> 3));
  383. switch (eMode) {
  384. case MB_REG_READ:
  385. while (usCoils > 0) {
  386. UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
  387. xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress - usRegCoilsStart), 1, ucResult);
  388. iRegIndex++;
  389. usCoils--;
  390. }
  391. // Send an event to notify application task about event
  392. (void)send_param_access_notification(MB_EVENT_COILS_WR);
  393. (void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
  394. (uint8_t*)(pucCoilsDataBuf), (uint16_t)usNCoils);
  395. break;
  396. case MB_REG_WRITE:
  397. while (usCoils > 0) {
  398. UCHAR ucResult = xMBUtilGetBits(pucRegBuffer,
  399. iRegIndex - (usAddress - usRegCoilsStart), 1);
  400. xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
  401. iRegIndex++;
  402. usCoils--;
  403. }
  404. // Send an event to notify application task about event
  405. (void)send_param_access_notification(MB_EVENT_COILS_WR);
  406. (void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
  407. (uint8_t*)pucCoilsDataBuf, (uint16_t)usNCoils);
  408. break;
  409. } // switch ( eMode )
  410. } else {
  411. // If the configuration or input parameters are incorrect then return error to stack
  412. eStatus = MB_ENOREG;
  413. }
  414. return eStatus;
  415. }
  416. // Callback function for reading of MB Discrete Input Registers
  417. eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
  418. USHORT usNDiscrete)
  419. {
  420. assert(pucRegBuffer != NULL);
  421. USHORT usRegDiscreteNregs = (USHORT)(mb_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
  422. USHORT usRegDiscreteStart = (USHORT)mb_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
  423. UCHAR* pucRegDiscreteBuf = (UCHAR*)mb_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
  424. eMBErrorCode eStatus = MB_ENOERR;
  425. USHORT iRegIndex, iRegBitIndex, iNReg;
  426. UCHAR* pucDiscreteInputBuf;
  427. iNReg = usNDiscrete / 8 + 1;
  428. pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
  429. // It already plus one in modbus function method.
  430. usAddress--;
  431. if ((usAddress >= usRegDiscreteStart)
  432. && (usRegDiscreteNregs >= 1)
  433. && (pucRegDiscreteBuf != NULL)
  434. && ((usAddress + usNDiscrete) <= (usRegDiscreteStart + (usRegDiscreteNregs * 16)))
  435. && (usNDiscrete >= 1)) {
  436. iRegIndex = (USHORT) (usAddress - usRegDiscreteStart) / 8; // Get register index in the buffer for bit number
  437. iRegBitIndex = (USHORT)(usAddress - usRegDiscreteStart) % 8; // Get bit index
  438. UCHAR* pucTempBuf = &pucDiscreteInputBuf[iRegIndex];
  439. while (iNReg > 0) {
  440. *pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8);
  441. iNReg--;
  442. }
  443. pucRegBuffer--;
  444. // Last discrete
  445. usNDiscrete = usNDiscrete % 8;
  446. // Filling zero to high bit
  447. *pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
  448. *pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
  449. // Send an event to notify application task about event
  450. (void)send_param_access_notification(MB_EVENT_DISCRETE_RD);
  451. (void)send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)usAddress,
  452. (uint8_t*)pucTempBuf, (uint16_t)usNDiscrete);
  453. } else {
  454. eStatus = MB_ENOREG;
  455. }
  456. return eStatus;
  457. }
  458. #pragma GCC diagnostic pop // require GCC