I am currently working on RS485 usart interrupts on AT32UCB1258 custom board and struggling with txempty interrupt.
Basically, I want to disable tx after 5 bytes have been sent.
My rx interrupt (receive command from master board) and tx ready interrupts are firing correctly, however, tx empty interrupt is not firing at all.
I am checking channel status register to distinguish which interrupt to be fired in the USART2 interrupt handler.
Any help is greatly appreciated. Thanks in advance!
//***********************USART INT HANDLER***************************************
__attribute__((__interrupt__)) static void rs485Interrupt(void)
{
if(AVR32_USART2.CSR.rxrdy)
{
gpio_set_pin_low(AVR32_PIN_PA08);
AVR32_USART2.IDR.txrdy = 1;
AVR32_USART2.IDR.txempty = 1;
rs485RxInterrupt();
}
else if(AVR32_USART2.CSR.txrdy)
{
AVR32_USART2.IDR.rxrdy = 1;
AVR32_USART2.IDR.txempty = 1;
rs485TxInterrupt();
}
else if(AVR32_USART2.CSR.txempty)
{
gpio_set_pin_low(AVR32_PIN_PA06);
rs485TxEmpty();
}
}
//**********************************************************************************
// this function is in main, and calls txrdy interrupt if data is available to send
static void rs485_write(void)
{
gpio_set_pin_high(AVR32_PIN_PA10);
txBuf[0] = ATEAddress;
AVR32_USART2.THR.txchr = txBuf[0];
tx_outctr = 1;
txLength = 5;
//txempty_flag = false;
txBuf[1] = peaks[loop][0];
txBuf[2] = peaks[loop][1];
txBuf[3] = peaks[loop][2];
txBuf[4] = peaks[loop][3];
AVR32_USART2.IER.txrdy = 1;
}
//************************TX INTERRUPT ROUTINE******************************************
static void rs485TxInterrupt(void)
{
//gpio_set_pin_low(AVR32_PIN_PA06);
for(tx_outctr=1; tx_outctr<=5; tx_outctr++)
{
//AVR32_USART2.THR.txchr = 0x01;
AVR32_USART2.THR.txchr = txBuf[tx_outctr];
//usart_write_char(&AVR32_USART2,(int)txBuf[tx_outctr]);
if (tx_outctr == 5)
{
// if 5btyes are sent, disable tx by causing txempty interrupt
AVR32_USART2.IDR.txrdy = 1;
AVR32_USART2.IER.txempty = 1;
txempty_flag = true;
}
}
//gpio_set_pin_low(AVR32_PIN_PA08);
}
//**********************************************************************************
Currently, watching gpio_pin06 going low if there is txempty interrupt, but never going to that stage.
I think usart initilisations, gpio enabling modules and interrupt registerations are correctly set since other interrupts are firing correctly.
Related
I am trying to establish UART communication between a PC and a STM32f407-DISC1 board using an arduino nano as a middle man.
The PC sends 'r' to the arduino to indicate a request.
The request is then communicated to the stm32 with a GPIO interrupt, which then should be transmitting 480 bytes of data using HAL_UART_Transmit_IT.
It however sends the data twice, with only a single request made.
The code on the STM32 is generated by STM32CubeMX
Data request made by the arduino
void loop() {
digitalWrite(4, 0); // Clear EXTI11 line.
if (mySerial.available() && received < 480) { // STM32 sending data and is not done.
buff[received] = mySerial.read(); // Append received data to the buffer.
received++;
}
if (received >= 480) { // If the buffer is full
received = 0; // transmit it to PC.
Serial.println(buff);
}
if (Serial.available()) {
if (Serial.read() == 'r') { // PC requests data from the STM32
digitalWrite(4, 1); // Triggers STM32 EXTI11 line.
while (Serial.available()) // Empty the buffer.
Serial.read();
}
}
}
data transmission on the STM32
void EXTI15_10_IRQHandler(void)
{
// Make sure that the interrupt is the good one.
if (HAL_GPIO_ReadPin(data_req_IRQ_GPIO_Port, data_req_IRQ_Pin)) {
if (is_sending_data == FALSE) // If no transmission is happening
should_send_data = TRUE; // raise transmission flag.
}
// IRQ handling stuff...
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef * huart) {
is_sending_data = FALSE; // Transmition is completed, unblock requests.
}
void main(void){
// Init and other stuff...
while (1) {
if (should_send_data == TRUE) { // If data was requested
HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_RESET);
HAL_UART_Transmit_IT(&huart3, matrice, 480); // Start transmission by interrupt.
is_sending_data = TRUE; // Block requests.
should_send_data = FALSE; // Clear flag.
}
// matrice acquisition stuff here
}
}
Alright so I found a solution, but it involved just rethinking my approach, so sorry for those looking for an answer to this problem.
I removed the arduino middle man by replacing it with a USB to RS232 converter and made UART reception work by interrupt. The STM detects the 'r' character which triggers the data communication.
Here is the interrupt part:
void USART3_IRQHandler(void)
{
if (USART3->SR & UART_IT_RXNE) { // If a byte is received
rxBuff[0] = (uint8_t) (huart3.Instance->DR & (uint8_t) 0xFF); // Read it.
__HAL_UART_FLUSH_DRREGISTER(&huart3); // Clear the buffer to avoid errors.
rx_new_char_flag = TRUE; // Raise the new_char flag.
return; // Stops the IRQHandler from disabling interrupts.
}
}
and the gestion of that in the main
while (1) {
if (rx_new_char_flag == TRUE) {
rx_new_char_flag = FALSE;
if (rxBuff[0] == 'r') {
rxBuff[0] = 0;
HAL_UART_Transmit_IT(&huart3, matrice, 480); // Start transmission by interrupt.
}
}
On the PC side, to optimize performance, instead of waiting for the full 480 bytes, I wait for only one character, if one is received I keep reading the serial port, as shown in the code bellow
int i = 0;
do {
ReadFile(m_hSerial, &temp_rx[i], 1, &dwBytesRead, NULL);
i++;
} while (dwBytesRead > 0 && i < 480);
for (int j = i; j < 480; j++) // If the transmission is incomplete, fill the buffer with 0s to avoid garbage data.
temp_rx[j] = 0;
if(i>=480) // If all the bytes has been received, copy the data in the working buffer.
std::copy(std::begin(temp_rx), std::end(temp_rx), std::begin(m_touch_state));
This works well with pretty decent performance, so that may be a permanent solution to my problem.
I am facing a problem while implementing a timer based interrupt in mikroC for PIC.
I want to toggle a port pin for 8 times if there is a keypress at PORTC.F0 and there should be a delay of say 100ms between the toggles.
Normally this would be very easy using a delay function
for (i=0;i<=8;i++)
{
PORTB.F0=~PORTB.F0;
Delay_ms(100);
}
But during the period, any other keypresses are missed by the system. So I thought of implementing the solution using interrupts.
#define SW PORTC.F0
char ttime,i;
volatile flag;
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
void Interrupt()
{
if (TMR1IF_bit)
{
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
ttime--;
if (ttime==0)
{
flag=1;
}
}
}
void main()
{
Inittimer1();
TRISB = 0;
TRISC.F0 = 1;
PORTB = 0x00;
while(1)
{
if (SW==0)
{
ttime=3;
}
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
}
}
Nothing is working. Can somebody please help me to correct the code?
Well this doesn't look right:
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
When you first see that flag is set, you immediately loop and toggle the output 8 times, without waiting for flag to turn back to 1. That's not right, it's overly simplified.
You need to look for the flag, then toggle the output and clear the flag, and wait for it to to get set again, maintaining the counter in parallel. The for loop is not the proper structure for this, since it will "lock out" the rest of the program and might cause keypresses to be missed.
When you initialize your timer:
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06; // No prescaler? I doubt your clock speed is 40-some KHz!
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
Why don't you control the LED directly from the ISR ?
if (ttime)
PORTB.F0 = (--ttime & 1); // ttime is not decremented when led is not blinking.
else
PORTB.F0 = 0; // ensures the LED is off.
To start blinking 8 times:
if (SW==0)
{
PORTB.F0 = 1;
ttime = 16;
}
Note that with a 100ms clock interrupt, the first 'blink' of the LED may last up to 200ms... This is why many like to work with a faster timer interrupt (this has usually other uses as well), controlling the led would require adding a soft post-scaler
if (blinking)
{
if (--blinkTimer == 0)
{
blinkTimer = BLINK_DELAY; // whatever number it takes for 100ms.
PORTB.F0 = (--blinking & 1);
}
}
else
{
PORTB.F0 = 0
}
To start blinking:
if (SW==0)
{
blinking = (2 * BLINKS) - 1;
blinkTimer = BLINK_DELAY;
PORTB.F0 = 1;
}
This should get you a more even first blink.
I am having a problem about RTC Alarm configuration. First of all, I tried example code which is provided by Cube, and It works. But in my code, it doesn't work at all. I am guessing the RTC formats are not matching. even so, I tried all possibilities both BCD and BIN.
Reading Time&Date functions are working. I can show them on OLED screen.
But can't generate alarm interrupt. What am I doing wrong?
void StartRTC_Routine(void)
{
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_RTC_ENABLE();
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR0) != RTC_BKP_DATE_TIME_UPDTATED)
{
/*RTC Init*/
MX_RTC_Init();
RTC_Default();
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0,RTC_BKP_DATE_TIME_UPDTATED);
}
else
{
/* Check if the Power On Reset flag is set */
if(__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET)
{
MyFlag = 0xBA;
}
/* Check if the Soft Reset flag is set */
if(__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) != RESET)
{
MyFlag = 0xFB;
}
}
}
void RTC_TimeShow(uint8_t* showtime)
{
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BCD);
/* Display time Format : hh:mm:ss */
sprintf((char*)showtime,"%02X:%02X:%02X",stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
ssd1306_SetCursor(20,20);
ssd1306_WriteString((char*)showtime,Font_11x18,White);
ssd1306_UpdateScreen();
}
/* Initial Values of RTC **/
void RTC_Default(void)
{
sdatestructureget.Date = 0x14;
sdatestructureget.Year = 0x17;
sdatestructureget.WeekDay = 0x05;
sdatestructureget.Month = 0x01;
stimestructureget.Hours = 0x02;
stimestructureget.Minutes = 0x04;
stimestructureget.Seconds = 0x30;
stimestructureget.TimeFormat = RTC_HOURFORMAT_24;
stimestructureget.DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
stimestructureget.StoreOperation = RTC_STOREOPERATION_RESET;
HAL_RTC_SetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BCD);
HAL_RTC_SetTime(&hrtc, &stimestructureget, RTC_FORMAT_BCD);
}
void Set_Alarm(void)
{
/*################# Configure the RTC Alarm peripheral #################*/
salarmstructure.Alarm = RTC_ALARM_A;
salarmstructure.AlarmDateWeekDay = RTC_WEEKDAY_MONDAY;
salarmstructure.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
salarmstructure.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
salarmstructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
salarmstructure.AlarmTime.TimeFormat = RTC_HOURFORMAT_24;
salarmstructure.AlarmTime.Hours = 0x02;
salarmstructure.AlarmTime.Minutes = 0x34;
salarmstructure.AlarmTime.Seconds = 0x10;
salarmstructure.AlarmTime.SubSeconds = 00;
HAL_RTC_SetAlarm_IT(&hrtc,&salarmstructure,RTC_FORMAT_BCD);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
/* Turn LED2 on: Alarm generation */
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
}
Some things you could check:
Make sure you call Set_Alarm() as i cannot see a function call in your code sample.
Try configuring your alarm to go off every second and set a breakpoint in HAL_RTC_AlarmAEventCallback() and see if the function is run.
You need to enable NVIC configuration for RTC in order to use _IT methods (ex. HAL_RTC_SetAlarm_IT).
If you are using CubeMx this can be done simple in Configuration -> RTC, then go to NVIC.
I'm able to receive with the following code, but unfortunately, nothing is sent back. What am I doing wrong?
#include <pic18f25k80.h>
#include "config.h"
#include <usart.h>
int i = 0;
unsigned char MessageBuffer[200];
void main() {
OSCCONbits.IRCF = 0b110; // 8MHz
TRISB6 = 0; // TX set as output
TRISB7 = 0; // RX set as output
// Clear TX interrupt
// Set RX interrupt
// 8-bit Asynch. mode
// BRGH = 1 = high baud mode
// 51 = ((8MHz/baud)/16)-1 with baud = 9600
Open2USART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE
& USART_EIGHT_BIT & USART_BRGH_HIGH, 51 );
RC2IF = 0; // reset RX2 flag
RC2IP = 0; // not high priority
RC2IE = 1; // Eneble RX2 interrupt
INTCONbits.PEIE = 1; // enable peripheral interrupts
INTCONbits.GIE = 1; // enable interrupts
RCSTA2bits.SPEN = 1; // enable USART
while(1){
}
}
void interrupt ISR () {
if(PIR3bits.RC2IF == 1) {
if(i<200) { // buffer size
MessageBuffer[i] = Read2USART(); // read byte from RX reg
if (MessageBuffer[i] == 0x0D) { // check for return key
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i=0;
return;
}
i++;
RC2IF = 0; // clear RX flag
} else {
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i = 0;
return;
}
}
}
I'm transmitting the 0x41 hex code, I checked with the scope and see that is is being received. And according to the code I have, an echo of the received data should be sent back. When I check the TX pin, nothing is happening.
Add USART_CONT_RX to Open2USART to enable continuous receive.
Also, it's a good idea to do the minimum necessary in the interrupt service routine. Consider something like:
void interrupt ISR () {
char data;
if(PIR3bits.RC2IF == 1) {
data = Read2USART(); // always read byte from RX reg (clears RC2IF)
if(i<200) { // buffer size
MessageBuffer[i] = data; // read byte from RX reg
i++;
}
else{
// flag buffer full error
}
}
}
and doing the rest of what you are doing in the while(1) loop.
I've been doing a project about home automation in which I have to use timer interrupts with 8051 microcontroller. I've constructed the following code, however I couldn't manage to get interrupt working. It seems that the program does not go into timer ISR at all. I use a buton to simulate PIR input, therefore lampControl is triggered, no worries there. I use as a library.
Any ideas or help will be greately appreciated:
void timer0_isr(void) interrupt 1 //Timer 0 Interrupt
{
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
if (++lamp_interrupt_count == 6000)
{
sendCharacterShowAsHex(0x8F);
lamp_interrupt_count = 0;
TR0 = 0;
}
}
void main()
{
unsigned char chr;
IE = 0x93;
while(1)
{
serialInput();
if (getPIRInput() == 0x00)
{
lampControl(0x80);
}
....
....
....
}
void lampControl(unsigned char serial_data_in)
{
if (serial_data_in == 0x80)
{
sendCharacterShowAsHex(0x80);
//enable interrupts
IE = 0x93;
device_interrupt = 2; //Lamp
TMOD = 0x21; // Timer0 Gate=0, Mode 1, 16bit timer
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
}
else if(serial_data_in == 0x8F)
{
sendCharacterShowAsHex(0x8F);
}
}
You need to configure the timer and interrupts before you can use them.
In main() you need at least the following configuration bits set in order to be able to turn
the timer on with "TR0 = 1;" :
Set those bits first thing in main() and this should do the trick:
TMOD = 0x01; // 16-bit no auto reload
TH0 = 0xDC; //Set high and low bits to count 0xFFFF - 0xDC00 = 0x23FF counts
TL0 = 0x00;
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable all interrupts
//TR0 = 1; //Enable Timer0 immediately
The rest of your code should run fine.
Note: you could change your interrupt function definition to:
"void timer0_isr(void) interrupt 1 using 1" to force it to use register bank 1 for the interrupt function operation.