Add modbus module (currently not work)
This commit is contained in:
parent
4746b6269c
commit
cf049a6101
@ -66,6 +66,18 @@ typedef enum {
|
||||
START, STOP
|
||||
} OptStates;
|
||||
extern OptStates opt_state;
|
||||
extern uint16_t modbus_cnt;
|
||||
|
||||
#define DEVICE_CONTROL 0x01
|
||||
#define DEVICE_CNT modbus_cnt
|
||||
#define DEVICE_MIN_RECEIVE_PULSES 5
|
||||
#define DEVICE_MAX_LOST_PULSES 20
|
||||
#define DEVICE_MIN_PULSE_WIDTH 250
|
||||
#define DEVICE_MAX_PULSE_WIDTH 750
|
||||
|
||||
typedef struct {} ModbusRegistersStruct;
|
||||
extern ModbusRegistersStruct ModbusRegisters;
|
||||
|
||||
/* USER CODE END Private defines */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
36
Core/Inc/modbus_crc.h
Normal file
36
Core/Inc/modbus_crc.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: mbcrc.h,v 1.5 2006/12/07 22:10:34 wolti Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MB_CRC_H
|
||||
#define _MB_CRC_H
|
||||
|
||||
CRC_t usMBCRC16( UCHAR * pucFrame, USHORT usLen );
|
||||
|
||||
#endif
|
84
Core/Inc/modbus_lib.h
Normal file
84
Core/Inc/modbus_lib.h
Normal file
@ -0,0 +1,84 @@
|
||||
#define MODBUS_LIB_MAX_BUFFER 256
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_ADDRESS_BROADCAST ( 0 ) /*! Modbus broadcast address. */
|
||||
#define MB_ADDRESS_MIN ( 1 ) /*! Smallest possible slave address. */
|
||||
#define MB_ADDRESS_MAX ( 247 ) /*! Biggest possible slave address. */
|
||||
#define MB_FUNC_NONE ( 0 )
|
||||
#define MB_FUNC_READ_COILS ( 1 )
|
||||
#define MB_FUNC_READ_DISCRETE_INPUTS ( 2 )
|
||||
#define MB_FUNC_WRITE_SINGLE_COIL ( 5 )
|
||||
#define MB_FUNC_WRITE_MULTIPLE_COILS ( 15 )
|
||||
#define MB_FUNC_READ_HOLDING_REGISTERS ( 3 )
|
||||
#define MB_FUNC_READ_INPUT_REGISTER ( 4 )
|
||||
#define MB_FUNC_WRITE_REGISTER ( 6 )
|
||||
#define MB_FUNC_WRITE_MULTIPLE_REGISTERS ( 16 )
|
||||
#define MB_FUNC_READWRITE_MULTIPLE_REGISTERS ( 23 )
|
||||
#define MB_FUNC_DIAG_READ_EXCEPTION ( 7 )
|
||||
#define MB_FUNC_DIAG_DIAGNOSTIC ( 8 )
|
||||
#define MB_FUNC_DIAG_GET_COM_EVENT_CNT ( 11 )
|
||||
#define MB_FUNC_DIAG_GET_COM_EVENT_LOG ( 12 )
|
||||
#define MB_FUNC_OTHER_REPORT_SLAVEID ( 17 )
|
||||
#define MB_FUNC_ERROR ( 128 )
|
||||
|
||||
/*----------------------- Response types ------------------------------*/
|
||||
#define MBUS_RESPONSE_OK 0x00
|
||||
#define MBUS_RESPONSE_NONE 0xFF
|
||||
/* MBUS_RESPONSE_ILLEGAL_FUNCTION
|
||||
The function code received in the query is not an allowable action
|
||||
for the server. This may be because the function code is only
|
||||
applicable to newerdevices, and was not implemented in the unit
|
||||
selected. It could also indicate that the serveris in the wrong state to
|
||||
process a request of this type, for example because it is
|
||||
unconfigured and is being asked to return register values.
|
||||
*/
|
||||
#define MBUS_RESPONSE_ILLEGAL_FUNCTION 0x01
|
||||
|
||||
/* MBUS_RESPONSE_ILLEGAL_DATA_ADDRESS
|
||||
The data address received in the query is not an allowable address
|
||||
for the server. More specifically, the combination of reference number
|
||||
and transfer length is invalid. For a controller with 100 registers,
|
||||
the PDU addresses the first register as 0, and the last one as 99. If
|
||||
a request is submitted with a starting register address of 96 and a
|
||||
quantity of registers of 4, then this request will successfully
|
||||
operate (address-wise at least) on registers 96, 97, 98, 99. If
|
||||
a request is submitted with a starting register address of 96 and
|
||||
a quantity of registers of 5, then this request will fail with
|
||||
Exception Code 0x02 “Illegal Data Address” since it attempts to
|
||||
operate on registers 96, 97, 98, 99 and 100, and there is no
|
||||
register with address 100.
|
||||
*/
|
||||
#define MBUS_RESPONSE_ILLEGAL_DATA_ADDRESS 0x02
|
||||
|
||||
/* A value contained in the query data field is not an allowable value
|
||||
for server. This indicates a fault in the structure of the remainder of
|
||||
a complex request, such as that the implied length is
|
||||
incorrect. It specifically does NOT mean that a data item submitted for
|
||||
storage in a register has a value outside the expectation of the application
|
||||
program, since the MODBUS protocol is unaware of the significance of
|
||||
any particular val ue of any particular register.
|
||||
*/
|
||||
#define MBUS_RESPONSE_ILLEGAL_DATA_VALUE 0x03
|
||||
/*
|
||||
An unrecoverable error occurred while the server
|
||||
was attempting to perform the requested action.
|
||||
*/
|
||||
#define MBUS_RESPONSE_SERVICE_DEVICE_FAILURE 0x04
|
||||
|
||||
|
||||
#define MB_ADDRESS_HOLDING_REGISTER_OFFSET (40001)
|
||||
|
||||
typedef struct ModbusConfig_t{
|
||||
uint16_t address;
|
||||
} ModbusConfig_t;
|
||||
|
||||
extern uint8_t g_modbus_lib_received_telegram[MODBUS_LIB_MAX_BUFFER];
|
||||
extern uint16_t g_modbus_lib_received_length;
|
||||
|
||||
extern int modbus_lib_init(ModbusConfig_t*);
|
||||
extern void modbus_lib_append_data(uint8_t);
|
||||
extern void modbus_lib_end_of_telegram(void);
|
||||
extern uint16_t modbus_lib_send_error(int error_code);
|
||||
extern int modbus_lib_transport_write(uint8_t* buffer, uint16_t length);
|
||||
extern uint16_t modbus_lib_read_handler(uint16_t la);
|
||||
extern uint16_t modbus_lib_write_handler(uint16_t la, uint16_t value);
|
10
Core/Inc/modbus_lib_types.h
Normal file
10
Core/Inc/modbus_lib_types.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _MODBUS_LIB_H_
|
||||
#define _MODBUS_LIB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MODBUS_LIB_MIN_TELEGRAM_SIZE 4 /*!< Minimum size of a Modbus RTU frame. */
|
||||
|
||||
|
||||
#endif // _MODBUS_LIB_H_
|
21
Core/Inc/port.h
Normal file
21
Core/Inc/port.h
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char BOOL;
|
||||
|
||||
typedef unsigned char UCHAR;
|
||||
typedef char CHAR;
|
||||
|
||||
typedef unsigned int USHORT;
|
||||
typedef int SHORT;
|
||||
|
||||
typedef unsigned long ULONG;
|
||||
typedef long LONG;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t low;
|
||||
uint8_t high;
|
||||
} bytes;
|
||||
uint16_t value;
|
||||
} CRC_t, MbDataField;
|
||||
|
@ -41,12 +41,15 @@
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
TIM_HandleTypeDef htim2;
|
||||
TIM_HandleTypeDef htim3;
|
||||
TIM_HandleTypeDef htim4;
|
||||
|
||||
UART_HandleTypeDef huart1;
|
||||
UART_HandleTypeDef huart2;
|
||||
|
||||
/* USER CODE BEGIN PV */
|
||||
OptStates opt_state = STOP;
|
||||
ModbusRegistersStruct ModbusRegisters;
|
||||
/* USER CODE END PV */
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
@ -55,6 +58,8 @@ static void MX_GPIO_Init(void);
|
||||
static void MX_TIM4_Init(void);
|
||||
static void MX_USART1_UART_Init(void);
|
||||
static void MX_TIM2_Init(void);
|
||||
static void MX_TIM3_Init(void);
|
||||
static void MX_USART2_UART_Init(void);
|
||||
/* USER CODE BEGIN PFP */
|
||||
|
||||
/* USER CODE END PFP */
|
||||
@ -95,6 +100,8 @@ int main(void)
|
||||
MX_TIM4_Init();
|
||||
MX_USART1_UART_Init();
|
||||
MX_TIM2_Init();
|
||||
MX_TIM3_Init();
|
||||
MX_USART2_UART_Init();
|
||||
/* USER CODE BEGIN 2 */
|
||||
HAL_TIM_Base_Start_IT(&htim2);
|
||||
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
|
||||
@ -191,6 +198,7 @@ static void MX_TIM2_Init(void)
|
||||
htim2.Init.Prescaler = 72;
|
||||
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim2.Init.Period = 5000;
|
||||
|
||||
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
|
||||
@ -220,12 +228,63 @@ static void MX_TIM2_Init(void)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
|
||||
sConfigIC.ICFilter = 0;
|
||||
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
/* USER CODE BEGIN TIM2_Init 2 */
|
||||
|
||||
/* USER CODE END TIM2_Init 2 */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TIM3 Initialization Function
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
static void MX_TIM3_Init(void)
|
||||
{
|
||||
|
||||
/* USER CODE BEGIN TIM3_Init 0 */
|
||||
|
||||
/* USER CODE END TIM3_Init 0 */
|
||||
|
||||
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
|
||||
/* USER CODE BEGIN TIM3_Init 1 */
|
||||
|
||||
/* USER CODE END TIM3_Init 1 */
|
||||
htim3.Instance = TIM3;
|
||||
htim3.Init.Prescaler = 0;
|
||||
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
htim3.Init.Period = 65535;
|
||||
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
|
||||
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
/* USER CODE BEGIN TIM3_Init 2 */
|
||||
|
||||
/* USER CODE END TIM3_Init 2 */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TIM4 Initialization Function
|
||||
* @param None
|
||||
@ -318,6 +377,39 @@ static void MX_USART1_UART_Init(void)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART2 Initialization Function
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
static void MX_USART2_UART_Init(void)
|
||||
{
|
||||
|
||||
/* USER CODE BEGIN USART2_Init 0 */
|
||||
|
||||
/* USER CODE END USART2_Init 0 */
|
||||
|
||||
/* USER CODE BEGIN USART2_Init 1 */
|
||||
|
||||
/* USER CODE END USART2_Init 1 */
|
||||
huart2.Instance = USART2;
|
||||
huart2.Init.BaudRate = 115200;
|
||||
huart2.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
huart2.Init.StopBits = UART_STOPBITS_1;
|
||||
huart2.Init.Parity = UART_PARITY_NONE;
|
||||
huart2.Init.Mode = UART_MODE_TX_RX;
|
||||
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
||||
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
|
||||
if (HAL_UART_Init(&huart2) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
/* USER CODE BEGIN USART2_Init 2 */
|
||||
|
||||
/* USER CODE END USART2_Init 2 */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GPIO Initialization Function
|
||||
* @param None
|
||||
|
98
Core/Src/modbus_crc.c
Normal file
98
Core/Src/modbus_crc.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: mbcrc.c,v 1.7 2007/02/18 23:50:27 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "port.h"
|
||||
|
||||
static const UCHAR aucCRCHi[] = {
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40
|
||||
};
|
||||
|
||||
static const UCHAR aucCRCLo[] = {
|
||||
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
|
||||
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
|
||||
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
|
||||
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
|
||||
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
|
||||
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
|
||||
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
|
||||
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
|
||||
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
|
||||
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
|
||||
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
|
||||
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
|
||||
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
|
||||
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
|
||||
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
|
||||
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
|
||||
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
|
||||
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
|
||||
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
|
||||
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
|
||||
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
|
||||
0x41, 0x81, 0x80, 0x40
|
||||
};
|
||||
|
||||
CRC_t
|
||||
usMBCRC16( UCHAR * pucFrame, USHORT usLen )
|
||||
{
|
||||
UCHAR ucCRCHi = 0xFF;
|
||||
UCHAR ucCRCLo = 0xFF;
|
||||
int iIndex;
|
||||
|
||||
while( usLen-- )
|
||||
{
|
||||
iIndex = ucCRCLo ^ *( pucFrame++ );
|
||||
ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );
|
||||
ucCRCHi = aucCRCLo[iIndex];
|
||||
}
|
||||
return ( CRC_t ){ .bytes.high = ucCRCHi, .bytes.low = ucCRCLo };
|
||||
}
|
141
Core/Src/modbus_lib.c
Normal file
141
Core/Src/modbus_lib.c
Normal file
@ -0,0 +1,141 @@
|
||||
/*******************************************************
|
||||
*
|
||||
* Implementation reference:
|
||||
*
|
||||
* http://www.mayor.de/lian98/doc.en/html/u_mbusser-rtu_struct.htm
|
||||
* https://www.modbustools.com/modbus.html
|
||||
*
|
||||
* Example telegrams:
|
||||
* Read holding registers: 010300000002c40b (read 2 registers starting from 40001)
|
||||
* Read holding registers: 0103000000044409 (read 4 registers starting from 40001)
|
||||
*
|
||||
* Write single register: 01060000007bc9e9 (write "123" to register 40001)
|
||||
*
|
||||
* Write multiple registers: 01100000000204007b0237c300
|
||||
*
|
||||
*******************************************************/
|
||||
|
||||
#include "modbus_lib_types.h"
|
||||
#include "modbus_lib.h"
|
||||
#include "port.h"
|
||||
#include "modbus_crc.h"
|
||||
|
||||
uint8_t g_modbus_lib_received_telegram[MODBUS_LIB_MAX_BUFFER];
|
||||
uint16_t g_modbus_lib_received_length = 0;
|
||||
uint8_t g_modbus_lib_exception_occurred = 0;
|
||||
ModbusConfig_t* config;
|
||||
|
||||
int modbus_lib_init(ModbusConfig_t* cfg){
|
||||
config = cfg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void modbus_lib_append_data(uint8_t byte){
|
||||
if (g_modbus_lib_received_length < MODBUS_LIB_MAX_BUFFER){
|
||||
g_modbus_lib_received_telegram[g_modbus_lib_received_length++] = byte;
|
||||
}
|
||||
}
|
||||
|
||||
void modbus_lib_end_of_telegram(){
|
||||
(void) 0; // debugger: p/x *g_modbus_lib_received_telegram@g_modbus_lib_received_length
|
||||
|
||||
// Check length
|
||||
if (g_modbus_lib_received_length < MODBUS_LIB_MIN_TELEGRAM_SIZE){
|
||||
modbus_lib_send_error(MBUS_RESPONSE_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check CRC
|
||||
CRC_t expected = usMBCRC16(g_modbus_lib_received_telegram, g_modbus_lib_received_length-2);
|
||||
UCHAR got_low = g_modbus_lib_received_telegram[g_modbus_lib_received_length-2];
|
||||
UCHAR got_high = g_modbus_lib_received_telegram[g_modbus_lib_received_length-1];
|
||||
if ((expected.bytes.low != got_low) || (expected.bytes.high != got_high)){
|
||||
modbus_lib_send_error(MBUS_RESPONSE_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check address
|
||||
if (config->address != g_modbus_lib_received_telegram[0]){
|
||||
modbus_lib_send_error(MBUS_RESPONSE_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Telegram is okay, call the relevant handler
|
||||
// -------------------------------------------
|
||||
uint8_t outgoing_telegram[MODBUS_LIB_MAX_BUFFER];
|
||||
uint16_t oindex = 0;
|
||||
|
||||
outgoing_telegram[oindex++] = config->address;
|
||||
|
||||
volatile MbDataField start_addr, count, res, addr, value;
|
||||
|
||||
switch (g_modbus_lib_received_telegram[1]){
|
||||
case MB_FUNC_READ_HOLDING_REGISTERS:
|
||||
start_addr.bytes.high = g_modbus_lib_received_telegram[2];
|
||||
start_addr.bytes.low = g_modbus_lib_received_telegram[3];
|
||||
count.bytes.high = g_modbus_lib_received_telegram[4];
|
||||
count.bytes.low = g_modbus_lib_received_telegram[5];
|
||||
|
||||
outgoing_telegram[oindex++] = g_modbus_lib_received_telegram[1]; // function code
|
||||
outgoing_telegram[oindex++] = count.value * 2; // byte count
|
||||
|
||||
for (uint16_t i=0; i < count.value; i++){
|
||||
res.value = modbus_lib_read_handler(start_addr.value + i + MB_ADDRESS_HOLDING_REGISTER_OFFSET);
|
||||
if (g_modbus_lib_exception_occurred){
|
||||
g_modbus_lib_exception_occurred = 0;
|
||||
return;
|
||||
}
|
||||
outgoing_telegram[oindex++] = res.bytes.high;
|
||||
outgoing_telegram[oindex++] = res.bytes.low;
|
||||
}
|
||||
|
||||
CRC_t crc = usMBCRC16(outgoing_telegram, oindex);
|
||||
outgoing_telegram[oindex++] = crc.bytes.low;
|
||||
outgoing_telegram[oindex++] = crc.bytes.high;
|
||||
|
||||
modbus_lib_transport_write(outgoing_telegram, oindex);
|
||||
break;
|
||||
case MB_FUNC_WRITE_REGISTER:
|
||||
addr.bytes.high = g_modbus_lib_received_telegram[2];
|
||||
addr.bytes.low = g_modbus_lib_received_telegram[3];
|
||||
value.bytes.high = g_modbus_lib_received_telegram[4];
|
||||
value.bytes.low = g_modbus_lib_received_telegram[5];
|
||||
|
||||
if (modbus_lib_write_handler(addr.value + MB_ADDRESS_HOLDING_REGISTER_OFFSET, value.value) == 0){
|
||||
// success
|
||||
// normal response is the echo of received telegram
|
||||
modbus_lib_transport_write(g_modbus_lib_received_telegram, g_modbus_lib_received_length);
|
||||
} else {
|
||||
// error
|
||||
g_modbus_lib_exception_occurred = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unimplemented
|
||||
modbus_lib_send_error(MBUS_RESPONSE_SERVICE_DEVICE_FAILURE);
|
||||
g_modbus_lib_exception_occurred = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
g_modbus_lib_received_length = 0;
|
||||
}
|
||||
|
||||
#define MB_EXCEPTION_LENGTH 5
|
||||
|
||||
uint16_t modbus_lib_send_error(int error_code){
|
||||
if (error_code != MBUS_RESPONSE_NONE){
|
||||
g_modbus_lib_exception_occurred = 1;
|
||||
uint8_t res[MB_EXCEPTION_LENGTH] = {
|
||||
config->address,
|
||||
g_modbus_lib_received_telegram[1] | 0x80,
|
||||
error_code
|
||||
};
|
||||
CRC_t crc = usMBCRC16(res, MB_EXCEPTION_LENGTH - 2);
|
||||
res[MB_EXCEPTION_LENGTH - 2] = crc.bytes.low;
|
||||
res[MB_EXCEPTION_LENGTH - 1] = crc.bytes.high;
|
||||
|
||||
modbus_lib_transport_write(res, MB_EXCEPTION_LENGTH);
|
||||
}
|
||||
g_modbus_lib_received_length = 0;
|
||||
return -1;
|
||||
}
|
@ -101,13 +101,15 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
|
||||
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
/**TIM2 GPIO Configuration
|
||||
PA0-WKUP ------> TIM2_CH1
|
||||
PA15 ------> TIM2_CH1
|
||||
*/
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_0;
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_15;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
__HAL_AFIO_REMAP_TIM2_PARTIAL_1();
|
||||
|
||||
/* TIM2 interrupt Init */
|
||||
HAL_NVIC_SetPriority(TIM2_IRQn, 14, 0);
|
||||
HAL_NVIC_EnableIRQ(TIM2_IRQn);
|
||||
@ -115,6 +117,17 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
|
||||
|
||||
/* USER CODE END TIM2_MspInit 1 */
|
||||
}
|
||||
else if(htim_base->Instance==TIM3)
|
||||
{
|
||||
/* USER CODE BEGIN TIM3_MspInit 0 */
|
||||
|
||||
/* USER CODE END TIM3_MspInit 0 */
|
||||
/* Peripheral clock enable */
|
||||
__HAL_RCC_TIM3_CLK_ENABLE();
|
||||
/* USER CODE BEGIN TIM3_MspInit 1 */
|
||||
|
||||
/* USER CODE END TIM3_MspInit 1 */
|
||||
}
|
||||
else if(htim_base->Instance==TIM4)
|
||||
{
|
||||
/* USER CODE BEGIN TIM4_MspInit 0 */
|
||||
@ -170,9 +183,9 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
|
||||
__HAL_RCC_TIM2_CLK_DISABLE();
|
||||
|
||||
/**TIM2 GPIO Configuration
|
||||
PA0-WKUP ------> TIM2_CH1
|
||||
PA15 ------> TIM2_CH1
|
||||
*/
|
||||
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0);
|
||||
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_15);
|
||||
|
||||
/* TIM2 interrupt DeInit */
|
||||
HAL_NVIC_DisableIRQ(TIM2_IRQn);
|
||||
@ -180,6 +193,17 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
|
||||
|
||||
/* USER CODE END TIM2_MspDeInit 1 */
|
||||
}
|
||||
else if(htim_base->Instance==TIM3)
|
||||
{
|
||||
/* USER CODE BEGIN TIM3_MspDeInit 0 */
|
||||
|
||||
/* USER CODE END TIM3_MspDeInit 0 */
|
||||
/* Peripheral clock disable */
|
||||
__HAL_RCC_TIM3_CLK_DISABLE();
|
||||
/* USER CODE BEGIN TIM3_MspDeInit 1 */
|
||||
|
||||
/* USER CODE END TIM3_MspDeInit 1 */
|
||||
}
|
||||
else if(htim_base->Instance==TIM4)
|
||||
{
|
||||
/* USER CODE BEGIN TIM4_MspDeInit 0 */
|
||||
@ -230,6 +254,33 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart)
|
||||
|
||||
/* USER CODE END USART1_MspInit 1 */
|
||||
}
|
||||
else if(huart->Instance==USART2)
|
||||
{
|
||||
/* USER CODE BEGIN USART2_MspInit 0 */
|
||||
|
||||
/* USER CODE END USART2_MspInit 0 */
|
||||
/* Peripheral clock enable */
|
||||
__HAL_RCC_USART2_CLK_ENABLE();
|
||||
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
/**USART2 GPIO Configuration
|
||||
PA2 ------> USART2_TX
|
||||
PA3 ------> USART2_RX
|
||||
*/
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_2;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_3;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
/* USER CODE BEGIN USART2_MspInit 1 */
|
||||
|
||||
/* USER CODE END USART2_MspInit 1 */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -259,6 +310,24 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
|
||||
|
||||
/* USER CODE END USART1_MspDeInit 1 */
|
||||
}
|
||||
else if(huart->Instance==USART2)
|
||||
{
|
||||
/* USER CODE BEGIN USART2_MspDeInit 0 */
|
||||
|
||||
/* USER CODE END USART2_MspDeInit 0 */
|
||||
/* Peripheral clock disable */
|
||||
__HAL_RCC_USART2_CLK_DISABLE();
|
||||
|
||||
/**USART2 GPIO Configuration
|
||||
PA2 ------> USART2_TX
|
||||
PA3 ------> USART2_RX
|
||||
*/
|
||||
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
|
||||
|
||||
/* USER CODE BEGIN USART2_MspDeInit 1 */
|
||||
|
||||
/* USER CODE END USART2_MspDeInit 1 */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -213,15 +213,29 @@ void TIM2_IRQHandler(void)
|
||||
_state_counter = 0;
|
||||
LED_GPIO_Port->BSRR = LED_Pin;
|
||||
}
|
||||
// задний фронт, убывающий
|
||||
if (sr & TIM_SR_CC2IF) {
|
||||
uint32_t ccr = TIM2->CCR2;
|
||||
if (ccr >= DEVICE_MIN_PULSE_WIDTH && ccr <= DEVICE_MAX_PULSE_WIDTH) {
|
||||
// можно считать что это импульс
|
||||
if (_state_counter > DEVICE_MIN_RECEIVE_PULSES) {
|
||||
LED_GPIO_Port->BSRR = (uint32_t)LED_Pin << 16;
|
||||
opt_state = START;
|
||||
} else {
|
||||
_state_counter++;
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// // какое-то говно, а не импульс
|
||||
// // ну я ничего не делаю
|
||||
// }
|
||||
}
|
||||
|
||||
// передний возрастающий фронт
|
||||
// тут по сути надо обнулить CNT, и вроде все
|
||||
if (sr & TIM_SR_CC1IF) {
|
||||
TIM2->SR = TIM_SR_CC1IF;
|
||||
TIM2->CNT = 0; // обнуляем счетчик таймера
|
||||
if (_state_counter > 20) {
|
||||
LED_GPIO_Port->BSRR = (uint32_t)LED_Pin << 16;
|
||||
opt_state = START;
|
||||
} else {
|
||||
_state_counter++;
|
||||
}
|
||||
}
|
||||
/* USER CODE END TIM2_IRQn 0 */
|
||||
HAL_TIM_IRQHandler(&htim2);
|
||||
|
@ -7,25 +7,31 @@ Mcu.IP0=NVIC
|
||||
Mcu.IP1=RCC
|
||||
Mcu.IP2=SYS
|
||||
Mcu.IP3=TIM2
|
||||
Mcu.IP4=TIM4
|
||||
Mcu.IP5=USART1
|
||||
Mcu.IPNb=6
|
||||
Mcu.IP4=TIM3
|
||||
Mcu.IP5=TIM4
|
||||
Mcu.IP6=USART1
|
||||
Mcu.IP7=USART2
|
||||
Mcu.IPNb=8
|
||||
Mcu.Name=STM32F103C(8-B)Tx
|
||||
Mcu.Package=LQFP48
|
||||
Mcu.Pin0=PD0-OSC_IN
|
||||
Mcu.Pin1=PD1-OSC_OUT
|
||||
Mcu.Pin10=VP_SYS_VS_Systick
|
||||
Mcu.Pin11=VP_TIM2_VS_ClockSourceINT
|
||||
Mcu.Pin12=VP_TIM4_VS_ClockSourceINT
|
||||
Mcu.Pin2=PA0-WKUP
|
||||
Mcu.Pin3=PB12
|
||||
Mcu.Pin4=PA9
|
||||
Mcu.Pin5=PA10
|
||||
Mcu.Pin6=PA13
|
||||
Mcu.Pin7=PA14
|
||||
Mcu.Pin8=PB5
|
||||
Mcu.Pin9=PB6
|
||||
Mcu.PinsNb=13
|
||||
Mcu.Pin10=PB5
|
||||
Mcu.Pin11=PB6
|
||||
Mcu.Pin12=VP_SYS_VS_Systick
|
||||
Mcu.Pin13=VP_TIM2_VS_ClockSourceINT
|
||||
Mcu.Pin14=VP_TIM2_VS_indirect_ch1
|
||||
Mcu.Pin15=VP_TIM3_VS_ClockSourceINT
|
||||
Mcu.Pin16=VP_TIM4_VS_ClockSourceINT
|
||||
Mcu.Pin2=PA2
|
||||
Mcu.Pin3=PA3
|
||||
Mcu.Pin4=PB12
|
||||
Mcu.Pin5=PA9
|
||||
Mcu.Pin6=PA10
|
||||
Mcu.Pin7=PA13
|
||||
Mcu.Pin8=PA14
|
||||
Mcu.Pin9=PA15
|
||||
Mcu.PinsNb=17
|
||||
Mcu.ThirdPartyNb=0
|
||||
Mcu.UserConstants=
|
||||
Mcu.UserName=STM32F103C8Tx
|
||||
@ -43,13 +49,17 @@ NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false
|
||||
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true
|
||||
NVIC.TIM2_IRQn=true\:14\:0\:true\:false\:true\:true\:true
|
||||
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false
|
||||
PA0-WKUP.Signal=S_TIM2_CH1_ETR
|
||||
PA10.Mode=Asynchronous
|
||||
PA10.Signal=USART1_RX
|
||||
PA13.Mode=Serial_Wire
|
||||
PA13.Signal=SYS_JTMS-SWDIO
|
||||
PA14.Mode=Serial_Wire
|
||||
PA14.Signal=SYS_JTCK-SWCLK
|
||||
PA15.Signal=S_TIM2_CH1_ETR
|
||||
PA2.Mode=Asynchronous
|
||||
PA2.Signal=USART2_TX
|
||||
PA3.Mode=Asynchronous
|
||||
PA3.Signal=USART2_RX
|
||||
PA9.Mode=Asynchronous
|
||||
PA9.Signal=USART1_TX
|
||||
PB12.GPIOParameters=GPIO_Label
|
||||
@ -119,8 +129,9 @@ SH.S_TIM4_CH1.0=TIM4_CH1,PWM Generation1 CH1
|
||||
SH.S_TIM4_CH1.ConfNb=1
|
||||
TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
|
||||
TIM2.Channel-Input_Capture1_from_TI1=TIM_CHANNEL_1
|
||||
TIM2.Channel-Input_Capture2_from_TI1_63=TIM_CHANNEL_2
|
||||
TIM2.ICFilter_CH1=1
|
||||
TIM2.IPParameters=Channel-Input_Capture1_from_TI1,ICFilter_CH1,Prescaler,Period,AutoReloadPreload
|
||||
TIM2.IPParameters=Channel-Input_Capture1_from_TI1,ICFilter_CH1,Prescaler,Period,AutoReloadPreload,Channel-Input_Capture2_from_TI1_63
|
||||
TIM2.Period=5000
|
||||
TIM2.Prescaler=72
|
||||
TIM4.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
|
||||
@ -130,10 +141,16 @@ TIM4.Prescaler=72
|
||||
TIM4.Pulse-PWM\ Generation1\ CH1=500
|
||||
USART1.IPParameters=VirtualMode
|
||||
USART1.VirtualMode=VM_ASYNC
|
||||
USART2.IPParameters=VirtualMode
|
||||
USART2.VirtualMode=VM_ASYNC
|
||||
VP_SYS_VS_Systick.Mode=SysTick
|
||||
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
|
||||
VP_TIM2_VS_ClockSourceINT.Mode=Internal
|
||||
VP_TIM2_VS_ClockSourceINT.Signal=TIM2_VS_ClockSourceINT
|
||||
VP_TIM2_VS_indirect_ch1.Mode=Input_Capture2_from_TI1_63
|
||||
VP_TIM2_VS_indirect_ch1.Signal=TIM2_VS_indirect_ch1
|
||||
VP_TIM3_VS_ClockSourceINT.Mode=Internal
|
||||
VP_TIM3_VS_ClockSourceINT.Signal=TIM3_VS_ClockSourceINT
|
||||
VP_TIM4_VS_ClockSourceINT.Mode=Internal
|
||||
VP_TIM4_VS_ClockSourceINT.Signal=TIM4_VS_ClockSourceINT
|
||||
board=custom
|
||||
|
Loading…
x
Reference in New Issue
Block a user