Перейти к содержанию

Visual Studio SerialPort_приём данных


Рекомендуемые сообщения

Доброго времени!
За несколько дней пришлось в спешке освоить C# и по примерам накидать приложение.
По большому счёту всё работает, но есть небольшая проблема, которую нужно исправить.

Описание проекта. 
- микроконтроллер подключён к ПК через COM-порт;
- программа раз в секунду шлёт МК символы поочерёдно "0","1"или "2";
- МК в зависимости от пришедшего символа даёт соответствующий ответ: если "0", то возвращает "333", "1" - "322", "2" - "655";
- Программа получив ответы, разлаживает их в соответствующие элементы: Если было отправлено "0", то вернувшийся ответ "333" положить в label14;          "1" - "322" = label15;          "2" - "655" = label10

Вот такая тестовая незамысловатая логика для теста работоспособности.

Проблема в том, что значения не всегда выводятся полным значением. Как можно видеть на скрине, первое значение вместо "333" вывелось только "33", "322" вывелось полностью, и "655" только "55".
При следующей итерации может уже получиться другой вывод....

Краткое описание кода программы:
- код имеет подключенный Таймер, прерывание которого срабатывает раз в 1000 мс
- после соединения по КОМ-порту включается таймер и раз в секунду отправляется значение "0" "1" либо "2".
- в функции serialPort1_DataReceived происходить приём данных в переменную типа string dataIN
- в функции ShowData происходит работа с содержимым dataIN. В зависимости от отправленного символа, ответ помещается в соответствующий элемент

Скрытый текст

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;

namespace AstroCanon
{
    public partial class Form1 : Form
    {
        string dataIN;
        private int counter_timer; // хранит передаваемое число плате для получения данных
        private int q;
        private int count;
        private int label_2, label_3;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string[] myPort;

            myPort = System.IO.Ports.SerialPort.GetPortNames();
            comboBox1.Items.AddRange(myPort);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.BaudRate = 115200;
            serialPort1.PortName = comboBox1.Text.ToString();
            if (serialPort1.IsOpen == false)
                serialPort1.Open();

            button1.Enabled = false;
            timer1.Enabled = true;

            label1.Text = "ON";
            label1.ForeColor = Color.Green;            // изменить цет на зелёный 

            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Close();
            button1.Enabled = true;

            label1.Text = "OFF";
            label1.ForeColor = Color.DarkRed;

            timer1.Enabled = false;
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            dataIN = serialPort1.ReadExisting();
            this.Invoke(new EventHandler(ShowData));



     


        }

        private void ShowData(object sender, EventArgs e)
        {
            richTextBox1.Text += dataIN;

             if (count == 0)
            {
                // int data0 = Convert.ToInt32(str);
                label14.Text = dataIN;
                return;
            }
            if (count == 1)
            {
                //  int data1 = Convert.ToInt32(str);
                label15.Text = dataIN;
                return;
            }
            if (count == 2)
            {
                //  int data2 = Convert.ToInt32(str);
                label10.Text = dataIN;
                return;
            } 

           
        }
            
        private void timer1_Tick(object sender, EventArgs e)
        {

            if (count <= 3) count++;                    // инкрементирует значение для дальнейшей передачи в устройство;
            if (count >= 3) count = 0;                 

            string str_с = Convert.ToString(count);
            serialPort1.Write(str_с);

           

            

           // label7.Text = data0;


            //  string str = Convert.ToString(counter_timer);

            //  serialPort1.Write(str);

            //  richTextBox1.Text += dataIN;
            //  int a = Convert.ToInt32(dataIN);
            //  return;
            //label2.Text = Convert.ToString(a);

            // label2.Text = Convert.ToString(counter_timer);
            // if (count == 3)   label3.Text = Convert.ToString(label_3);

        }

        private void button3_Click(object sender, EventArgs e)
        {
            count = 2;
            string str = Convert.ToString(count);
            serialPort1.Write(str);



            // string str = Convert.ToString(counter_timer);
            //serialPort1.Write((string)counter_timer);
            // serialPort1.Write("3");
            //label2.Text = Convert.ToString(dataIN);


        }

        private void button4_Click(object sender, EventArgs e)
        {
            count = 3;
            string str = Convert.ToString(count);
            serialPort1.Write(str);
        }

        private void button6_Click(object sender, EventArgs e)
        {

        }

        private void label16_Click(object sender, EventArgs e)
        {

        }

        private void label20_Click(object sender, EventArgs e)
        {

        }

        private void label2_Click(object sender, EventArgs e)
        {

        }
    }
}

 

 

Безымянный1.jpg

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

Тут надо знать, что функция serialPort1_DataReceived() вызывается на каждый принятый байт. К примеру, посылаете от МК на компьютер строку "655". Строка поылается символ за символом, т.е. сначала принимается символ "6". Приняли только первый символ, как тут же вызывается serialPort1_DataReceived(), и в переменную dataIN записывается "6".

Затем принимаемся следующий байт "5". Но Windows не система реального времени, в ней вызов функций может происходить с задержкой. Поэтому пока вызывается serialPort1_DataReceived() может успеть приняться следующий байт. В буфере КОМ-порта уже будет сидеть "55", и в переменную dataIN это и запишется, предыдущая "6" пропадёт.

После этого всё успокоится, и настанет время выводить данные в label, и нарисуется только последний принятый обрывок строки, т.е."55".

Надо поступить так, например. Перед посылкой в МК команды, надо очистить переменную

dataIN = "";

Затем при приёме в переменной dataIN накапливать полученные байты операцией +=

dataIN += serialPort1.ReadExisting();

Как только приняли 3 байта - можно выводить  накопленную строку в label

if(dataIN.Length >= 3 ) this.Invoke(new EventHandler(ShowData));

 

 

Изменено пользователем Yurkin2015
Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

2 часа назад, Yurkin2015 сказал:

Тут надо знать, что функция serialPort1_DataReceived() вызывается на каждый принятый байт. К примеру, посылаете от МК на компьютер строку "655". Строка поылается символ за символом, т.е. сначала принимается символ "6". Приняли только первый символ, как тут же вызывается serialPort1_DataReceived(), и в переменную dataIN записывается "6".

Затем принимаемся следующий байт "5". Но Windows не система реального времени, в ней вызов функций может происходить с задержкой. Поэтому пока вызывается serialPort1_DataReceived() может успеть приняться следующий байт. В буфере КОМ-порта уже будет сидеть "55", и в переменную dataIN это и запишется, предыдущая "6" пропадёт.

После этого всё успокоится, и настанет время выводить данные в label, и нарисуется только последний принятый обрывок строки, т.е."55".

Надо поступить так, например. Перед посылкой в МК команды, надо очистить переменную


dataIN = "";

Затем при приёме в переменной dataIN накапливать полученные байты операцией +=


dataIN += serialPort1.ReadExisting();

Как только приняли 3 байта - можно выводить  накопленную строку в label


if(dataIN.Length >= 3 ) this.Invoke(new EventHandler(ShowData));

 

 

Замечательно!
Спасибо. Вечером обязательно попробую реализовать и после тпишусь.
А пока еще небольшой вопрос. 
В коде, в функции serialPort1_DataReceived есть такая стока как:
 

this.Invoke(new EventHandler(ShowData));

Что она делает??? не могу понять

Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Ваша программа состоит из нескольких частей-потоков, которые работают параллельно, то есть якобы одновременно выполняются разные части программы. Один поток рисует лэйблы на экране, другой принимает данные из КОМ-порта. В C# нельзя просто так использовать один ресурс из разных потоков. Функция приёма байтов может активизироваться внезапно при поступлении байта в порт, из неё нельзя рисовать лэйбл, потому что , может, в этот же момент другой поток тоже что-то рисует на этой же лэйбле. Возникает конфликт "кто главнее". 

Вместо прямого рисования при поступлении байта функция генерирует событие ShowData в системе, мол, обнови метку на экране. Это событие помещается в общую очередь событий. И когда у рисовального потока дойдут руки и он среагирует на это событие, тогда метка спокойно обновится на экране новой строкой.

Ссылка на комментарий
Поделиться на другие сайты

2 минуты назад, Yurkin2015 сказал:

Ваша программа состоит из нескольких частей-потоков, которые работают параллельно, то есть якобы одновременно выполняются разные части программы. Один поток рисует лэйблы на экране, другой принимает данные из КОМ-порта. В C# нельзя просто так использовать один ресурс из разных потоков. Функция приёма байтов может активизироваться внезапно при поступлении байта в порт, из неё нельзя рисовать лэйбл, потому что , может, в этот же момент другой поток тоже что-то рисует на этой же лэйбле. Возникает конфликт "кто главнее". 

Вместо прямого рисования при поступлении байта функция генерирует событие ShowData в системе, мол, обнови метку на экране. Это событие помещается в общую очередь событий. И когда у рисовального потока дойдут руки и он среагирует на это событие, тогда метка спокойно обновится на экране новой строкой.

Спасибо за объяснение. Да, принцип другой нежели у микроконтроллеров, где один поток, да прерывания....

Ссылка на комментарий
Поделиться на другие сайты

8 часов назад, Yurkin2015 сказал:

Тут надо знать, что функция serialPort1_DataReceived() вызывается на каждый принятый байт. К примеру, посылаете от МК на компьютер строку "655". Строка поылается символ за символом, т.е. сначала принимается символ "6". Приняли только первый символ, как тут же вызывается serialPort1_DataReceived(), и в переменную dataIN записывается "6".

Затем принимаемся следующий байт "5". Но Windows не система реального времени, в ней вызов функций может происходить с задержкой. Поэтому пока вызывается serialPort1_DataReceived() может успеть приняться следующий байт. В буфере КОМ-порта уже будет сидеть "55", и в переменную dataIN это и запишется, предыдущая "6" пропадёт.

После этого всё успокоится, и настанет время выводить данные в label, и нарисуется только последний принятый обрывок строки, т.е."55".

Надо поступить так, например. Перед посылкой в МК команды, надо очистить переменную


dataIN = "";

Затем при приёме в переменной dataIN накапливать полученные байты операцией +=


dataIN += serialPort1.ReadExisting();

Как только приняли 3 байта - можно выводить  накопленную строку в label


if(dataIN.Length >= 3 ) this.Invoke(new EventHandler(ShowData));

 

 

Сделал всё так как Вы описали.
Всё заработало. 
Большое Вам спасибо!

Ссылка на комментарий
Поделиться на другие сайты

9 часов назад, Yurkin2015 сказал:

Тут надо знать, что функция serialPort1_DataReceived() вызывается на каждый принятый байт. К примеру, посылаете от МК на компьютер строку "655". Строка поылается символ за символом, т.е. сначала принимается символ "6". Приняли только первый символ, как тут же вызывается serialPort1_DataReceived(), и в переменную dataIN записывается "6".

Затем принимаемся следующий байт "5". Но Windows не система реального времени, в ней вызов функций может происходить с задержкой. Поэтому пока вызывается serialPort1_DataReceived() может успеть приняться следующий байт. В буфере КОМ-порта уже будет сидеть "55", и в переменную dataIN это и запишется, предыдущая "6" пропадёт.

После этого всё успокоится, и настанет время выводить данные в label, и нарисуется только последний принятый обрывок строки, т.е."55".

Надо поступить так, например. Перед посылкой в МК команды, надо очистить переменную


dataIN = "";

Затем при приёме в переменной dataIN накапливать полученные байты операцией +=


dataIN += serialPort1.ReadExisting();

Как только приняли 3 байта - можно выводить  накопленную строку в label


if(dataIN.Length >= 3 ) this.Invoke(new EventHandler(ShowData));

 

 

Если строка  if (dataIN.Length >= 3) this.Invoke(new EventHandler(ShowData)); ожидает пока не наберётся масссив на три элемента , то как сделать так, чтобы оператор if ждал пока не появится стоп-символ? Например, "%"

Ссылка на комментарий
Поделиться на другие сайты

  • 1 год спустя...

Вот вроде все точно также. Делал по видео... Но не принимает и все данные... Передает нормально .. геркулесом их вижу.. а вот обратно не выдает ничего. Подсобите кто чем может ) 

Спойлер

код

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.IO.Ports;
using System.Data.OleDb;

namespace Homeserver
{
    public partial class fmAddbutton : Form
    {
        public static string SQLadr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source = Database.mdb";// строка подключение к базе
        private OleDbConnection SQLcon; //класс подключение
        //private SerialPort Serial;
        
        string Ports;
        int Speed;
        string readstring;
        public fmAddbutton()
        {
            InitializeComponent();
        }

        private void btCancel_Click(object sender, EventArgs e)
        {
            Form Main = Application.OpenForms[0];
            Main.Show();
            this.Close();
        }

        private void fmAddbutton_Load(object sender, EventArgs e)
        {

            SQLcon = new OleDbConnection(SQLadr);
            string query = "SELECT Port,Speed FROM USB WHERE id = 1";
            SQLcon.Open();
            OleDbCommand command = new OleDbCommand(query, SQLcon);
            command.Connection = SQLcon;
            OleDbDataReader reader = command.ExecuteReader();
            reader.Read();
            Ports = reader[0].ToString();
            Speed = int.Parse(reader[1].ToString());
            reader.Close();
            try
            {                
                Serial = new SerialPort(Ports, Speed, Parity.None, 8, StopBits.One);
                Serial.Open();
            }
            catch
            {
                MessageBox.Show("Ошибка открытия порта "+Ports, "Внимание!");
            }
            Serial.Write("find");
        }


        private void fmAddbutton_FormClosing(object sender, FormClosingEventArgs e)
        {
            SQLcon.Close();// закрываем базу данных
            Serial.Close();
        }

        private void btFind_Click(object sender, EventArgs e)
        {
            Serial.Write("FIND");
        }

        private void Serial_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
        {
            readstring = Serial.ReadExisting();
            this.Invoke(new EventHandler(ShowData));

        }

        private void ShowData(object sender, EventArgs e)
        {
            lbSerial.Items.Add("readstring");
        }
    }
}

 

 

Изменено пользователем Flat
Украшение
Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...