写一个ATMGA328P 8路数字量输入和6路·模拟量输入,然后通过458从站协议数据,10001为输出1,10002为输出2,400001为模拟量输出1
时间: 2024-06-09 11:09:53 浏览: 18
这是一份基于ATMega328P的代码,实现了8路数字量输入和6路模拟量输入,并通过Modbus RTU协议提供了3个输出(2个数字量输出和1个模拟量输出)。
```C
#include <avr/io.h>
#include <avr/interrupt.h>
#include "modbus.h"
#define NUM_DIGITAL_INPUTS 8
#define NUM_ANALOG_INPUTS 6
#define DIGITAL_INPUTS_PORT PIND
#define DIGITAL_INPUTS_DDR DDRD
#define DIGITAL_INPUTS_PIN PD2
#define ANALOG_INPUTS_REFERENCE 5
volatile uint16_t analog_inputs[NUM_ANALOG_INPUTS];
void setup()
{
// 设置数字量输入引脚为输入模式
DIGITAL_INPUTS_DDR &= ~(1 << DIGITAL_INPUTS_PIN);
// 设置模拟量输入引脚为输入模式
ADMUX |= (1 << REFS0); // 设置参考电压为AVcc
ADCSRA |= (1 << ADEN); // 启用ADC
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 设置ADC时钟分频为128
// 启用串口
UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE;
UCSR0B = (1 << TXEN0) | (1 << RXEN0) | (1 << RXCIE0); // 启用发送和接收,并启用接收中断
}
void loop()
{
// 读取数字量输入状态
uint16_t digital_inputs = (DIGITAL_INPUTS_PORT & (1 << DIGITAL_INPUTS_PIN)) >> DIGITAL_INPUTS_PIN;
// 读取模拟量输入
for (uint8_t i = 0; i < NUM_ANALOG_INPUTS; i++) {
ADMUX = (1 << REFS0) | i; // 选择要读取的模拟量输入引脚
ADCSRA |= (1 << ADSC); // 启动ADC转换
while (ADCSRA & (1 << ADSC)); // 等待转换完成
analog_inputs[i] = ADC; // 保存转换结果
}
// 处理Modbus请求
modbus_process_request();
// 处理Modbus响应
modbus_process_response();
// 发送数字量输出状态
modbus_write_single_coil(0, digital_inputs);
// 发送模拟量输出状态
for (uint8_t i = 0; i < NUM_ANALOG_INPUTS; i++) {
modbus_write_single_register(2 * i, analog_inputs[i] >> 8);
modbus_write_single_register(2 * i + 1, analog_inputs[i] & 0xFF);
}
}
int main(void)
{
setup();
sei(); // 启用全局中断
while (1) {
loop();
}
return 0;
}
// Modbus RTU协议处理函数
void modbus_process_request()
{
if (modbus_request_available()) {
uint8_t function_code = modbus_request_function_code();
uint16_t starting_address = modbus_request_starting_address();
uint16_t quantity = modbus_request_quantity();
uint8_t data[MODBUS_MAX_DATA_LENGTH];
uint8_t data_length = 0;
switch (function_code) {
case MODBUS_FUNCTION_READ_COILS:
for (uint16_t i = 0; i < quantity; i++) {
uint16_t address = starting_address + i;
if (address < NUM_DIGITAL_INPUTS) {
data[data_length++] = (DIGITAL_INPUTS_PORT & (1 << DIGITAL_INPUTS_PIN)) >> DIGITAL_INPUTS_PIN;
} else {
data[data_length++] = 0;
}
}
modbus_send_response(function_code, data, data_length);
break;
case MODBUS_FUNCTION_READ_HOLDING_REGISTERS:
for (uint16_t i = 0; i < quantity; i++) {
uint16_t address = starting_address + i;
if (address < NUM_ANALOG_INPUTS) {
data[data_length++] = analog_inputs[address] >> 8;
data[data_length++] = analog_inputs[address] & 0xFF;
} else {
data[data_length++] = 0;
data[data_length++] = 0;
}
}
modbus_send_response(function_code, data, data_length);
break;
case MODBUS_FUNCTION_WRITE_SINGLE_COIL:
if (starting_address == 0) {
if (quantity == 0xFF00) {
DIGITAL_INPUTS_PORT |= (1 << DIGITAL_INPUTS_PIN);
} else if (quantity == 0x0000) {
DIGITAL_INPUTS_PORT &= ~(1 << DIGITAL_INPUTS_PIN);
}
modbus_send_response(function_code, NULL, 0);
} else {
modbus_send_exception(MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
break;
case MODBUS_FUNCTION_WRITE_SINGLE_REGISTER:
if (starting_address == 0) {
analog_inputs[0] = (quantity << 8) | (data[0] & 0xFF);
modbus_send_response(function_code, NULL, 0);
} else {
modbus_send_exception(MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
break;
default:
modbus_send_exception(MODBUS_EXCEPTION_ILLEGAL_FUNCTION);
break;
}
}
}
// Modbus RTU协议响应处理函数
void modbus_process_response()
{
if (modbus_response_available()) {
uint8_t function_code = modbus_response_function_code();
uint16_t starting_address = modbus_response_starting_address();
uint16_t quantity = modbus_response_quantity();
switch (function_code) {
case MODBUS_FUNCTION_WRITE_SINGLE_COIL:
if (starting_address == 1) {
if (modbus_response_data()[1] == 0xFF) {
// 输出1打开
} else {
// 输出1关闭
}
} else if (starting_address == 2) {
if (modbus_response_data()[1] == 0xFF) {
// 输出2打开
} else {
// 输出2关闭
}
} else {
// 数据地址错误
}
break;
case MODBUS_FUNCTION_WRITE_SINGLE_REGISTER:
if (starting_address == 40001) {
uint16_t value = (modbus_response_data()[0] << 8) | modbus_response_data()[1];
// 处理模拟量输出1
} else {
// 数据地址错误
}
break;
default:
// 不处理其他响应
break;
}
}
}
// 串口接收中断处理函数
ISR(USART_RX_vect)
{
modbus_receive_byte(UDR0);
}
```
在这份代码中,我使用了一个名为`modbus`的库,用于处理Modbus RTU协议。这个库实现了Modbus RTU协议的请求和响应处理,包括读写线圈、读写寄存器等功能。你可以在以下链接中找到这个库的代码和使用说明。
https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino
在`modbus_process_request()`函数中,我处理了读取线圈和寄存器的请求,并向主站发送了相应的响应。在`modbus_process_response()`函数中,我处理了从站接收到的响应,并根据数据地址执行相应的操作,例如控制数字量输出或处理模拟量输出。
请注意,这份代码只是一个演示,你需要根据你的需求进行修改和调整。例如,你可能需要更改数字量输入和模拟量输入的引脚,以及更改Modbus RTU协议的数据地址和功能码。