Compare commits

...

41 Commits

Author SHA1 Message Date
72dda3c816 Merge pull request 'changed license to conform to general license policy of institute' (#1) from mohr/ArduinoIO:changedLicense into master
Reviewed-on: eive/eive_arduino_interface#1
2022-02-02 17:26:25 +01:00
3ea528dc5f ndentation test 2020-10-13 20:45:35 +02:00
05bc5e474c tab indentation 2020-10-13 20:45:04 +02:00
162cb8d90d some more info 2020-10-13 20:44:05 +02:00
80cc6b6098 first time setup update 2020-10-13 20:41:53 +02:00
976c15c9b6 added informative comments 2020-10-13 15:48:13 +02:00
651ae6ed44 line breaks added 2020-10-13 13:45:11 +02:00
3af98f2025 readme update 2020-10-13 13:43:47 +02:00
b9f8798538 readme update 2020-10-13 13:42:17 +02:00
2bc352afb3 main tweaks 2020-10-13 13:32:54 +02:00
de9bb974c4 clarifications 2020-10-13 13:30:32 +02:00
a343c9728b trying nested indentation 2020-10-13 13:29:31 +02:00
0571eb9c89 instructions to set up libraries 2020-10-13 13:28:14 +02:00
4176037c2c pci fix 2020-10-13 13:22:33 +02:00
8442bf45f3 test 2020-10-13 13:18:16 +02:00
5fe5ad5b88 pci not working 2020-10-13 13:16:55 +02:00
9dc8c6f4d7 png small now 2020-10-13 13:16:01 +02:00
1bd2fd0b0a readme update 2020-10-13 13:14:41 +02:00
866ed4a577 readme update 2020-10-13 13:01:07 +02:00
1e8a64c7bf readme continued 2020-10-13 12:57:46 +02:00
3b8c1a5fba readme update 2020-10-13 12:53:27 +02:00
a1d2f30e08 readme continued 2020-10-13 12:52:49 +02:00
e72df1c557 readme continued 2020-10-13 12:48:32 +02:00
aa83552739 init readme 2020-10-13 12:45:51 +02:00
a9543a6f7e fixed timer 2020-10-13 12:32:15 +02:00
5f32dbb595 added new sources and includes 2020-10-13 02:07:39 +02:00
48de9591f7 timer lib added 2020-10-13 02:00:04 +02:00
325f7c0bca new io bard file 2020-10-13 01:05:02 +02:00
51554fe4a6 some more output, can be disabled optionally 2020-10-13 00:56:03 +02:00
da92a31910 packet field beautified 2020-10-13 00:45:05 +02:00
8e97270bb7 packet layout beautified 2020-10-13 00:43:33 +02:00
55caac63da doc added, mega port definition 2020-10-13 00:37:09 +02:00
78792e5d4a more stuff moved to cfg 2020-10-12 23:56:30 +02:00
8c5bb21d70 config file created 2020-10-12 23:49:49 +02:00
efdb44b759 include optimized 2020-10-12 23:24:39 +02:00
8f7bb757a4 core update 2020-10-12 22:03:06 +02:00
c346d1b27d renamed submodule 2020-10-12 22:02:10 +02:00
a0dc6f1017 new submodule is fork now 2020-10-12 22:01:28 +02:00
9302f11e15 core update 2020-10-12 22:00:21 +02:00
cf6253216f another line break added 2020-10-12 21:45:36 +02:00
bf94f8332b makefile lines split 2020-10-12 20:45:41 +02:00
11 changed files with 376 additions and 168 deletions

2
.gitignore vendored
View File

@ -103,3 +103,5 @@ local.properties
.cache-main
.scala_dependencies
.worksheet
/Release/
/sloeber.ino.cpp

4
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "arduino_core"]
[submodule "arduino"]
path = arduino_core
url = https://egit.irs.uni-stuttgart.de/mohr/arduino_core.git
url = https://egit.irs.uni-stuttgart.de/eive/arduino_core.git

38
ArduinoConfig.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef ARDUINOCONFIG_H_
#define ARDUINOCONFIG_H_
static const uint8_t COMMAND_TRANSFER_SPI = 1;
static const uint8_t COMMAND_TRANSFER_I2C = 2;
// Can be set to one for additional programming information from the primary
// serial port which is usually also used to flash the Arduino
// Can be disabled if this output interferes with the usual
// serial communication logic
#define PROGRAMMING_OUTPUT 1
#define BAUD_RATE 9600
#define SERIAL_RX_BUFFER_SIZE 256
#define RING_BUFFER_SIZE 100
#define MAX_PACKET_LENGTH 100
#define RING_BUFFER_CHECK_INTVL 1000
// Define which port to use for the SPI Chip Select by using the register
// definitions. The data direction register is assigned as well.
// The ports can be looked up on the official Arduino pinout schematics.
#ifdef ARDUINO_AVR_MEGA2560
// Defines for the Arduino Mega
#define CS_PORT PORTK
#define CS_DDR DDRK
#elif defined(ARDUINO_AVR_UNO)
#define CS_PORT PORTD
#define CS_DDR DDRD
#elif defined(__SAM3X8E__)
// Define for the Arduino Due
#define CS_PORT PORTC
#define CS_DDR DDRC
#else
#define CS_PORT PORTC
#define CS_DDR DDRC
#endif
#endif /* ARDUINOCONFIG_H_ */

173
IOBoard.cpp Normal file
View File

@ -0,0 +1,173 @@
#include "IOBoard.h"
#include <SPI.h>
#include "ArduinoConfig.h"
#include "helper/SimpleRingBuffer.h"
#include "helper/DleEncoder.h"
#include "helper/crc_ccitt.h"
SimpleRingBuffer ringBuffer(RING_BUFFER_SIZE, true);
uint8_t rawData[2 * RING_BUFFER_SIZE];
size_t rawDataSize = 0;
#define PACKET_COMMAND_LENGTH 1
#define PACKET_ADDRESS_LENGTH 1
#define PACKET_SIZE_FIELD_LENGTH 2
#define PACKET_HEADER_LENGTH 4
namespace IOBoard {
void handleNewData() {
ringBuffer.readData(rawData, sizeof(rawData), true, &rawDataSize);
if (rawDataSize == 0) {
return;
}
//look for STX
size_t firstSTXinRawData = 0;
while ((firstSTXinRawData < rawDataSize)
&& (rawData[firstSTXinRawData] != DleEncoder::STX)) {
Serial.println(rawData[firstSTXinRawData]);
firstSTXinRawData++;
}
if (rawData[firstSTXinRawData] != DleEncoder::STX) {
//there is no STX in our data, throw it away...
Serial.println("NO STX");
ringBuffer.deleteData(rawDataSize);
return;
}
uint8_t packet[MAX_PACKET_LENGTH];
size_t packetLen;
size_t readSize;
ReturnValue_t result = DleEncoder::decode(rawData + firstSTXinRawData,
rawDataSize - firstSTXinRawData, &readSize, packet, sizeof(packet),
&packetLen);
size_t toDelete = firstSTXinRawData;
if (result == HasReturnvaluesIF::RETURN_OK) {
handlePacket(packet, packetLen);
// after handling the packet, we can delete it from the raw stream,
// it has been copied to packet
toDelete += readSize;
}
//remove Data which was processed
ringBuffer.deleteData(toDelete);
}
void handlePacket(uint8_t *packet, size_t packetLen) {
/* Paket layout is:
------------------------------
|byte | field |
| | |
|------------------------------|
|1 | 8 bit command |
|------------------------------|
|1 | 8 bit address |
|------------------------------|
|2 | 16bit length |
|------------------------------|
|length | <length> byte data |
|------------------------------|
|2 | 16 bit crc |
|------------------------------|
------------------------------
*/
uint16_t crc = Calculate_CRC(packet, packetLen);
if (crc != 0) {
Serial.println("-AI- Invalid packet checksum detected!");
return;
}
uint16_t payloadLen = (packet[2] << 8) | packet[3];
if (payloadLen != packetLen - 6) {
Serial.println("-AI- Invalid packet length detected!");
return;
}
uint8_t command = packet[0];
uint8_t address = packet[1];
switch (command) {
case COMMAND_TRANSFER_SPI:
transferSPI(address, packet + PACKET_HEADER_LENGTH, payloadLen);
//echo the data back, no need to change the header fields, they are the same
//checksum will be written by sendReply()
//check reply:
Serial.println("Data response check: ");
for(size_t i =0; i< packetLen; i++){
Serial.print("packet nr ");Serial.print(i);Serial.print(" ");
Serial.println(packet[i]);
}
sendReply(packet, packetLen);
break;
default:
Serial.println("invalid command");
break;
}
}
void transferSPI(uint8_t address, uint8_t *data, size_t datalen) {
SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE3));
// The specified address is the bit where the last bit is port 0
// and the first bit is port 7. It is inverted because the SPI protocol
// requires the slave select to be driven low.
CS_PORT = ~address;
SPI.transfer(data, datalen);
// some MGM stuff. This delay might not be needed for other devices.
delay(100);
// Pull the slave select high again.
CS_PORT = 0xff;
SPI.endTransaction();
}
/**
* Encode and send the data, last two bytes of data are assumed to be for the checksum
* and will be overwritten with it
*/
void sendReply(uint8_t *data, size_t len) {
uint16_t crc = Calculate_CRC(data, len - 2);
data[len - 2] = crc >> 8;
data[len - 1] = crc;
//we're being conservative here
//TODO move this to global so other protocols can use it too
uint8_t buffer[2 * len + 2];
buffer[0] = DleEncoder::STX;
size_t writtenLen;
ReturnValue_t result = DleEncoder::encode(data, len, buffer, sizeof(buffer),
&writtenLen, true);
if (result != HasReturnvaluesIF::RETURN_OK) {
return;
}
Serial.write(buffer, writtenLen);
}
//TODO check if this is thread safe by arduino
// I think it is, see HardwareSerial::available()
void serialEvent() {
//Serial.println(ringBuffer.availableWriteSpace());
//uint8_t i = 0;
while (Serial.available()>0) {
uint8_t byte = Serial.read();
ringBuffer.writeData(&byte, 1);
}
//Serial.println(ringBuffer.availableWriteSpace());
}
}

17
IOBoard.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef IOBOARD_H_
#define IOBOARD_H_
#include <Arduino.h>
namespace IOBoard {
void handleNewData();
void handlePacket(uint8_t *packet, size_t packetLen);
void transferSPI(uint8_t address, uint8_t *data, size_t datalen);
void sendReply(uint8_t *data, size_t len);
}
#endif /* IOBOARD_H_ */

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
TARGET = ArduinoIO
# The C source files
SRC =
# The C++ source files
PSRC += main.cpp
PSRC += IOBoard.cpp
PSRC += $(wildcard helper/*.cpp)
PSRC += arduino_core/ArduinoCore-avr/libraries/SPI/src/SPI.cpp
# extra Arduino libraries
ARDLIBS =
EXTRAINCDIRS += arduino_core/ArduinoCore-avr/libraries/SPI/src
EXTRAINCDIRS += arduino_core/src
# this line includes the Makefile.base
include arduino_core/arduino-base.mk

88
README.md Normal file
View File

@ -0,0 +1,88 @@
Arduino IO Interface Board for EIVE
======
## General Information
This is the software for an Arduino board to set it up as an interface board.
The interface board will be used to relay commands from a host computer serial
interface to connected I2C or SPI sensors. This allows device handler testing
on a host machine. There are 2 options to build and flash an Arduino with this repository:
1. The software is compiled and programmed on the Arduino with make and avrdude, provided the AVR GCC toolchain is installed
2. Eclipse with the Sloeber plugin is used. The plugin and Eclipse take care of the build management, and an Arduino IDE like interface is provided in Eclipse
It is recommended to use Sloeber for enhanced convenience, as it takes care of the building process. Furthermore, the interface is similar to the Arduino IDE, which lowers the entry barrier for new developers. It is also integrated into Eclipse, which has a powerful indexer and several other tools to ease development.
## Instructions to build with make
### Prerequisites
1. When compiling on a Windows system, make sure the [Windows Development Tools](https://xpack.github.io/windows-build-tools/install/) are installed and added to the system path.This is required for make to work.
2. Install the [AVR toolchain](https://www.microchip.com/mplab/avr-support/avr-and-arm-toolchains-c-compilers) for your OS
3. If building the binary with make, initiate the contained arduino\_core submodule and the required core contained within that submodule by running
```sh
git submodule update --init --recursively
```
### Build Instructions
Make sure to add `WINDOWS=1` when building on Windows!
1. Run following command to build the software.
```sh
make all -j
```
2. Run following command to clean up the directories
```sh
make clean -j
```
## Instructions build with Sloeber
### Prerequisite
1. Install [Eclipse for C/C++ developers](https://www.eclipse.org/downloads/packages/installer)
2. Install the Eclipse Sloeber plugin by going to Help &rarr; Eclipse Marketplace and searching for Sloeber V4 and installing it
3. First time setup:
- Create a new Arduino sketch by going to File &rarr; New &rarr; Other &rarr; Arduino Sketch.
- Unselect Use Default destination and pick the arduino folder inside eive\_obsw. The name does not really matter, arduino was taken for the example.
- The board specific information can changed later so its okay to take any option here and change it later.
- Take any example sketch (.cpp or .ino). An example file will be generated and should be deleted manually in the tree view on the left
- Eclipse should ask whether it should switch to the Arduino View. Confirm this with yes.
Now the top menu should have an Arduino section which can be used to install all required libraries and Arduino cores. The project properties can be accessed in the tree view on the left by right clicking on the arduino folder and selecting properties
4. It is recommended to get familiar with the interface provided by the plugin. A majority of functions is provided by top bar elements as shown in the following picture.
<img src="helper/eclipse_top_bar.png" width="80%"> <br>
5. The Sloeber plugin build mangement system will parse the arduino folder and all contained subfolders for source files. If you want to exclude source files from the build (for example, to exclude the arduino\_core, Sloeber will take care to include the core files via its integrated board manager!), right click on project folder (arduino) in the tree view on the left, go to Resource Configureation and configure Exclude from Build.
6. Set up the plugin for the used board and install all required libraries.
- Install all required board libraries by going to Arduino &rarr; Preferences and using the options Library Manager and Platform and Boards.
- It is necessary to install the `arduino-timer` library by searching for timer in the Library Manager.
- It is recommended to install the Arduino AVR Boards and the Arduino megaAVR Boards in the Platforms and Boards manager.
- It is also necessary to go to Arduino &rarr; Preferences &rarr; Add a library to the selected project and then adding the `arduino-timer` and `SPI` library there.
### Build and flash instructions
1. Right click on the arduino folder inside the tree view on the left and go to Preferences.
Go to the Arduino section and make sure the Arduino Board Selection tab is configured correctly.
Please note that board libraries and libaries need to be installed and added as beforehand as specified in Prerequisites.
It is recommended to go to the Compile Options tab and addding following define to `to append to C and C++`
```sh
-DSERIAL_RX_BUFFER_SIZE=256
```
2. An Arduino like bar can be seen at the top, which can be used to compile the software and flash it to the board. The check symbol is used to compile while the right arrow symbol is used to flash the software. There are also symbols to open the serial monitor or the serial plot.
3. The serial monitor can be opened in the Serial monitor view tab at the bottom or with the icons at the top bar. Take care to pick the correct USB port and baud rate.

@ -1 +1 @@
Subproject commit 2e1a21773763b2e17b24825f6f7f1e4015e0e9d8
Subproject commit b9aa89f6d2393a59b1103870b7fc2f95f1d8ba76

BIN
helper/eclipse_top_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

196
main.cpp
View File

@ -1,172 +1,48 @@
#include "IOBoard.h"
#include "ArduinoConfig.h"
#include <Arduino.h>
#include "helper/SimpleRingBuffer.h"
#include "helper/DleEncoder.h"
#include "helper/crc_ccitt.h"
#include <arduino-timer.h>
#include <avr/io.h>
//SPI is formally a library, so it is not part of the objects compiled
//from the core and we need to include it explicitly
#include "arduino_core/ArduinoCore-avr/libraries/SPI/src/SPI.h"
#include <SPI.h>
//Define which port to use for the SPI Chip Select
#define CS_PORT PORTC
#define CS_DDR DDRC
#define RING_BUFFER_SIZE 100
#define MAX_PACKET_LENGTH 100
#define SERIAL_RX_BUFFER_SIZE 256
static const uint8_t COMMAND_TRANSFER_SPI = 1;
SimpleRingBuffer ringBuffer(RING_BUFFER_SIZE, true);
uint8_t rawData[2 * RING_BUFFER_SIZE];
size_t rawDataSize = 0;
/**
* Encode and send the data, last two bytes of data are assumed to be for the checksum
* and will be overwritten with it
*/
void sendData(uint8_t *data, size_t len) {
uint16_t crc = Calculate_CRC(data, len - 2);
data[len - 2] = crc >> 8;
data[len - 1] = crc;
//we're being conservative here
//TODO move this to global so other protocols can use it too
uint8_t buffer[2 * len + 2];
buffer[0] = DleEncoder::STX;
size_t writtenLen;
ReturnValue_t result = DleEncoder::encode(data, len, buffer, sizeof(buffer),
&writtenLen, true);
if (result != HasReturnvaluesIF::RETURN_OK) {
return;
}
Serial.write(buffer, writtenLen);
}
void transferSPI(uint8_t address, uint8_t *data, size_t datalen) {
SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE3));
CS_PORT = ~address;
SPI.transfer(data, datalen);
delay(100);
CS_PORT = 0xff;
SPI.endTransaction();
}
void handlePacket(uint8_t *packet, size_t packetLen) {
//Paket layout is:
// 8 bit command | 8 bit address | 16bit length | <length> byte data | 16 bit crc
uint16_t crc = Calculate_CRC(packet, packetLen);
if (crc != 0) {
Serial.println("invalid Checksum");
return;
}
uint16_t payloadLen = (packet[2] << 8) | packet[3];
if (payloadLen != packetLen - 6) {
Serial.println("invalid len");
return;
}
uint8_t command = packet[0];
uint8_t address = packet[1];
switch (command) {
case COMMAND_TRANSFER_SPI:
transferSPI(address, packet + 4, payloadLen);
//echo the data back, no need to change the header fields, they are the same
//checksum will be written by sendData()
//check reply:
Serial.println("Data response check: ");
for(size_t i =0; i< packetLen; i++){
Serial.print("packet nr ");Serial.print(i);Serial.print(" ");Serial.println(packet[i]);
}
sendData(packet, packetLen);
break;
default:
Serial.println("invalid command");
break;
}
}
void handleNewData() {
ringBuffer.readData(rawData, sizeof(rawData), true, &rawDataSize);
if (rawDataSize == 0) {
return;
}
//look for STX
size_t firstSTXinRawData = 0;
while ((firstSTXinRawData < rawDataSize)
&& (rawData[firstSTXinRawData] != DleEncoder::STX)) {
Serial.println(rawData[firstSTXinRawData]);
firstSTXinRawData++;
}
if (rawData[firstSTXinRawData] != DleEncoder::STX) {
//there is no STX in our data, throw it away...
Serial.println("NO STX");
ringBuffer.deleteData(rawDataSize);
return;
}
uint8_t packet[MAX_PACKET_LENGTH];
size_t packetLen;
size_t readSize;
ReturnValue_t result = DleEncoder::decode(rawData + firstSTXinRawData,
rawDataSize - firstSTXinRawData, &readSize, packet, sizeof(packet),
&packetLen);
size_t toDelete = firstSTXinRawData;
if (result == HasReturnvaluesIF::RETURN_OK) {
handlePacket(packet, packetLen);
//after handling the packet, we can delete it from the raw stream, it has been copied to packet
toDelete += readSize;
}
//remove Data which was processed
ringBuffer.deleteData(toDelete);
}
//TODO check if this is thread safe by arduino
void serialEvent() {
//Serial.println(ringBuffer.availableWriteSpace());
uint8_t i = 0;
while (Serial.available()>0) {
uint8_t byte = Serial.read();
ringBuffer.writeData(&byte, 1);
}
//Serial.println(ringBuffer.availableWriteSpace());
}
// Crete default timer which is able to manage 10 concurrent tasks
// and uses milliseconds as a timebase.
auto timer = timer_create_default();
bool periodicHandler1(void* args);
void setup() {
CS_DDR = 0xff;
CS_PORT = 0xff;
Serial.begin(9600);
SPI.begin();
// Set data direction of selected port to output.
CS_DDR = 0xff;
// Drive all the slave selects high as required for the SPI protocol
// if no transfer is going on.
CS_PORT = 0xff;
Serial.begin(BAUD_RATE);
#if PROGRAMMING_OUTPUT == 1
Serial.println("-AI- Setting up Arduino IO interface board.");
Serial.print("-AI- Configured baud rate for serial communication: ");
Serial.println(BAUD_RATE, DEC);
Serial.print("-AI- Size of serial receiver buffer: ");
Serial.print(SERIAL_RX_BUFFER_SIZE, DEC);
Serial.println(" bytes");
#endif
SPI.begin();
// Call periodic handler with certain interval
timer.every(RING_BUFFER_CHECK_INTVL, periodicHandler1);
}
bool periodicHandler1(void* args) {
if(args) {};
IOBoard::handleNewData();
// repeat action
return true;
}
void loop() {
;
handleNewData();
delay(1000);
timer.tick();
}

View File

@ -1,5 +0,0 @@
TARGET = ArduinoIO
SRC = # the C source files
PSRC = main.cpp $(wildcard helper/*.cpp) arduino_core/ArduinoCore-avr/libraries/SPI/src/SPI.cpp # the C++ source files
ARDLIBS = # extra Arduino libraries
include arduino_core/arduino-base.mk # this line includes the Makefile.base