initial commit
This commit is contained in:
BIN
code/Builds/main.elf
Executable file
BIN
code/Builds/main.elf
Executable file
Binary file not shown.
120
code/Builds/main.hex
Normal file
120
code/Builds/main.hex
Normal file
@@ -0,0 +1,120 @@
|
||||
:100000000C9434000C9451000C9451000C94510049
|
||||
:100010000C9451000C9451000C9451000C9451001C
|
||||
:100020000C9451000C9451000C9451000C9451000C
|
||||
:100030000C9451000C9451000C9451000C945100FC
|
||||
:100040000C9488030C9451000C9419010C948901B0
|
||||
:100050000C94CB010C9451000C9451000C94510061
|
||||
:100060000C9451000C94510011241FBECFEFD8E026
|
||||
:10007000DEBFCDBF11E0A0E0B1E0ECE5F7E002C0EB
|
||||
:1000800005900D92A230B107D9F722E0A2E0B1E0CD
|
||||
:1000900001C01D92A431B207E1F70E9473030C94D2
|
||||
:1000A000AC030C94000080910F0120910E0190E0B0
|
||||
:1000B000922B909304018093030180910D0186306F
|
||||
:1000C00079F080910D01853059F0809111012091D6
|
||||
:1000D000100190E0922B9093060180930501089502
|
||||
:1000E00081E090E09093060180930501089580914E
|
||||
:1000F0000B010895809302010895CF93DF93EC01E3
|
||||
:100100006F5F09F43EC0FC01A62FB0E0BC016A0F8E
|
||||
:100110007B1F2FEF3FEF9081292798E0A9014170C5
|
||||
:10012000552736952795452B21F081E0282780EA31
|
||||
:100130003827915099F73196E617F70761F7A9012B
|
||||
:100140005527632F7727FE01EA0FFB1F808190E080
|
||||
:100150004817590749F01196AC0FBD1F20836C93C7
|
||||
:1001600080E0DF91CF910895AD014F5F5F4FDE01D9
|
||||
:10017000A40FB51F8C9190E08617970779F781E05F
|
||||
:10018000F0CF6FEF70E04FEF50E02FEF3FEFA0E0C8
|
||||
:10019000B0E0D9CF80910B0185FF19C08091090192
|
||||
:1001A00090910A01019690930A0180930901809130
|
||||
:1001B0000B0181FF0DC08091090190910A01079701
|
||||
:1001C00009F18091090190910A01429769F008951F
|
||||
:1001D0008091090190910A014097C9F780910B0124
|
||||
:1001E000816080930B01089590910C018091020130
|
||||
:1001F000981771F080E280930B0110920A0110921F
|
||||
:100200000901089580910B01806480930B0108958A
|
||||
:10021000609107017091080163508CE091E00E94A9
|
||||
:100220007D00882339F30E94530088E080930B01FE
|
||||
:1002300008951F920F920FB60F9211242F933F93A0
|
||||
:100240008F939F93EF93FF939091C60010920A01B2
|
||||
:100250001092090180910B0183FD04C080910B0174
|
||||
:1002600084FF2AC080910B0183FD1BC080910B018C
|
||||
:1002700084FD17C080910B0182FD13C080910B019A
|
||||
:1002800081FD0FC080910B0180FF0BC090930C018A
|
||||
:1002900082E280930B0181E090E0909308018093CB
|
||||
:1002A0000701FF91EF919F918F913F912F910F90B7
|
||||
:1002B0000FBE0F901F90189580910B0182FDD2CF39
|
||||
:1002C00080910B0181FFCECF80910B0180FDCACFC1
|
||||
:1002D00020910701309108012F3F310549F040F08E
|
||||
:1002E00080E280930B0110920A0110920901D9CF8C
|
||||
:1002F000E0910701F0910801E45FFE4F9083809147
|
||||
:1003000007019091080101969093080180930701DD
|
||||
:10031000C8CF1F920F920FB60F9211242F933F93C5
|
||||
:100320008F939F93EF93FF9380910B018F7E809328
|
||||
:100330000B0180910B01846080930B01E091070118
|
||||
:10034000F0910801E45FFE4F80818093C6008091A8
|
||||
:10035000070190910801019690930801809307018D
|
||||
:1003600080910001209107013091080190E00196F1
|
||||
:100370008217930729F48091C1008F7D8093C1007B
|
||||
:10038000FF91EF919F918F913F912F910F900FBE11
|
||||
:100390000F901F9018951F920F920FB60F92112475
|
||||
:1003A0008F935A9880E280930B0110920A01109269
|
||||
:1003B00009018F910F900FBE0F901F90189510920A
|
||||
:1003C000C5008BE38093C40082E08093C00086E088
|
||||
:1003D0008093C20088ED8093C100529A5A9880E2BF
|
||||
:1003E00080930B01089592E0980F90930001682F7D
|
||||
:1003F0008CE091E00E947D0080910B0180618093F0
|
||||
:100400000B0110920801109207015A9AE1ECF0E0FA
|
||||
:1004100080818062808380910B01877F80930B01B4
|
||||
:100420000895ECE0F1E0918190689183828382E00D
|
||||
:100430000C94F301CF93DF932091030130910401D9
|
||||
:100440002617370778F1E0910501F0910601209118
|
||||
:10045000030130910401460F571F2E0F3F1F421713
|
||||
:10046000530700F120910D01233059F120910D0126
|
||||
:10047000243039F120910D01203139F020910D0106
|
||||
:10048000263009F459C080E019C040911201209132
|
||||
:1004900005013091060150E0220F331F4217530728
|
||||
:1004A00008F065C080910D01806880930D0183E0A4
|
||||
:1004B00080930E0182E00E94F30180E0DF91CF91F2
|
||||
:1004C00008952091050130910601220F331F2C3F22
|
||||
:1004D000310540F72091050130910601220F20934C
|
||||
:1004E0000E012091050130910601422F2091030158
|
||||
:1004F00030910401261B370B220F331F442399F040
|
||||
:10050000DC01A20FB31FE0E1F1E080E0EF012197F1
|
||||
:100510002D913C91119738832D913D9120838F5FD0
|
||||
:1005200032964813F3CF80910E018E5F0E94F30143
|
||||
:1005300081E0DF91CF910895A0910301B091040172
|
||||
:10054000A61BB70BAA0FBB1FA80FB91F80911001E4
|
||||
:100550002091110190E0982F8827820F911D11960C
|
||||
:100560009C938E9385E00E94F30181E0A7CF409198
|
||||
:10057000070150910801209112014950510930E0C2
|
||||
:100580004217530708F48ECF2091050130910601E0
|
||||
:10059000422FA0910301B0910401A61BB70BAA0F33
|
||||
:1005A000BB1F2223F9F2E4E1F1E0A80FB91F90E0AC
|
||||
:1005B000EF0121972881808130E0322F2227280FF8
|
||||
:1005C000311D11963C932E9312969F5F32964913DC
|
||||
:1005D000EFCFC8CFCF92DF92EF92FF9221E030E0D1
|
||||
:1005E000F90102C0EE0FFF1F8A95E2F7CF017E2FBF
|
||||
:1005F000E62F682F609558E180E090E0DC0125B19E
|
||||
:10060000272B25B926E02A95F1F700006C017D0122
|
||||
:10061000CC0CDD1CEE1CFF1C23B130E00E2E02C002
|
||||
:10062000359527950A94E2F7C90181709927092E1B
|
||||
:10063000000CAA0BBB0B8C299D29AE29BF2925B123
|
||||
:10064000262325B926E02A95F1F700005150B9F686
|
||||
:10065000403229F1403401F5E3E0F0E030E025B12B
|
||||
:10066000272B25B926E02A95F1F7000025B126238E
|
||||
:1006700025B926E02A95F1F700003F5F432F50E0AF
|
||||
:100680004E175F0764F3A7FDBF6FBC01CD01FF905C
|
||||
:10069000EF90DF90CF900895E1E0F0E0DFCFE2E06F
|
||||
:1006A000F0E0DCCF0E94770083FF1CC080910D0139
|
||||
:1006B000833019F081E00C941102419A40E864E023
|
||||
:1006C0000E94EA0270930D0260930C0290930F0255
|
||||
:1006D00080930E0244E050E060E070E08CE092E035
|
||||
:1006E0000C941A02089587B1836087B9239A81E038
|
||||
:1006F0000E947A000E94DF01789485B5826085BDF2
|
||||
:1007000080916E00816080936E000E945203FDCF45
|
||||
:100710001F920F920FB60F9211242F933F934F9376
|
||||
:100720005F936F937F938F939F93AF93BF93EF9359
|
||||
:10073000FF930E94CA00FF91EF91BF91AF919F91EB
|
||||
:100740008F917F916F915F914F913F912F910F907A
|
||||
:0C0750000FBE0F901F901895F894FFCF7B
|
||||
:02075C00070094
|
||||
:00000001FF
|
||||
BIN
code/Builds/main.o
Normal file
BIN
code/Builds/main.o
Normal file
Binary file not shown.
BIN
code/Builds/modbus.o
Normal file
BIN
code/Builds/modbus.o
Normal file
Binary file not shown.
BIN
code/Builds/uart.o
Normal file
BIN
code/Builds/uart.o
Normal file
Binary file not shown.
55
code/Makefile
Normal file
55
code/Makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
TARGET = main
|
||||
SRCS := $(shell find -name '*.c')
|
||||
FILES = $(SRCS:%.c=%) #main uart avrIOhelper/io-helper #uart#hier alle c-Datein reinschreiben, trennung durch " " und ohne .c-Endung
|
||||
MCU = atmega328p
|
||||
PROGC = m328p
|
||||
CC = avr-gcc
|
||||
TOOL = atmelice_isp
|
||||
#TOOL = avrispmkii
|
||||
#TOOL = usbasp-clone
|
||||
|
||||
BUILDDIR = Builds
|
||||
|
||||
DEFINES = -I . -IInternet/MQTT -I Internet/MQTT/MQTTPacket/src -I Ethernet/W5500 -I Ethernet -DF_CPU=18432000UL -D_WIZCHIP_=W5100
|
||||
|
||||
CFLAGS =-mmcu=$(MCU) -O2 -Wall -Wpedantic $(DEFINES) -std=c99 -ffunction-sections -fdata-sections
|
||||
LDFLAGS =-mmcu=$(MCU) -Wl,--gc-sections
|
||||
|
||||
LDFILES = $(foreach FILE,$(FILES),$(BUILDDIR)/$(FILE).o)
|
||||
|
||||
all: clean $(BUILDDIR)/$(TARGET).elf
|
||||
|
||||
$(BUILDDIR)/%.o: %.c
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $(BUILDDIR)/$*.o
|
||||
|
||||
$(BUILDDIR)/$(TARGET).elf: $(LDFILES)
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(LDFLAGS) $(LDFILES) -o $(BUILDDIR)/$(TARGET).elf
|
||||
|
||||
$(BUILDDIR)/$(TARGET).hex : $(BUILDDIR)/$(TARGET).elf
|
||||
avr-objcopy -j .data -j .text -O ihex $< $@
|
||||
|
||||
fuse:
|
||||
avrdude -p $(PROGC) -c $(TOOL) -U lfuse:w:0xff:m -U hfuse:w:0xd9:m -U efuse:w:0xff:m
|
||||
|
||||
load: $(BUILDDIR)/$(TARGET).hex
|
||||
avrdude -p $(PROGC) -c $(TOOL) -U flash:w:$(BUILDDIR)/$(TARGET).hex -v -B 250kHz
|
||||
|
||||
program: clean load
|
||||
|
||||
size: $(BUILDDIR)/$(TARGET).elf
|
||||
avr-size -C --mcu=$(MCU) $(BUILDDIR)/$(TARGET).elf
|
||||
|
||||
.PHONY=clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
|
||||
#Fuse m1284p external Osz. Long startuptime
|
||||
# avrdude -c usbasp-clone -p m1284p -U lfuse:w:0xff:m -U hfuse:w:0xd9:m -U efuse:w:0xff:m
|
||||
|
||||
#Fuse m1284p internal Osz. Long startuptime
|
||||
# avrdude -c usbasp-clone -p m1284p -U lfuse:w:0xe2:m -U hfuse:w:0xd9:m -U efuse:w:0xff:m
|
||||
|
||||
|
||||
108
code/main.c
Normal file
108
code/main.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include "modbus.h"
|
||||
|
||||
#define HIGH_LOW_CYCLE_TIME 1
|
||||
|
||||
uint16_t holdingRegisters[4];
|
||||
|
||||
void timer0100us_start(void) {
|
||||
TCCR0B|=(1<<CS01); //prescaler 8
|
||||
TIMSK0|=(1<<TOIE0);
|
||||
}
|
||||
|
||||
//Return raw ADC data
|
||||
uint32_t HX711_get_data(uint8_t clk_PIN, uint8_t data_PIN, uint8_t gain_for_next_conv){
|
||||
uint32_t data = 0;
|
||||
for(uint8_t i = 0; i < 24; i++){ //Start 24bit transmission
|
||||
PORTB |= (1 << clk_PIN); //Set clock-PIN high
|
||||
_delay_us(HIGH_LOW_CYCLE_TIME);
|
||||
/* Shift data to free LSB. Operation placed before writing data to avoid
|
||||
manuell shiftig after loop finsihed.*/
|
||||
data <<= 1;
|
||||
/* Shift input-status of data containing bit to the
|
||||
LSB and set all other bits to 0. Add result to
|
||||
currently LSB in data-var.*/
|
||||
data |= ((PINB>>data_PIN) & 0x01); //Read and store data
|
||||
PORTB &= ~(1 << clk_PIN); //Set clock-PIN low
|
||||
_delay_us(HIGH_LOW_CYCLE_TIME);
|
||||
}
|
||||
|
||||
/* Add extra clk-pluses to set PGA-Gain for next conversion
|
||||
1 extra pulse : Channel A Gain = 128
|
||||
2 extra pluses: Channel B Gain = 32
|
||||
3 extra pluses: Channel A Gain = 64*/
|
||||
int8_t extra_clk_pluses = 1;
|
||||
switch(gain_for_next_conv){
|
||||
case 128:
|
||||
extra_clk_pluses = 1;
|
||||
break;
|
||||
case 64:
|
||||
extra_clk_pluses = 3;
|
||||
break;
|
||||
case 32:
|
||||
extra_clk_pluses = 2;
|
||||
break;
|
||||
default:
|
||||
extra_clk_pluses = 1;
|
||||
}
|
||||
for(uint8_t i = 0; i < extra_clk_pluses; i++){
|
||||
PORTB |= (1 << clk_PIN); //set clock-PIN high
|
||||
_delay_us(HIGH_LOW_CYCLE_TIME);
|
||||
PORTB &= ~(1 << clk_PIN); //set clock-PIN low
|
||||
_delay_us(HIGH_LOW_CYCLE_TIME);
|
||||
}
|
||||
|
||||
/* Convert 24bit Two's complement to 32bit Two's complement
|
||||
if MSB from 24Bit result is 1 aka negative*/
|
||||
if(data & (1L<<23)){
|
||||
data |= 0xFFL << 24;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void modbusGet(void) {
|
||||
if (modbusGetBusState() & (1<<ReceiveCompleted))
|
||||
{
|
||||
switch(rxbuffer[1]) {
|
||||
case fcReadHoldingRegisters:
|
||||
PORTC |= 1 << 1;
|
||||
// _delay_ms(10);
|
||||
// PORTC &= ~(1 << 1);
|
||||
uint32_t tmp = HX711_get_data(3, 4, 128);
|
||||
//int32_t tmp = -1000;
|
||||
holdingRegisters[0] = tmp & 0xFFFF;
|
||||
holdingRegisters[1] = (tmp>>16) & 0xFFFF;
|
||||
modbusExchangeRegisters(holdingRegisters,0,4);
|
||||
break;
|
||||
|
||||
default: {
|
||||
modbusSendException(ecIllegalFunction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void){
|
||||
DDRC |= 3 << 0;
|
||||
DDRB |= (1<<3);
|
||||
|
||||
modbusSetAddress(1);
|
||||
modbusInit();
|
||||
sei();
|
||||
|
||||
timer0100us_start();
|
||||
|
||||
while(1){
|
||||
modbusGet();
|
||||
//PORTC ^= 1 << 0;
|
||||
//_delay_ms(100);
|
||||
//PORTC &= ~(1 << 1);
|
||||
}
|
||||
}
|
||||
|
||||
ISR(TIMER0_OVF_vect) { //this ISR is called 9765.625 times per second
|
||||
modbusTickTimer();
|
||||
}
|
||||
433
code/modbus.c
Normal file
433
code/modbus.c
Normal file
@@ -0,0 +1,433 @@
|
||||
/*************************************************************************
|
||||
Title: Yet another (small) modbus (server) implementation for the avr.
|
||||
Author: Max Brueggemann
|
||||
Hardware: any AVR with hardware UART, tested on Atmega 88/168 at 20Mhz
|
||||
License: BSD-3-Clause
|
||||
|
||||
DESCRIPTION:
|
||||
Refer to the header file yaMBSiavr.h.
|
||||
|
||||
USAGE:
|
||||
Refer to the header file yaMBSiavr.h.
|
||||
|
||||
LICENSE:
|
||||
|
||||
Copyright 2017 Max Brueggemann, www.maxbrueggemann.de
|
||||
|
||||
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. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
|
||||
*************************************************************************/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include "modbus.h"
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
volatile unsigned char BusState = 0;
|
||||
volatile uint16_t modbusTimer = 0;
|
||||
volatile unsigned char rxbuffer[MaxFrameIndex+1];
|
||||
volatile uint16_t DataPos = 0;
|
||||
volatile unsigned char PacketTopIndex = 7;
|
||||
volatile unsigned char modBusStaMaStates = 0;
|
||||
volatile uint16_t modbusDataAmount = 0;
|
||||
volatile uint16_t modbusDataLocation = 0;
|
||||
|
||||
/* @brief: save address and amount
|
||||
*
|
||||
*/
|
||||
void modbusSaveLocation(void)
|
||||
{
|
||||
modbusDataLocation=(rxbuffer[3]|(rxbuffer[2]<<8));
|
||||
if (rxbuffer[1]==fcPresetSingleRegister || rxbuffer[1]==fcForceSingleCoil) modbusDataAmount=1;
|
||||
else modbusDataAmount=(rxbuffer[5]|(rxbuffer[4]<<8));
|
||||
}
|
||||
|
||||
/* @brief: returns 1 if data location adr is touched by current command
|
||||
*
|
||||
* Arguments: - adr: address of the data object
|
||||
*
|
||||
*/
|
||||
uint8_t modbusIsInRange(uint16_t adr)
|
||||
{
|
||||
if((modbusDataLocation <= adr) && (adr<(modbusDataLocation+modbusDataAmount)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief: returns 1 if range of data locations is touched by current command
|
||||
*
|
||||
* Arguments: - startAdr: address of first data object in range
|
||||
* - lastAdr: address of last data object in range
|
||||
*
|
||||
*/
|
||||
uint8_t modbusIsRangeInRange(uint16_t startAdr, uint16_t lastAdr)
|
||||
{
|
||||
if(modbusIsInRange(startAdr) && modbusIsInRange(lastAdr))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t modbusGetBusState(void)
|
||||
{
|
||||
return BusState;
|
||||
}
|
||||
|
||||
#if ADDRESS_MODE == SINGLE_ADR
|
||||
volatile unsigned char Address = 0x00;
|
||||
uint8_t modbusGetAddress(void)
|
||||
{
|
||||
return Address;
|
||||
}
|
||||
|
||||
void modbusSetAddress(unsigned char newadr)
|
||||
{
|
||||
Address = newadr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PHYSICAL_TYPE == 485
|
||||
void transceiver_txen(void)
|
||||
{
|
||||
TRANSCEIVER_ENABLE_PORT|=(1<<TRANSCEIVER_ENABLE_PIN);
|
||||
}
|
||||
|
||||
void transceiver_rxen(void)
|
||||
{
|
||||
TRANSCEIVER_ENABLE_PORT&=~(1<<TRANSCEIVER_ENABLE_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* @brief: A fairly simple Modbus compliant 16 Bit CRC algorithm.
|
||||
*
|
||||
* Returns 1 if the crc check is positive, returns 0 and saves the calculated CRC bytes
|
||||
* at the end of the data array if it fails.
|
||||
*
|
||||
*/
|
||||
uint8_t crc16(volatile uint8_t *ptrToArray,uint8_t inputSize) //A standard CRC algorithm
|
||||
{
|
||||
uint16_t out=0xffff;
|
||||
uint16_t carry;
|
||||
unsigned char n;
|
||||
inputSize++;
|
||||
for (int l=0; l<inputSize; l++) {
|
||||
out ^= ptrToArray[l];
|
||||
for (n = 0; n < 8; n++) {
|
||||
carry = out & 1;
|
||||
out >>= 1;
|
||||
if (carry) out ^= 0xA001;
|
||||
}
|
||||
}
|
||||
//out=0x1234;
|
||||
if ((ptrToArray[inputSize]==out%256) && (ptrToArray[inputSize+1]==out/256)) //check
|
||||
{
|
||||
return 1;
|
||||
} else {
|
||||
ptrToArray[inputSize]=out%256; //append Lo
|
||||
ptrToArray[inputSize+1]=out/256; //append Hi
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief: copies a single bit from one char to another char (or arrays thereof)
|
||||
*
|
||||
*
|
||||
*/
|
||||
void listBitCopy(volatile uint8_t *source, uint16_t sourceNr,volatile uint8_t *target, uint16_t targetNr)
|
||||
{
|
||||
if(*(source+(sourceNr/8))&(1<<(sourceNr-((sourceNr/8)*8))))
|
||||
{
|
||||
*(target+(targetNr/8))|=(1<<(targetNr-((targetNr/8)*8)));
|
||||
} else *(target+(targetNr/8))&=~(1<<(targetNr-((targetNr/8)*8)));
|
||||
}
|
||||
|
||||
/* @brief: Back to receiving state.
|
||||
*
|
||||
*/
|
||||
void modbusReset(void)
|
||||
{
|
||||
BusState=(1<<TimerActive); //stop receiving (error)
|
||||
modbusTimer=0;
|
||||
}
|
||||
|
||||
void modbusTickTimer(void)
|
||||
{
|
||||
if (BusState&(1<<TimerActive))
|
||||
{
|
||||
modbusTimer++;
|
||||
if (BusState&(1<<Receiving)) //we are in receiving mode
|
||||
{
|
||||
if ((modbusTimer==modbusInterCharTimeout)) {
|
||||
BusState|=(1<<GapDetected);
|
||||
} else if ((modbusTimer==modbusInterFrameDelayReceiveEnd)) { //end of message
|
||||
#if ADDRESS_MODE == MULTIPLE_ADR
|
||||
if (crc16(rxbuffer,DataPos-3)) { //perform crc check only. This is for multiple/all address mode.
|
||||
modbusSaveLocation();
|
||||
BusState=(1<<ReceiveCompleted);
|
||||
} else modbusReset();
|
||||
#endif
|
||||
#if ADDRESS_MODE == SINGLE_ADR
|
||||
if (rxbuffer[0]==Address && crc16(rxbuffer,DataPos-3)) { //is the message for us? => perform crc check
|
||||
modbusSaveLocation();
|
||||
BusState=(1<<ReceiveCompleted);
|
||||
} else modbusReset();
|
||||
#endif
|
||||
}
|
||||
} else if (modbusTimer==modbusInterFrameDelayReceiveStart){
|
||||
BusState|=(1<<BusTimedOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ISR(UART_RECEIVE_INTERRUPT)
|
||||
{
|
||||
unsigned char data;
|
||||
data = UART_DATA;
|
||||
//printf("%x ", data);
|
||||
modbusTimer=0; //reset timer
|
||||
if (!(BusState & (1<<ReceiveCompleted)) && !(BusState & (1<<TransmitRequested)) && !(BusState & (1<<Transmitting)) && (BusState & (1<<Receiving)) && !(BusState & (1<<BusTimedOut)))
|
||||
{
|
||||
if (DataPos>MaxFrameIndex) modbusReset();
|
||||
else
|
||||
{
|
||||
rxbuffer[DataPos]=data;
|
||||
DataPos++; //TODO: maybe prevent this from exceeding 255?
|
||||
}
|
||||
} else
|
||||
// Bus state is Timed out..
|
||||
if (!(BusState & (1<<ReceiveCompleted)) && !(BusState & (1<<TransmitRequested)) && !(BusState & (1<<Transmitting)) && !(BusState & (1<<Receiving)) && (BusState & (1<<BusTimedOut)))
|
||||
{
|
||||
rxbuffer[0]=data;
|
||||
BusState=((1<<Receiving)|(1<<TimerActive));
|
||||
DataPos=1;
|
||||
}
|
||||
}
|
||||
|
||||
ISR(UART_TRANSMIT_INTERRUPT)
|
||||
{
|
||||
BusState&=~(1<<TransmitRequested);
|
||||
BusState|=(1<<Transmitting);
|
||||
UART_DATA=rxbuffer[DataPos];
|
||||
DataPos++;
|
||||
if (DataPos==(PacketTopIndex+1)) {
|
||||
UART_CONTROL&=~(1<<UART_UDRIE);
|
||||
}
|
||||
}
|
||||
|
||||
ISR(UART_TRANSMIT_COMPLETE_INTERRUPT)
|
||||
{
|
||||
#if PHYSICAL_TYPE == 485
|
||||
transceiver_rxen();
|
||||
#endif
|
||||
modbusReset();
|
||||
}
|
||||
|
||||
void modbusInit(void)
|
||||
{
|
||||
UBRRH = (unsigned char)((_UBRR) >> 8);
|
||||
UBRRL = (unsigned char) _UBRR;
|
||||
UART_STATUS = (1<<U2X); //double speed mode.
|
||||
#ifdef URSEL // if UBRRH and UCSRC share the same I/O location , e.g. ATmega8
|
||||
UCSRC = (1<<URSEL)|(3<<UCSZ0); //Frame Size
|
||||
#else
|
||||
UCSRC = (3<<UCSZ0); //Frame Size
|
||||
#endif
|
||||
UART_CONTROL = (1<<TXCIE)|(1<<RXCIE)|(1<<RXEN)|(1<<TXEN); // USART receiver and transmitter and receive complete interrupt
|
||||
#if PHYSICAL_TYPE == 485
|
||||
TRANSCEIVER_ENABLE_PORT_DDR|=(1<<TRANSCEIVER_ENABLE_PIN);
|
||||
transceiver_rxen();
|
||||
#endif
|
||||
BusState=(1<<TimerActive);
|
||||
}
|
||||
|
||||
/* @brief: Sends a response.
|
||||
*
|
||||
* Arguments: - packtop: Position of the last byte containing data.
|
||||
* modbusSendException is a good usage example.
|
||||
*/
|
||||
void modbusSendMessage(unsigned char packtop)
|
||||
{
|
||||
PacketTopIndex=packtop+2;
|
||||
crc16(rxbuffer,packtop);
|
||||
BusState|=(1<<TransmitRequested);
|
||||
DataPos=0;
|
||||
#if PHYSICAL_TYPE == 485
|
||||
transceiver_txen();
|
||||
#endif
|
||||
UART_CONTROL|=(1<<UART_UDRIE);
|
||||
BusState&=~(1<<ReceiveCompleted);
|
||||
}
|
||||
|
||||
/* @brief: Sends an exception response.
|
||||
*
|
||||
* Arguments: - exceptionCode
|
||||
*
|
||||
*/
|
||||
void modbusSendException(unsigned char exceptionCode)
|
||||
{
|
||||
rxbuffer[1]|=(1<<7); //setting MSB of the function code (the exception flag)
|
||||
rxbuffer[2]=exceptionCode; //Exceptioncode. Also the last byte containing data
|
||||
modbusSendMessage(2);
|
||||
}
|
||||
|
||||
|
||||
/* @brief: Returns the amount of requested data objects (coils, discretes, registers)
|
||||
*
|
||||
*/
|
||||
uint16_t modbusRequestedAmount(void)
|
||||
{
|
||||
return modbusDataAmount;
|
||||
}
|
||||
|
||||
/* @brief: Returns the address of the first requested data object (coils, discretes, registers)
|
||||
*
|
||||
*/
|
||||
uint16_t modbusRequestedAddress(void)
|
||||
{
|
||||
return modbusDataLocation;
|
||||
}
|
||||
|
||||
/* @brief: copies a single or multiple bytes from one array of bytes to an array of 16-bit-words
|
||||
*
|
||||
*/
|
||||
void intToModbusRegister(volatile uint16_t *inreg, volatile uint8_t *outreg, uint8_t amount)
|
||||
{
|
||||
for (uint8_t c=0; c<amount; c++)
|
||||
{
|
||||
*(outreg+c*2) = (uint8_t)(*(inreg+c) >> 8);
|
||||
*(outreg+1+c*2) = (uint8_t)(*(inreg+c));
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief: copies a single or multiple 16-bit-words from one array of integers to an array of bytes
|
||||
*
|
||||
*/
|
||||
void modbusRegisterToInt(volatile uint8_t *inreg, volatile uint16_t *outreg, uint8_t amount)
|
||||
{
|
||||
for (uint8_t c=0; c<amount; c++)
|
||||
{
|
||||
*(outreg+c) = (*(inreg+c*2) << 8) + *(inreg+1+c*2);
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief: Handles single/multiple register reading and single/multiple register writing.
|
||||
*
|
||||
* Arguments: - ptrToInArray: pointer to the user's data array containing registers
|
||||
* - startAddress: address of the first register in the supplied array
|
||||
* - size: input array size in the requested format (16bit-registers)
|
||||
*
|
||||
*/
|
||||
uint8_t modbusExchangeRegisters(volatile uint16_t *ptrToInArray, uint16_t startAddress, uint16_t size)
|
||||
{
|
||||
if ((modbusDataLocation>=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation))) {
|
||||
|
||||
if ((rxbuffer[1]==fcReadHoldingRegisters) || (rxbuffer[1]==fcReadInputRegisters) )
|
||||
{
|
||||
if ((modbusDataAmount*2)<=(MaxFrameIndex-4)) //message buffer big enough?
|
||||
{
|
||||
rxbuffer[2]=(unsigned char)(modbusDataAmount*2);
|
||||
intToModbusRegister(ptrToInArray+(modbusDataLocation-startAddress),rxbuffer+3,modbusDataAmount);
|
||||
modbusSendMessage(2+rxbuffer[2]);
|
||||
return 1;
|
||||
} else modbusSendException(ecIllegalDataValue);
|
||||
}
|
||||
else if (rxbuffer[1]==fcPresetMultipleRegisters)
|
||||
{
|
||||
if (((rxbuffer[6])>=modbusDataAmount*2) && ((DataPos-9)>=rxbuffer[6])) //enough data received?
|
||||
{
|
||||
modbusRegisterToInt(rxbuffer+7,ptrToInArray+(modbusDataLocation-startAddress),(unsigned char)(modbusDataAmount));
|
||||
modbusSendMessage(5);
|
||||
return 1;
|
||||
} else modbusSendException(ecIllegalDataValue);//too few data bytes received
|
||||
}
|
||||
else if (rxbuffer[1]==fcPresetSingleRegister)
|
||||
{
|
||||
modbusRegisterToInt(rxbuffer+4,ptrToInArray+(modbusDataLocation-startAddress),1);
|
||||
modbusSendMessage(5);
|
||||
return 1;
|
||||
}
|
||||
//modbusSendException(ecSlaveDeviceFailure); //inapropriate call of modbusExchangeRegisters
|
||||
return 0;
|
||||
} else {
|
||||
modbusSendException(ecIllegalDataValue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief: Handles single/multiple input/coil reading and single/multiple coil writing.
|
||||
*
|
||||
* Arguments: - ptrToInArray: pointer to the user's data array containing bits
|
||||
* - startAddress: address of the first bit in the supplied array
|
||||
* - size: input array size in the requested format (bits)
|
||||
*
|
||||
*/
|
||||
uint8_t modbusExchangeBits(volatile uint8_t *ptrToInArray, uint16_t startAddress, uint16_t size)
|
||||
{
|
||||
if ((modbusDataLocation>=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation)))
|
||||
{
|
||||
if ((rxbuffer[1]==fcReadInputStatus) || (rxbuffer[1]==fcReadCoilStatus))
|
||||
{
|
||||
if (modbusDataAmount<=((MaxFrameIndex-4)*8)) //message buffer big enough?
|
||||
{
|
||||
rxbuffer[2]=(modbusDataAmount/8);
|
||||
if (modbusDataAmount%8>0)
|
||||
{
|
||||
rxbuffer[(uint8_t)(modbusDataAmount/8)+3]=0x00; //fill last data byte with zeros
|
||||
rxbuffer[2]++;
|
||||
}
|
||||
for (uint16_t c = 0; c<modbusDataAmount; c++)
|
||||
{
|
||||
listBitCopy(ptrToInArray,modbusDataLocation-startAddress+c,rxbuffer+3,c);
|
||||
}
|
||||
modbusSendMessage(rxbuffer[2]+2);
|
||||
return 1;
|
||||
} else modbusSendException(ecIllegalDataValue); //too many bits requested within single request
|
||||
}
|
||||
else if (rxbuffer[1]==fcForceMultipleCoils)
|
||||
{
|
||||
if (((rxbuffer[6]*8)>=modbusDataAmount) && ((DataPos-9)>=rxbuffer[6])) //enough data received?
|
||||
{
|
||||
for (uint16_t c = 0; c<modbusDataAmount; c++)
|
||||
{
|
||||
listBitCopy(rxbuffer+7,c,ptrToInArray,modbusDataLocation-startAddress+c);
|
||||
}
|
||||
modbusSendMessage(5);
|
||||
return 1;
|
||||
} else modbusSendException(ecIllegalDataValue);//exception too few data bytes received
|
||||
}
|
||||
else if (rxbuffer[1]==fcForceSingleCoil) {
|
||||
listBitCopy(rxbuffer+4,0,ptrToInArray,modbusDataLocation-startAddress);
|
||||
modbusSendMessage(5);
|
||||
return 1;
|
||||
}
|
||||
//modbusSendException(ecSlaveDeviceFailure); //inanpropriate call of modbusExchangeBits
|
||||
return 0;
|
||||
} else
|
||||
{
|
||||
modbusSendException(ecIllegalDataValue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
420
code/modbus.h
Normal file
420
code/modbus.h
Normal file
@@ -0,0 +1,420 @@
|
||||
#ifndef yaMBIavr_H
|
||||
#define yaMBIavr_H
|
||||
/************************************************************************
|
||||
Title: Yet another (small) Modbus (server) implementation for the avr.
|
||||
Author: Max Brueggemann
|
||||
Hardware: any AVR with hardware UART, tested on Atmega 88/168 at 20Mhz
|
||||
License: BSD-3-Clause
|
||||
|
||||
LICENSE:
|
||||
|
||||
Copyright 2017 Max Brueggemann, www.maxbrueggemann.de
|
||||
|
||||
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. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
|
||||
************************************************************************/
|
||||
#include <avr/io.h>
|
||||
/**
|
||||
* @code #include <yaMBSIavr.h> @endcode
|
||||
*
|
||||
* @brief Interrupt-based Modbus implementation for small avr microcontrollers.
|
||||
* The Modbus implementation guidelines at modbus.org call for response
|
||||
* timeouts in the range of several seconds , hence only timing critical
|
||||
* parts have been implemented within ISRs. The actual handling of the Modbus
|
||||
* frame can easily be done in the main while loop.
|
||||
*
|
||||
* @author Max Brueggemann www.maxbrueggemann.de
|
||||
*/
|
||||
|
||||
/* define baudrate of modbus */
|
||||
#ifndef BAUD
|
||||
#define BAUD 38400L
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Definitions for transceiver enable pin.
|
||||
*/
|
||||
#ifndef TRANSCEIVER_ENABLE_PORT
|
||||
#define TRANSCEIVER_ENABLE_PORT PORTD
|
||||
#endif
|
||||
|
||||
#ifndef TRANSCEIVER_ENABLE_PIN
|
||||
#define TRANSCEIVER_ENABLE_PIN 2
|
||||
#endif
|
||||
|
||||
#ifndef TRANSCEIVER_ENABLE_PORT_DDR
|
||||
#define TRANSCEIVER_ENABLE_PORT_DDR DDRD
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* At the moment the user has to set the value for Baudrate and
|
||||
* speed mode manually. The values depend on the operating frequency
|
||||
* of your AVR and can be found in its datasheet.
|
||||
*/
|
||||
#if defined(__AVR_ATtiny2313__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||
#define UART_STATUS UCSRA
|
||||
#define UART_CONTROL UCSRB
|
||||
#define UART_DATA UDR
|
||||
#define UART_UDRIE UDRIE
|
||||
|
||||
#elif defined(__AVR_ATmega164P__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART1_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART1_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART1_UDRE_vect
|
||||
#define UART_STATUS UCSR1A
|
||||
#define UART_CONTROL UCSR1B
|
||||
#define UART_DATA UDR1
|
||||
#define UART_UDRIE UDRIE1
|
||||
#define UCSRC UCSR1C
|
||||
#define RXCIE RXCIE1
|
||||
#define TXCIE TXCIE1
|
||||
#define RXEN RXEN1
|
||||
#define TXEN TXEN1
|
||||
#define UCSZ0 UCSZ10
|
||||
#define U2X U2X1
|
||||
#define UBRRH UBRR1H
|
||||
#define UBRRL UBRR1L
|
||||
|
||||
#elif defined(__AVR_ATmega168PA__)|(__AVR_ATmega88PA__)|(__AVR_ATmega328P__)|(__AVR_ATmega168P__)|(__AVR_ATmega88P__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||
#define UART_STATUS UCSR0A
|
||||
#define UART_CONTROL UCSR0B
|
||||
#define UART_DATA UDR0
|
||||
#define UART_UDRIE UDRIE0
|
||||
#define UCSRC UCSR0C
|
||||
#define RXCIE RXCIE0
|
||||
#define TXCIE TXCIE0
|
||||
#define RXEN RXEN0
|
||||
#define TXEN TXEN0
|
||||
#define UCSZ0 UCSZ00
|
||||
#define U2X U2X0
|
||||
#define UBRRH UBRR0H
|
||||
#define UBRRL UBRR0L
|
||||
|
||||
#elif defined(__AVR_ATmega328PB__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||
#define UART_STATUS UCSR0A
|
||||
#define UART_CONTROL UCSR0B
|
||||
#define UART_DATA UDR0
|
||||
#define UART_UDRIE UDRIE0
|
||||
#define UCSRC UCSR0C
|
||||
#define RXCIE RXCIE0
|
||||
#define TXCIE TXCIE0
|
||||
#define RXEN RXEN0
|
||||
#define TXEN TXEN0
|
||||
#define UCSZ0 UCSZ00
|
||||
#define U2X U2X0
|
||||
#define UBRRH UBRR0H
|
||||
#define UBRRL UBRR0L
|
||||
|
||||
#elif defined(__AVR_ATtiny441__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||
#define UART_STATUS UCSR0A
|
||||
#define UART_CONTROL UCSR0B
|
||||
#define UART_DATA UDR0
|
||||
#define UART_UDRIE UDRIE0
|
||||
#define UCSRC UCSR0C
|
||||
#define RXCIE RXCIE0
|
||||
#define TXCIE TXCIE0
|
||||
#define RXEN RXEN0
|
||||
#define TXEN TXEN0
|
||||
#define UCSZ0 UCSZ00
|
||||
#define U2X U2X0
|
||||
#define UBRRH UBRR0H
|
||||
#define UBRRL UBRR0L
|
||||
|
||||
#elif defined(__AVR_ATmega8__)|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) || defined(__AVR_ATmega323__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TXC_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART_RXC_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||
#define UART_STATUS UCSRA
|
||||
#define UART_CONTROL UCSRB
|
||||
#define UART_DATA UDR
|
||||
#define UART_UDRIE UDRIE
|
||||
|
||||
#elif defined(__AVR_AT90PWM3B__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||
#define UART_STATUS UCSRA
|
||||
#define UART_CONTROL UCSRB
|
||||
#define UART_DATA UDR
|
||||
#define UART_UDRIE UDRIE
|
||||
|
||||
#elif defined(__AVR_ATmega1284P__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||
#define UART_STATUS UCSR0A
|
||||
#define UART_CONTROL UCSR0B
|
||||
#define UART_DATA UDR0
|
||||
#define UART_UDRIE UDRIE0
|
||||
#define UCSRC UCSR0C
|
||||
#define RXCIE RXCIE0
|
||||
#define TXCIE TXCIE0
|
||||
#define RXEN RXEN0
|
||||
#define TXEN TXEN0
|
||||
#define UCSZ0 UCSZ00
|
||||
#define U2X U2X0
|
||||
#define UBRRH UBRR0H
|
||||
#define UBRRL UBRR0L
|
||||
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART3_TX_vect
|
||||
#define UART_RECEIVE_INTERRUPT USART3_RX_vect
|
||||
#define UART_TRANSMIT_INTERRUPT USART3_UDRE_vect
|
||||
#define UART_STATUS UCSR3A
|
||||
#define UART_CONTROL UCSR3B
|
||||
#define UART_DATA UDR3
|
||||
#define UART_UDRIE UDRIE3
|
||||
#define UCSRC UCSR3C
|
||||
#define RXCIE RXCIE3
|
||||
#define TXCIE TXCIE3
|
||||
#define RXEN RXEN3
|
||||
#define TXEN TXEN3
|
||||
#define UCSZ0 UCSZ30
|
||||
#define U2X U2X0
|
||||
#define UBRRH UBRR3H
|
||||
#define UBRRL UBRR3L
|
||||
|
||||
#else
|
||||
#error "no definition available"
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#error " F_CPU not defined "
|
||||
#else
|
||||
#define _UBRR (F_CPU / 8 / BAUD ) -1
|
||||
#endif /* F_CPU */
|
||||
/*
|
||||
* Available address modes.
|
||||
*/
|
||||
#define MULTIPLE_ADR 2
|
||||
#define SINGLE_ADR 1
|
||||
|
||||
/*
|
||||
* Use SINGLE_ADR or MULTIPLE_ADR, default: SINGLE_ADR
|
||||
* This is useful for building gateways, routers or clients that for whatever reason need multiple addresses.
|
||||
*/
|
||||
#define ADDRESS_MODE SINGLE_ADR
|
||||
|
||||
/*
|
||||
* Use 485 or 232, default: 485
|
||||
* Use 232 for testing purposes or very simple applications that do not require RS485 and bus topology.
|
||||
*/
|
||||
#define PHYSICAL_TYPE 485 //possible values: 485, 232
|
||||
|
||||
#if BAUD>=19200
|
||||
#define modbusInterFrameDelayReceiveStart 16
|
||||
#define modbusInterFrameDelayReceiveEnd 18
|
||||
#define modbusInterCharTimeout 7
|
||||
#else
|
||||
#define modbusBlocksize 10
|
||||
#define modbusBlockTime ((float)modbusBlocksize*1000000)/((float) BAUD) //is 260 for 38400
|
||||
#define timerISROccurenceTime 100 //time in microseconds between two calls of modbusTickTimer
|
||||
#define modbusInterFrameDelayReceiveStart (uint16_t)(modbusBlockTime*3.5/(float)timerISROccurenceTime)
|
||||
#define modbusInterFrameDelayReceiveEnd (uint16_t)(modbusBlockTime*4/(float)timerISROccurenceTime)
|
||||
#define modbusInterCharTimeout (uint16_t)(modbusBlockTime*1.5/(float)timerISROccurenceTime)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum Modbus frame size accepted by the device. 255 is the default
|
||||
* and also the maximum value. However, it might be useful to set this to lower
|
||||
* values, with 8 being the lowest possible value, in order to save on ram space.
|
||||
*/
|
||||
#define MaxFrameIndex 255
|
||||
|
||||
/**
|
||||
* @brief Modbus Function Codes
|
||||
* Refer to modbus.org for further information.
|
||||
* It's good practice to return exception code 01 in case you receive a function code
|
||||
* that you haven't implemented in your application.
|
||||
*/
|
||||
#define fcReadCoilStatus 1 //read single/multiple coils
|
||||
#define fcReadInputStatus 2 //read single/multiple inputs
|
||||
#define fcReadHoldingRegisters 3 //read analog output registers
|
||||
#define fcReadInputRegisters 4 //read analog input registers (2 Bytes per register)
|
||||
#define fcForceSingleCoil 5 //write single bit
|
||||
#define fcPresetSingleRegister 6 //write analog output register (2 Bytes)
|
||||
#define fcForceMultipleCoils 15 //write multiple bits
|
||||
#define fcPresetMultipleRegisters 16 //write multiple analog output registers (2 Bytes each)
|
||||
#define fcReportSlaveID 17 //read device description, run status and other device specific information
|
||||
|
||||
/**
|
||||
* @brief Modbus Exception Codes
|
||||
* Refer to modbus.org for further information.
|
||||
* It's good practice to return exception code 01 in case you receive a function code
|
||||
* that you haven't implemented in your application.
|
||||
*/
|
||||
#define ecIllegalFunction 1
|
||||
#define ecIllegalDataAddress 2
|
||||
#define ecIllegalDataValue 3
|
||||
#define ecSlaveDeviceFailure 4
|
||||
#define ecAcknowledge 5
|
||||
#define ecSlaveDeviceBusy 6
|
||||
#define ecNegativeAcknowledge 7
|
||||
#define ecMemoryParityError 8
|
||||
|
||||
/**
|
||||
* @brief Internal bit definitions
|
||||
*/
|
||||
#define BusTimedOut 0
|
||||
#define Receiving 1
|
||||
#define Transmitting 2
|
||||
#define ReceiveCompleted 3
|
||||
#define TransmitRequested 4
|
||||
#define TimerActive 5
|
||||
#define GapDetected 6
|
||||
|
||||
/**
|
||||
* @brief Configures the UART. Call this function only once.
|
||||
*/
|
||||
extern void modbusInit(void);
|
||||
|
||||
/**
|
||||
* @brief receive/transmit data array
|
||||
*/
|
||||
extern volatile unsigned char rxbuffer[MaxFrameIndex+1];
|
||||
|
||||
/**
|
||||
* @brief Current receive/transmit position
|
||||
*/
|
||||
extern volatile uint16_t DataPos;
|
||||
|
||||
/**
|
||||
* This only applies to single address mode.
|
||||
*/
|
||||
#if ADDRESS_MODE == SINGLE_ADR
|
||||
/**
|
||||
* @brief: Read the device address
|
||||
*/
|
||||
extern uint8_t modbusGetAddress(void);
|
||||
|
||||
/**
|
||||
* @brief: Set the device address
|
||||
* Arguments: - newadr: the new device address
|
||||
*/
|
||||
extern void modbusSetAddress(unsigned char newadr);
|
||||
#endif
|
||||
|
||||
/* @brief: Sends a response.
|
||||
*
|
||||
* Arguments: - packtop, index of the last byte in rxbuffer
|
||||
* that contains payload. Maximum value is
|
||||
* MaxFrameIndex-2.
|
||||
*/
|
||||
extern void modbusSendMessage(unsigned char packtop);
|
||||
|
||||
/* @brief: Sends a Modbus exception.
|
||||
*
|
||||
* Arguments: - exceptionCode
|
||||
*/
|
||||
extern void modbusSendException(unsigned char exceptionCode);
|
||||
|
||||
/* @brief: Discards the current transaction. For MULTIPLE_ADR-mode and general
|
||||
* testing purposes. Call this function if you don't want to reply at all.
|
||||
*/
|
||||
void modbusReset(void);
|
||||
|
||||
/**
|
||||
* @brief Call this function whenever possible and check if its return value has the ReceiveCompleted Bit set.
|
||||
* Preferably do this in the main while. I do not recommend calling this function within ISRs.
|
||||
* @example if (modbusGetBusState() & (1<<ReceiveCompleted)) {
|
||||
* modbusSendExcepton(ecIllegalFunction);
|
||||
* }
|
||||
*/
|
||||
extern uint8_t modbusGetBusState(void);
|
||||
|
||||
/**
|
||||
* @brief Call every 100us using a timer ISR.
|
||||
*/
|
||||
extern void modbusTickTimer(void);
|
||||
|
||||
/**
|
||||
* @brief Returns amount of bits/registers requested.
|
||||
*/
|
||||
extern uint16_t modbusRequestedAmount(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the address of the first requested bit/register.
|
||||
*/
|
||||
extern uint16_t modbusRequestedAddress(void);
|
||||
|
||||
/* A fairly simple and hopefully Modbus compliant 16 Bit CRC algorithm.
|
||||
* Returns 1 if the crc check is positive, returns 0 if it fails.
|
||||
* Appends two crc bytes to the array.
|
||||
*/
|
||||
extern uint8_t crc16(volatile uint8_t *ptrToArray,uint8_t inputSize);
|
||||
|
||||
/* @brief: Handles single/multiple input/coil reading and single/multiple coil writing.
|
||||
*
|
||||
* Arguments: - ptrToInArray: pointer to the user's data array containing bits
|
||||
* - startAddress: address of the first bit in the supplied array
|
||||
* - size: input array size in the requested format (bits)
|
||||
*
|
||||
*/
|
||||
extern uint8_t modbusExchangeBits(volatile uint8_t *ptrToInArray, uint16_t startAddress, uint16_t size);
|
||||
|
||||
/* @brief: Handles single/multiple register reading and single/multiple register writing.
|
||||
*
|
||||
* Arguments: - ptrToInArray: pointer to the user's data array containing registers
|
||||
* - startAddress: address of the first register in the supplied array
|
||||
* - size: input array size in the requested format (16bit-registers)
|
||||
*
|
||||
*/
|
||||
extern uint8_t modbusExchangeRegisters(volatile uint16_t *ptrToInArray, uint16_t startAddress, uint16_t size);
|
||||
|
||||
/* @brief: returns 1 if data location adr is touched by current command
|
||||
*
|
||||
* Arguments: - adr: address of the data object
|
||||
*
|
||||
*/
|
||||
extern uint8_t modbusIsInRange(uint16_t adr);
|
||||
|
||||
/* @brief: returns 1 if range of data locations is touched by current command
|
||||
*
|
||||
* Arguments: - startAdr: address of first data object in range
|
||||
* - lastAdr: address of last data object in range
|
||||
*
|
||||
*/
|
||||
extern uint8_t modbusIsRangeInRange(uint16_t startAdr, uint16_t lastAdr);
|
||||
|
||||
extern volatile uint16_t modbusDataAmount;
|
||||
extern volatile uint16_t modbusDataLocation;
|
||||
#endif
|
||||
|
||||
52
code/uart.c
Normal file
52
code/uart.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <avr/io.h>
|
||||
#include "uart.h"
|
||||
|
||||
static int uart_putchar(char c, FILE *stream);
|
||||
|
||||
FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
void uart_init()
|
||||
{
|
||||
DDRD |= 1 << 1; // TX
|
||||
UART_BAUD_REGH = (BAUDRATE>>8);
|
||||
UART_BAUD_REGL = BAUDRATE; // set baud rate
|
||||
|
||||
UART_CTRL_REGB |= (1<<UART_TXEN_BM)
|
||||
//|(1<<UART_RXEN_BM)
|
||||
|(1<<UART_RXCIE_BM); // enable receiver and transmitter
|
||||
|
||||
UART_CTRL_REGC |= (1<<UART_URSEL_BM)
|
||||
|(1<<UART_UCSZ0_BM)
|
||||
|(1<<UART_UCSZ1_BM); // 8bit data format
|
||||
|
||||
stdout = &uart_output;
|
||||
}
|
||||
|
||||
static int uart_putchar(char c, FILE *stream) {
|
||||
if (c == '\n') {
|
||||
uart_putchar('\r', stream);
|
||||
}
|
||||
loop_until_bit_is_set(UART_CTRL_REGA, UART_UDRE_BM);
|
||||
UART_DATA_REG = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void read_sync(char buffer[], uint8_t buffersize, uint8_t * bufferindex){
|
||||
for(int i=0; i < buffersize; i++){
|
||||
buffer[i]=0;
|
||||
}
|
||||
*bufferindex=0;
|
||||
|
||||
char input;
|
||||
do{
|
||||
while ((UART_CTRL_REGA & (1 << UART_RXC_BM)) == 0);
|
||||
input = UART_DATA_REG;
|
||||
|
||||
putchar(input); //echo
|
||||
buffer[*bufferindex]=input;
|
||||
*bufferindex=*bufferindex+1;
|
||||
}while(!(input == '\n' || input == '\r'));
|
||||
buffer[*bufferindex]=0;
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
37
code/uart.h
Normal file
37
code/uart.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _UART_H_
|
||||
#define _UART_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define BAUD 9600
|
||||
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
|
||||
|
||||
#define UART_BAUD_REGH UBRR0H
|
||||
#define UART_BAUD_REGL UBRR0L
|
||||
|
||||
#define UART_CTRL_REGA UCSR0A
|
||||
#define UART_CTRL_REGB UCSR0B
|
||||
#define UART_CTRL_REGC UCSR0C
|
||||
|
||||
// UCSRA
|
||||
#define UART_UDRE_BM UDRE0
|
||||
#define UART_RXC_BM RXC0
|
||||
|
||||
// UCSRB
|
||||
#define UART_TXEN_BM TXEN0
|
||||
#define UART_RXEN_BM RXEN0
|
||||
#define UART_RXCIE_BM RXCIE0
|
||||
|
||||
// UCSRC
|
||||
#define UART_URSEL_BM 0 /* only for old atmega */
|
||||
#define UART_UCSZ0_BM UCSZ00
|
||||
#define UART_UCSZ1_BM UCSZ01
|
||||
|
||||
#define UART_DATA_REG UDR0
|
||||
|
||||
void uart_init (void);
|
||||
void read_sync(char buffer[], uint8_t buffersize, uint8_t * bufferindex);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user