मैं STM32 पर प्रिंटफ फ़ंक्शन का उपयोग कैसे करूं?


19

मैं यह जानने की कोशिश कर रहा हूं कि सीरियल पोर्ट पर प्रिंट करने के लिए प्रिंटफ फ़ंक्शन का उपयोग कैसे करें।

मेरा वर्तमान सेटअप STM32Fube7 डिस्कवरी बोर्ड के साथ STM32CubeMX जनरेट कोड और SystemWorkbench32 है ।

मैं stdio.h में देखता हूं कि Printf प्रोटोटाइप को किस रूप में परिभाषित किया गया है:

int _EXFUN(printf, (const char *__restrict, ...)
               _ATTRIBUTE ((__format__ (__printf__, 1, 2))));

इसका क्या मतलब है? इस फ़ंक्शन की परिभाषा का सटीक स्थान कहाँ है? आउटपुट के लिए इस प्रकार के फ़ंक्शन का उपयोग करने का तरीका जानने का सामान्य बिंदु क्या होगा?


5
मैं "(, पूर्णांक फ़ाइल चार * ptr, पूर्णांक लेन) पूर्णांक _write" आप अपने खुद के लिखने की ज़रूरत लगता है जैसे अपने सीरियल पोर्ट के लिए अपने मानक आउटपुट भेजने के लिए यहां । मेरा मानना ​​है कि यह आमतौर पर Syscalls.c नामक एक फाइल में किया जाता है जो "सिस्टम कॉल रिमैपिंग" को हैंडल करता है। Googling "Syscalls.c" की कोशिश करें।
टुट

1
यह एक इलेक्ट्रॉनिक्स समस्या नहीं है। अपने कंपाइलर लाइब्रेरी के लिए मैनुअल देखें।
ओलिन लेट्रोप

क्या आप सेमीहोस्टिंग (डीबगर के ऊपर) या सामान्य रूप से प्रिंटफ़ के माध्यम से प्रिंटफ़ डीबगिंग करना चाहते हैं?
डैनियल

जैसा कि @Tut ने कहा, _write()फ़ंक्शन वही है जो मैंने किया था। नीचे मेरे उत्तर में विवरण।
cp.engr

यह एक महान ट्यूटोरियल था: youtu.be/Oc58Ikj-lNI
जोसेफ पिगेटी

जवाबों:


19

मुझे मेरे STM32F072 पर काम करने वाले इस पेज से पहली विधि मिली।

http://www.openstm32.org/forumthread1055

जैसा कि वहां कहा गया है,

जिस तरह से मुझे काम करने के लिए प्रिंटफ़ (और अन्य सभी कंसोल-ओरिएंटेड स्टडीओ फ़ंक्शंस) मिले, वह निम्न-स्तरीय I / O फ़ंक्शंस के कस्टम कार्यान्वयन को बनाकर _read()और जैसा था _write()

निम्न स्तर I / O करने के लिए GCC C लाइब्रेरी निम्न कार्यों के लिए कॉल करती है:

int _read(int file, char *data, int len)
int _write(int file, char *data, int len)
int _close(int file)
int _lseek(int file, int ptr, int dir)
int _fstat(int file, struct stat *st)
int _isatty(int file)

इन कार्यों को जीसीसी सी लाइब्रेरी को "कमजोर" लिंकेज के साथ स्टब रूटीन के रूप में लागू किया जाता है। यदि उपरोक्त में से किसी भी फ़ंक्शन की घोषणा आपके स्वयं के कोड में दिखाई देती है, तो आपकी स्थानापन्न दिनचर्या पुस्तकालय में घोषणा को ओवरराइड कर देगी और डिफ़ॉल्ट (गैर-कार्यात्मक) दिनचर्या के बजाय इसका उपयोग किया जाएगा।

मैंने huart1धारावाहिक पोर्ट के रूप में USART1 ( ) को सेटअप करने के लिए STM32CubeMX का उपयोग किया । चूंकि मैं केवल चाहता था printf(), मुझे केवल _write()फ़ंक्शन को पॉप्युलेट करने की आवश्यकता थी , जो मैंने निम्नानुसार किया था। यह पारंपरिक रूप से निहित है syscalls.c

#include  <errno.h>
#include  <sys/unistd.h> // STDOUT_FILENO, STDERR_FILENO

int _write(int file, char *data, int len)
{
   if ((file != STDOUT_FILENO) && (file != STDERR_FILENO))
   {
      errno = EBADF;
      return -1;
   }

   // arbitrary timeout 1000
   HAL_StatusTypeDef status =
      HAL_UART_Transmit(&huart1, (uint8_t*)data, len, 1000);

   // return # of bytes written - as best we can tell
   return (status == HAL_OK ? len : 0);
}

अगर मैं मेकफाइल का उपयोग करता हूं और आईडीई का नहीं तो क्या होगा? मेरे मेकफाइल प्रोजेक्ट में काम करने के लिए इसमें से कुछ गायब है ... क्या कोई मदद कर सकता है?
टेडी

@ टेडी, क्या आप जीसीसी का उपयोग कर रहे हैं? यह एक संकलक-विशिष्ट उत्तर है। आपने क्या प्रयास किया है, और परिणाम क्या थे?
cp.engr

हां मैं जीसीसी का उपयोग करता हूं। मुझे समस्या मिल गई। _Write () फ़ंक्शन को कहा जाता था। स्ट्रिंग भेजने के लिए समस्या मेरे कोड में थी। मैंने Segger की RTT लाइब्रेरी का दुरुपयोग किया है लेकिन अब ठीक काम करता है। धन्यवाद। अभी सब काम कर रहे हैं।
ताड़ी

5

@ एडमहौन का जवाब आपको चाहिए, इसके साथ sprintf()एक स्ट्रिंग बनाना और फिर भेजना आसान है। लेकिन अगर आप वास्तव printf()में अपना खुद का एक फ़ंक्शन चाहते हैं, तो वैरिएबल आर्ग्यूमेंट फ़ंक्शंस (va_list) का तरीका है।

साथ va_listनिम्नलिखित की तरह एक कस्टम प्रिंट समारोह दिखता है:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

void vprint(const char *fmt, va_list argp)
{
    char string[200];
    if(0 < vsprintf(string,fmt,argp)) // build string
    {
        HAL_UART_Transmit(&huart1, (uint8_t*)string, strlen(string), 0xffffff); // send message via UART
    }
}

void my_printf(const char *fmt, ...) // custom printf() function
{
    va_list argp;
    va_start(argp, fmt);
    vprint(fmt, argp);
    va_end(argp);
}

उपयोग उदाहरण:

uint16_t year = 2015;
uint8_t month = 12;
uint8_t day   = 18;
char* date = "date";

// "Today's date: 2015-12-18"
my_printf("Today's %s: %d-%d-%d\r\n", date, year, month, day);

ध्यान दें कि जब यह समाधान आपको उपयोग करने के लिए सुविधाजनक फ़ंक्शन देता है, लेकिन यह कच्चे डेटा भेजने या यहां तक ​​कि उपयोग करने की तुलना में धीमा है sprintf()। मुझे लगता है कि यह पर्याप्त नहीं होगा।


एक और विकल्प, और शायद बेहतर विकल्प एसटी-लिंक यूटिलिटी के साथ एसटी-लिंक, एसडीडी डीबगर का उपयोग करना है। और SWO दर्शक के माध्यम से Printf का उपयोग करें , यहां ST-Link उपयोगिता का मैनुअल है , प्रासंगिक भाग 31 पृष्ठ पर शुरू होता है।

SWO व्यूअर के माध्यम से प्रिंटफ़, SWO के माध्यम से लक्ष्य से भेजे गए प्रिंटफ़ डेटा को प्रदर्शित करता है। यह चल रहे फर्मवेयर पर कुछ उपयोगी जानकारी प्रदर्शित करने की अनुमति देता है।


आप va_arg का उपयोग नहीं करते हैं, आप चर तर्कों के माध्यम से कैसे कदम उठाते हैं?
इउद्र

4

_EXFUN एक मैक्रो है, जिसमें संभवत: कुछ दिलचस्प निर्देश हैं जो संकलक को बताते हैं कि इसे प्रिंटफ-संगत होने के लिए प्रारूप-स्ट्रिंग की जांच करनी चाहिए और यह सुनिश्चित करना चाहिए कि प्रिंटफ के तर्क प्रारूप स्ट्रिंग से मेल खाते हैं।

प्रिंटफ का उपयोग कैसे करें, यह जानने के लिए, मैं सुझाव देता हूं कि मैन पेज और थोड़ा अधिक गोलगप्पा। कुछ सरल सी प्रोग्राम लिखें जो प्रिंट का उपयोग करते हैं और माइक्रो पर इसका उपयोग करने के लिए संक्रमण करने से पहले अपने कंप्यूटर पर उन्हें चलाते हैं।

दिलचस्प सवाल यह होगा कि "मुद्रित पाठ कहाँ जाता है?"। एक यूनिक्स जैसी प्रणाली पर, यह "मानक आउट" पर जाता है, लेकिन एक माइक्रोकंट्रोलर के पास ऐसी कोई चीज नहीं है। सीएमएसआईएस डिबग लाइब्रेरी अपने सेमीफाइनल में डिबग पोर्ट, यानी आपके जीडीबी या ओपनोकैड सत्र में प्रिंटफ़ टेक्स्ट भेज सकती है, लेकिन मुझे नहीं पता कि SystemWorkbench32 क्या करेगा।

यदि आप डिबगर में काम नहीं कर रहे हैं, तो स्प्रिंट का उपयोग करने के लिए और अधिक समझदारी हो सकती है कि आप जिस स्ट्रिंग्स को प्रिंट करना चाहते हैं उसे प्रारूपित करें और फिर उन स्ट्रिंग्स को सीरियल पोर्ट पर या आपके द्वारा संलग्न किए जाने वाले किसी भी डिस्प्ले पर भेजें।

खबरदार: प्रिंटफ और उससे संबंधित कोड बहुत बड़े हैं। यह शायद 32F407 पर ज्यादा मायने नहीं रखता है, लेकिन यह थोड़ा फ्लैश वाले उपकरणों पर एक वास्तविक समस्या है।


IIRC _EXFUN एक फ़ंक्शन को निर्यात किया जाता है, अर्थात उपयोगकर्ता कोड में उपयोग किया जाता है, और कॉलिंग सम्मेलन को परिभाषित करता है। अधिकांश (एम्बेडेड) प्लेटफार्मों पर डिफ़ॉल्ट कॉलिंग कन्वेंशन का उपयोग किया जा सकता है। लेकिन गतिशील पुस्तकालयों के साथ x86 पर, आपको __cdeclत्रुटियों से बचने के लिए फ़ंक्शन को परिभाषित करने की आवश्यकता हो सकती है । कम से कम newlib / cygwin पर, _EXFUN का उपयोग केवल सेट करने के लिए किया जाता है __cdecl
erebos

1

प्रिंटफ () सी मानक पुस्तकालय का (आमतौर पर) हिस्सा है। यदि लाइब्रेरी का आपका संस्करण स्रोत कोड के साथ आता है, तो आपको वहां एक कार्यान्वयन मिल सकता है।

स्ट्रिंग उत्पन्न करने के लिए स्प्रिंटफ () का उपयोग करना शायद आसान होगा, फिर सीरियल पोर्ट के माध्यम से स्ट्रिंग भेजने के लिए एक और फ़ंक्शन का उपयोग करें। इस तरह आपके लिए सभी कठिन प्रारूपण हो गए हैं और आपको मानक पुस्तकालय को हैक करने की आवश्यकता नहीं है।


2
प्रिंटफ () आमतौर पर लाइब्रेरी का हिस्सा होता है, हां, लेकिन यह तब तक कुछ नहीं कर सकता है जब तक कि कोई व्यक्ति वांछित हार्डवेयर के आउटपुट के लिए अंतर्निहित क्षमता को सेट नहीं करता है । यह लाइब्रेरी को "हैकिंग" नहीं कर रहा है, लेकिन इसका उपयोग करना है।
क्रिस स्ट्रैटन

यह डिबगर कनेक्शन के माध्यम से पाठ बाहर भेजने के लिए पूर्वनिर्मित हो सकता है, जैसा कि बेंस और विलियम ने अपने जवाबों में सुझाया था। (यह प्रश्नकर्ता नहीं चाहता था, निश्चित रूप से।)
एडम हुन

यह आमतौर पर केवल कोड के परिणामस्वरूप होता है जिसे आप अपने बोर्ड का समर्थन करने के लिए लिंक करने के लिए चुनते हैं, लेकिन इसकी परवाह किए बिना कि आप अपने स्वयं के आउटपुट फ़ंक्शन को परिभाषित करके बदलते हैं। अपने प्रस्ताव के साथ समस्या यह है कि यह कैसे समझ पुस्तकालय है पर आधारित नहीं है है मतलब के बजाय आप प्रत्येक ouput, अन्य बातों के अलावा जो किसी भी मौजूदा पोर्टेबल की गड़बड़ी कर देगा के लिए बहु-चरण प्रक्रिया का प्रस्ताव कर रहे हैं कि - प्रयोग की जाने वाली कोड जो चीजों को सामान्य तरीके से करता है।
क्रिस स्ट्रैटन

STM32 एक फ्रीस्टैंडिंग वातावरण है। कंपाइलर को stdio.h प्रदान करने की आवश्यकता नहीं है - यह पूरी तरह से वैकल्पिक है। लेकिन अगर यह stdio.h प्रदान करता है, तो उसे सभी लाइब्रेरी प्रदान करनी होगी । फ्रीस्टैंडिंग सिस्टम कंपाइलर जो प्रिंटफ को लागू करते हैं, वे इसे UART संचार के रूप में करते हैं।
लुंडिन

1

संघर्ष करने वालों के लिए, syscalls.c पर निम्न जोड़ें:

extern UART_HandleTypeDef huart1; // access huart1 instance
//extern int __io_putchar(int ch) __attribute__((weak)); // comment this out

__attribute__((weak)) int __io_putchar(int ch)
{
    HAL_StatusTypeDef status = HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
    return (status == HAL_OK ? ch : 0);
}

पोटीन के माध्यम से अपने COM पोर्ट से कनेक्ट करें और आपको अच्छा होना चाहिए।


1

यदि आप एक अलग दृष्टिकोण चाहते हैं और कार्यों को अधिलेखित नहीं करना चाहते हैं या अपनी खुद की घोषणा करना चाहते हैं, तो आप बस snprintfउसी लक्ष्य को प्राप्त करने के लिए उपयोग कर सकते हैं । लेकिन यह उतना सुरुचिपूर्ण नहीं है।

char buf[100];
snprintf(buf, 100, "%X %X", val1, val2);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 1000);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.