5 years, 9 months ago.

Serial scanf does not work

I use STMF303RE.
I connected PC_12 and PD_12 directly. And I ran the following program, but it stopped at line 11, scanf

main.cpp

#include "mbed.h"

Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int data;

void toggle_led() {
    led1 = !led1;
    pc.printf("before\n\r");
    self.scanf("%d",data);
    pc.printf("get %d\n\r",data);
}

int main() {
    led1 = 1;
    //double test = 0x0123456789abcdef;
    //double test = 0xfedcba9876543210;
    int test2 = 0x1234;
    self.attach(toggle_led,Serial::RxIrq);
    while(1){
        wait(2);
        //self.printf("%lf",test);
        self.printf("%d",test2);
        //self.putc('a');
    }
}

What is wrong? Please help me.

3 Answers

5 years, 9 months ago.

Hi, the scanf works normal but try this...

try

void toggle_led() {
    led1 = !led1;
     /*pc.printf("before\n\r");*/ // without this line it works fine
    self.scanf("%d",data);
    pc.printf("get %d\n\r",data);
}

or choose another way

Thank you for the reply

I tried it, but it does not work...
And I tried the following, but it did not work.

main.cpp

void toggle_led() {
    //pc.printf("before\n\r");
    self.scanf("%d",&data);
    led1 = !led1;
    //pc.printf("get %d\n\r",data);
}

And I tried following too.

main.cpp

#include "mbed.h"

Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int data;
char data2;

void toggle_led() {
    //pc.printf("before\n\r");
    self.scanf("%d",&data);
    led1 = !led1;
    //pc.printf("get %d\n\r",data);
}

void toggle2_led(){
    data2 = self.getc();
    led1 = !led1;
}


void toggle3_led(){
    led1 = !led1;
    self.scanf("%c",&data2);
}

int main() {
    led1 = 1;
    //double test = 0x0123456789abcdef;
    //double test = 0xfedcba9876543210;
    int test2 = 0x1234;
    self.attach(toggle3_led,Serial::RxIrq);
    while(1){
        wait(2);
        //self.printf("%lf",test);
        //self.printf("%d",test2);
        self.putc('a');
    }
}

main.cpp

#include "mbed.h"

Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int data;
char data2;

void toggle_led() {
    //pc.printf("before\n\r");
    self.scanf("%d",&data);
    led1 = !led1;
    //pc.printf("get %d\n\r",data);
}

void toggle2_led(){
    data2 = self.getc();
    led1 = !led1;
}


void toggle3_led(){
    led1 = !led1;
    self.scanf("%c",&data2);
}

int main() {
    led1 = 1;
    //double test = 0x0123456789abcdef;
    //double test = 0xfedcba9876543210;
    int test2 = 0x1234;
    self.attach(toggle2_led,Serial::RxIrq);
    while(1){
        wait(2);
        //self.printf("%lf",test);
        //self.printf("%d",test2);
        self.putc('a');
    }
}

toggle2_led worked fine, but toggle2_led did not work.
I think scanf is a problem. Is it correct to write "%c" or "%d" ?

posted by yuki takezawa 18 Feb 2019
5 years, 9 months ago.

I'm not looking at whether your code works or not, but something very important that stands out to me is that not to put too much code in an interrupt callback. Generally use the 'callback' to set a flag and act on that flag in your 'main' code. I would expect problems with scanf and printf in your callback. It may work most of the time but will usually fail with MCU lock out at some point. I know this by experience and many hours of scratching my head :)

Thank you for the reply.
I think this idea is very good. However how should I implemented ?
I wrote the following, but this program has the problem that toggle_led is kept calling, so main does not proceed.

main.cpp

#include "mbed.h"
#define STATE0 0
#define STATE1 1

Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int state = STATE0;

void toggle_led() {
    state = STATE1;
    return;
}

int main() {
    int data;
    led1 = 1;
    int test = 1;
    self.attach(toggle_led,Serial::RxIrq);
    
    while(1){
        switch(state){
            case STATE0:
                pc.printf("case1\n\r");
                wait(1);
                break;
            case STATE1:
                pc.printf("case2\n\r");
                self.scanf("%d",&data);
                pc.printf("get %c\n\r",data);
                state = STATE0;
                wait(1);
                break;
        }
        pc.printf("send\n\r");  
        self.printf("%d",test);  
    }
}
posted by yuki takezawa 18 Feb 2019
5 years, 9 months ago.

As Paul said, even if it did work you shouldn't be doing it that way. printf and scanf should never be used in an interrupt, it's just asking for trouble if you do.

What it looks like you want to do is output some text every 2 second except if the user presses a key at which point everything pauses and waits until they finish entering a number. A better way to do that would be like this:

#include "mbed.h"
 
Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int data;

Ticker outputTick;
volatile bool outputNow = false;
void onTick() {
  outputNow  = true;
}
 
int main() {
    led1 = 1;
    //double test = 0x0123456789abcdef;
    //double test = 0xfedcba9876543210;
    int test2 = 0x1234;
//    self.attach(toggle_led,Serial::RxIrq);
    outputTick.attach(&onTick, 2);
    while(1){
       if (outputNow) {
          self.printf("%d",test2);
          outputNow = false;
       }
       if (self.readable()) {
          pc.printf("before\n\r")
          self.scanf("%d",data);
          pc.printf("get %d\n\r",data);
       }
   }
}

Thank you for the reply.
I agree with that idea.
However, the program stopped when calling scanf, too.
I think scanf is a problem. Is it correct to write "%d" ?

posted by yuki takezawa 18 Feb 2019

Scanf should be &data, not just data like I had, sorry made a typo there.

%d is correct if you want to read an integer, I don't think you need a /n or /r/n on the end.

To be honest I normally avoid using scanf whenever possible since it blocks execution for a potentially unlimited time. I much prefer reading the serial input into a buffer and then once a new line is detected using sscanf on the contents of the buffer. That way nothing is blocking.

e.g. with the code below the 2 second output loop doesn't stop as soon as you enter a key until the scanf completes, it keeps running while the number is input. Once a new line is entered the data received is checked and if it contained an integer the value of data is updated.

#include "mbed.h"
 
Serial self(PC_12,PD_2);
Serial pc(USBTX,USBRX);
DigitalOut led1(LED1);
int data;

#define _bufferSize_ 16
char stringBuffer[_bufferSize_];
volatile bool inputReady = false;;


Ticker outputTick;
volatile bool outputNow = false;
void onTick() {
  outputNow  = true;
}

int inputCount = 0;

void toggle_led() {
  stringBuffer[inputCount] = self.getc();
  if ((stringBuffer[inputCount] == '/n')  || (stringBuffer[inputCount] == '/r')) {
    stringBuffer[inputCount] = 0;
    inputReady = true;
  }
 inputCount++;
 if (inputCount == _bufferSize_)
    inputCount--;
    led1 = !led1;
}
 
int main() {
    led1 = 1;
    //double test = 0x0123456789abcdef;
    //double test = 0xfedcba9876543210;
    int test2 = 0x1234;
    self.attach(toggle_led,Serial::RxIrq);
    outputTick.attach(&onTick, 2);
    while(1){
       if (outputNow) {
          self.printf("%d",test2);
          outputNow = false;
       }
       if (inputReady) {
          int tmp;
          __disable_irq();
          int result = sscanf(stringBuffer,"%d",&tmp);
         inputReady = false;
         inputCount = 0;
          __enable_irq();
          if (result == 1) {  // if correctly decoded a value
            data = tmp;
            pc.printf("get %d\n\r",data);
          }
       }
   }
}
posted by Andy A 18 Feb 2019