📜 ⬆️ ⬇️

STM32, C ++ and FreeRTOS. Development from scratch. Part 4 (Interrupts, UART and UnHART)

Conducting


Once on vacation in the city on the Neva and visiting many beautiful places, I still, in the evenings over a cup of beer, dealt with the UART. Moreover, I bought good Fisher FA011 headphones, which I had to buy USB SOUND BLASTER X-FI HD and wanted to listen to music.
Previous articles first moved to Geektime, then I overtook them back, I don’t even know where to put them now :)
But just in case they are here:
STM32, C ++ and FreeRTOS. Development from scratch. Part 1
STM32, C ++ and FreeRTOS. Development from scratch. Part 2 and
STM32, C ++ and FreeRTOS. Development from scratch. Part 3 (LCD and Screens)

UART


After a detailed study of the microcontroller, it seemed to me that everything is simple. Setup and test sending of a byte to the port went without a hitch, everything worked like a clock, and then I decided to use interrupts. It was necessary to make the interrupt handler be a static class method. And IAR in the compiler manual, and wrote:
Special function types can be used for static member functions. For example, in the
The following example is the interrupt function:
class Device { static __irq void handler(); }; 

But bad luck, for Cortex M this method does not fit and
On the ARM Cortex-M, an interrupt service line
normal function, which means no special keywords are required. Thus, the keywords
__irq, __fiq, and __nested are not available when you compile for ARM Cortex-M.
These exception function names are defined in cstartup_M.c and cstartup_M.s.
Vector code:
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
...
The vector table is implemented as an array. It should always have the name
__vector_table

Or simply, your interrupt handler must have the same name as it has in the vector table defined in the startup file. This is done using a special keyword - the weak link __weak (in the PUBWEAK assembler), which means that this definition will be used until it is found that at least one matching letter is written without the keyword __week. Well, that is, if you define a function with the exact same name without this directive, then the compiler will use this definition, and if you do not define, then it is marked with __weak.
It is clear that I can’t insert C in the startup_stm32l1xx_md.s or startup_stm32l1xx_md.c file . C ++ name of the static method of the type cUart :: USART2_IRQHandler () , the assembler simply won’t understand it.
And just “USART2_IRQHandler” does not match the definition of “cUart :: USART2_IRQHandler ()” .
You can use extern "C" {void USART2_IRQHandler (void) {...}} , but this means that I will insert C from here, which I do not need at all, and in general access from such a function to the attributes of my class, for example the buffer will not, and it will be necessary to fence a bunch of ugly code :).
Therefore, I decided to go the other way and create the startup_stm32l1xx_md.cpp file. An Internet search found that some people had the exact same problem. For example,
In general, the idea is as follows: In startup_stm32l1xx_md.cpp, we define classes with static methods (which will be interrupt handlers), create a table __vector_table, where each interrupt vector has a pointer to these static methods. Further we do __weak definition of each method
And now when in the code the compiler sees the implementation of void cUart1 :: handler () , it takes it without a second thought. Of course, your classes and methods should be named exactly as defined in startup_stm32l1xx_md.cpp .
It is necessary not to forget about the functions of FreeRtos: vPortSVCHandler , xPortPendSVHandler , xPortSysTickHandler and put them on the right interrupt and voila - everything works:
startup_stm32l1xx_md.cpp
 #pragma language = extended #pragma segment = "CSTACK" extern "C" void __iar_program_start( void ); extern "C" void vPortSVCHandler(void); extern "C" void xPortPendSVHandler(void); extern "C" void xPortSysTickHandler(void); class cNMI { public: static void handler(void); }; class cHardFault { public: static void handler(void); }; class cMemManage { public: static void handler(void); }; class cBusFault { public: static void handler(void); }; class cUsageFault { public: static void handler(void); }; class cDebugMon { public: static void handler(void); }; class cWindowWatchdog { public: static void handler(void); }; class cPvd { public: static void handler(void); }; class cTamperTimeStamp { public: static void handler(void); }; class cRtcWakeup { public: static void handler(void); }; class cFlash { public: static void handler(void); }; class cRcc { public: static void handler(void); }; class cExti { public: static void line0Handler(void); static void line1Handler(void); static void line2Handler(void); static void line3Handler(void); static void line4Handler(void); static void line9Handler(void); static void line15_10Handler(void); }; class cDma { public: static void channellHandler(void); static void channel2Handler(void); static void channel3Handler(void); static void channel4Handler(void); static void channel5Handler(void); static void channel6Handler(void); static void channel7Handler(void); }; class cAdc { public: static void handler(void); }; class cDac { public: static void handler(void); }; class cUsb { public: static void highPriorityHandler(void); static void lowPriorityHandler(void); static void fsWakeupHandler(void); }; class cComp { public: static void handler(void); }; class cLcdDriver { public: static void handler(void); }; class cTim9 { public: static void handler(void); }; class cTim2 { public: static void handler(void); }; class cTim3 { public: static void handler(void); }; class cTim4 { public: static void handler(void); }; class cTim10 { public: static void handler(void); }; class cTim6 { public: static void handler(void); }; class cTim7 { public: static void handler(void); }; class cTim11 { public: static void handler(void); }; class cI2C1 { public: static void eventHandler(void); static void errorHandler(void); }; class cI2C2 { public: static void eventHandler(void); static void errorHandler(void); }; class cSpi1 { public: static void handler(void); }; class cSpi2 { public: static void handler(void); }; class cUart1 { public: static void handler(void); }; class cUart2 { public: static void handler(void); }; class cUart3 { public: static void handler(void); }; class cRtcAlarm { public: static void handler(void); }; typedef void( *intfunc )( void ); typedef union { intfunc __fun; void * __ptr; } intvec_elem; // The vector table is normally located at address 0. // When debugging in RAM, it can be located in RAM, aligned to at least 2^6. // If you need to define interrupt service routines, // make a copy of this file and include it in your project. // The name "__vector_table" has special meaning for C-SPY: // it is where the SP start value is found, and the NVIC vector // table register (VTOR) is initialized to this address if != 0. #pragma location = ".intvec" extern "C" const intvec_elem __vector_table[] = { { .__ptr = __sfe( "CSTACK" ) }, __iar_program_start, cNMI::handler, cHardFault::handler, cMemManage::handler, cBusFault::handler, cUsageFault::handler, 0, 0, 0, 0, vPortSVCHandler, // freeRTOS  ! cDebugMon::handler, 0, xPortPendSVHandler, // freeRTOS  ! xPortSysTickHandler, // freeRTOS  ! //External Interrupts cWindowWatchdog::handler, //Window Watchdog cPvd::handler, //PVD through EXTI Line detect cTamperTimeStamp::handler, //Tamper and Time Stamp cRtcWakeup::handler, //RTC Wakeup cFlash::handler, //FLASH cRcc::handler, //RCC cExti::line0Handler, //EXTI Line 0 cExti::line1Handler, //EXTI Line 1 cExti::line2Handler, //EXTI Line 2 cExti::line3Handler, //EXTI Line 3 cExti::line4Handler, //EXTI Line 4 cDma::channellHandler, //DMA1 Channel 1 cDma::channel2Handler, //DMA1 Channel 2 cDma::channel3Handler, //DMA1 Channel 3 cDma::channel4Handler, //DMA1 Channel 4 cDma::channel5Handler, //DMA1 Channel 5 cDma::channel6Handler, //DMA1 Channel 6 cDma::channel7Handler, //DMA1 Channel 7 cAdc::handler, //ADC1 cUsb::highPriorityHandler, //USB High Priority cUsb::lowPriorityHandler, //USB Low Priority cDac::handler, //DAC cComp::handler, //COMP through EXTI Line cExti::line9Handler, //EXTI Line 9..5 cLcdDriver::handler, //LCD cTim9::handler, //TIM9 cTim10::handler, //TIM10 cTim11::handler, //TIM11 cTim2::handler, //TIM2 cTim3::handler, //TIM3 cTim4::handler, //TIM4 cI2C1::eventHandler, //I2C1 Event cI2C1::errorHandler, //I2C1 Error cI2C2::eventHandler, //I2C2 Event cI2C2::errorHandler, //I2C2 Error cSpi1::handler, //SPI1 cSpi2::handler, //SPI2 cUart1::handler, //USART1 cUart2::handler, //USART2 cUart3::handler, //USART3 cExti::line15_10Handler, //EXTI Line 15..10 cRtcAlarm::handler, //RTC Alarm through EXTI Line cUsb::fsWakeupHandler, //USB FS Wakeup from suspend cTim6::handler, //TIM6 cTim7::handler //TIM7 }; __weak void cNMI::handler() { while (1) {} } __weak void cHardFault::handler() { while (1) {} } __weak void cMemManage::handler() { while (1) {} } __weak void cBusFault::handler() { while (1) {} } __weak void cUsageFault::handler() { while (1) {} } __weak void cDebugMon::handler() { while (1) {} } __weak void cWindowWatchdog::handler() { while (1) {} } __weak void cPvd::handler() { while (1) {} } __weak void cTamperTimeStamp::handler() { while (1) {} } __weak void cRtcWakeup::handler() { while (1) {} } __weak void cFlash::handler() { while (1) {} } __weak void cRcc::handler() { while (1) {} } __weak void cExti::line0Handler() { while (1) {} } __weak void cExti::line1Handler() { while (1) {} } __weak void cExti::line2Handler() { while (1) {} } __weak void cExti::line3Handler() { while (1) {} } __weak void cExti::line4Handler() { while (1) {} } __weak void cExti::line9Handler() { while (1) {} } __weak void cExti::line15_10Handler() { while (1) {} } __weak void cDma::channellHandler() { while (1) {} } __weak void cDma::channel2Handler() { while (1) {} } __weak void cDma::channel3Handler() { while (1) {} } __weak void cDma::channel4Handler() { while (1) {} } __weak void cDma::channel5Handler() { while (1) {} } __weak void cDma::channel6Handler() { while (1) {} } __weak void cDma::channel7Handler() { while (1) {} } __weak void cAdc::handler() { while (1) {} } __weak void cUsb::fsWakeupHandler() { while (1) {} } __weak void cUsb::highPriorityHandler() { while (1) {} } __weak void cUsb::lowPriorityHandler() { while (1) {} } __weak void cDac::handler() { while (1) {} } __weak void cComp::handler() { while (1) {} } __weak void cLcdDriver::handler() { while (1) {} } __weak void cTim2::handler() { while (1) {} } __weak void cTim3::handler() { while (1) {} } __weak void cTim4::handler() { while (1) {} } __weak void cTim6::handler() { while (1) {} } __weak void cTim7::handler() { while (1) {} } __weak void cTim9::handler() { while (1) {} } __weak void cTim10::handler() { while (1) {} } __weak void cTim11::handler() { while (1) {} } __weak void cI2C1::errorHandler() { while (1) {} } __weak void cI2C1::eventHandler() { while (1) {} } __weak void cI2C2::errorHandler() { while (1) {} } __weak void cI2C2::eventHandler() { while (1) {} } __weak void cSpi1::handler() { while (1) {} } __weak void cSpi2::handler() { while (1) {} } __weak void cUart1::handler() { while (1) {} } __weak void cUart2::handler() { while (1) {} } __weak void cUart3::handler() { while (1) {} } __weak void cRtcAlarm::handler() { while (1) {} } extern "C" void __cmain( void ); extern "C" __weak void __iar_init_core( void ); extern "C" __weak void __iar_init_vfp( void ); #pragma required=__vector_table void __iar_program_start( void ) { __iar_init_core(); __iar_init_vfp(); __cmain(); } 





DWART. Receive message transfer


So, with the interrupts figured out, now you can take up the implementation of a thread of a simple protocol. Modbus was dropped immediately - not very difficult :). HART, an industrial protocol for devices such as pressure sensors, temperature sensors and flowmeters — will work just fine.
And the task that I set for myself will be communicating using this protocol with the program setting up the devices, say Pactware , or the Elemental HARTConfig , having faked some popular sensor, say the pressure sensor - Yokogawa EJA, so that this program does not detect a catch. I must say that everything turned out :)
image
And even managed to remove the trend in air temperature, measured by the internal temperature sensor of the microcontroller:
image
')
This is how my Pactware device is seen :)
image

HART documentation is available only to a member of the fund. But I managed to find fragmentary descriptions that would be fine for my demo project. Here is an example of one of these resources: Description of the HART protocol or here is the OSI HART Model Protocol
In short - on the line there is a master (main) and slave (slave) device. The main thing sends requests, the slave responds at a speed of 1200 bits per second - everything is simple.
There may be two main devices on the line, but I will simply communicate with ONE RS232 master.
For the same reason, I don’t need to particularly follow the bus arbitration and determine when it’s difficult to have a token, and when not, and the whole arbitration in my case is only that - I have the token, after 2 symbols of silence on the line. At a speed of 1200 bits per second it is approximately 19 ms. Those. if within 19 ms, I have no interruption in reception - the parcel is considered accepted, and the token is with me, I can answer and should start responding within about 250 ms, otherwise the token will go to the master again.
In general, I called this protocol Dwart , a symbiosis between Dwarf - dwarf, gnome and HART.
First you need to select the entity that will operate:


In order to observe the principle of puff pie - the classes of the lower layer will not know about the classes of the layer located above, i.e. For example, Command will work only with Frame, and Frame with LinkLayer, while LinkLayer will not know anything about Command and Frame, and Frame about Command.
So what is the idea - to accept bytes until the silence timer has been activated, then to notify Dwart, which will call the parsing method of the received message and, depending on the request, execute the necessary command.
In general, ideally, say when requesting command 1 from the master, I want the answer to it to look something like this:
 Command1.Response.PrimaryVariable = 3.54 Command1.Send(); 

First, let's estimate the channel level, this figure shows only public methods, so as not to litter the picture.
image

I will clarify the picture a bit, the methods of the interrupt handler are static, and in order to access the methods and fields of the class instance in these static methods, it is necessary that either the fields and methods are also static, or they can be addressed directly to the class instance. I went to the second - the right way, referring to a specific instance of the class, which I initialize in the constructor with the this pointer.
LinkLayer will receive bytes from the UART port and add them to the receive buffer.
But since HART has 0xFF synchronization bytes, called preambles, and their number can reach 20, (and we don’t need them at all, because they don’t carry any information, and all we need is to determine the beginning of the frame by sequence ( 0xFF 0xFF <start byte>)), it will waste 20 preambles to the buffer, so I’ll just watch them right in the interrupt and as soon as they run out, I’ll start to add bytes to the buffer.
After receiving each byte, I will restart the timer for 19 ms, if it works, the package is considered to be accepted.
Another task is to notify the linklayer from the linklayertimer class as soon as the timer interrupt is triggered, indicating the end of receiving the request from the master. For this, I used the Designer Observer template, as a result LinkLayer simply subscribes to events from LinkLayerTimer. It looks like this:
linklayertimer.h
 #include "susutypes.h" #include "observable.h" class cLinkLayerTimer : public iObservable { public: explicit cLinkLayerTimer(tU16 timeout); void start(void) const; private: static void irqHandler(void); static cLinkLayerTimer* instance; }; 


linklayertimer.cpp
 #include "linklayertimer.h" #include "susuassert.h" #include <stdio.h> cLinkLayerTimer* cLinkLayerTimer::instance = NULL; /******************************************************************************* * Function: constructor * Description:    TIM2 ******************************************************************************/ cLinkLayerTimer::cLinkLayerTimer(tU16 timeout) { ASSERT(instance != NULL); this->instance = this; TIM2->ARR = (uint16_t)timeout; } /******************************************************************************* * Function: start * Description:  .     ******************************************************************************/ void cLinkLayerTimer::start(void) const { TIM2->CNT = (uint16_t)0; TIM2->CR1 |= TIM_CR1_CEN; } /******************************************************************************* * Function: irqHandler * Description:     . *     ,   ******************************************************************************/ void cLinkLayerTimer::irqHandler(void) { ASSERT(instance != NULL); instance->notifyObservers(); TIM2->CR1 &=~ TIM_CR1_CEN; TIM2->SR &= ~TIM_SR_UIF; } 


linklayer.h
 #include "susutypes.h" #include "linklayertimer.h" #include "observer.h" #define PREAMBUL_SYMBOL (uint16_t) 0xFF typedef enum { LLS_none = 0, LLS_write = 1, LLS_writeComplete = 2, LLS_readComplete = 3, LLS_error = 4 } tLinkLayerStatus; class cLinkLayer : private iObserver, public iObservable { public: explicit cLinkLayer(tU8 *pRxBuf, const tU8 rxBufSize,tU8 *pTxBuf, const tU8 preambulCount); void writeData(tU8 dataSize); tLinkLayerStatus getStatus() const { return eStatus; }; virtual void eventHandle(const iObservable* pObservable); tU8* pTxBuffer; tU8* pRxBuffer; private: static void irqHandler(void); static cLinkLayer* instance; void endMessageHandler(void); void enableReceive(void) const { USART2->CR1 |= USART_CR1_RXNEIE;}; void disableReceive(void){USART2->CR1 &=~ USART_CR1_RXNEIE;}; void enableTransmit(void) const { USART2->CR1 |= USART_CR1_TCIE; }; void disableTransmit(void) const { USART2->CR1 &=~ USART_CR1_TCIE; }; tLinkLayerStatus eStatus; cLinkLayerTimer* pEndTransmitTimer; tU8 rxBufferSize; tU8 rxBufferIndex; tU8 txBufferIndex; tU8 txBufferSize; tU8 preambulsCount; tU8 preambulIndex; tBoolean readPreambuls; }; 


linllayer.cpp
 #include <stm32l1xx.h> #include "linklayer.h" #include "susuassert.h" #include <stdio.h> #define END_MESSAGE_TIMEOUT (tU16) 19 #define GOOD_COUNT_RX_PREAMBULS (tU8) 2 cLinkLayer* cLinkLayer::instance = NULL; /******************************************************************************* * Function: constructor * Description:       ******************************************************************************/ cLinkLayer::cLinkLayer(tU8 *pRxBuf, const tU8 rxBufSize,tU8 *pTxBuf, const tU8 preambulCount) { ASSERT (rxBuffer != NULL); ASSERT (txBuffer != NULL); //     3 ASSERT(preambulCount > (tU8)2); this->preambulsCount = preambulCount; this->preambulIndex = (tU8)0; this->readPreambuls = TRUE; this->pRxBuffer = pRxBuf; this->rxBufferSize = rxBufSize; this->rxBufferIndex = (tU8)0; this->pTxBuffer = pTxBuf; this->txBufferSize = (tU8)0; this->txBufferIndex = (tU8)0; this->eStatus = LLS_none; this->instance = this; this->pEndTransmitTimer = new cLinkLayerTimer(END_MESSAGE_TIMEOUT); //    this->pEndTransmitTimer->addObserver(this); this->disableTransmit(); this->enableReceive(); } /******************************************************************************* * Function: writeData * Description:  ,     ******************************************************************************/ void cLinkLayer::writeData(tU8 dataSize) { //    ,     ,    // if (this->eStatus != LLS_write) { this->disableReceive(); this->txBufferSize = dataSize; this->eStatus = LLS_write; USART2->DR = PREAMBUL_SYMBOL; this->preambulIndex ++; this->enableTransmit(); } } /******************************************************************************* * Function: handler * Description:   ******************************************************************************/ void cLinkLayer::irqHandler(void) { ASSERT(instance != NULL); // if (USART2->SR & USART_SR_TC) { //    ,   3   if (instance->preambulIndex != instance->preambulsCount) { USART2->DR = PREAMBUL_SYMBOL; instance->preambulIndex ++; } else { //  -    if(instance->txBufferIndex < instance->txBufferSize) { USART2->DR = (uint16_t)instance->pTxBuffer[instance->txBufferIndex++]; } else { instance->txBufferIndex = (tU8)0; instance->txBufferSize = (tU8)0; instance->disableTransmit(); instance->eStatus = LLS_writeComplete; instance->preambulIndex = (tU8)0; instance->readPreambuls = TRUE; instance->enableReceive(); } } USART2->SR &=~ USART_SR_TC; }; // if (USART2->SR & USART_SR_RXNE) { instance->pRxBuffer[instance->rxBufferIndex] = (tU8)USART2->DR; instance->pEndTransmitTimer->start(); //    if (instance->readPreambuls) { if (instance->pRxBuffer[instance->rxBufferIndex] == (tU8)PREAMBUL_SYMBOL) { instance->preambulIndex++; } else { instance->readPreambuls = FALSE; instance->rxBufferIndex++; } } else { //      2 if ((instance->rxBufferIndex <= instance->rxBufferSize) && (instance->preambulIndex >= GOOD_COUNT_RX_PREAMBULS)) { instance->rxBufferIndex++; } else { instance->eStatus = LLS_error; instance->preambulIndex = (tU8)0; } } } } /******************************************************************************* * Function: endMessageHandler * Description:    ******************************************************************************/ void cLinkLayer::endMessageHandler(void) { this->eStatus = LLS_readComplete; this->rxBufferIndex = (tU8) 0; this->readPreambuls = TRUE; instance->preambulIndex = (tU8)0; } /******************************************************************************* * Function: eventHandle * Description:     ******************************************************************************/ void cLinkLayer::eventHandle(const iObservable* pObservable) { this->endMessageHandler(); this->notifyObservers(); } 


LinkLayer itself also notifies the end of the acceptance of the request from the master of its subscribers - we need it later

DWART. Team building


Now the most interesting thing is that the teams, as I have already said, would like to make everything look beautiful, and it was possible to refer to the commands conveniently - like this
  Command0.Response.PrimaryVariable = 3.54 

It is clear that if for each command I keep the whole Response request structure, and there can be 254 commands, then I don’t have enough memory, so I will only store a pointer to the Response request structure, and each time assign this pointer to a pointer to the data in the transfer buffer, which I will receive using the buildFrameBeforeData class method withFrame.
For all the teams I will do the basic interface iBaseDwartCommand with common methods, and since I have requests and responses from each command of different lengths and different types, I will create a template class derived from iDwartCommand. It looks like this:
image
Command 0 - returns just information about the device.
Commands 1 - returns the value of the primary variable, in my case it is with Trimmer, access to which is provided by the cVariables class, so I have to pass it on to the constructor.
As a result, filling in the data of command 1 looks like this:
 void cCommand1::setNewData(void) { //          this->pResponse = (tCommand1Response*) this->pFrame->buildFrameBeforeData(COMMAND1, (tU8)sizeof(tCommand1Response)); this->pResponse->status1 = (tU8)0; this->pResponse->status2 = (tU8)0; this->pResponse->PrimaryVariableUnits = (tU8) pVariables->pTrimmer->getUnits(); this->pResponse->PrimaryVariableValue = std::swap<tF32>(pVariables->pTrimmer->getValue()); this->pFrame->setCheckSumm(); } 

Here, the cConversion class swap method is used to swap bytes in places, since HART uses the Big Endian view, and my microcontroller is Little Endian. Well, almost got what I wanted :)
Completely the whole implementation looks like this:
basedwartcommand.h
 class iBaseDwartCommand { public: virtual void send(void) = 0; virtual void setNewData(void) = 0; }; 


dwartcommand.h
 #include "frame.h" #include "basecommand.h" #include "susuassert.h" template <class req, class resp> class iDwartCommand : public iBaseDwartCommand { public: explicit iDwartCommand(cFrame *pDwratFrame); req *pRequest; resp *pResponse; void send(void); protected: cFrame *pFrame; }; /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ template <class req, class resp> iDwartCommand<req, resp>::iDwartCommand(cFrame *pDwartFrame) { ASSERT (pFrame != NULL); this->pFrame = pDwartFrame; } /******************************************************************************* * Function: send * Description:   ******************************************************************************/ template <class req, class resp> void iDwartCommand<req, resp>::send(void) { this->pFrame->send(); } 


command0.h
 #include "command.h" #define COMMAND0 (tU8)0 #pragma pack(push, 1) typedef struct { tU8 status1; tU8 status2; tU8 expansion; tU8 manufacturer; tU8 deviceType; tU8 numberOfpreambuls; tU8 universalCommandRevision; tU8 deviceSpecificCommandRevision; tU8 softwareRevision; tU8 hardwareRevision; tU8 deviceFlags; tU8 deviceID[3]; } tCommand0Response; typedef struct { } tCommand0Request; #pragma pack(pop) class cCommand0: public iDwartCommand<tCommand0Request, tCommand0Response> { public: explicit cCommand0(cFrame *pDwartFrame); virtual void setNewData(void); }; 


command0.cpp
 include "susuassert.h" #include "command0.h" #include "frame.h" /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cCommand0::cCommand0(cFrame *pDwartFrame): iDwartCommand(pDwartFrame) { } /******************************************************************************* * Function: setNewData * Description:     ******************************************************************************/ void cCommand0::setNewData(void) { //          this->pResponse = (tCommand0Response*) this->pFrame->buildFrameBeforeData(COMMAND0, (tU8)sizeof(tCommand0Response)); this->pResponse->status1 = (tU8)0; this->pResponse->status2 = (tU8)0; this->pResponse->manufacturer = (tU8)0x37; this->pResponse->deviceType = (tU8)0x04; this->pResponse->expansion = (tU8)0; this->pResponse->deviceSpecificCommandRevision = (tU8)5; this->pResponse->universalCommandRevision = (tU8)5; this->pResponse->hardwareRevision = (tU8)1; this->pResponse->softwareRevision = (tU8)201; this->pResponse->numberOfpreambuls = (tU8)5; this->pResponse->deviceID[0] = (tU8)0; this->pResponse->deviceID[1] = (tU8)0; this->pResponse->deviceID[2] = (tU8)1; this->pResponse->deviceFlags = (tU8)0; this->pFrame->setCheckSumm(); } 


command1.h
 #include "command.h" #include "vars.h" #define COMMAND1 (tU8)1 #pragma pack(push, 1) typedef struct { tU8 status1; tU8 status2; tU8 PrimaryVariableUnits; tF32 PrimaryVariableValue; } tCommand1Response; typedef struct { } tCommand1Request; #pragma pack(pop) class cCommand1: public iDwartCommand<tCommand1Request, tCommand1Response> { public: explicit cCommand1(cFrame *pDwartFrame,Variablesr *Vars); virtual void setNewData(void); private: Variables *pVariables; }; 


command1.cpp
 #include "susuassert.h" #include "command1.h" #include "frame.h" /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cCommand1::cCommand1(cFrame *pDwartFrame, Variables *pVars): iDwartCommand(pDwartFrame) { ASSERT(pVars != NULL); this->pVariables = pVars; } /******************************************************************************* * Function: setNewData * Description:     ******************************************************************************/ void cCommand1::setNewData(void) { //          this->pResponse = (tCommand1Response*) this->pFrame->buildFrameBeforeData(COMMAND1, (tU8)sizeof(tCommand1Response)); this->pResponse->status1 = (tU8)0; this->pResponse->status2 = (tU8)0; this->pResponse->PrimaryVariableUnits = (tU8) pVariables->pTrimmer->getUnits(); this->pResponse->PrimaryVariableValue = std::swap<tF32>(pVariables->pTrimmer->getValue()); this->pFrame->setCheckSumm(); } 


. , , , 254 , . cCommandSet:
 #include "command.h" #include "variables.h" #define COMMANDS_COUNT 254 class cCommandSet { public: cCommandSet(cFrame *pFrame, Variables *pVariables); iBaseDwartCommand *pCommands[COMMANDS_COUNT]; }; 


, Pactware HartConfig Yokogawa, , .
commandset.cpp :
 #include "susuassert.h" #include "commandset.h" #include "command0.h" #include "command1.h" #include "command2.h" #include "command3.h" #include "command12.h" #include "command13.h" #include "command14.h" #include "command15.h" #include "command157.h" #include "command159.h" #include "command160.h" #include "command180.h" cCommandSet::cCommandSet(cFrame *pFrame, cVariables *pVariables) { this->pCommands[COMMAND0] = new cCommand0(pFrame); this->pCommands[COMMAND1] = new cCommand1(pFrame, pVariables); this->pCommands[COMMAND2] = new cCommand2(pFrame, pVariables); this->pCommands[COMMAND3] = new cCommand3(pFrame, pVariables); this->pCommands[COMMAND13] = new cCommand13(pFrame); this->pCommands[COMMAND12] = new cCommand12(pFrame); this->pCommands[COMMAND14] = new cCommand14(pFrame); this->pCommands[COMMAND15] = new cCommand15(pFrame, pVariables); this->pCommands[COMMAND160] = new cCommand160(pFrame); this->pCommands[COMMAND157] = new cCommand157(pFrame); this->pCommands[COMMAND159] = new cCommand159(pFrame); this->pCommands[COMMAND180] = new cCommand180(pFrame); } 

, , :) , .
, 157, 159 160, ( , - , , , ( , , , )), - , .
- , , —
 pCommandSet->pCommands[this->pFrame->getCurrentCommand()]->setNewData(); pCommandSet->pCommands[this->pFrame->getCurrentCommand()]->send(); 

swith case, « » :)

DWART. Completion


, .
, LinkLayer,
, - , , , , cFrame, , .
cFrame, cLinkLayer CommandSet cLinkLayer . -, , .
image
dwart.h
 #include "susutypes.h" #include "frame.h" #include "observer.h" #include "linklayer.h" #include "variables.h" #include "commandset.h" #include "frtosWrapper.h" #define MAX_BUFFER_SIZE (tU8)255 class cDwart: private iObserver, public iActiveObject { public: cDwart(Variables *pVariables); virtual void eventHandle(const iObservable* pObservable); virtual void run(void); private: cCommandSet *pCommandSet; cLinkLayer *pLinkLayer; cFrame *pFrame; tU8 buffer[MAX_BUFFER_SIZE]; tBoolean isToken; static const tU8 deviceID[5]; }; 


dwart.cpp
 #include "susuassert.h" #include "frame.h" #include <stddef.h> #include "dwart.h" #define SHORT_ADDR (tU8)0 #define PREAMBULS_COUNT (tU8)7 #define DWART_WAITING (tU32) (50/portTICK_PERIOD_MS) const tU8 cDwart::deviceID[5] = {(tU8)0x37,(tU8)0x04,(tU8)0x00,(tU8)0x00,(tU8)0x01}; /******************************************************************************* * Function: constructor * Description:    cLinkLayer, cFrame cCommandSet  *         ******************************************************************************/ cDwart::cDwart(Variables *pVariables) { this->pLinkLayer = new cLinkLayer(this->buffer,MAX_BUFFER_SIZE, this->buffer, PREAMBULS_COUNT); this->pFrame = new cFrame((tU8*)deviceID, SHORT_ADDR,this->pLinkLayer); this->pCommandSet = new cCommandSet(this->pFrame, pVariables); this->pLinkLayer->addObserver(this); this->isToken = FALSE; } /******************************************************************************* * Function: eventHandle * Description:    .       ******************************************************************************/ void cDwart::eventHandle(const iObservable* pObservable) { ASSERT(pObservable != NULL); this->isToken = TRUE; } /******************************************************************************* * Function: run * Description:      ******************************************************************************/ void cDwart::run(void) { for(;;) { if (this->isToken) { this->isToken = FALSE; if (this->pFrame->decode() == FE_good) { pCommandSet->pCommands[this->pFrame->getCurrentCommand()]->setNewData(); pCommandSet->pCommands[this->pFrame->getCurrentCommand()]->send(); } } oRTOS.taskDelay(DWART_WAITING); } } 


cDwart main.cpp
 void main( void ) { const cAdcDirector* pAdcDirector = new cAdcDirector(); pAdcDirector->startConversion(); cVariables *pVariables = new cVariables(pAdcDirector); oRTOS.taskCreate(pVariablesDirector, VARIABLESDIRECTOR_STACK_SIZE, VARIABLESDIRECTOR_PRIORITY, "Var"); cDwart *pDwart = new cDwart(pVariablesDirector); oRTOS.taskCreate(pDwart, DWART_STACK_SIZE, DWART_PRIORITY, "Dwart"); ... oRTOS.startScheduler(); } 

Pactware .



.



, , 255 , , switch case, placeholder , , .
, .

Source: https://habr.com/ru/post/262133/


All Articles