main.c
1 | /*
| 2 | * Author: Manuel Koch
| 3 | * Modbus Test Configuration
| 4 | * µC: Atmega 32
| 5 | * Read & Write Holding registers
| 6 | */
| 7 |
| 8 | #include "avr/io.h"
| 9 | #include "avr/signal.h"
| 10 | #include "avr/interrupt.h"
| 11 | #include "stdio.h"
| 12 | #include "stdint.h"
| 13 | #include "util/delay.h"
| 14 | #include <avr/pgmspace.h>
| 15 |
| 16 |
| 17 | //Defines
| 18 |
| 19 | #define F_CPU 14745600UL
| 20 | #define slaveadress 17
| 21 | #define rxbuffersize 32
| 22 | #define txbuffersize 32
| 23 |
| 24 |
| 25 | //CRC16 table for crc calculation
| 26 |
| 27 | static uint8_t table_crc_hi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 28 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 29 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 30 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
| 31 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 32 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 33 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 34 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
| 35 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 36 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 37 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 38 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
| 39 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 40 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
| 41 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
| 42 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
| 43 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 44 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 45 | 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 46 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
| 47 | 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
| 48 | 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 };
| 49 |
| 50 | /* Table of CRC values for low-order byte */
| 51 | static uint8_t table_crc_lo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02,
| 52 | 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D,
| 53 | 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08,
| 54 | 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF,
| 55 | 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16,
| 56 | 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31,
| 57 | 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34,
| 58 | 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B,
| 59 | 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A,
| 60 | 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25,
| 61 | 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20,
| 62 | 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7,
| 63 | 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E,
| 64 | 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9,
| 65 | 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC,
| 66 | 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3,
| 67 | 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52,
| 68 | 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D,
| 69 | 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58,
| 70 | 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F,
| 71 | 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46,
| 72 | 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 };
| 73 |
| 74 | // Prototypen
| 75 |
| 76 | void cleanrxbuffer(void);
| 77 | void cleantxbuffer(void);
| 78 | void dataprocessing(void);
| 79 | void buildframe(void);
| 80 | // Globale Variablen
| 81 |
| 82 | //Write Holding Register
| 83 |
| 84 | //Write Register 40000 - 40003 = holdingregister [0-3]
| 85 | //Read Register 40100 - 40105 = holdingregister [4-10]
| 86 |
| 87 | uint16_t holdingregister[10] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0xABFF, 0xFFCD,0xFEFF,0x00FF,0xFF00,0x11FF} ;
| 88 |
| 89 | volatile uint8_t resetflag = 0;
| 90 | volatile uint8_t readbytes = 0;
| 91 | volatile uint8_t dataprocessingstat = 0;
| 92 | volatile uint8_t txposition = 0;
| 93 | volatile uint8_t txstartcomplete = 0;
| 94 | volatile uint8_t txstart[5];
| 95 | uint8_t rxbuffer[rxbuffersize];
| 96 | uint8_t txbuffer[txbuffersize];
| 97 | volatile uint8_t txbuffer_length;
| 98 | volatile uint8_t rxbuffer_length;
| 99 | volatile uint8_t rxbyte = 0 ;
| 100 | volatile uint16_t checksum;
| 101 | volatile uint8_t crcokay;
| 102 | volatile uint8_t prestatus = 0;
| 103 | volatile uint8_t writebytes = 0;
| 104 | volatile uint8_t data;
| 105 | //Status UART
| 106 |
| 107 | typedef enum {
| 108 | free, receiving, complete, sending
| 109 | } receive_status_t;
| 110 |
| 111 | typedef enum {
| 112 | FC03, FC10, Error, Reset, E01, E02, E03, E04
| 113 | } function_t;
| 114 |
| 115 | typedef struct {
| 116 | volatile receive_status_t status;
| 117 | volatile uint16_t tics;
| 118 | } uart_status_t ;
| 119 |
| 120 | volatile function_t functioncode = Reset;
| 121 |
| 122 |
| 123 | volatile static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) {
| 124 | uint8_t crc_hi = 0xFF; // high CRC byte initialized
| 125 | uint8_t crc_lo = 0xFF; // low CRC byte initialized
| 126 | unsigned int i; // will index into CRC lookup
| 127 |
| 128 | //pass through message buffer
| 129 | while (buffer_length--) {
| 130 | i = crc_hi ^ *buffer++;
| 131 | crc_hi = crc_lo ^ table_crc_hi[i];
| 132 | crc_lo = table_crc_lo[i];
| 133 | }
| 134 |
| 135 | return (crc_hi << 8 | crc_lo);
| 136 | }
| 137 |
| 138 | volatile static uint8_t checkcrc(uint8_t *argrxbuffer, uint8_t argrxbyte, uint8_t argchecksumrxlow, uint8_t argchecksumrxhigh){
| 139 | uint16_t checksumrx;
| 140 | uint16_t checksumcalced;
| 141 | checksumrx = crc16(argrxbuffer, argrxbyte);
| 142 | checksumcalced = (argchecksumrxhigh << 8 | argchecksumrxlow);
| 143 |
| 144 | if (checksumrx == checksumcalced){ //
| 145 | return 1;
| 146 | }
| 147 | else{
| 148 | return 0;
| 149 | }
| 150 | }
| 151 |
| 152 | volatile uint8_t *bufferpoint;
| 153 |
| 154 | uart_status_t uart_status;
| 155 |
| 156 | int main(void){
| 157 |
| 158 |
| 159 | // Locale Variablen
| 160 |
| 161 | // Initalisierung
| 162 | sei();
| 163 | // UART
| 164 | UCSRB |= (1<<RXCIE) | (1 << RXEN) | (1 << TXEN) ;
| 165 | UCSRC |= (1<<URSEL)| (1<<UCSZ1) | (1<<UCSZ0);
| 166 | UBRRL = 95; //Baudrate 9600
| 167 | uart_status.status = free;
| 168 | // Timer
| 169 | TIMSK |= (1<<OCIE0);
| 170 | TCCR0 |= (1<<WGM01) | (1<<CS02) | (1<<CS00);
| 171 | OCR0 = 2;
| 172 | // GPIO
| 173 | DDRD |= (1<<PD2);
| 174 | DDRB |= (1<<PB0) | (1<<PB1);
| 175 | PORTB |= (1<<PB0);
| 176 | PORTD &= ~(1<<PD2);
| 177 |
| 178 | // main loop
| 179 |
| 180 | while(1){
| 181 | // data processing
| 182 | if (prestatus != uart_status.status) {
| 183 | prestatus = uart_status.status;
| 184 |
| 185 | if (uart_status.status == complete){
| 186 | dataprocessing();
| 187 | functioncode = Reset;
| 188 | }
| 189 | }
| 190 | // frame transmitted
| 191 | if(resetflag){
| 192 | resetflag = 0;
| 193 | txposition = 0;
| 194 | cleantxbuffer();
| 195 | PORTB &= ~(1<<PB1);
| 196 | PORTB &= ~(1<<PB0);
| 197 | }
| 198 | } //main Loop
| 199 | return 0;
| 200 | }
| 201 |
| 202 |
| 203 |
| 204 |
| 205 | // frame complete & timeout
| 206 | ISR (TIMER0_COMP_vect) {
| 207 |
| 208 | if (uart_status.status == receiving) {
| 209 | uart_status.tics++;
| 210 |
| 211 | // frame complete
| 212 | if(uart_status.tics != free){
| 213 | if (uart_status.tics >= 21) {
| 214 | uart_status.status = complete;
| 215 | PORTD |= (1<<PD2);
| 216 | }
| 217 |
| 218 | // time out
| 219 | if ((uart_status.tics <= 21) && (uart_status.tics >= 10)) {
| 220 | uart_status.status = complete;
| 221 | PORTD |= (1<<PD2);
| 222 | }
| 223 | }
| 224 | }
| 225 | }
| 226 |
| 227 | // receive frame
| 228 |
| 229 | ISR(USART_RXC_vect)
| 230 |
| 231 | {
| 232 |
| 233 | data = UDR;
| 234 |
| 235 | // watch for time outs
| 236 |
| 237 | uart_status.tics = 0;
| 238 |
| 239 | if (uart_status.status == free) {
| 240 | uart_status.status = receiving;
| 241 | rxbyte = 0;
| 242 | }
| 243 |
| 244 |
| 245 | if (uart_status.status == receiving) {
| 246 |
| 247 | rxbuffer[rxbyte] = data;
| 248 | rxbyte++;
| 249 | if (rxbyte >= rxbuffersize) {
| 250 | uart_status.status = complete;
| 251 | PORTD |= (1<<PD2);
| 252 | }
| 253 | }
| 254 | }
| 255 |
| 256 | //send frame
| 257 |
| 258 | ISR(USART_UDRE_vect ){
| 259 | if (txposition < txbuffer_length){ //
| 260 | UDR = txbuffer[txposition];
| 261 | txposition++;
| 262 | }
| 263 | else{
| 264 | UCSRB |= (1<<TXCIE);
| 265 | UCSRB &= ~(1<<UDRIE);
| 266 | }
| 267 | }
| 268 |
| 269 | //last byte send
| 270 |
| 271 | ISR(USART_TXC_vect){
| 272 | resetflag = 1;
| 273 | UCSRB |= (1<<RXCIE) | (1 << RXEN);
| 274 | UCSRB &= ~(1<<TXCIE);
| 275 | PORTD &= ~(1<<PD2);
| 276 | uart_status.status = free;
| 277 | cleanrxbuffer();
| 278 | }
| 279 |
| 280 | void dataprocessing(void){
| 281 |
| 282 | //check crc
| 283 | crcokay = checkcrc(rxbuffer, rxbyte-2, rxbuffer[rxbyte-1], rxbuffer[rxbyte -2 ]);
| 284 | if (crcokay){
| 285 | //check slave adress
| 286 | if (rxbuffer[0]==slaveadress){ //rxbuffer[0]==slavedadress
| 287 | //check functioncode
| 288 | if(rxbuffer[1] == 0x03){
| 289 | functioncode = FC03;
| 290 | }
| 291 | else if(rxbuffer[1] == 0x10){
| 292 | functioncode = FC10;
| 293 | }
| 294 | else{
| 295 | functioncode = E02;
| 296 | }
| 297 | //dataprocessing
| 298 | switch (functioncode){
| 299 | case FC03:
| 300 | readbytes = rxbuffer[5]*2;
| 301 | // create frame head
| 302 | txbuffer[0] = rxbuffer[0];
| 303 | txbuffer[1] = rxbuffer[1];
| 304 | txbuffer[2] = readbytes;
| 305 | txbuffer_length = 3 + readbytes + 2;
| 306 | if(rxbuffer[3]>= 100 && rxbuffer[3]<= 106 ){
| 307 | int k = 0;
| 308 | // load holding register in txbuffer
| 309 | for (int i=3; i<readbytes+3; i += 2 ){
| 310 |
| 311 | txbuffer[i] = (holdingregister[rxbuffer[3]-96+k]>>8);
| 312 | txbuffer[i+1] = holdingregister[rxbuffer[3]-96+k];
| 313 |
| 314 | k++;
| 315 | }
| 316 | }
| 317 | // create crc
| 318 | checksum = crc16(txbuffer, txbuffer_length-2);
| 319 | txbuffer[3+readbytes+1] = checksum;
| 320 | txbuffer[3+readbytes+0] = (checksum>>8);
| 321 | PORTB |= (1<<PB1);
| 322 | PORTD |= (1<<PD2);
| 323 | // set transmit interrupt
| 324 | UCSRB |= (1<<UDRIE) ;
| 325 | UCSRB &= ~(1<<RXCIE) | ~(1<<RXEN);
| 326 | // send dummy value to start transmit
| 327 | UDR = 0x1B; //Dummy Value
| 328 | uart_status.status = sending;
| 329 | break;
| 330 |
| 331 | case FC10:
| 332 | writebytes = rxbuffer[5]*2;
| 333 | for(int i = 0; i<=5; i++){
| 334 | txbuffer[i] = rxbuffer[i];
| 335 | }
| 336 | txbuffer_length = 8;
| 337 | uint8_t j = 0;
| 338 | for(int i = 0; i<writebytes; i++){
| 339 | holdingregister[rxbuffer[5+j]] = (rxbuffer[6+i]<<8 | rxbuffer[7+1+i]);
| 340 | j++;
| 341 | }
| 342 | checksum = crc16(txbuffer, txbuffer_length-2);
| 343 | txbuffer_length++;
| 344 | txbuffer[7] = checksum;
| 345 | txbuffer[6] = (checksum>>8);
| 346 | PORTB |= (1<<PB1);
| 347 | PORTD |= (1<<PD2);
| 348 | UCSRB |= (1<<UDRIE) ;
| 349 | UCSRB &= ~(1<<RXCIE) | ~(1<<RXEN);
| 350 | UDR = 0x00; //Dummy Value
| 351 | uart_status.status = sending;
| 352 | break;
| 353 |
| 354 | case Reset:
| 355 | uart_status.status = free;
| 356 | break;
| 357 |
| 358 | case Error:
| 359 | uart_status.status = free;
| 360 | cleanrxbuffer();
| 361 | cleantxbuffer();
| 362 | PORTB |= (1<<PB1);
| 363 | break;
| 364 |
| 365 | default:
| 366 | uart_status.status = free;
| 367 | cleanrxbuffer();
| 368 | cleantxbuffer();
| 369 | functioncode = Reset;
| 370 | PORTD &= ~(1<<PD2);
| 371 | UCSRB &= ~(1 << TXEN) | ~(1<<UDRIE);
| 372 | UCSRB |= (1<<RXCIE) | (1 << RXEN);
| 373 | break;
| 374 | }
| 375 | }
| 376 |
| 377 | // slave id not okay
| 378 |
| 379 | else{
| 380 | cleanrxbuffer();
| 381 | cleantxbuffer();
| 382 | uart_status.status = free;
| 383 | functioncode = Reset;
| 384 | PORTD &= ~(1<<PD2);
| 385 | UCSRB &= ~(1 << TXEN) | ~(1<<UDRIE);
| 386 | UCSRB |= (1<<RXCIE) | (1 << RXEN);
| 387 | }
| 388 | }
| 389 |
| 390 | // checksum not okay
| 391 | else{
| 392 | cleanrxbuffer();
| 393 | cleantxbuffer();
| 394 | uart_status.status = free;
| 395 | functioncode = Reset;
| 396 | PORTD &= ~(1<<PD2);
| 397 | UCSRB &= ~(1 << TXEN) | ~(1<<UDRIE);
| 398 | UCSRB |= (1<<RXCIE) | (1 << RXEN);
| 399 | }
| 400 | }
| 401 |
| 402 | void cleanrxbuffer(void){
| 403 | for (int i = 0; i<=rxbuffersize; i++){
| 404 | rxbuffer[i] = 0;
| 405 | }
| 406 | rxbyte = 0;
| 407 | }
| 408 |
| 409 | void cleantxbuffer(void){
| 410 | for (int i = 0; i<=txbuffersize; i++){
| 411 | txbuffer[i] = 0;
| 412 | }
| 413 |
| 414 | }
|
|