반응형

https://youtube.com/live/0GP7utrIFTw

[STM32#26] stm32를 modbus rtu 슬레이브(slave)로 C# winform을 마스터(master)로 설정해서 제어하는 방법(2)!(녹칸다 내맘대로 STM32)

심심한녹칸다의 내맘대로 STM32시리즈이다!

STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3b2751bc412_11_154#slide=id.g3b2751bc412_11_154

마스터인 C#윈폼에서는 nmodbus4 라이브러리를 적용해보도록 하자!

1.예제25편-2에서 만든 예제를 마스터쪽에 nmodbus4라이브러리를 적용해서 stm32가 정상적으로 작동되는지 보이시오!

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 //녹칸다의 STM32쉴드의 8개의 LED를 배열로 뭉쳐놓은것!
 GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
 uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 /* USER CODE END 2 */

while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  uint16_t coil_addr = (req[2] << 8) | req[3];
			  uint16_t coil_data = (req[4] << 8) | req[5];
			  uint16_t crc16 = (req[7] << 8) | req[6];
			  //STM32가 CRC16을 계산함!
			  uint16_t my_crc16 = cal_crc(req,6);
			  if(crc16 == my_crc16){
				  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,coil_addr,coil_data,crc16);
				  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
				  //write single coil명령어 수행
				  if(coil_data == 0xFF00){
					  //ON
					  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 1);
				  }else{
					  //OFF
					  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 0);
				  }
				  //응답전송! 그런데 FC=0x05는 req와 res가 같다!
				  HAL_UART_Transmit(&huart1, req, 8, 100);
			  }else{
				  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
			  }
		  }
	  }
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //coil 0번을 ON한다!
            msm.WriteSingleCoil(1, 0, true);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 0, false);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, true);
        }
        private void button5_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, false);
        }

        private void button6_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, true);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, false);
        }

        private void button8_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, true);
        }

        private void button9_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, false);
        }

        

        private void button10_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, true);
        }

        private void button11_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, false);
        }
        private void button12_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, true);
        }

        private void button13_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, false);
        }

        private void button14_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, true);
        }

        private void button15_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, false);
        }
        private void button16_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, true);
        }
        private void button17_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, false);
        }


2.예제1에 더해서 마스터쪽에서 readcoil을 실행했을때 STM32의 8개의 LED의 작동상태를 읽어와서 화면에 출력할수있도록 하시오!

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 //녹칸다의 STM32쉴드의 8개의 LED를 배열로 뭉쳐놓은것!
 GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
 uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 uint8_t res[6]; //read coils 전용
 uint8_t write_coil[1];
 /* USER CODE END 2 */


while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  if(fc == 0x01){
				  //read coils
				  uint16_t start_addr = (req[2] << 8) | req[3];
				  uint16_t count = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  //read coil 응답데이터 만들기
					  //write_coil[0]여기에 순서에 맞게 bit 상태를 넣으면 됨!
					  write_coil[0] = 0;
					  for(int i = 0; i < count; i++)
					  {
					      if(mygroup[i]->ODR & mypin[i])
					          write_coil[0] |= (1 << i);
					  }
					  sprintf(tx_buff,"id=%02X, fc=%02X, start_addr=%04X, cnt=%04X, crc=%04X, data=%d\n",id,fc,start_addr,count,crc16,write_coil[0]);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  res[0] = id;
					  res[1] = fc;
					  res[2] = 1; //응답데이터는 1byte이다!
					  res[3] = write_coil[0];
					  uint16_t res_crc = cal_crc(res,4);

					  res[4] = res_crc & 0xFF; //res_crc의 하위8bit
					  res[5] = res_crc >> 8; //res_crc의 상위8bit
					  HAL_UART_Transmit(&huart1, res, 6, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }
			  }else if(fc == 0x05){
				  //write single coil
				  uint16_t coil_addr = (req[2] << 8) | req[3];
				  uint16_t coil_data = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,coil_addr,coil_data,crc16);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  //write single coil명령어 수행
					  if(coil_data == 0xFF00){
						  //ON
						  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 1);
					  }else{
						  //OFF
						  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 0);
					  }
					  //응답전송! 그런데 FC=0x05는 req와 res가 같다!
					  HAL_UART_Transmit(&huart1, req, 8, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }
			  }
		  }
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //coil 0번을 ON한다!
            msm.WriteSingleCoil(1, 0, true);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 0, false);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, true);
        }
        private void button5_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, false);
        }

        private void button6_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, true);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, false);
        }

        private void button8_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, true);
        }

        private void button9_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, false);
        }
        private void button10_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, true);
        }

        private void button11_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, false);
        }
        private void button12_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, true);
        }
        private void button13_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, false);
        }

        private void button14_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, true);
        }

        private void button15_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, false);
        }
        private void button16_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, true);
        }
        private void button17_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, false);
        }
        private void button18_Click(object sender, EventArgs e)
        {
            //라이브러리가 stm32에게 0번지부터 8개의 쓰기코일값을 요구한다!
            bool[] data = msm.ReadCoils(1, 0, 8);

            string text = "";
            for(int i = 0; i < 8; i++)
            {
                text += data[i].ToString() + ", ";
            }
            richTextBox1.Text += text + "\n";
        }


3.예제2번에서 마스터쪽에 STM32쪽 LED가 켜져있으면 ON버튼의 배경색을 녹색으로 하고, OFF상태면 OFF버튼의 배경색을 빨간색으로 하시오! 여기서 C#의 timer를 하나 추가해서 100밀리초 간격으로 read coils 명령을 수행하도록하시오!

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 //녹칸다의 STM32쉴드의 8개의 LED를 배열로 뭉쳐놓은것!
 GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
 uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 uint8_t res[6]; //read coils 전용
 uint8_t write_coil[1];
 /* USER CODE END 2 */


while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  if(fc == 0x01){
				  //read coils
				  uint16_t start_addr = (req[2] << 8) | req[3];
				  uint16_t count = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  //read coil 응답데이터 만들기
					  //write_coil[0]여기에 순서에 맞게 bit 상태를 넣으면 됨!
					  write_coil[0] = 0;
					  for(int i = 0; i < count; i++)
					  {
					      if(mygroup[i]->ODR & mypin[i])
					          write_coil[0] |= (1 << i);
					  }
					  sprintf(tx_buff,"id=%02X, fc=%02X, start_addr=%04X, cnt=%04X, crc=%04X, data=%d\n",id,fc,start_addr,count,crc16,write_coil[0]);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  res[0] = id;
					  res[1] = fc;
					  res[2] = 1; //응답데이터는 1byte이다!
					  res[3] = write_coil[0];
					  uint16_t res_crc = cal_crc(res,4);

					  res[4] = res_crc & 0xFF; //res_crc의 하위8bit
					  res[5] = res_crc >> 8; //res_crc의 상위8bit
					  HAL_UART_Transmit(&huart1, res, 6, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }
			  }else if(fc == 0x05){
				  //write single coil
				  uint16_t coil_addr = (req[2] << 8) | req[3];
				  uint16_t coil_data = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,coil_addr,coil_data,crc16);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  //write single coil명령어 수행
					  if(coil_data == 0xFF00){
						  //ON
						  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 1);
					  }else{
						  //OFF
						  HAL_GPIO_WritePin(mygroup[coil_addr], mypin[coil_addr], 0);
					  }
					  //응답전송! 그런데 FC=0x05는 req와 res가 같다!
					  HAL_UART_Transmit(&huart1, req, 8, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }
			  }
		  }
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //coil 0번을 ON한다!
            msm.WriteSingleCoil(1, 0, true);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 0, false);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, true);
        }
        private void button5_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 1, false);
        }

        private void button6_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, true);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 2, false);
        }

        private void button8_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, true);
        }

        private void button9_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 3, false);
        }
        private void button10_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, true);
        }

        private void button11_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 4, false);
        }
        private void button12_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, true);
        }
        private void button13_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 5, false);
        }

        private void button14_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, true);
        }

        private void button15_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 6, false);
        }
        private void button16_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, true);
        }
        private void button17_Click(object sender, EventArgs e)
        {
            msm.WriteSingleCoil(1, 7, false);
        }
        private void button18_Click(object sender, EventArgs e)
        {
            timer1.Start();
        }


        private void timer1_Tick(object sender, EventArgs e)
        {
            //0.1초마다 이부분이 실행되는데 뭐할래?
            //라이브러리가 stm32에게 0번지부터 8개의 쓰기코일값을 요구한다!
            bool[] data = msm.ReadCoils(1, 0, 8);

            if (data[0])
            {
                button2.BackColor = Color.Green;
                button3.BackColor = SystemColors.Control;
            }
            else
            {
                button2.BackColor = SystemColors.Control;
                button3.BackColor = Color.Red;
            }
            if (data[1])
            {
                button4.BackColor = Color.Green;
                button5.BackColor = SystemColors.Control;
            }
            else
            {
                button4.BackColor = SystemColors.Control;
                button5.BackColor = Color.Red;
            }
            if (data[2])
            {
                button6.BackColor = Color.Green;
                button7.BackColor = SystemColors.Control;
            }
            else
            {
                button6.BackColor = SystemColors.Control;
                button7.BackColor = Color.Red;
            }

            if (data[3])
            {
                button8.BackColor = Color.Green;
                button9.BackColor = SystemColors.Control;
            }
            else
            {
                button8.BackColor = SystemColors.Control;
                button9.BackColor = Color.Red;
            }
            if (data[4])
            {
                button10.BackColor = Color.Green;
                button11.BackColor = SystemColors.Control;
            }
            else
            {
                button10.BackColor = SystemColors.Control;
                button11.BackColor = Color.Red;
            }
            if (data[5])
            {
                button12.BackColor = Color.Green;
                button13.BackColor = SystemColors.Control;
            }
            else
            {
                button12.BackColor = SystemColors.Control;
                button13.BackColor = Color.Red;
            }

            if (data[6])
            {
                button14.BackColor = Color.Green;
                button15.BackColor = SystemColors.Control;
            }
            else
            {
                button14.BackColor = SystemColors.Control;
                button15.BackColor = Color.Red;
            }
            if (data[7])
            {
                button16.BackColor = Color.Green;
                button17.BackColor = SystemColors.Control;
            }
            else
            {
                button16.BackColor = SystemColors.Control;
                button17.BackColor = Color.Red;
            }
        }


4.이번예제는 지금까지 했던 coil내용을 다 제거하고 write single register에 대해서 예제를 만들어보도록 한다! 마스터쪽에서 설정한 값을 stm32가 받아서 putty에 단순 출력하는 예시를 보이시오!

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 uint16_t w_reg[6] = {0}; //6word 정도의 레지스터가 있다고 가정함
 /* USER CODE END 2 */



while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  //fc가 0x06이 올 예정!
			  uint16_t reg_addr = (req[2] << 8) | req[3];
			  uint16_t reg_data = (req[4] << 8) | req[5];
			  uint16_t crc16 = (req[7] << 8) | req[6];
			  //STM32가 CRC16을 계산함!
			  uint16_t my_crc16 = cal_crc(req,6);
			  if(crc16 == my_crc16){
				  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,reg_addr,reg_data,crc16);
				  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
				  w_reg[reg_addr] = reg_data;
				  //레지스터가 6개 있으니까 6개의 값을 출력한다!
				  sprintf(tx_buff,"w_reg=%d, %d, %d, %d, %d, %d\n",w_reg[0],w_reg[1],w_reg[2],w_reg[3],w_reg[4],w_reg[5]);
				  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
				  //응답은 req를 그대로 응답하면 된다!
				  HAL_UART_Transmit(&huart1, req, 8, 100);
			  }else{
				  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
			  }
		  }
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ushort addr = ushort.Parse(textBox2.Text);
            ushort value = ushort.Parse(textBox3.Text);
            msm.WriteSingleRegister(1, addr, value);
        }


5.마스터쪽에서 STM32의 쓰기레지스터 0번지에 값을 쓰면 녹칸다의 STM32쉴드에 붙어있는 FND모듈에 그 값이 출력되도록 하시오! 

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
//여기 추가해야하는 코드는 FND모듈에추가해야하는 코드 그대로 복붙하기!
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 uint16_t w_reg[6] = {0}; //6word 정도의 레지스터가 있다고 가정함
 tm1637Init();
 tm1637SetBrightness(4);
 tm1637DisplayDecimalTrim(0, 0);
 /* USER CODE END 2 */




while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  //fc가 0x06이 올 예정!
			  uint16_t reg_addr = (req[2] << 8) | req[3];
			  uint16_t reg_data = (req[4] << 8) | req[5];
			  uint16_t crc16 = (req[7] << 8) | req[6];
			  //STM32가 CRC16을 계산함!
			  uint16_t my_crc16 = cal_crc(req,6);
			  if(crc16 == my_crc16){
				  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,reg_addr,reg_data,crc16);
				  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
				  w_reg[reg_addr] = reg_data;
				  if(reg_addr == 0)
					  tm1637DisplayDecimalTrim(w_reg[0], 0);
				  //레지스터가 6개 있으니까 6개의 값을 출력한다!
				  sprintf(tx_buff,"w_reg=%d, %d, %d, %d, %d, %d\n",w_reg[0],w_reg[1],w_reg[2],w_reg[3],w_reg[4],w_reg[5]);
				  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
				  //응답은 req를 그대로 응답하면 된다!
				  HAL_UART_Transmit(&huart1, req, 8, 100);
			  }else{
				  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
			  }
		  }
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ushort addr = ushort.Parse(textBox2.Text);
            ushort value = ushort.Parse(textBox3.Text);
            msm.WriteSingleRegister(1, addr, value);
        }


6.이번에는 예제4에 더해서 쓰기 레지스터에서 값을 읽는 명령인 read holding register를 구현해서 C#화면에 출력하도록 한다! (마스터는 1워드의 데이터만 요구할수있는 제약조건을 주고 다음주 예제부터 다시 이어서 구현하도록 함 시간관계상)

/* USER CODE BEGIN 0 */
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
   uint16_t crc = 0xFFFF;
   for(uint8_t i = 0; i < len; i++)
   {
       crc ^= data[i];   // LSB부터 XOR
       for(uint8_t j = 0; j < 8; j++)
       {
           if(crc & 0x0001)
           {
               crc >>= 1;
               crc ^= 0xA001;   // Modbus polynomial
           }
           else
           {
               crc >>= 1;
           }
       }
   }
   return crc;
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 uint8_t slave_id = 0x01;
 uint8_t tx_buff[100];
 uint8_t req[8];
 uint8_t res[17];
 uint16_t w_reg[6] = {0}; //6word 정도의 레지스터가 있다고 가정함
 /* USER CODE END 2 */





while (1)
 {
	  //usart1은 RS485통신, usart2는 PC와 통신
	  //usart1으로 수신해서 usart2로 결과를 송신한다!
	  if(HAL_UART_Receive(&huart1, req, 8, 500) == HAL_OK){
		  //응답예시데이터
		  uint8_t id = req[0];
		  //내 id에게 req가 왔을때만 res를 전송한다!
		  if(id == slave_id){
			  uint8_t fc = req[1];
			  if(fc == 0x06){
				  uint16_t reg_addr = (req[2] << 8) | req[3];
				  uint16_t reg_data = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,reg_addr,reg_data,crc16);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  w_reg[reg_addr] = reg_data;
					  //레지스터가 6개 있으니까 6개의 값을 출력한다!
					  sprintf(tx_buff,"w_reg=%d, %d, %d, %d, %d, %d\n",w_reg[0],w_reg[1],w_reg[2],w_reg[3],w_reg[4],w_reg[5]);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  //응답은 req를 그대로 응답하면 된다!
					  HAL_UART_Transmit(&huart1, req, 8, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }

			  }else if(fc == 0x03){
				  //read holding register
				  uint16_t start_addr = (req[2] << 8) | req[3];
				  uint16_t count = (req[4] << 8) | req[5];
				  uint16_t crc16 = (req[7] << 8) | req[6];
				  //STM32가 CRC16을 계산함!
				  uint16_t my_crc16 = cal_crc(req,6);
				  if(crc16 == my_crc16){
					  sprintf(tx_buff,"id=%02X, fc=%02X, addr=%04X, data=%04X, crc=%04X\n",id,fc,start_addr,count,crc16);
					  HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
					  //마스터가 요구한 워드에 갯수만큼 응답한다!
					  res[0] = id;
					  res[1] = fc;
					  res[2] = count * 2;
					  res[3] = (w_reg[start_addr] >> 8);
					  res[4] = (w_reg[start_addr] & 0xFF);
					  uint16_t res_crc16 = cal_crc(res,5);
					  res[5] = (res_crc16 & 0xFF);
					  res[6] = (res_crc16 >> 8);
					  HAL_UART_Transmit(&huart1, res, 7, 100);
				  }else{
					  HAL_UART_Transmit(&huart2, "CRC16 ERROR!\n", 13, 100);
				  }
			  }
		  }
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
using Modbus.Device; //네임스페이스를 추가!

//nmodbus4 객체선언(modbus rtu전용)
        ModbusSerialMaster msm;

private void button1_Click(object sender, EventArgs e)
        {
            //접속버튼 클릭
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = textBox1.Text;

            serialPort1.Open();

            if (serialPort1.IsOpen)
            {
                //접속이 완료된 serialport 객체를 라이브러리에게 넘긴다!
                msm = ModbusSerialMaster.CreateRtu(serialPort1);
                MessageBox.Show("연결완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ushort addr = ushort.Parse(textBox2.Text);
            ushort value = ushort.Parse(textBox3.Text);
            msm.WriteSingleRegister(1, addr, value);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            ushort addr = ushort.Parse(textBox4.Text);
            ushort[] data = msm.ReadHoldingRegisters(1, addr, 1);

            richTextBox1.Text += "응답데이터=" + data[0].ToString() + "\n";
        }
반응형
Posted by 덕력킹
,