Raspberry PI SPI read from Arduino slave with wiringPI2? - arduino

I've got wiringpi2 and the the wiringpi2 python wrapper installed on the NOOBS Raspbian PI distro. The Adafruit 4 channel logic level converter kept the PI safe from 5v and sending data to the Arduino was as simple as this on the PI side:
import wiringpi2
wiringpi2.wiringPiSPISetup(1,5000)
wiringpi2.wiringPiSPIDataRW(1,'HELLO WORLD\n')
and the corresponding Arduino code[3].
EDIT: Apologies - from this point on, I can't post any more of the links I carefully added to show my working, sources and example code. You'll have to Google it and thank the 2-link rule.
So, I know the wiring works. But that's not the way round I actually want - I want to read a pin from the Arduino to the PI.
The Arduino SPI reference states:
This library allows you to communicate with SPI devices, with the Arduino as the master device.
The PI must be the master device. I thought I was doomed until I read Nick Gammon's excellent page about SPI which demonstrates 2 Arduinii talking to each other.
Also, the SPI transfer() command would suggest you CAN write from the Arduino.
I'm now at the stage where all the links of the the first 4 result pages of Google show as "followed" - so it's not for lack of Googling!
In theory, shouldn't this work if I use the READ method on the PI end? (Note: this is just one of many, many attempts, not the only one!)
On the Arduino:
#include <SPI.h>
void setup (void)
{
SPI.begin();
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
}
void loop (void) {
byte data[] = {0x00, 0x00, 0x00, 0x00}; // this is 24 bits (8bits/byte * 4 bytes)
// Transfer 24 bits of data
for (int i=0; i<4; i++) {
SPI.transfer(data[i]); // Send 8 bits
}
}
And on the PI end of things:
import wiringpi2
wiringpi2.wiringPiSPISetup(1,5000)
stuff = wiringpi2.wiringPiSPIDataRW(1,'\n')
print stuff
WiringPI says the incoming data will overwrite my data, and the SPIDataRW takes exactly 2 inputs, so shouldn't I be getting "test" back?
What am I missing here? Any pointers greatly appreciated.

The SPI library assumes you want the arduino to act as a master. You can't use it to get an arduino to act as a slave. Sometimes you have to dive past the libraries into the chip's datasheet and see how things work. (and then, ideally, make a library from all your troubles)
An SPI slave device has to react to the master initiating the communication.
So the Pi, as the SPI master, will have to send dummy bytes over the MOSI line and read what the Arduino replies on the MISO line. ie, master initiate communication.
On the arduino side you can turn on the SPI interrupt with:
SPCR |= _BV(SPIE);
It's built into the atmega328 chip. So include this next bit on the arduino side to see incoming messages and set the response for the next message. The data that the arduino SPI slave responds with is whatever is in the data register when the master sends the message.
int gCurrentSpiByte; //or set up your a buffer or whatever
ISR (SPI_STC_vect)
{
gCurrentSpiByte = SPDR; // grab byte from SPI Data Register
SPDR = buf[messageCount++]; //Set the data to be sent out on the NEXT message.
}
And remember, you GOTTAGOFAST. If the arduino doesn't exit that interrupt service routine before the next SPI message comes it, it all goes to hell.
Also also, check to make sure the clock's polarity and phase are the same between the Pi and the Arduino (otherwise known as modes 0-3).
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| SPIE | SPE | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
SPIE - Enables the SPI interrupt when 1
SPE - Enables the SPI when 1
DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
MSTR - Sets the Arduino in master mode when 1, slave mode when 0
CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
CPHA - Samples data on the falling edge of the data clock when 1, rising edge when 0
SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)
So to turn on SPI, the SPI interrupt, and to set the polarity to... whatever that is...
SPCR |= _BV(SPIE) | _BV(SPE) | _BV(CPOL) ;
Anyway, I spent a couple days banging around with the Arduino SPI and that's what I learned.

Related

Problems sending SPI Data from STM32 to arduino

I'm trying to learn how to communicate via SPI with STM32 but I've run into some problems.
The first step i took was to implement SPI communications using two arduino unos: the master writes a byte and the slave responds with another byte according to the input (when the master sends anything, triggering the exchange). Everything worked as it should to the best of my knowledge.
The second step is the one I'm stuck at which is to replace the master arduino with a NUCLEO-F767ZI board. I've set up SPI1 using the auto generated code for now, and I've set it up with what I think are the default options. In the main, I send a single byte to the arduino, and this is where the problem starts:
By using the serial port in the arduino I can see that it does receive data, but usually bit shifted. So if I send a 16, i usually get a 32 or other power of two in the arduino (see attached image)
I'm using clock polarity 0 and clock phase 0 on both microcontrollers (so clock idle on low and sampling on the rising edge). Just to be sure I've tried all possibilities but to no avail, it still doesn't work properly, and I don't really know why. Another thing I considered was that perhaps the clock is running too fast (the peripheral clock is set to 16 MHz), but if I change the prescaler to anything other than SPI_BAUDRATEPRESCALER_2, i stop getting any data on the arduino, only 0s, which I find odd, I would expect it to work just the same if only the master controls the clock.
Just for completeness, here are the spi settings I'm using:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
The arduino slave code (I've removed the reply part for now):
void setup (void)
{
pinMode(MISO, OUTPUT);
Serial.begin(115200);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
//Check arduino SPI settings, outputs 11000000, so CPHA and CPOL = 0
Serial.println("SPCR");
Serial.println(SPCR,BIN);
} // end of setup
void loop(void){
}
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;
Serial.println(c);
}
}
STM32 master code (ommiting irrelevant parts):
MX_SPI1_Init();
uint8_t data = 16;
while (1)
{
HAL_SPI_Transmit(&hspi1, &data, 1, 100);
HAL_Delay(500);
}
Does anyone have a clue as to why this could be happening? I've found nothing on the prescaler issues and all bitshifting issue posts say that the problem is with clock phase or polarity discrepancies, which I've verified is not.
Thanks in advance
SPI clock rate input to Atmega328P should be lower than Fosc/4
SPI requires the use of nCS wire. An SPI slave will reset it's logic on deassertion of nCS, but if nCS is always at a low level - any missed clock pulse would propagate to the next transfer, shifting the data.
Stm32F7 has NSSP: NSS pulse management - the option for issuing CLK pulse between data bytes. To deassert nCS pin between larger transfers SPI shoud either be disabled and then reenabled before the next transfer, or nCS management should be implemented in the software. In a later case make sure to deassert nCS only after all CLK pulses are completed.
Arduino UNO have Atmega powered from 5V, Stm32 is powered from 3V (on some boards it's 3.3V). Logic high input minimum for the Atmega328 is 0.6VCC, 5V * 0.6V = 3V, wich is on the edge. See this question
A scope would help a lot to solve such cases.

Wire Library - just the first byte device address is being transmitted

I am starting to study the Wire library (no previous Arduino Wire library experience), I read some info taken from here.
As you all know, this really simple example changes the value of a AD5171 digital potentiometer via I2C. Written by Nicholas Zambetti and Shawn Bonkowski, demonstrates use of the Wire library.
I just copied and reduced the code below a little from the example. I am an experienced assembler and C/C++ programmer and hardware developer/designer. Although several I2C devices like DS3231 RTC, etc. work fine using standard Arduino libraries, the mentioned example doesn't work for me in my working NANO board. What am I doing wrong?
This code should transmit:
first the I2C protocol device address - Start / 8 + 1bits
test instruction data byte
variable 1 test byte constantly incremented
I2C Stop condition
The only byte I can see in my oscilloscope is just the first one (please see picture below). The 2 data bytes are not being transmitted. If I reduce the transmission to just the step #2 instruction single data byte, the same result is shown.
#include <Wire.h>
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
}
byte val = 0;
void loop()
{
Wire.beginTransmission(44); // transmit to device #44 (0x2c)
Wire.write(byte(0x55)); // sends instruction byte
Wire.write(val++); // sends potentiometer value byte
Wire.endTransmission(); // stop transmitting
delay(50); // some time delay for my oscilloscope
}
This is what this code produces:
I see transferred data as 01011000 1. I hope the is right decoded. Why you not zoom it more for better reading? It is hard see data levels on raising clock edges. This match to address 44, 0x2C . As you can also see that you get NACK on the end of address octet. So this means no device with transmitting address is on the bus.
You can get error code about sending process from endTransmission function as return int. Send this value to serial monitor.
Error codes you can see in arduino documentation
uint8_t err;
err= Wire.endTransmission(); // stop transmitting and get status
You can also use sketch i2c_scanner from arduino Wire examples to discovery right address of your device.

Issues in Uart configuration at a ATmega4809 microcontroller

hope you are having a great time getting to the point these past days I have been working with a MicroChip Curiosity Nano (ATmega4809) trying to enable a communication by UART ports on Microchip Studio, when the only way I could establish this is when I use al the default configuration (8 bits frame,none parity,1 stop bit) but when try to configure to a 9 bit frame I started to recieve some weird characters.
I have done several changes on this configuration according to datasheet based on registers managment but always the same results.
void USART0_init(void)
{
USART0.BAUD = (uint16_t)USART0_BAUD_RATE(9600);
USART0.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
//Tries I have done
//#define USART_CHSIZE_gm 0x07
//USART0.CTRLC |= USART_CHSIZE_gm;
//USART0.CTRLC |= (7<<USART_CHSIZE_gm);
//USART0.CTRLC |= (1<<USART_CHSIZE2_bm) | (1<<USART_CHSIZE1_bm) | (1<<USART_CHSIZE0_bm);
PORTA.DIR |= PIN0_bm;
PORTA.DIR &= ~PIN1_bm;
}
This code is the UART port's inizialization, and the lines I have commented are about the configuration I need to get 9 bits frame communication, also I mentioned before when I commented this part it works correctly to 8 bits frame communication.
Thanks for taking the time and have a nice day.

pic16f877a RS232 not working

i am trying to make rs232 protocol communcication from my pic to serial port in pc I made sure all myconnections are right by connecting tx and rx of serial port each step I connect them anywhere else so I guess the circuit is ok and here it is:
http://i.imgur.com/0uVxFDC.png
this circuit I did connect in real life and just did It in proteus to demonstrate
I just want the pic to send every byte it receives and I set the baud rate and stop, parity bits the same in both. I made the code in mikroc, here it is:
char uart_rd;
void main() {
UART1_Init(9600); // Initialize UART module at 9600 bps
Delay_ms(100); // Wait for UART module to stabilize
while (1) { // Endless loop
if (UART1_Data_Ready()) { // If data is received,
uart_rd = UART1_Read(); // read the received data,
UART1_Write(uart_rd); // and send data via UART
}
}
}
just this small part. I don't really know where the problem is everything seems right but it is not working I would very much appreciate replies from anyone it would really help me a lot thanks in advance
On the schematic P1 appears to be a DE-9 connector with pin 2 wired as an input and pin 3 as an output. A PC is wired the same way. If P1 is a male connector you must use a null modem cable between your circuit and the PC. A null modem cable has female connectors on both ends with signals crossed over internally. If P1 is a female connector wire pin 2 as the output and pin 3 as the input and use a male to female cable.

Arduino serial: inverted 7E1. Possible?

I'm trying to talk serial with an SDI-12 device, and it requires inverted seven data bits, even parity and one stop bit (7E1) serial at 1200 baud.
From the datasheet:
SDI-12 communication sends characters at 1200 bits per second. Each character has 1 start bit, 7 data bits (LSB first), 1 even parity bit, and 1 stop bit (Active low or inverted logic levels):
All SDI-12 commands and response must adhere to the following format on the data line. Both the command and response are preceded by an address and terminated by a carriage return line feed combination.
Is this possible with the Serial or SoftwareSerial libraries? I am trying to avoid additional hardware (beyond a levelshifter to 3.3 V), but I will do so if it is the only way.
I have seen that SoftwareSerial can do inverted, and Serial can do 7E1, but I can't find if either can do both.
I have access to a Arduino Mega (R2), and Arduino Uno (R3).
Here is the device I want to communicate with: http://www.decagon.com/products/sensors/soil-moisture-sensors/gs3-soil-moisture-temperature-and-ec/ and here, http://www.decagon.com/assets/Uploads/GS3-Integrators-Guide.pdf is the document explaining the protocol. Page 6 talks about its implementation of SDI.
I'm not familiar with Arduino, however the SDI-12 physical layer is inverted from the standard TTL levels - probably for two reasons:
Since the idle voltage is 0V, this results in lower standby power (due to nominal pull-down resistors in a typical SDI-12 sensor.
It facilitates simple bus 'sniffing' using a standard RS-232 serial port.
Short of bit-banging a 5V IO pin - yes, if using a standard microcontroller UART you will need an external inverter (or 2) and a 3-state buffer. Possibly requiring level shifting, depending on your hardware.
Thumbs down to the Wikipedia entry - SDI-12 uses entirely standard UART bit timings (very much like RS-232), just different signal levels (0 - 5V); see point #2. However, there are specific break sequences and strict timing requirements, which makes firmware development more difficult.
If you are serious about SDI-12 firmware development, you may want to invest in an SDI-12 Verifier. A thorough study of the specification is essential.
A little late... but better late than never
I have actually just written a library for exactly that (actually exactly that including the sensors ... so it should work exactly with the included examples )
https://github.com/joranbeasley/SDISerial (Arduino Library)
#include <SDISerial.h> //https://github.com/joranbeasley/SDISerial (Arduino Library)
#include <string.h>
#define DATA_PIN 2
SDISerial connection(DATA_PIN);
char output_buffer[125]; // just for uart prints
char tmp_buffer[4];
char sensor_info[]
//initialize variables
void setup(){
connection.begin();
Serial.begin(9600);//so we can print to standard uart
//small delay to let the sensor do its startup stuff
delay(3000);//3 seconds should be more than enough
char* sensor_info = connection.sdi_query("0I!",1000); // get sensor info for address 0
}
//main loop
void loop(){
//print to uart
Serial.println("Begin Command: ?M!");
//send measurement query (M) to the first device on our bus
char* resp = connection.service_request("0M!","0D0!");//Get Measurement from address 0
sprintf(output_buffer,"RECV: %s",resp?resp:"No Response Recieved!!");
Serial.println(output_buffer);
delay(10000);//sleep for 10 seconds before the next read
}

Resources