Why does UART transmit interrupt fail to work in this case? - interrupt

I am using stm32f0 MCU.
I have a simple UART echo code in which every byte received will be transmitted out. I tested that it works. Here it is;
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) //current UART
{
HAL_UART_Transmit(&huart1, &Rx_data[0], 1, tx_timeout);
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
I do not feel comfortable with the code even though it works. Firstly, tx_timeout is 0 and most code examples are non-zero. I do not know the side effect. Secondly, HAL_UART_Transmit() is a blocking call and it is not advisable to use blocking calls inside an interrupt. So, I decided to use an interrupt for uart transmission HAL_UART_Transmit_IT()instead of a blocking call. Here is the modified code;
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) //current UART
{
HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1);
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
However, it does not work as expected. My PC transmits ASCII 12345678 to stm32. If things work as expected, the PC should be receiving 12345678 back. However, the PC receives 1357 instead. What is wrong with this code that uses HAL_UART_Transmit_IT()?

First:
As has been described in answers to your previous question null timeout just exclude wait for flag state. If you open HAL_UART_Transmit code - you will see that when you send 1 byte without timeout no any blocking state will!
Second:
It's not true method to send/receive one byte from a huge HAL's functions and their callbacks. I guess: next your question will "how i must implement parse there?". And I hope you will not insert you parse function in IRQ callback!
So generally you need buffers. And it is good idea to use cyclic buffer.
mxconstants.h:
/* USER CODE BEGIN Private defines */
/* Buffer's length must be select according to real messages frequency */
#define RXBUF_LEN 128 // must be power of 2
#define TXBUF_LEN 128 // must be power of 2
#define RXBUF_MSK (RXBUF_LEN-1)
#define TXBUF_MSK (TXBUF_LEN-1)
/* USER CODE END Private defines */
main.c:
uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
/* xx_i - counter of input bytes (tx - pushed for transmit, rx - received)
xx_o - counter of output bytes (tx - transmitted, rx - parsed)
xx_e - counter of echoed bytes */
volatile uint16_t rx_i = 0, tx_o = 0;
uint16_t rx_o = 0, rx_e = 0, tx_i = 0;
volatile uint8_t tx_busy = 0;
void transmit(uint8_t byte)
{
tx_buf[TXBUF_MSK & tx_i] = byte;
tx_i++;
tx_busy = 1;
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
}
void main(void)
{
/* Initialization code */
/* ... */
/* Enable usart 1 receive IRQ */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
for (;;) {
/* Main cycle */
while (rx_i != rx_e) {
/* echo here */
transmit(rx_buf[RXBUF_MSK & rx_e]);
rx_e++;
}
while (rx_i != rx_o) {
/* parse here */
/* ... */
rx_o++;
}
/* Power save
while (tx_busy);
HAL_UART_DeInit(&huart1);
*/
}
}
stm32f0xx_it.c:
extern uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
extern volatile uint16_t rx_i, tx_o;
extern uint16_t rx_o, rx_e, tx_i;
extern volatile uint8_t tx_busy;
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if((__HAL_UART_GET_IT(&huart1, UART_IT_RXNE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE) != RESET))
{
rx_buf[rx_i & RXBUF_MSK] = (uint8_t)(huart1.Instance->RDR & 0x00FF);
rx_i++;
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(&huart1, UART_RXDATA_FLUSH_REQUEST);
}
if((__HAL_UART_GET_IT(&huart1, UART_IT_TXE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE) != RESET))
{
if (tx_i == tx_o) {
__HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);
} else {
huart1.Instance->TDR = (uint8_t)(tx_buf[TXBUF_MSK & tx_o] & (uint8_t)0xFF);
tx_o++;
}
}
if((__HAL_UART_GET_IT(&huart1, UART_IT_TC) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TC) != RESET))
{
tx_busy = 0;
__HAL_UART_DISABLE_IT(&huart1, UART_IT_TC);
}
/* And never call default handler */
return;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
And third!!!
And about this:
Why HAL_UART_Transmit_IT not help/work?
Because it's too slow! And if you try to count HAL_BUSY results:
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint32_t hal_busy_counter = 0;
if (huart->Instance == USART1) //current UART
{
if (HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1) == HAL_BUSY) {
hal_busy_counter++;
}
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
When you pause MCU in debugger after data exchange - you will be suprised: it will be equal to count of missed chars.

Related

Do DLLs have a lower priority that may affect ethernet functionality? Experiencing TCP retransmissions

I have some modbus ethernet tcp communications that I'm attempting to do in a DLL. I get numerous TCP Retransmissions from the target device, as seen in WireShark.
(In this image, 192.168.1.5 is the Modbus device. 192.168.1.72 is the computer)
However, when the same code is inserted directly into an application, there are no communication errors.
I'm wondering if DLLs have some sort of lower priority that can cause slower communications, or if anyone may have any insight as to why this code would run without TCP issue in an application, but not in a DLL.
Here is the dll header:
#ifndef __MAIN_H__
#define __MAIN_H__
#include <windows.h>
typedef void *eioTHandle;
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) int __stdcall eioConnect( unsigned short ModelId, char *Ip, eioTHandle *Handle );
#ifdef __cplusplus
}
#endif
#endif
And here is the source file:
#include "main.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#define EIO500_S 0
#define EIO500_MS 1000
#define eioERROR -1
#define eioSUCCESS 0
static uint8_t m_UnitId = 0xff;
static SOCKET m_Sock;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
{
// Perform actions based on the reason for calling.
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
int __stdcall eioConnect( unsigned short ModelId, char *Ip, eioTHandle *Handle )
{
WSADATA Wsa;
struct sockaddr_in Server;
int Result;
char Buffer[256];
char InBuffer[256];
// CONNECTION --------------------------------------------------------------
if (WSAStartup(MAKEWORD(2,2), &Wsa) != 0)
{
return eioERROR;
}
m_Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_Sock == INVALID_SOCKET)
{
WSACleanup();
return eioERROR;
}
Server.sin_addr.s_addr = inet_addr(Ip);
Server.sin_family = AF_INET;
Server.sin_port = htons(502);
if (connect(m_Sock, (struct sockaddr *)&Server, sizeof(Server))
== SOCKET_ERROR)
{
closesocket(m_Sock);
m_Sock = INVALID_SOCKET;
WSACleanup();
return eioERROR;
}
// -------------------------------------------------------------------------
for (int Ctr = 0; Ctr < 50000; Ctr++)
{
// SEND COMMAND --------------------------------------------------------
// 5 bytes in a Send Read Multiple Coils command.
int NumBytes = 5;
Buffer[0] = 0;
Buffer[1] = 0;
Buffer[2] = 0;
Buffer[3] = 0;
Buffer[4] = 0;
Buffer[5] = NumBytes + 1; // 1 for unit id.
Buffer[6] = m_UnitId;
// 0 = Function code.
Buffer[7] = 0x01;
// 1+2 = Address.
Buffer[8] = 0;
Buffer[9] = 8;
// 3+4 = Number of bits to read.
Buffer[10] = 0;
Buffer[11] = 8;
if (send(m_Sock, Buffer, NumBytes + 7, 0) == SOCKET_ERROR)
{
continue;
}
// ---------------------------------------------------------------------
// WAIT FOR RECEIVE ----------------------------------------------------
WSAEVENT RecvEvent;
int Ret;
RecvEvent = WSACreateEvent();
WSAEventSelect( m_Sock, RecvEvent, FD_READ );
Ret = WSAWaitForMultipleEvents(1, &RecvEvent, TRUE, 1000, FALSE);
WSAResetEvent(RecvEvent);
if (Ret == WSA_WAIT_TIMEOUT)
continue;
// -------------------------------------------------------------------------
// Check for any reply.
recv(m_Sock, InBuffer, 256, 0);
}
// DISCONNECT --------------------------------------------------------------
Result = shutdown(m_Sock, SD_SEND);
if (Result == SOCKET_ERROR)
{
closesocket(m_Sock);
WSACleanup();
m_Sock = INVALID_SOCKET;
return eioERROR;
}
// Receive until the peer closes the connection.
while (recv(m_Sock, Buffer, 256, 0) > 0);
closesocket(m_Sock);
WSACleanup();
m_Sock = INVALID_SOCKET;
// ------------------------------------------------------------------------
return eioSUCCESS;
}
I've simplified the code as much as possible. The communication is in a loop for testing. The original application would poll this data from the device.
No. From the network's perspective there's no difference in TCP segments sent some way or other. There may be a protocol prioritation though (QoS) that may cause packet drops when links are saturated.
A more likely cause could be a problem with the checksums: invalid checksums cause packet drops which in turn cause retransmissions. Possibly the API works slightly different when called from a DLL, so the checksums are calculated (correctly).

UART sending wrong characters on pic18

I'm trying to simply send characters trough my UART Interface by calling the funktion: UART_Write_Text("hello");
this is the code executed in the the uart.c file:
void UART_Init(void)
{
//115200bps deafult value for RN4678
BRGH = 0; //Setting High Baud Rat
BRG16 = 0; //8-Bit mode
SPBRG = 8; //Writing SPBRG Register
TXSTAbits.SYNC = 0; //Setting Asynchronous Mode, ie UART
RCSTAbits.SPEN = 1; //Enables Serial Port
RCSTAbits.CREN = 1; //Enables Reception
TXSTAbits.TXEN = 1; //Enables Transmission
}
/******************************************************************************/
//-----UART write byte
/******************************************************************************/
void UART_Write(char data)
{
while(!TRMT);
TXREG = data;
}
/******************************************************************************/
//-----UART check tx queue
/******************************************************************************/
char UART_TX_Empty(void)
{
return TRMT;
}
/******************************************************************************/
//-----UART write string
/******************************************************************************/
void UART_Write_Text(char *text)
{
for(int i=0; text[i]!='\0' || text[i] !=0x00; i++){
UART_Write(text[i]);
}
UART_Write('\n');
}
/******************************************************************************/
//-----UART data ready to read
/******************************************************************************/
char UART_Data_Ready(void)
{
return RCIF;
}
/******************************************************************************/
//-----UART read byte
/******************************************************************************/
char UART_Read(void)
{
while(!RCIF);
return RCREG;
}
/******************************************************************************/
//-----UART read string
/******************************************************************************/
void UART_Read_Text(char *Output, unsigned int length)
{
unsigned int i;
for(int i=0;i<length;i++)
Output[i] = UART_Read();
}
Now when I'm debugging it, I see that it writes the wright charakters into the TXREG. At the end it sended the following characters: 'h', 'e', 'l', 'l', 'o', '\n'.
I send them to the bluetooth module RN4678 with a baud rate of 115200bps which is also the default baud rate of the BT module. However when I read the sended character on my phone with a Bluetooth terminal i get only some characters wright and the other ones are questionmarks, so it doesn't recognise them (not always the same character unrecognised).
I already experimented with the baud rate, but it looks like its the right value I writed into the SPBRG.
I'm also polling the TRMT so there shouldn't be any collisions...
Someone knows what I'm doing wrong?
#include "Configuration_bits.h"
#include <xc.h>
#include <stdio.h>
#include <p18f67k22.h>
#include <string.h>
#include <stdlib.h>
#define _XTAL_FREQ 16000000
void UART_Init_1(){
TRISCbits.TRISC6=1; // TX1 Pin-31
TRISCbits.TRISC7=1; // RX1 Pin-32
RC1IE=1;
SPBRG1=34; // Baud Rate 115200
BAUDCON1bits.BRG16=1;
TXSTA1bits.BRGH=1;
//TXSTA 1 Register
TXSTA1bits.TX9=0; // 8-bit transmission
TXSTA1bits.TXEN=0;
TXSTA1bits.TXEN=1; // Transmit is enabled
TXSTA1bits.SYNC=0; // Asynchronous mode
TXSTA1bits.TRMT=1; // Transmit Shift Register Status bit
//RXSTA 1 Register
RCSTA1bits.SPEN=1; // Serial Port Enable bit
RCSTA1bits.RX9=0; // 8-bit reception
RCSTA1bits.CREN=1; // Continuous Receive Enable bit
}
void UART_Tx_1(unsigned char z[])
{
unsigned int uart_tx1=0;
while(z[uart_tx1]!='\0')
{
while(!TX1IF);
TXREG1=z[uart_tx1];
uart_tx1++;
}
}
void UART_Rx_1()
{
string_rx1[uart_rx1]=RCREG1;
uart_rx1++;
}
void interrupt ISR(void)
{
if(RC1IF && RC1IE) // UART_1
{
}
}
void main(void)
{
System_Initialization();
UART_Init_1();
while(1)
{
UART_Tx_1("HELLO\r\n"); // Transmit Hello
}
}

STM32 HAL_UART_Transmit_IT never returns

I have created a project using cube mx and want to use the uart4 tx and rx to send and receive bytes in interrupt mode.
I have :
uint8_t buffer[] = "test\r\n";
if(HAL_UART_Transmit_IT(&huart4, (uint8_t*)buffer, strlen(buffer))!= HAL_OK)
{
}
The uart initialisation is
static void MX_UART4_Init(void)
{
huart4.Instance = UART4;
huart4.Init.BaudRate = 9600;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
The call to Transmit never returns and just sits there.
In the msp file I have
HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
and have in the it file
void UART4_IRQHandler(void)
{
/* USER CODE BEGIN UART4_IRQn 0 */
/* USER CODE END UART4_IRQn 0 */
HAL_UART_IRQHandler(&huart4);
/* USER CODE BEGIN UART4_IRQn 1 */
/* USER CODE END UART4_IRQn 1 */
}
what am I missing?
the problem is related to uint8_t buffer[] = "test\r\n";
the right side is char and the left side is uint8. So, you must do as follows
char buffer[] = "test\r\n";
if(HAL_UART_Transmit_IT(&huart4, (char*)buffer, strlen(buffer))!= HAL_OK)
{
}
or you can do this (use sizeof for int8_t not strlen)
unit8_t buffer[] = "test\r\n";
if(HAL_UART_Transmit_IT(&huart4, (uint8_t*)buffer, sizeof(buffer))!= HAL_OK)
{
}
please add "string.h" and in the first line of you code
Adding a delay too allow the data to be sent fixed this problem. HAL_Delay(100)

Interrupt Handling 18F4550 PIC

I'm trying to get myself familiar with Interrupts when it comes to the 18F4550 PIC. At this point I've written a software that would light up an LED whenever the INT0 raises a flag.
My code states that every 500 ms, INT0 will raise a flag and "is supposed to" jump into an ISR and fire up a LED all while "transferring dummy data between Port C and Port D"
I've tried debugging, but once the debugger gets to the code that sets the INT0 flag to 1, the debugger says "No source code lines were found at current PC".
Here's the code below. Any kind of help is greatly appreciated. Thanks a lot!
Please note that I am using MPLAB X and a PicKit3
#include <xc.h>
#include <p18f4450.h>
#include <stdlib.h>
#define mybit LATBbits.LATB1 //LED
#define _XTAL_FREQ 10000000 //Using a crystal oscillator
void chk_isr(void);
void INT0_ISR(void);
void delay(unsigned int);
#pragma interrupt chk_isr //used for high-priority interrupt only
void chk_isr (void)
{
if(INTCONbits.INT0IF == 1) //INT0 causes interrupt?
INT0_ISR(); //Yes, Execute INT0ISR
}
#pragma code My_HiPrio_Int=0x08//high priority interrupt
void My_HiPrio_Int (void)
{
asm("GOTO chk_isr");
}
void main(void)
{
TRISBbits.TRISB1 = 0; //RB1 = output
mybit = 0; //Initially LED is OFF
TRISC = 0xFF; //PORTC = input
TRISD = 0; //PORTD = output
INTCONbits.INT0IF = 0; //clear INT0
INTCONbits.INT0IE = 1; //enable INT0 interrupt
INTCONbits.GIE = 1; //enable all interrupts globally
while(1)
{
PORTD = PORTC;
delay(500); //After 500 ms
INTCONbits.INT0IF = 1; //Flag
}
}
void INT0_ISR(void)
{
mybit=~mybit; //Toggle LED
INTCONbits.INT0IF = 0; //clear INT0 Flag
}
void delay(unsigned int delayInput) {
unsigned int mul = delayInput/50;
unsigned int count = 0;
for (count = 0; count <= mul; count ++)
__delay_ms(50);
}

External interrupts of 8051 in c

This code written to display speed in mph by calculating number of external interrupts at INT0 for a particular time delay, works a part ie it just displays 'Read out:'
and then nothing.
I have seen 8051 derivatives page in c51 development tools. It shows me no needed information.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0002a/c51_dv_8051deriv.htm
I have not understood why sometimes its written
void ex0_isr(void) interrupt 0 using 2
Can someone help me understand where am wrong?
Simulation is working fine.
//Calculating speed
#include <reg51.h>
#include <string.h>
void read(unsigned int q);
void delay(void);
void delay1(unsigned long int time);
void SerInit();
void SerTx(unsigned char x);
void SerTx_str(unsigned char msg[]);
unsigned char msg[]="Speed= ";
unsigned char buffer[12];
sbit in_port=P3^2;
sbit EDGT=TCON^0;
unsigned char volatile count;
unsigned int q;
void ex0_isr(void) interrupt 0
{
count++;
}
void timer0(void) interrupt 1
{
TR0=0;
{
void main()
{
SerInit(); //serial initialisation
SerTx_str("Read out:"); //send string to hyperterminal
SerTx('\n');
delay1(20); //delay using loop
SerTx('\r');
in_port=1; //input port pin
EDGT=1; //make IT0 =1 an edge triggered
while(1)
{
count=0; //initialize count
IE=IE|0x81; //enable global and INT0
delay(); //delay using timers for a time to measure count
SerTx_str("Dbg"); //check
IE=IE&0xFE; //disable INT0
q=count;
read(q); //function called for ASCII conversion
SerTx_str(msg); //send message to terminal
SerTx_str(buffer); // send count to terminal
SerTx('\n');
delay1(2000);
SerTx('\r');
}
}
void read( unsigned int q)
{
unsigned int d1,d2,d3;
d1=q%10;
q=q/10;
d2=q%10;
d3=q/10;
buffer[0]=d3+'0';
buffer[1]=d2+'0';
buffer[2]=d1+'0';
}
void delay(void)
{
unsigned int i;
for(i=0;i<45;i++)
{
TMOD=0x01; //Timer 0, mode1(16 bit)
TL0=0xFD; //load TL0
TH0=0x4B; //load TH0
IE=IE|0x02; //enable Timer 0 interrupt
TR0=1; //turn on Timer 0
do
{
q=count;
}
while(TF0==0);
//if there's overflow ie, TF=1, goto interrupt 1
//
//> TMOD=0x01;
> TL0=0xFD;
> TH0=0x4B;
> TR0=1;
> while(TF==0);
> TR0=0;
> TF0=0; // previous code
}
}
My guess is that by doing:
IE=0x81;
You are accidentally disabling the timer interrupts, which means that your delay() function never returns. I suspect you probably want to do something like this instead:
IE = IE | 0x01; // enable INT0
delay(); // delay using timers for a time to measure count
IE = IE & 0xFE; // disable INT0