I recently started writing a packet sniffer using libpcap & linux specific libraries and headers like netinet/tcp.h.
The problem is: When I get the tcp header using TH_OFF(tcp)*4, its value is frequently less than 20 bytes. Ok, I know, it's malformed, but Wireshark is displaying other value(20<). The same happens with the flags.
Here is the code:
struct nread_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
u_short th_seq; /* sequence number */
u_short th_ack; /* acknowledgement number */
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:4, /* data offset */
th_x2:4; /* (unused) */
#endif
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_NS 0x100
#define TH_RS 0xE00
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
};
const struct nread_tcp* tcp = (struct nread_tcp*)(pachet + sizeof(struct ether_header) + sizeof(struct crt_ip));
...
int hlen = TH_OFF(tcp)*4;
char * tmp = new char[strlen("000000000000")+1];
strcpy(tmp,"000000000000");
if (tcp->th_flags & TH_NS){
tmp[3] = '1';
}
else
tmp[3] = '0';
if (tcp->th_flags & TH_ECE){
tmp[4] = '1';
}
else
tmp[4] = '0';
if (tcp->th_flags & TH_CWR){
tmp[5] = '1';
}
else
tmp[5] = '0';
if (tcp->th_flags & TH_URG){
tmp[6] = '1';
}
else
tmp[6] = '0';
if (tcp->th_flags & TH_ACK){
tmp[7] = '1';
}
else
tmp[7] = '0';
if (tcp->th_flags & TH_PUSH){
tmp[8] = '1';
}
else
tmp[8] = '0';
if (tcp->th_flags & TH_RST){
tmp[9] = '1';
}
else
tmp[9] = '0';
if (tcp->th_flags & TH_SYN){
tmp[10] = '1';
}
else
tmp[10] = '0';
if (tcp->th_flags & TH_FIN){
tmp[11] = '1';
}
else
tmp[11] = '0';
I've googled it quite a bit and haven't find something useful. Maybe it's another begginer's error but i can't figure it out.
Thanks in advance.
Once again, thanks for the tips/ideas.
All pcap handling functions including handling the link-layer headers are correct. My app is written in Qt 4.7.4. Let me give you some more info about my logic. Maybe Im using pointers that are not pointing correctly. I wish to emphasize the fact that tcp is the only protocol with this issue. The others that Im analyzing in detail are Ethernet frame header, IP header, UDP header, ARP/RARP header and ICMP header.
As for the logic, it goes like this:
I have a thread witch listens
-pcap_loop(captureHandler,-1,packetHandler,(unsigned char*)dumpfile);
The packetHandler is on a separate .cpp named "engine.cpp"
In that .cpp I have the packet interpreter witch, accordingly to the protocol type, instantiates the right class(tcpPacketHandler for tcp protocol).
My tcp struct is declared in engine.h (not the tcpPacketHandler header).
The tcp struct is the one posted in my initial question.
The tcpPacketHandler constructor looks like this:
tcpPacketHandler::tcpPacketHandler(u_char *arg, const pcap_pkthdr * header,const u_char * pachet)
{
(void)arg;
(void)header;
const struct nread_tcp* tcp = (struct nread_tcp*)(pachet + sizeof(struct ether_header) + sizeof(struct crt_ip));
//fprintf(stdout,"\nSEQUENCE NUMBER %u \nACK %u \nWINDOW %u\nURGENT POINTER %u\n", tcp->th_seq, tcp->th_ack, tcp->th_win,tcp->th_urp);
//qDebug() <<sizeof(struct ether_header)<< " "<< sizeof(struct crt_ip);
this->seqNr = ntohs(tcp->th_seq);
this->ackNr = ntohs(tcp->th_ack);
this->window = ntohs(tcp->th_win);
this->urgentPointer = ntohs(tcp->th_urp);
this->portSursa = ntohs(tcp->th_sport);
this->portDestinatie = ntohs(tcp->th_dport);
this->checksum = ntohs(tcp->th_sum);
char * tmp = new char[strlen("000000000000")+1];
strcpy(tmp,"000000000000");
if (tcp->th_flags & TH_NS){
tmp[3] = '1';
}
else
tmp[3] = '0';
if (tcp->th_flags & TH_ECE){
tmp[4] = '1';
}
else
tmp[4] = '0';
if (tcp->th_flags & TH_CWR){
tmp[5] = '1';
}
else
tmp[5] = '0';
if (tcp->th_flags & TH_URG){
tmp[6] = '1';
}
else
tmp[6] = '0';
if (tcp->th_flags & TH_ACK){
tmp[7] = '1';
}
else
tmp[7] = '0';
if (tcp->th_flags & TH_PUSH){
tmp[8] = '1';
}
else
tmp[8] = '0';
if (tcp->th_flags & TH_RST){
tmp[9] = '1';
}
else
tmp[9] = '0';
if (tcp->th_flags & TH_SYN){
tmp[10] = '1';
}
else
tmp[10] = '0';
if (tcp->th_flags & TH_FIN){
tmp[11] = '1';
}
else
tmp[11] = '0';
this->hdrLen = TH_OFF(tcp)*4;
// qDebug() << this->hdrLen;
this->flags = new char[13];
strcpy(this->flags,tmp);
if(tmp)
{
delete [] tmp;
tmp = NULL;
}
}
All the tcp header fields are displayed correctly until I hit the "data offset field"
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
From this point on the remainder of the tcp header data does not correspond with the Wireshark result (Data offset, Flags and Payload).
Maybe the problem is that the nread_tcp struct is declared in a file shared by multiple packets and the pointer goes wrong when capturing lots of packets in a short interval.
I really have no clue.
Every thing that I found on Google uses this struct, but I dont know whats the problem in my code.
And as a secondarry question: can the packetHandler function (from pcap_loop function) be declared as member of a class? (because I noticed it has no type and when I tried to make it a class member the compiler thrown me an error).
Thanks in advance.
There is no data offset field in the TCP header after the urgent pointer; the TCP header has options after the urgent pointer. Instead, remove the
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:4, /* data offset */
th_x2:4; /* (unused) */
#endif
from your structure and move
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
up to replace it, so the structure looks like
struct nread_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
u_short th_seq; /* sequence number */
u_short th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_NS 0x100
#define TH_RS 0xE00
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
and recompile your program, and see whether that works.
Here's what worked for me.
u_short th_seq; /* sequence number */
u_short th_ack; /* acknowledgement number */
Changed to:
u_int th_seq; /* sequence number */
u_int th_ack; /* acknowledgement number */
Related
I am trying to follow a tutorial from youtube on using ROS with Arduino to control motors, and I have connected my L298N with the battery precisely as the video describes and have uploaded sketch 1 with the supporting folder and it loads properly. The Arduino is powered properly via USB, but that connection is not shown in the diagram. When I type the "e" command, I get the proper response of "0 0" and when I do the "o 255 255" it says "OK" and drives properly but upon using "e" to recheck the encoders I am getting the same "0 0". If anyone can spot something wrong with this, I would really appreciate the help in fixing it. Diagram and Code Below
Code:
#define USE_BASE // Enable the base controller code
//#undef USE_BASE // Disable the base controller code
/* Define the motor controller and encoder library you are using */
#ifdef USE_BASE
/* The Pololu VNH5019 dual motor driver shield */
//#define POLOLU_VNH5019
/* The Pololu MC33926 dual motor driver shield */
//#define POLOLU_MC33926
/* The RoboGaia encoder shield */
//#define ROBOGAIA
/* Encoders directly attached to Arduino board */
#define ARDUINO_ENC_COUNTER
/* L298 Motor driver*/
#define L298_MOTOR_DRIVER
#endif
//#define USE_SERVOS // Enable use of PWM servos as defined in servos.h
#undef USE_SERVOS // Disable use of PWM servos
/* Serial port baud rate */
#define BAUDRATE 57600
/* Maximum PWM signal */
#define MAX_PWM 255
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
/* Include definition of serial commands */
#include "commands.h"
/* Sensor functions */
#include "sensors.h"
/* Include servo support if required */
#ifdef USE_SERVOS
#include <Servo.h>
#include "servos.h"
#endif
#ifdef USE_BASE
/* Motor driver function definitions */
#include "motor_driver.h"
/* Encoder driver function definitions */
#include "encoder_driver.h"
/* PID parameters and functions */
#include "diff_controller.h"
/* Run the PID loop at 30 times per second */
#define PID_RATE 30 // Hz
/* Convert the rate into an interval */
const int PID_INTERVAL = 1000 / PID_RATE;
/* Track the next time we make a PID calculation */
unsigned long nextPID = PID_INTERVAL;
/* Stop the robot if it hasn't received a movement command
in this number of milliseconds */
#define AUTO_STOP_INTERVAL 2000
long lastMotorCommand = AUTO_STOP_INTERVAL;
#endif
/* Variable initialization */
// A pair of varibles to help parse serial commands (thanks Fergs)
int arg = 0;
int index = 0;
// Variable to hold an input character
char chr;
// Variable to hold the current single-character command
char cmd;
// Character arrays to hold the first and second arguments
char argv1[16];
char argv2[16];
// The arguments converted to integers
long arg1;
long arg2;
/* Clear the current command parameters */
void resetCommand() {
cmd = NULL;
memset(argv1, 0, sizeof(argv1));
memset(argv2, 0, sizeof(argv2));
arg1 = 0;
arg2 = 0;
arg = 0;
index = 0;
}
/* Run a command. Commands are defined in commands.h */
int runCommand() {
int i = 0;
char *p = argv1;
char *str;
int pid_args[4];
arg1 = atoi(argv1);
arg2 = atoi(argv2);
switch(cmd) {
case GET_BAUDRATE:
Serial.println(BAUDRATE);
break;
case ANALOG_READ:
Serial.println(analogRead(arg1));
break;
case DIGITAL_READ:
Serial.println(digitalRead(arg1));
break;
case ANALOG_WRITE:
analogWrite(arg1, arg2);
Serial.println("OK");
break;
case DIGITAL_WRITE:
if (arg2 == 0) digitalWrite(arg1, LOW);
else if (arg2 == 1) digitalWrite(arg1, HIGH);
Serial.println("OK");
break;
case PIN_MODE:
if (arg2 == 0) pinMode(arg1, INPUT);
else if (arg2 == 1) pinMode(arg1, OUTPUT);
Serial.println("OK");
break;
case PING:
Serial.println(Ping(arg1));
break;
#ifdef USE_SERVOS
case SERVO_WRITE:
servos[arg1].setTargetPosition(arg2);
Serial.println("OK");
break;
case SERVO_READ:
Serial.println(servos[arg1].getServo().read());
break;
#endif
#ifdef USE_BASE
case READ_ENCODERS:
Serial.print(readEncoder(LEFT));
Serial.print(" ");
Serial.println(readEncoder(RIGHT));
break;
case RESET_ENCODERS:
resetEncoders();
resetPID();
Serial.println("OK");
break;
case MOTOR_SPEEDS:
/* Reset the auto stop timer */
lastMotorCommand = millis();
if (arg1 == 0 && arg2 == 0) {
setMotorSpeeds(0, 0);
resetPID();
moving = 0;
}
else moving = 1;
leftPID.TargetTicksPerFrame = arg1;
rightPID.TargetTicksPerFrame = arg2;
Serial.println("OK");
break;
case MOTOR_RAW_PWM:
/* Reset the auto stop timer */
lastMotorCommand = millis();
resetPID();
moving = 0; // Sneaky way to temporarily disable the PID
setMotorSpeeds(arg1, arg2);
Serial.println("OK");
break;
case UPDATE_PID:
while ((str = strtok_r(p, ":", &p)) != '\0') {
pid_args[i] = atoi(str);
i++;
}
Kp = pid_args[0];
Kd = pid_args[1];
Ki = pid_args[2];
Ko = pid_args[3];
Serial.println("OK");
break;
#endif
default:
Serial.println("Invalid Command");
break;
}
}
/* Setup function--runs once at startup. */
void setup() {
Serial.begin(BAUDRATE);
// Initialize the motor controller if used */
#ifdef USE_BASE
#ifdef ARDUINO_ENC_COUNTER
//set as inputs
DDRD &= ~(1<<LEFT_ENC_PIN_A);
DDRD &= ~(1<<LEFT_ENC_PIN_B);
DDRC &= ~(1<<RIGHT_ENC_PIN_A);
DDRC &= ~(1<<RIGHT_ENC_PIN_B);
//enable pull up resistors
PORTD |= (1<<LEFT_ENC_PIN_A);
PORTD |= (1<<LEFT_ENC_PIN_B);
PORTC |= (1<<RIGHT_ENC_PIN_A);
PORTC |= (1<<RIGHT_ENC_PIN_B);
// tell pin change mask to listen to left encoder pins
PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
// tell pin change mask to listen to right encoder pins
PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
// enable PCINT1 and PCINT2 interrupt in the general interrupt mask
PCICR |= (1 << PCIE1) | (1 << PCIE2);
#endif
initMotorController();
resetPID();
#endif
/* Attach servos if used */
#ifdef USE_SERVOS
int i;
for (i = 0; i < N_SERVOS; i++) {
servos[i].initServo(
servoPins[i],
stepDelay[i],
servoInitPosition[i]);
}
#endif
}
/* Enter the main loop. Read and parse input from the serial port
and run any valid commands. Run a PID calculation at the target
interval and check for auto-stop conditions.
*/
void loop() {
while (Serial.available() > 0) {
// Read the next character
chr = Serial.read();
// Terminate a command with a CR
if (chr == 13) {
if (arg == 1) argv1[index] = NULL;
else if (arg == 2) argv2[index] = NULL;
runCommand();
resetCommand();
}
// Use spaces to delimit parts of the command
else if (chr == ' ') {
// Step through the arguments
if (arg == 0) arg = 1;
else if (arg == 1) {
argv1[index] = NULL;
arg = 2;
index = 0;
}
continue;
}
else {
if (arg == 0) {
// The first arg is the single-letter command
cmd = chr;
}
else if (arg == 1) {
// Subsequent arguments can be more than one character
argv1[index] = chr;
index++;
}
else if (arg == 2) {
argv2[index] = chr;
index++;
}
}
}
// If we are using base control, run a PID calculation at the appropriate intervals
#ifdef USE_BASE
if (millis() > nextPID) {
updatePID();
nextPID += PID_INTERVAL;
}
// Check to see if we have exceeded the auto-stop interval
if ((millis() - lastMotorCommand) > AUTO_STOP_INTERVAL) {;
setMotorSpeeds(0, 0);
moving = 0;
}
#endif
// Sweep servos
#ifdef USE_SERVOS
int i;
for (i = 0; i < N_SERVOS; i++) {
servos[i].doSweep();
}
#endif
}
Encoder Pin Designations:
/* *************************************************************
Encoder driver function definitions - by James Nugen
************************************************************ */
#ifdef ARDUINO_ENC_COUNTER
//below can be changed, but should be PORTD pins;
//otherwise additional changes in the code are required
#define LEFT_ENC_PIN_A PD2 //pin 2
#define LEFT_ENC_PIN_B PD3 //pin 3
//below can be changed, but should be PORTC pins
#define RIGHT_ENC_PIN_A PC4 //pin A4
#define RIGHT_ENC_PIN_B PC5 //pin A5
#endif
long readEncoder(int i);
void resetEncoder(int i);
void resetEncoders();
Encoder Driver:
/* *************************************************************
Encoder definitions
Add an "#ifdef" block to this file to include support for
a particular encoder board or library. Then add the appropriate
#define near the top of the main ROSArduinoBridge.ino file.
************************************************************ */
#ifdef USE_BASE
#ifdef ROBOGAIA
/* The Robogaia Mega Encoder shield */
#include "MegaEncoderCounter.h"
/* Create the encoder shield object */
MegaEncoderCounter encoders = MegaEncoderCounter(4); // Initializes the Mega Encoder Counter in the 4X Count mode
/* Wrap the encoder reading function */
long readEncoder(int i) {
if (i == LEFT) return encoders.YAxisGetCount();
else return encoders.XAxisGetCount();
}
/* Wrap the encoder reset function */
void resetEncoder(int i) {
if (i == LEFT) return encoders.YAxisReset();
else return encoders.XAxisReset();
}
#elif defined(ARDUINO_ENC_COUNTER)
volatile long left_enc_pos = 0L;
volatile long right_enc_pos = 0L;
static const int8_t ENC_STATES [] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0}; //encoder lookup table
/* Interrupt routine for LEFT encoder, taking care of actual counting */
ISR (PCINT2_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Interrupt routine for RIGHT encoder, taking care of actual counting */
ISR (PCINT1_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Wrap the encoder reading function */
long readEncoder(int i) {
if (i == LEFT) return left_enc_pos;
else return right_enc_pos;
}
/* Wrap the encoder reset function */
void resetEncoder(int i) {
if (i == LEFT){
left_enc_pos=0L;
return;
} else {
right_enc_pos=0L;
return;
}
}
#else
#error A encoder driver must be selected!
#endif
/* Wrap the encoder reset function */
void resetEncoders() {
resetEncoder(LEFT);
resetEncoder(RIGHT);
}
#endif
I think if you use a Mega instead of an Uno, the pin ports are different.
So change the port from PD4 to PE4 and PD3 to PE5. Also, change PC4 to PF4 and PC5 to PF5.
In the Encoder.ino, you also have to change the ports accordingly.
Encoder.h:
#define LEFT_ENC_PIN_A PE4 //pin 2
#define LEFT_ENC_PIN_B PE5 //pin 3
//below can be changed, but should be PORTC pins
#define RIGHT_ENC_PIN_A PF5 //pin A4
#define RIGHT_ENC_PIN_B PF5 //pin A5
Encoder.ino:
/* Interrupt routine for LEFT encoder, taking care of actual counting */
ISR (PCINT2_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINE & (3 << 2)) >> 2; //read the current state into lowest 2 bits
left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Interrupt routine for RIGHT encoder, taking care of actual counting */
ISR (PCINT1_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINF & (3 << 4)) >> 4; //read the current state into lowest 2 bits
right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
I'm trying to get the Arduino into programming mode. I want to examine the flash to see if my software is already installed and then upload one of several binaries depending on its need. For testing, I have a simple sketch that does nothing in the loop but sends "In Regular Mode" over the serial port on setup.
Toggling DTR/RTS does indeed reset the Arduino but sending a sync request does nothing and the bootloader times out and passes control to the sketch.
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/io.h>
#include <sys/ioctl.h>
#include <termios.h>
int main()
{
static const uint8_t bSyncString [] = {0x30, 0x20}; // {CMD_STK_GET_SYNC, SYNC_CRC_EOP};
uint8_t bBuffer [256];
unsigned int ctl;
struct termios tty;
int iFd = open ("/dev/ttyACM0", O_RDWR | O_NOCTTY);
if (iFd >= 0)
{
// From STK500 spec: 115.2kbps, 8 data bits, 1stop bits, no parity
if (tcgetattr (iFd, &tty) == 0)
{
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
tty.c_cflag |= CS8|CSTOPB;
tty.c_cflag &= ~(PARENB|PARODD);
if (tcsetattr(iFd, TCSANOW|TCSADRAIN, &tty) == 0)
printf ("Attributes set\n");
}
if (ioctl (iFd, TIOCMGET, &ctl) >= 0) // Get current line states
{
ctl &= ~(TIOCM_DTR|TIOCM_RTS);
if (ioctl(iFd, TIOCMSET, &ctl) >= 0) // DTR and RTS low
{
usleep (50 * 1000);
ctl |= TIOCM_DTR|TIOCM_RTS;
if (ioctl(iFd, TIOCMSET, &ctl) >= 0) // DTR and RTS high
{
usleep (50 * 1000);
if (write (iFd, bSyncString, 2) == 2)
if (read (iFd, bBuffer, sizeof (bBuffer)) > 0)
printf ("Received %s\n", bBuffer);
}
}
}
close (iFd);
}
}
Got it. I took the source of avrdude, extracted the relevant bits and then cut them down to the bone, pasting functionality back and forth until it worked.
Some of my serial settings were off and the delay before sending the write was too short so a minimal setup is as follows:
int main()
{
static const uint8_t bSyncString [] = {0x30, 0x20}; // {CMD_STK_GET_SYNC, SYNC_CRC_EOP};
uint8_t bBuffer [256];
unsigned int ctl;
struct termios tty;
int iFd = open ("/dev/ttyACM0", O_RDWR | O_NOCTTY);
if (iFd >= 0)
{
// From STK500 spec: 115.2kbps, 8 data bits, 1stop bits, no parity
if (tcgetattr (iFd, &tty) == 0)
{
tty.c_iflag = IGNBRK;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_cflag = (CS8 | CREAD | CLOCAL);
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
if (tcsetattr(iFd, TCSANOW, &tty) == 0)
printf ("Attributes set\n");
}
if (ioctl (iFd, TIOCMGET, &ctl) >= 0) // Get current line states
{
ctl &= ~(TIOCM_DTR|TIOCM_RTS);
if (ioctl(iFd, TIOCMSET, &ctl) >= 0) // DTR and RTS low
{
usleep (50 * 1000);
ctl |= TIOCM_DTR|TIOCM_RTS;
if (ioctl(iFd, TIOCMSET, &ctl) >= 0) // DTR and RTS high
usleep (250 * 1000);
}
}
if (write (iFd, bSyncString, 2) == 2)
{
int iRead = read (iFd, bBuffer, sizeof (bBuffer));
if (iRead > 0)
{
printf ("Received %s\n", bBuffer);
for (int i = 0; i < iRead; i++)
printf ("%02x ", bBuffer [i]);
printf ("\n");
}
}
close (iFd);
}
}
I want to use DAM transfer for calculating CRC with ATSAMC21G17A MCU (E-revision).
I have already implemented 2 independent DMA channels controlling UART transfer, they are working good, I have no problems with them.
Now it's time to get CRC calculating working to calculate CRC of the Flash memory. I just use one timer to generate triggering events for DMA, I know the size of my firmware and just want to get it working. Timer works with period of 100usec.
But it doesn't really work, I get errors with another channels after starting DMA channel with CRC calculation, transfer process of another channel can be interrupted and doesn't work afterwards. It means it could transfer only some bytes, but not all of them. What am I doing wrong?
I have already read the whole errata about my MCU and don't find anything similar. I don't use link descriptors.
Here is my source code:
DMA initialization:
volatile bool DMA_TransferComplete[DMA_CHANNELS_NUM];
static DmacDescriptor dma_descr[DMA_CHANNELS_NUM] __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
static DmacDescriptor writeback[DMA_CHANNELS_NUM] __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
static DmacDescriptor descriptor __attribute__((aligned(16))) SECTION_DMAC_DESCRIPTOR;
/** \brief Initialize DMA module
*
* \return Nothing
*
*/
void DMA_Init(void)
{
uint8_t i;
MCLK->AHBMASK.reg |= MCLK_AHBMASK_DMAC;
DMAC->CTRL.reg &= ~DMAC_CTRL_DMAENABLE;
DMAC->CTRL.reg = DMAC_CTRL_SWRST;
DMAC->BASEADDR.reg = (uint32_t)dma_descr;
DMAC->WRBADDR.reg = (uint32_t)writeback;
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
for (i = 0; i < DMA_CHANNELS_NUM; i++)
DMA_TransferComplete[i] = true;
NVIC_EnableIRQ(DMAC_IRQn);
NVIC_SetPriority(DMAC_IRQn, 1);
}
Setup for DMA channel:
/** \brief Setup DMA transfer for selected channel
*
* \param [in] channel Number of the channel to act
* \param [in] settings Pointer to TDmaSettings structure with DMA settings
* \return Nothing
*
*/
void DMA_SetupChannel(uint8_t channel, TDmaSettings *settings)
{
uint16_t btctrlVal;
if (channel >= DMA_CHANNELS_NUM)
return;
/**< Setup channel */
DMAC->CHID.reg = DMAC_CHID_ID(channel);
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << channel));
if (settings->trig_src == 0)
DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(settings->priority) | DMAC_CHCTRLB_TRIGACT_BLOCK;
else
DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(settings->priority) | DMAC_CHCTRLB_TRIGSRC(settings->trig_src) | DMAC_CHCTRLB_TRIGACT_BEAT;
/**< Enable interrupts */
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK;
/**< Prepare descriptor block */
descriptor.DESCADDR.reg = 0; // only one block to transfer, so 0
descriptor.BTCNT.reg = settings->len;
btctrlVal = DMAC_BTCTRL_VALID | DMAC_BTCTRL_BEATSIZE(settings->beat_size);
descriptor.DSTADDR.reg = (uint32_t)settings->dst_addr;
if (settings->dst_inc == true)
{
descriptor.DSTADDR.reg += settings->len;
btctrlVal |= DMAC_BTCTRL_DSTINC;
}
descriptor.SRCADDR.reg = (uint32_t)settings->src_addr;
if (settings->src_inc == true)
{
descriptor.SRCADDR.reg += settings->len;
btctrlVal |= DMAC_BTCTRL_SRCINC;
}
descriptor.BTCTRL.reg = btctrlVal;
memcpy(&dma_descr[channel], &descriptor, sizeof(DmacDescriptor));
}
Interrupt function for detection of "transfer complete" event:
/**< Interrupt for DMA transfer completion */
void DMAC_Handler(void)
{
uint8_t channel;
uint8_t old_channel;
uint32_t res;
/**< Get old channel number */
old_channel = DMAC->CHID.reg;
res = DMAC->INTSTATUS.reg;
/**< Processing all affected channels */
for (channel = 0; channel < DMA_CHANNELS_NUM; channel++)
{
if (res & (1 << channel))
{
/**< Set transfer complete flag */
DMA_TransferComplete[channel] = true;
/**< Select channel to work with */
DMAC->CHID.reg = DMAC_CHID_ID(channel);
/**< Clear all flags for DMA channel */
DMAC->CHINTFLAG.reg = (DMAC_CHINTENCLR_TCMPL | DMAC_CHINTENCLR_TERR | DMAC_CHINTENCLR_SUSP);
/**< Disable DMA transfer */
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
}
}
DMAC->CHID.reg = old_channel;
}
Starting DMA transfer:
/** \brief Start DMA transfer, channel should be already initialized
*
* \param [in] channel Number of the channel to act
* \return Nothing
*
*/
void DMA_StartChannel(uint8_t channel)
{
if (channel >= DMA_CHANNELS_NUM)
return;
/**< Setup channel */
DMAC->CHID.reg = DMAC_CHID_ID(channel);
if (DMA_TransferComplete[channel] == false)
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
/**< Start DMA transfer */
DMA_TransferComplete[channel] = false;
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
}
Setup CRC calculation:
/** \brief Initialize CRC module
*
* \param
* \param
* \return Nothing
*
*/
void DMA_InitCRC(void)
{
/**< Power on timer */
MCLK->APBCMASK.reg |= DMA_CRC_TIMER_MCLK;
GCLK->PCHCTRL[DMA_CRC_TIMER_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
/**< Setup timer for 100usec events */
DMA_CRC_TIMER->COUNT8.CTRLA.reg = TC_CTRLA_SWRST;
while (DMA_CRC_TIMER->COUNT8.SYNCBUSY.reg & TC_SYNCBUSY_SWRST);
DMA_CRC_TIMER->COUNT8.CTRLA.reg |= (DMA_CRC_TIMER_PRESCALER << TC_CTRLA_PRESCALER_Pos) | TC_CTRLA_MODE_COUNT8;
DMA_CRC_TIMER->COUNT8.CTRLBSET.bit.DIR = 0;
DMA_CRC_TIMER->COUNT8.PER.reg = DMA_CRC_TIMER_TICK;
DMA_CRC_TIMER->COUNT8.COUNT.reg = 0;
DMA_CRC_TIMER->COUNT8.CTRLA.reg |= (TC_CTRLA_ENABLE);
DMAC->CTRL.reg &= ~DMAC_CTRL_CRCENABLE;
/**< Configure the CRC engine */
DMAC_CRCCTRL_Type crcctrl =
{
.bit.CRCSRC = DMAC_CRCCTRL_CRCSRC_IO_Val, /* I/O interface */
.bit.CRCPOLY = DMAC_CRCCTRL_CRCPOLY_CRC16_Val, /* CRC-16 (CRC-CCITT) */
.bit.CRCBEATSIZE = DMAC_CRCCTRL_CRCBEATSIZE_BYTE_Val, /* Byte bus access */
};
DMAC->CRCCTRL.reg = crcctrl.reg;
}
Start new CRC calculation:
/** \brief Start CRC conversion
*
* \param [in] crc_init CRC initial value
* \return Nothing
*
*/
void DMA_StartCRC(uint32_t crc_init)
{
/**< Clear the busy bit */
DMAC->CRCSTATUS.bit.CRCBUSY = 1;
DMAC->CTRL.bit.CRCENABLE = 0;
/**< Initialize start CRC value for the CRC16 */
DMAC->CRCCHKSUM.reg = crc_init;
/**< Enable the CRC engine */
DMAC->CTRL.bit.CRCENABLE = 1;
}
Get result of CRC calculation:
/** \brief Get CRC result from DMA module
*
* \return CRC as uint16_t
*
*/
uint16_t DMA_GetCRC(void)
{
return (uint16_t)DMAC->CRCCHKSUM.reg;
}
And now the source code of the thread with CRC calculation (I am using FreeRTOS):
/**< CRC initialization */
DMA_InitCRC();
/**< Setup DMA channel for transmission */
DmaSett.beat_size = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
DmaSett.trig_src = TC1_DMAC_ID_OVF;
DmaSett.dst_addr = (void*)&DMAC->CRCDATAIN.reg;
DmaSett.src_addr = APP_ADDRESS;
DmaSett.src_inc = true;
DmaSett.dst_inc = false;
DmaSett.len = addr;
DmaSett.priority = 0;
DMA_SetupChannel(DMA_CHANNEL_CRC, &DmaSett);
DMA_StartCRC(CRC16_INIT_VAL);
DMA_StartChannel(DMA_CHANNEL_CRC);
while (1)
{
// some periodical actions
// ...
if (DMA_IsReady(DMA_CHANNEL_CRC) == true)
{
crc = DMA_GetCRC();
DMA_StartCRC(CRC16_INIT_VAL);
DMA_StartChannel(DMA_CHANNEL_CRC);
// do something with CRC here
// ...
}
}
Does anybody have any experiences with DMA module of ATSAMC21? Actually it runs normally with UART, but I can't solve this problem with CRC interaction for some days already.
I will be grateful for any ideas!
I am using Arduino_STM32 rogerclark's library.https://github.com/rogerclarkmelbourne/Arduino_STM32 .
Now I want to Develop an In Application Programming(IAP) like a bootloader. Take Code from SD card and Store in Controller flash at 0x8010000 and then jump to that location and run the loaded new application.
the code took from SD and stored into flash successfully done but after jump function called its printing null like this
13:29:04.933 [RX] - Starts............
13:29:07.970 [RX] - 20480 uploaded successfully.
NULNULNULNUL NULNUL
my Main IAP code is
Header file
#include <SPI.h>
#include <SD.h>
#include <EEPROM.h>
#include <stdint.h>
#include "libmaple/scb.h"
#include "usb_lib.h"
typedef void (*FuncPtr)(void);
#define SCS_BASE ((u32)0xE000E000)
#define NVIC_BASE (SCS_BASE + 0x0100)
#define SCB_BASE (SCS_BASE + 0x0D00)
#define SCS 0xE000E000
#define NVIC (SCS+0x100)
#define SCB (SCS+0xD00)
#define STK (SCS+0x10)
#define SCB_VTOR (SCB+0x08)
#define STK_CTRL (STK+0x00)
#define RCC ((u32)0x40021000)
#define RCC_CR RCC
#define RCC_CFGR (RCC + 0x04)
#define RCC_CIR (RCC + 0x08)
#define USB_LP_IRQ ((u8)0x14)
#define SET_REG(addr,val) do{ *(volatile uint32_t*)(addr)=val;} while(0)
#define GET_REG(addr) (*(vu32*)(addr))
#define pRCC ((rcc_reg_map *) RCC)
typedef struct {
u8 NVIC_IRQChannel;
u8 NVIC_IRQChannelPreemptionPriority;
u8 NVIC_IRQChannelSubPriority;
bool NVIC_IRQChannelCmd; /* TRUE for enable */
} NVIC_InitTypeDef;
#define RegBase (0x40005C00L)
#define CNTR ((volatile unsigned *)(RegBase + 0x40))
#define _SetCNTR(wRegValue) (*CNTR = (u16)wRegValue)
#define _GetCNTR() ((u16) *CNTR)
#define ISTR ((volatile unsigned *)(RegBase + 0x44))
#define _SetISTR(wRegValue) (*ISTR = (u16)wRegValue)
#define _GetISTR() ((u16) *ISTR)
void setupFLASH();
void usbDsbISR(void);
void nvicInit(NVIC_InitTypeDef *NVIC_InitStruct);
void nvicDisableInterrupts();
void setMspAndJump(u32 usrAddr);
void systemReset(void);
RESULT usbPowerOff(void);
void setupFLASH()
{
/* configure the HSI oscillator */
if ((pRCC->CR & 0x01) == 0x00)
{
u32 rwmVal = pRCC->CR;
rwmVal |= 0x01;
pRCC->CR = rwmVal;
}
/* wait for it to come on */
while ((pRCC->CR & 0x02) == 0x00) {}
}
void usbDsbISR(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = FALSE;
nvicInit(&NVIC_InitStructure);
}
void nvicInit(NVIC_InitTypeDef *NVIC_InitStruct)
{
u32 tmppriority = 0x00;
u32 tmpreg = 0x00;
u32 tmpmask = 0x00;
u32 tmppre = 0;
u32 tmpsub = 0x0F;
scb_reg_map *rSCB = (scb_reg_map *) SCB_BASE;
nvic_reg_map *rNVIC = (nvic_reg_map *) NVIC_BASE;
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - (rSCB->AIRCR & (u32)0x700)) >> 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tmppriority = (u32)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
tmppriority = ((u32)tmppriority) << ((NVIC_InitStruct->NVIC_IRQChannel & (u8)0x03) * 0x08);
tmpreg = rNVIC->IP[(NVIC_InitStruct->NVIC_IRQChannel >> 0x02)];
tmpmask = (u32)0xFF << ((NVIC_InitStruct->NVIC_IRQChannel & (u8)0x03) * 0x08);
tmpreg &= ~tmpmask;
tmppriority &= tmpmask;
tmpreg |= tmppriority;
rNVIC->IP[(NVIC_InitStruct->NVIC_IRQChannel >> 0x02)] = tmpreg;
/* Enable the Selected IRQ Channels --------------------------------------*/
rNVIC->ISER[(NVIC_InitStruct->NVIC_IRQChannel >> 0x05)] =
(u32)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (u8)0x1F);
}
void nvicDisableInterrupts()
{
nvic_reg_map *rNVIC = (nvic_reg_map *) NVIC_BASE;
rNVIC->ICER[0] = 0xFFFFFFFF;
rNVIC->ICER[1] = 0xFFFFFFFF;
rNVIC->ICPR[0] = 0xFFFFFFFF;
rNVIC->ICPR[1] = 0xFFFFFFFF;
SET_REG(STK_CTRL, 0x04); /* disable the systick, which operates separately from nvic */
}
void usbDsbBus(void)
{
// setPin(USB_DISC_BANK,USB_DISC_PIN);
usbPowerOff();
// SET_REG(USB_DISC_CR,
// (GET_REG(USB_DISC_CR) & USB_DISC_CR_MASK) | USB_DISC_CR_OUTPUT);
// resetPin(USB_DISC_BANK, USB_DISC_PIN); /* Pull DP+ down */
// volatile unsigned x = 500000; do { ; }while(--x);
// SET_REG(USB_DISC_CR,
// (GET_REG(USB_DISC_CR) & USB_DISC_CR_MASK) | USB_DISC_CR_INPUT); //Sets the PA12 as floating
input
}
RESULT usbPowerOff(void)
{
#define CNTR_PDWN (0x0002) /* Power DoWN */
#define CNTR_FRES (0x0001) /* Force USB RESet */
_SetCNTR(CNTR_FRES);
_SetISTR(0);
_SetCNTR(CNTR_FRES + CNTR_PDWN);
/* note that all weve done here is powerdown the
usb peripheral. we have no disabled the clocks,
pulled the USB_DISC_PIN pin back up, or reset the
application state machines */
return USB_SUCCESS;
}
void systemReset(void)
{
SET_REG(RCC_CR, GET_REG(RCC_CR) | 0x00000001);
SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xF8FF0000);
SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFEF6FFFF);
SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFFFBFFFF);
SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xFF80FFFF);
SET_REG(RCC_CIR, 0x00000000); /* disable all RCC interrupts */
}
void setMspAndJump(u32 usrAddr)
{
// Dedicated function with no call to any function (appart the last call)
// This way, there is no manipulation of the stack here, ensuring that GGC
// didn't insert any pop from the SP after having set the MSP.
typedef void (*funcPtr)(void);
u32 jumpAddr = *(vu32 *)(usrAddr + 0x04); /* reset ptr in vector table */
funcPtr usrMain = (funcPtr) jumpAddr;
SET_REG(SCB_VTOR, (vu32) (usrAddr));
asm volatile("msr msp, %0"::"g"(*(volatile u32 *)usrAddr));
usrMain(); /* go! */
}
main code is
#include "core_test.h"
#define CS PB9
File myFile;
char Name[15] = "ASCII.bin";
uint32_t Count = 0;
uint32_t total = 0;
#define FLASH_FLAG_EOP ((uint32_t)0x00000020) /* FLASH End of Operation flag */
#define FLASH_FLAG_PGERR ((uint32_t)0x00000004) /* FLASH Program error flag */
#define FLASH_FLAG_WRPRTERR ((uint32_t)0x00000010) /* FLASH Write protected error flag */
#define myFLASH_APP_ADDR 0x08000000
#define FLASH_APP_ADDR 0x08010000 //The first application start address (stored in FLASH)
#define STM_PAGE_SIZE 2048 //Note: The FLASH page size of STM32F103ZET6 is 2K.
//****************************************************************************************************
// Global variable declaration
char buff[STM_PAGE_SIZE];
int res;
unsigned int br;
void Jump2App(uint32_t Addr)
{
if (((*(uint32_t*)Addr) & 0x2FFE0000) == 0x20000000) //Check if the top address of the stack is legal.
{
// FLASH_Lock();
usbDsbISR(); //Serial1.println("1");
nvicDisableInterrupts(); //Serial1.println("2");
usbDsbBus(); //Serial1.println("3");
// Does nothing, as PC12 is not connected on teh Maple mini according to the schemmatic setPin(GPIOC, 12); // disconnect usb from host. todo, macroize pin
systemReset(); // resets clocks and periphs, not core regs
//Serial1.println("4");
setMspAndJump(Addr);
}
}
void FirmwareUpdate(void)
{
int PageOffest = 0;
int ByteOffest;
if (! IsFileExists(Name)) return;
myFile = SD.open(Name);
while (1)
{
if (Read_data_from_File(buff))
{
FLASH_Unlock();
// FLASH_ClearFlag(((uint16_t)0x00000034));
// FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
FLASH_ErasePage(FLASH_APP_ADDR + PageOffest);
for (ByteOffest = 0; ByteOffest < STM_PAGE_SIZE; ByteOffest += 2)
{
total = total + 2;
FLASH_ProgramHalfWord(FLASH_APP_ADDR + PageOffest + ByteOffest, *(uint16_t*)(buff + ByteOffest));
}
FLASH_Lock();
PageOffest += STM_PAGE_SIZE;
// Serial1.println(PageOffest);
}
else if (myFile.size() <= total) break;
}
myFile.close();
Serial1.println(total);
//myFile.close();
}
void setup()
{
afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY);
Serial1.begin(9600);
if (!SD.begin(CS))
{
Serial1.println("No SD............");
Jump2App(FLASH_APP_ADDR);
}
delay(1000);
Serial1.println("Starts............"); delay(2000);
FirmwareUpdate();
Jump2App(FLASH_APP_ADDR);
while (1);
}
void loop()
{}
bool IsFileExists(char FileName[])
{
if (SD.exists(FileName)) return 1;
else return 0;
}
bool Read_data_from_File(char str[])
{
memset(str, 255, 2048);
uint16_t i = 0;
if (myFile.size() < total)
{
//memset(str, 255, 2048);
// Serial1.println("File Closed");
// Serial1.println(total);
return 0;
}
if (myFile)
{
while (i < 2048)
{
str[i++] = myFile.read();
Count = Count + 1;
if (myFile.size() < Count)
{
//memset(str, 255, 2048);
// Serial1.println("File Closed");
// Serial1.println(Count);
return 1;
}
}
}
else return 0;
}
my user Application code is ASCIItable example code which is given in arduino
before generation this ASCII.bin file
i have changed ROM location address 0x8000000 to 0x8010000 and compiled the file.
Is this process correct,
please help me where I am doing wrong
I want to read the switches next to the LEDS and cycle the LEDS from 0 to whichever switch is pressed
if none are pressed cycle through all of them with the delay.For this i have used timer0. Since I work on
atmega8515. I used INT0.
Here is my implementation:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BIT(n) (1 << n)
volatile uint8_t led, k, switches, i , j = 1;
/* uint8_t is the same as unsigned char */
int main(void)
{
DDRB = 0xff; /* use all pins on PortB for output */
led = 1; /* init variable representing the LED state */
PORTB = 0XFF;
cli( );
TCCR0|=(1<<CS02) |(1<<CS00);
//Enable Overflow Interrupt Enable
TIMSK|=(1<<TOIE0);
//Initialize Counter
TCNT0=0;
GICR = BIT(INT0);
MCUCR = BIT(ISC01) | BIT(ISC00);
sei( );
for ( ; ;);
}
ISR(TIMER0_OVF_vect)
{
if(switches == 0xff)
{
PORTB = ~led; /* invert the output since a zero means: LED on */
led <<= 1; /* move to next LED */
if (!led) /* overflow: start with Pin B0 again */
{
led = 1;
}
}
else
{
for (k = 0; k< 8;k++)
{
j = switches & (1 << k);
if(j == 0)
{
for(i=1;i<=(k +1);i++)
{
j = 1;
PORTB = ~j;
j = 1 << i;
_delay_ms(100); //without this delay it doesnt cycle the LEDS from to whichever switch is pressed
}
}
}
}
But using delay loops in ISR is a bad programming practice. How to use the same timer instead of the delay?
I think in the ISR, you should just update the LED status and in the main loop you could set the PORTB and read the switches values. In your code, it seems occupy so much time in ISR.