Related
I have build a prototype board with a STM8L, and I want it to be used and configured as a SPI slave. I am testing it with a raspberry pi as master.
I use the lib provided by ST called "STM8 Standard Peripherals Library" for this, but the documentation is very poor and doesn't expain how to do this...
I can send data from the Raspberry Pi with no issue and receive it on the STM8 but I can't send back any data to the raspberry from the STM8 on MISO.
Is anybody known how I can send back some data to the Raspberry Pi master? Where is my mistake?
Here is the main code:
void main(void)
{
// GPIO
GPIO_Init(GPIOA, GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
CLK_Config();
// Set the MOSI and SCK at high level
GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5, ENABLE);
SPI_DeInit(SPI1);
SPI_Init(SPI1, SPI_FirstBit_LSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
SPI_CPOL_Low, SPI_CPHA_2Edge, SPI_Direction_2Lines_FullDuplex,
SPI_NSS_Hard, (uint8_t)0x07);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
// Enable SPI
SPI_Cmd(SPI1, ENABLE);
/* Infinite loop */
while (1)
{
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
// SPI polling
if(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == SET) {
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
GPIO_ToggleBits(GPIOA, GPIO_Pin_7);
uint8_t data = SPI_ReceiveData(SPI1);
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
// I can't send back data here, it doesn't work
SPI_SendData(SPI1, 0xFA);
uint8_t test = SPI1->DR;
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
}
}
}
static void CLK_Config(void)
{
/* Select HSE as system clock source */
CLK_SYSCLKSourceSwitchCmd(ENABLE);
CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_HSI);
/*High speed external clock prescaler: 1*/
CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_2);
while (CLK_GetSYSCLKSource() != CLK_SYSCLKSource_HSI)
{}
/* Enable SPI clock */
CLK_PeripheralClockConfig(CLK_Peripheral_SPI1, ENABLE);
}
And the RPi simple code:
#include <iostream>
#include <wiringPi.h>
#include <wiringPiSPI.h>
using namespace std;
int main()
{
wiringPiSetup();
wiringPiSPISetup(0, 50000);
unsigned char data[] = {0x5A};
wiringPiSPIDataRW(0, data, 2);
std::cout<<data<<std::endl;
return 0;
Thank you for your help! :)
Edit: I think the mistake is in uC code because the spi data register still contain the data sent by the master after I read it. I can't change it even by trying to write directly in the register.
Also: is it normal that the device only contain one data register for SPI? How is it supposed to be full duplex if it haven't one for MOSI (Rx) and one for MISO(Tx)? I think there is something I don't understand about SPI. I am not very experienced with this serial protocol. I mainly used I2C before.
SPI requires the master to provide the clock. If you want the slave to send something - your master
has to send some dummuy data to generate the clock for the slave.
I finaly found where were my mistakes.
First, I forgot to configure a pullup resistor on the MISO pin:
// Set the MOSI and SCK at high level
GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_7, ENABLE);
Next, the SPI config were wrong. The Rpi was in MSB and the STM8 in LSB, and phase was on the second edge when it needed to be on the first edge:
SPI_Init(SPI1, SPI_FirstBit_MSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
SPI_CPOL_Low, SPI_CPHA_1Edge, SPI_Direction_2Lines_FullDuplex,
SPI_NSS_Hard, (uint8_t)0x07);
Finaly, not a mistake but a not optimal way to test: I were sending 0x81 with the master, but it is symetric in binary (0b10000001). I should have sent some asymetric message, for example 0x17 (0b00010111).
And the complete STM8 code:
#include "stm8l15x.h"
#include "stm8l15x_it.h" /* SDCC patch: required by SDCC for interrupts */
static void CLK_Config(void);
void Delay(__IO uint16_t nCount);
void main(void)
{
// GPIO
GPIO_Init(GPIOA, GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
CLK_Config();
// Set the MOSI and SCK at high level (I added MOSI)
GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_7, ENABLE);
SPI_DeInit(SPI1);
SPI_Init(SPI1, SPI_FirstBit_MSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
SPI_CPOL_Low, SPI_CPHA_1Edge, SPI_Direction_2Lines_FullDuplex,
SPI_NSS_Hard, (uint8_t)0x07);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
// Enable SPI
SPI_Cmd(SPI1, ENABLE);
/* Infinite loop */
while (1)
{
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY));
// SPI polling
if(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == SET) {
// maybe this line is not necessary, I didn't have the time to test without it yet
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_BSY);
uint8_t data = SPI_ReceiveData(SPI1);
while(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE));
if(data==0x82) SPI_SendData(SPI1, 0xCD);
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
}
}
}
/* Private functions ---------------------------------------------------------*/
static void CLK_Config(void)
{
/* Select HSE as system clock source */
CLK_SYSCLKSourceSwitchCmd(ENABLE);
CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_HSI);
/*High speed external clock prescaler: 1*/
CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_2);
while (CLK_GetSYSCLKSource() != CLK_SYSCLKSource_HSI)
{}
/* Enable SPI clock */
CLK_PeripheralClockConfig(CLK_Peripheral_SPI1, ENABLE);
}
void Delay(__IO uint16_t nCount)
{
/* Decrement nCount value */
while (nCount != 0)
{
nCount--;
}
}
/*******************************************************************************/
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
/* Infinite loop */
while (1)
{
}
}
#endif
PS:
I am on linux and soft tools were not adapted to my OS, so I used some tools to be able to develop with it.
I think it can be useful for some people, so I add it here:
First, the lib were not able to compile with SDCC, so I used the patch I found here:
https://github.com/gicking/STM8-SPL_SDCC_patch
To upload to the uC, I use stm8flash with a ST-LINK V2:
https://github.com/vdudouyt/stm8flash
I also had some trouble to find the lib for the STM8L. Here it is:
https://www.st.com/en/embedded-software/stsw-stm8016.html
PS2:
I understand that it is not easy to answer to hardware related questions. Does anybody knows some websites which are more specified on this kind of questions?
I am trying to connect my TM4C123GH6PM Microcontroller from Texas Instruments with my Smartphone and use it to control an alarm clock and LED Lights. (the LEDs are controlled over a Transistor, which is controlled over an GPIO Pin).
I have some experience with coding in C++ and the TM4C123GH6PM, but I am still learning a lot. So please excuse some foolish mistakes I might have made.
I want to connect the ESP8266 with the Microcontroller using UART and the TivaWare Framework.
I have written some code and my UART works correctly (I tested it by sending chars from UART 4 to 3).
According to the AT commands of ESP8266 It should respond to "AT" with "OK". But whenever I send something to the ESP it responds with exactly what I sent to it. I checked the wiring, and that's not The Issue. Or at least I think so. Please correct me, if the wiring is wrong.
ESP -> TM4C123GH6PM:
GND -> GND
VCC -> 3.3V
Tx -> Rx (UART3 / PC6)
Rx -> Tx (UART4 / PC5)
CH_PD -> 3.3V
I also checked for the power consumption of the ESP. Everything is powered by the USB-port of my laptop, since that helps keep the cable mess down. I monitor the power consumption with (https://www.amazon.de/gp/product/B07C8CM5TG/ref=ppx_yo_dt_b_asin_title_o08_s00?ie=UTF8&psc=1). The ESP is drawing about 150mA from the computer, but the port can provide a lot more. I checked with some LEDs and 400mA is not a problem.
Can anyone help me? I am working on this now for over two days and can't find a Solution. What is the Problem with the ESP not responding correctly to the AT command? The blue light is one, when the code is running.
PS: The attached code contains also code for the alarm clock control and LEDs. I attached it, since it could be part of the problem, but some of it is commented out and most of it is not used.
#include<stdint.h>
#include<stdbool.h>
#include"inc/hw_ints.h"
#include"inc/hw_memmap.h"
#include"inc/hw_types.h"
#include"driverlib/gpio.h"
#include"driverlib/sysctl.h"
#include"driverlib/timer.h"
#include"driverlib/interrupt.h"
#include"driverlib/uart.h"
#include"driverlib/pin_map.h"
#include "driverlib/rom.h"
// stores the time since system start in ms
uint32_t systemTime_ms;
//bools or controling the alarm clock and LEDS
bool an_aus = false;
bool alarm_clock = false;
void InterruptHandlerTimer0A (void)
{
// Clear the timer interrupt flag to avoid calling it up again directly
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// increase the ms counter by 1 ms
systemTime_ms++;
}
void clockSetup(void)
{
uint32_t timerPeriod;
//configure clock
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ| SYSCTL_OSC_MAIN);
//activate peripherals for the timer
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// configure timers as 32 bit timers in periodic mode
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
// set the variable timerPeriod to the number of periods to generate a timeout every ms
timerPeriod = (SysCtlClockGet()/1000);
// pass the variable timerPeriod to the TIMER-0-A
TimerLoadSet(TIMER0_BASE, TIMER_A, timerPeriod-1);
// register the InterruptHandlerTimer0A function as an interrupt service routine
TimerIntRegister(TIMER0_BASE, TIMER_A, &(InterruptHandlerTimer0A));
// activate the interrupt on TIMER-0-A
IntEnable(INT_TIMER0A);
// generate an interrupt when TIMER-0-A generates a timeout
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// all interrupts are activated
IntMasterEnable();
// start the timer
TimerEnable(TIMER0_BASE, TIMER_A);
}
void UART (void)
{
//configure UART 4:
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART4));
//GPIO pins for transmitting and receiving
GPIOPinConfigure(GPIO_PC4_U4RX);
GPIOPinConfigure(GPIO_PC5_U4TX);
GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5);
//configure UART 8Bit, no parity, baudrat 38400
UARTConfigSetExpClk(UART4_BASE, SysCtlClockGet(), 38400, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
//configure UART 3:
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART3);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART3));
GPIOPinConfigure(GPIO_PC6_U3RX);
GPIOPinConfigure(GPIO_PC7_U3TX);
GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_6 | GPIO_PIN_7);
UARTConfigSetExpClk(UART3_BASE, SysCtlClockGet(), 38400, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
}
void delay_ms(uint32_t waitTime)
{
// Saves the current system time in ms
uint32_t aktuell = systemTime_ms;
// Wait until the current system time corresponds to the sum of the time at the start of the delay and the waiting time
while(aktuell + waitTime > systemTime_ms);
}
void ex_int_handler(void)
{
// press the button to start timer for alarm clock
alarm_clock = true;
GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
}
int main(void)
{
//Peripherals for LED and GPIO
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
//UART
UART();
//Timer
clockSetup();
// button
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
//OnboardLED
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_1);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_3);
//Interrupt Timer
GPIOIntDisable(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_FALLING_EDGE);
GPIOIntRegister(GPIO_PORTF_BASE,ex_int_handler);
GPIOIntEnable(GPIO_PORTF_BASE,GPIO_PIN_4);
//Transistor Gate
GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,GPIO_PIN_0);
//GPIOPadConfigSet(GPIO_PORTB_BASE,GPIO_PIN_0,GPIO_STRENGTH_6MA,GPIO_PIN_TYPE_STD_WPU);
//debugging only: save all the received data from the ESP in an array to look at while debugging
int32_t data[20] = {0};
int32_t j = 0;
//Code for debugging the UART and ESP8266
while(1){
//Checks for Data in the FIFO
while(!UARTCharsAvail(UART4_BASE));
//send AT-command to ESP8266
UARTCharPut(UART4_BASE, 'A');
while(UARTBusy(UART4_BASE));
UARTCharPut(UART4_BASE, 'T');
while(UARTBusy(UART4_BASE));
if(UARTCharsAvail(UART3_BASE))
{
while(UARTCharsAvail(UART3_BASE))
{
//Read data from the FIFO in UART3 -> received from ESP8266
data[j] = UARTCharGet(UART3_BASE);
j++;
}
}
//clear array when its full
if (j >= 20)
{
j = 0;
for(int32_t a = 0; a <21; a++)
{
data[a] = 0;
}
}
}
//code to run the alarm clock and leds
/*
while(1)
{
if (alarm_clock)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,GPIO_PIN_3);
//Wait
delay_ms(30600000);
GPIOPinWrite(GPIO_PORTB_BASE,GPIO_PIN_0,GPIO_PIN_0);
alarm_clock = false;
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0x00);
//Start Red LED blinking when it is finished
while(1)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,GPIO_PIN_1);
delay_ms(1000);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0x00);
delay_ms(1000);
}
}
}
*/
}
According to the AT commands of ESP8266 It should respond to "AT" with
"OK". But whenever I send something to the ESP it responds with
exactly what I sent to it
Modems with AT Commands commonly ship with the echo mode turned on, so that when you are interacting with it manually through serial port, it will echo the characters you sent first, and then send the reply.
So, when you are automating the process, you first send the characters, then wait for the reply until you reach a '\r'. Well, you are reaching a '\r', but its the one from the echo. You might have some other characters next. You send AT, you should receive AT first, then you have the OK.
To solve this problem, you should turn echo mode off.
The command to turn off echo is ATE0.
I want to use hardware RX pin of Arduino as interrupt pin. If there is any data available on RX pin, an interrupt signal will be generated, call a callback function to read incoming serial data.I don't want my loop() function constant reading on serial port. I am using this code but my interrupt is not triggered.I also tried by removing digitalPintointerrupt() but getting no response.
`#include <SoftwareSerial.h>
const byte interruptPin = 0;//In arduino MEGA RX 19. TX 18
String msg = "";//Incomming message
#define Line_RX 3 //UART RX
#define Line_TX 2 //UART TX
SoftwareSerial mySerial (Line_TX, Line_RX); //initialize software serial
void setup() {
// put your setup code here, to run once:
Serial.begin(19200);
mySerial.begin(19200);
attachInterrupt(digitalPinToInterrupt(interruptPin), serial_read, HIGH);
}//end setup
void loop() {
// put your main code here, to run repeatedly:
}//end loop
void serial_read(){
char _bite;
sei();//Disable hardware interrupts for a moment
while(Serial.available()>0){
delay(1);//Do not delete this delay
if(Serial.available()>0){
_bite = (char)Serial.read();
msg += _bite;
if(_bite == '\n'){
mySerial.print(msg);//Do what you print your message
msg = "";//Clean message for new one
break;
}//end if
}//end if
}//end while
cli();//re-enabling hardware interrupts
}//en
d serial_read`
sei() ENABLES interrupts, while cli() disables them. Your comments suggest you have them backwards. Perhaps there are other problems, but these instructions are certainly not consistent with your intentions.
If you want to get lower-level, consider a pure interrupt-driven design like:
ISR (USART0_UDRE_vect)
{
// Send next byte and increment pointer
UDR0 = *ub_outptr++;
// Pointer wrapping
if (ub_outptr >= UART_buffer + BUFF_SIZE)
ub_outptr = UART_buffer;
// If buffer is empty: disable interrupt
if(--ub_buffcnt == 0)
UCSR0B &= ~(1 << UDRIE0);
}
I know this takes you out of the Arduino library stuff, so this may not be ideal for you. But it works (the example is for sending data, as I have an active project where the microcontroller sends data to an LCD display. Just an example in AVR-GCC C.)
Try my NeoHWSerial. It is a modified version of the Arduino core class, HardwareSerial, the class used for Serial, Serial1, etc. It adds the ability to register a callback for each received character.
I have just bought a SparkFun Pro Micro (https://www.sparkfun.com/products/12640) and am attempting to communicate to it using ReadFile and WriteFile on Windows 10.
I have tested and run my code with a Stellaris, Tiva, Arduino Mega, and even the Arduino Leonardo with little to no problems (it worked). However I have not been able to send any data from the Pro Micro or receive data on my computer using the micro USB cable and my own custom program. I can use the Arduino serial monitor to send and receive data just fine. I can also use a PuTTY terminal. The baud rates in both Arduino IDE and PuTTY seem to have no effect on the ability to send/receive data using the Pro Micro.
I want to be able to send and receive data using my own program as I am using it as a server for data logging, post-processing, and real-time graphing/display. If this project didn't require a smaller hardware package I would use the Arduino Mega, but that is sadly not an option.
I'm compiling on Windows 10, using Visual Studio 2015. I'm also using the official Arduino IDE with SparkFuns addon/drivers, v1.6.7 (updated to 1.6.8 with same problems).
This is my code to connect to the COM port, I have tried various baud rates as well as the BAUD_XXXX macros:
*port = CreateFile(COM, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); //CreateFile(TEXT("COM8:"), ...
if (*port == INVALID_HANDLE_VALUE){
printf("Invalid handle\n");
return(1);
}
/// COM Port Configuration
portDCB.DCBlength = sizeof(DCB); ///< Initialize the DCBlength member
GetCommState(*port, &portDCB); ///< Get the default port setting information.
/// Change the DCB structure settings
portDCB.BaudRate = 115200; ///< Current baud
portDCB.fBinary = TRUE; ///< Binary mode; no EOF check
portDCB.fParity = FALSE; ///< Disable parity checking
portDCB.fOutxCtsFlow = FALSE; ///< No CTS output flow control
portDCB.fOutxDsrFlow = FALSE; ///< No DSR output flow control
portDCB.fDtrControl = DTR_CONTROL_DISABLE; ///< Disable DTR flow control type
portDCB.fDsrSensitivity = FALSE; ///< DSR sensitivity
portDCB.fTXContinueOnXoff = TRUE; ///< XOFF continues Tx
portDCB.fOutX = FALSE; ///< No XON/XOFF out flow control
portDCB.fInX = FALSE; ///< No XON/XOFF in flow control
portDCB.fErrorChar = FALSE; ///< Disable error replacement
portDCB.fNull = FALSE; ///< Disable null stripping
portDCB.fRtsControl = RTS_CONTROL_DISABLE; ///< Disable RTS flow control
portDCB.fAbortOnError = FALSE; ///< Do not abort reads/writes on error
portDCB.ByteSize = 8; ///< Number of bits/byte, 4-8
portDCB.Parity = NOPARITY; ///< 0-4 = no, odd, even, mark, space
portDCB.StopBits = ONESTOPBIT; ///< 0, 1, 2 = 1, 1.5, 2
if (!SetCommState(*port, &portDCB)){
printf("Error Configuring COM Port\n");
return(1);
}
GetCommTimeouts(*port, &comTimeOut);
comTimeOut.ReadIntervalTimeout = 20;
comTimeOut.ReadTotalTimeoutMultiplier = 10;
comTimeOut.ReadTotalTimeoutConstant = 100;
comTimeOut.WriteTotalTimeoutMultiplier = 10;
comTimeOut.WriteTotalTimeoutConstant = 100;
SetCommTimeouts(*port, &comTimeOut);
My read and write functions:
char inChar(HANDLE port){
char output = 0;
DWORD noOfBytesRead = 0;
int retval = ReadFile(port, &output, 1, &noOfBytesRead, NULL);
if (retval == 0) {
return (0);
}
return(output);
}
void outChar(HANDLE port, char output){
DWORD bytesTransmitted = 0;
char buffer[] = { output, 0 };
WriteFile(port, buffer, 1, &bytesTransmitted, NULL);
}
I have this to test communication on the PC:
while (1) {
outChar(portHandle, 'b');
inchar = inChar(portHandle);
printf("%c", inchar);
}
On the Arduino:
void setup(){Serial.begin(115200);}
void loop(){
Serial.read();
Serial.println('a');
delay(10);
}
The Rx LED is flashing like crazy on the Arduino, but the Tx LED is doing nothing, signifying that there is only data going one way. I have done other tests and I have found the Arduino is reading the right information (tested by blinking an LED on and off if the input char is a specific char) but it just wont send anything to my program (PC side when not using Arduino IDE or PuTTY).
In PuTTY I have been able to initiate COM communication with any baud rate, regardless of the Arduinos Serial.begin(). 8 data bits, 1 stop bit, no parity, no flow control, the same as my setup in Visual Studio.
Edit:
I thought if I didn't configure it myself, I would just be piggybacking off the COM configuration left over from PuTTy so I modified my code and removed all the excess:
/// COM Port Configuration
portDCB.DCBlength = sizeof(DCB); ///< Initialize the DCBlength member
GetCommState(*port, &portDCB); ///< Get the default port setting information.
/// Change the DCB structure settings
portDCB.BaudRate = 115200; ///< Current baud
portDCB.ByteSize = 8; ///< Number of bits/byte, 4-8
portDCB.Parity = NOPARITY; ///< 0-4 = no, odd, even, mark, space
portDCB.StopBits = ONESTOPBIT; ///< 0, 1, 2 = 1, 1.5, 2
/*
portDCB.fBinary = TRUE; ///< Binary mode; no EOF check
portDCB.fParity = FALSE; ///< Disable parity checking
portDCB.fOutxCtsFlow = FALSE; ///< No CTS output flow control
portDCB.fOutxDsrFlow = FALSE; ///< No DSR output flow control
portDCB.fDtrControl = DTR_CONTROL_DISABLE; ///< Disable DTR flow control type
portDCB.fDsrSensitivity = FALSE; ///< DSR sensitivity
portDCB.fTXContinueOnXoff = TRUE; ///< XOFF continues Tx
portDCB.fOutX = FALSE; ///< No XON/XOFF out flow control
portDCB.fInX = FALSE; ///< No XON/XOFF in flow control
portDCB.fErrorChar = FALSE; ///< Disable error replacement
portDCB.fNull = FALSE; ///< Disable null stripping
portDCB.fRtsControl = RTS_CONTROL_DISABLE; ///< Disable RTS flow control
portDCB.fAbortOnError = FALSE; ///< Do not abort reads/writes on error
*/
It works just fine with the commented code, but why? What makes this Pro Micro so different from the other micro-controllers I've used? I'll go through and test them all one-by-one until I find out which is responsible, as this only works if I connect after first opening and closing the port in PuTTY (inconvenient).
The SparkFun Pro Micro doesn't like it when you disable RTS control in Windows DCB Structure.
The issue is resolved with:
portDCB.fRtsControl = RTS_CONTROL_ENABLE; //was RTS_CONTROL_DISABLE
portDCB.fOutxCtsFlow = TRUE; //was FALSE
As usual, it was a mistake in overlooking important information in the datasheet, I spent hours reading through the register information trying to confirm where or why I went wrong and the answer was simple as seen is the functionality list of the USART device in the datasheet:
USART:
...
• Flow control CTS/RTS signals hardware management
...
char inChar(HANDLE port){
char output = 0;
DWORD noOfBytesRead = 0;
int retval = ReadFile(port, &output, 1, &noOfBytesRead, NULL);
if (retval == NULL) {
return (NULL);
}
return(output);
}
This is not correct since you are comparing retval (which is int) to NULL, and your function returns NULL as return value of char function. Although I don't believe that this causes reported issue it should be changed.
Take a look on accepted answer here. I would suggest you to start from a working example on PC side and then to reduce it to your needs.
I have written some C++ code to talk to my arduino via serial. It just tries to make oscillations on two servo motors using sine and cosine, but it is skipping data. I'm not sure why this is happening. I am using the termios.h for the serial stuff. The output from C++ is something like "V180H90" i.e. Vertical 180, Horizontal 90. I was using fstream and usleep() to send data before and it was working, but I'd like to use a better method than delaying by some arbitrary number.
Thanks for any help or guidance.
My arduino code
#include <Servo.h>
typedef enum { NONE, GOT_V, GOT_H } states;
states state = NONE;
Servo pan;
Servo tilt;
int laser = 11;
unsigned int currentValue;
int v_pan = 0;
int v_tilt = 0;
void setup()
{
pan.attach(10);
tilt.attach(9);
Serial.begin(9600);
state = NONE;
}
void processVertical(const unsigned int value)
{
Serial.print("Vertical = ");
Serial.println(value);
int result = 1300 + (value - 90) * 2;
//Serial.println(result);
tilt.writeMicroseconds(result);
}
void processHorizontal(const unsigned int value)
{
Serial.print("Horizontal = ");
Serial.println(value);
int result = 1500 + (value - 180) * 1;
//Serial.println(result);
pan.writeMicroseconds(result);
}
void handlePreviousState()
{
switch(state)
{
case GOT_V:
processVertical(currentValue);
break;
case GOT_H:
processHorizontal(currentValue);
break;
}
currentValue = 0;
}
void processIncomingByte (const byte c)
{
if (isdigit(c))
{
currentValue *=10;
currentValue += c - '0';
}
else
{
handlePreviousState();
switch (c)
{
case 'V':
state = GOT_V;
break;
case 'H':
state = GOT_H;
break;
default:
state = NONE;
break;
}
}
}
void loop()
{
if(Serial.available() > 0)
{
processIncomingByte(Serial.read());
}
digitalWrite(laser, HIGH);
}
//check out writeMicroseconds
My C++ code
// Program for sending data to serial
#include <iostream>
#include <sstream>
#include <string>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
using namespace std;
//open serial port
int openPort(string path)
{
int fd; //file descriptor for port
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
cerr << "Cannot open port" << endl;
else
fcntl(fd, F_SETFL, 0);
return (fd);
}
//set options for an open serial port
void setOptions(int fd)
{
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
//No parity 8N1
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//No flow control
options.c_cflag &= ~CRTSCTS;
//Turn off s/w flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
//Turn on read and ignore ctrl lines
options.c_cflag |= (CLOCAL | CREAD);
if( tcsetattr(fd, TCSANOW, &options) < 0) {
cerr << "Could not set attributes" << endl;
}
}
//write to serial port
void writePort(int fd, string data)
{
int n = write(fd, data.c_str(), 9);
if (n < 0)
cerr << "Cannot write to port" << endl;
}
int main() {
string path = "/dev/tty.usbmodemfd131";
//string path = "/dev/tty.usbmodemfa141";
int fd = openPort(path);
setOptions(fd);
stringstream ss;
string output;
unsigned short vertical = 0;
unsigned short horizontal = 0;
unsigned short freq = 10;
for(int i = 0; i < 360; i++) {
vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90;
horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90;
ss << "V" << vertical << "H" << horizontal << endl;
output = ss.str();
ss.str("");
writePort(fd, output);
// cout << output; //DEBUG
}
close(fd);
return 0;
}
The "processIncomingByte" loop inside the device may have suffered a speed problem as you are processing the previous state (handlePreviousState) immediately after you receive a new mode.
The problem may be caused by doing a Serial.print in the corresponding function while the value-data bytes are still incoming continuously from the PC. Serial print is a relatively slow process in micro-controller logic.
I am not familiar with Arduino hardware, but some lower end micro-controller board is performing software serial interface using bitbanging method, so when you transmit, the receiving is completely stopped. To verify this you can remark the Serial.print to see whether it helps.
Anyway, doing lengthy processing in the middle of incoming data stream is alway problematic, unless you have a hardware serial interface in the device with lots of FIFO buffers.
A proper way to this problem is to receive the whole message inside a buffer first and then process it only when a end-of-message marker is received. For example, insert your message inside the [] pair like [V180H90]. Reset the buffer upon the "[" and process the buffer after you receive the "]". When you are collecting bytes into the buffer, make sure you also check for buffer overflow.
If you just shove data down the port's throat, it'll do its best not to set on fire, but the excess data isn't going to be sent. After all, the port operates at a finite speed and is a pretty limited and dump device.
So, before sending a character to the port you need to check the status of the port to see if it's actually ready to accept another character of data for transmission. Some serial ports can even generate interrupts when they can take more data to help you avoid wasteful status polling.
Also, sometimes two serial ports on the two devices can be connected with an extra pair of non-data signals (RTS and CTS) to indicate whether the receiving side is ready to receive more data. If you have those connected and your device is using them to indicate its readiness, your program should take the state of the device's CTS into account as well.
Clearly your device reads/process data slower than you send it via serial port. I see few possible solutions here:
1) Implement flow control and send data via serial port in blocking mode. You still have to wait after sending, but only as much as it is needed for your device to read and process data.
2) Implement two way communication so your device sends confirmation message (i.e. any single ASCII symbol) to indicate that it is ready to accept data.
3) Divide your code into two parallel parts i.e. : main loop (or an ISR) only reads data from serial port and stores it in a ring buffer, another loop polls the ring buffer and takes/process data from it as soon as there is some data available. This is the most difficult solution of the three as you need two separate threads (or a thread and an ISR) and protect ring buffer from concurrent access, but also the most powerful and flexible.
You are writing data out too quickly to the serial device and the device itself is spitting out data faster than you can read it back in on the other side of the device.
The correct way to cope with this is to throttle the speed of writes to the serial device to avoid flooding it with data.