I'm trying to write a class to have the possibility of sending data using UART by TXE (TDR is empty) Interrupt. The class uses only USART1. The class has a ring data buffer to send. I have written a similar class for AVR (ATmega) and I would like to repeat such functionality.
At the moment I don't have the hardware (STM32), but I am using Proteus v.8.6. The start of sending is to enable TXE Interrupt. The main problem is TXE Interrupt is not generated. If I enable RXNE Interrupt and send one byte to the STM32, I will see all my data (from sending buffer) correctly are sent. Hence I can enter in USART1_IRQHandler only due to RXNE Interrupt. I'm not sure, perhaps it's a simulation problem. Also I use Atollic TrueSTUDIO v.9.3.0. I'll try to provide comprehensive information about my code. Sorry for my question, I'm a novice with STM32.
main.cpp
usart_transmitter UsartESP; //Usart class object declaration
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
UsartESP.ini(); //USART1 Initialization
UsartESP.print("Hello from class!");
while(1){}
}
stm32f1xx_it.cpp
extern usart_transmitter UsartESP;
void USART1_IRQHandler(void){
//If Interrupt due to TDR is empty
if (USART1->SR & USART_SR_TXE){
UsartESP.handlerTDRE();
}
}
usart_transmitter.h
const uint8_t BUFFER_SIZE = 100; //Size of sending data buffer
class usart_transmitter{
private:
char _Data[BUFFER_SIZE]; //Data buffer
uint8_t _PushIndex;
uint8_t _PopIndex;
uint8_t _IsOverflow;
void push(char symbol);
char pop();
public:
void ini(); //Configuration Usart1
void print(char* message); //Put string message to sending buffer
void handlerTDRE(); //Interrupt TDR is empty handler
};
usart_transmitter.cpp
void usart_transmitter::ini(){
for (uint8_t i=0; i<BUFFER_SIZE; i++){
_Data[i] = 0;
}
_PushIndex = 0;
_PopIndex = 0;
_IsOverflow = 0;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
//Tx - Alternate function push-pull
GPIOA->CRH &= ~(GPIO_CRH_CNF9_0); //CNF9[0] = 0
GPIOA->CRH |= GPIO_CRH_CNF9_1; //CNF9[1] = 1
GPIOA->CRH |= GPIO_CRH_MODE9_0; //MODE9[0] = 1
GPIOA->CRH |= GPIO_CRH_MODE9_1; //MODE9[1] = 1
//Rx - Input pull-up
GPIOA->CRH &= ~(GPIO_CRH_CNF10_0); //CNF10[0] = 0
GPIOA->CRH |= GPIO_CRH_CNF10_1; //CNF10[1] = 1
GPIOA->CRH &= ~(GPIO_CRH_MODE10_0); //MODE10[0] = 0
GPIOA->CRH &= ~(GPIO_CRH_MODE10_1); //MODE10[1] = 0
GPIOA->BSRR |= GPIO_ODR_ODR10; //PxODR = 1
USART1->CR1 = USART_CR1_UE; //Usart1 Enable
USART1->BRR = 16; //4500000 baudrate
USART1->CR1 |=
(USART_CR1_TE) | //Enable Transmitter
(USART_CR1_RE) | //Enable Receiver
(USART_CR1_RXNEIE); //Enable RXNE Interrupt
USART1->CR2 = 0;
USART1->CR3 = 0;
NVIC_EnableIRQ(USART1_IRQn); //Enable USART1 global Interrupt
}
void usart_transmitter::push(char symbol){
_Data[_PushIndex] = symbol; //Put character to buffer
_PushIndex++;
if (_PushIndex > BUFFER_SIZE-1){
_PushIndex = 0;
_IsOverflow = 1;
}
}
char usart_transmitter::pop(){
char data = _Data[_PopIndex];
_PopIndex++;
if (_PopIndex > BUFFER_SIZE-1){
_PopIndex = 0;
_IsOverflow = 0;
}
return data;
}
void usart_transmitter::print(char* message){
uint8_t i = 0;
//Placing string to buffer
while(message[i] != '\0'){
push(message[i]);
i++;
}
USART1->CR1 |= USART_CR1_TXEIE; //Enable TXE Interrupt
}
void usart_transmitter::handlerTDRE(){
char byte = pop();
USART1->DR = byte;
//Checking criterion to stop sending
if (_PopIndex == _PushIndex){
if (!(_IsOverflow)){
//Disable TXE Interrupt to avoid circularity of interruption
USART1->CR1 &= ~(USART_CR1_TXEIE);
}
}
}