I've been trying to implement a basic per-byte UART Rx Interrupt on a STM32F4 board using HAL skeleton code generated by STMCubeMX version 4.26.0
Quite simply - I want to receive a character in UART1 via an Rx interrupt and transmit it on UART 6
I have successfully implemented a polled version of what I want to achieve
uint8_t in_usart1[10];
HAL_StatusTypeDef usart1_status;
usart1_status = HAL_UART_Receive(&huart1, in_usart1, 1, 1);
if (usart1_status != HAL_TIMEOUT)
{
HAL_UART_Transmit(&huart6, in_usart1, 1, 100);
}
I've enabled the UART 1 NVIC interrupt in STMCubeMX and stm32f4xx_it.c contains the IRQ handler which I've added my own user handler to:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
HAX_USART1_IRQHandler(&huart1); /* My Handler */
/* USER CODE END USART1_IRQn 1 */
}
I've seen lot's of commentary about UART_Receive_IT() - but I suspect this is based on older versions of HAL due to UART_Receive_IT() being defined in stm32f4xx_hal_uart.c
My suspicion is that I need to enable to interrupt / clear the interrupt flag as when I debug, USART1_IRQHandler() is NEVER called
Does any one have any code that demonstrates what I am trying to achieve? My google-foo has failed me
EDIT:
I've gotten a little closer... In main.c I added (comments are existing code)
/* USER CODE BEGIN PV */
uint8_t rx_buffer;
/* USER CODE END PV */
...
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)rx_buffer, 10);
/* USER CODE END 2 */
And then created:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
HAL_UART_Transmit(&huart6, &rx_buffer, 1, 100);
}
}
Now the Rx interrupt gets fired - but it's a bit flakey on the USART6 Tx, and the Rx interrupt only gets fired once
Do not block HAL_UART_RxCpltCallback for a long time! Just set a flag and check it and then send data from the main function.
And rx_buffer is variable so correct call HAL_UART_Receive_IT(&huart1, &rx_buffer, 1);
For anybody stumbling across this question, the answer is embarrassingly simple. I have two UARTs - One I was using an Rx Interrupt, and the other using DMA.
Turns out the one I thought I had configured for Interrupt was actually configured for DMA and visa-versa...
In STMCubeMX
- USART1 (RS485) has DMA Tx and DMA Rx enabled
- USART6 (Debug - RS232) has global interrupt enabled
In main.c
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(debug_uart(), debug_rx_buffer, BUFFER_SIZE);
HAL_UART_Receive_DMA(rs485_uart(), rs485_rx_buffer, BUFFER_SIZE);
/* USER CODE END 2 */
I have a user_main.c which has the following code:
#include <string.h>
#include "stm32f4xx_hal.h"
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart6;
UART_HandleTypeDef *debug_uart(void)
{
return &huart6;
}
UART_HandleTypeDef *rs485_uart(void)
{
return &huart1;
}
#define BUFFER_SIZE 1
uint8_t debug_rx_buffer[BUFFER_SIZE];
uint8_t debug_tx_buffer[BUFFER_SIZE];
uint8_t rs485_rx_buffer[BUFFER_SIZE];
uint8_t rs485_tx_buffer[BUFFER_SIZE];
static void rs485_tx(uint8_t *tx_buffer, uint16_t len)
{
HAL_UART_Transmit_DMA(rs485_uart(), tx_buffer, len);
}
static void debug_tx(uint8_t *tx_buffer, uint16_t len)
{
HAL_UART_Transmit(debug_uart(), tx_buffer, len, 1000);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == debug_uart())
{
memcpy(rs485_tx_buffer, debug_rx_buffer, BUFFER_SIZE);
rs485_tx(rs485_tx_buffer, BUFFER_SIZE);
HAL_UART_Receive_IT(debug_uart(), debug_rx_buffer, BUFFER_SIZE);
}
else if (huart == rs485_uart())
{
memcpy(debug_tx_buffer, rs485_rx_buffer, BUFFER_SIZE);
debug_tx(debug_tx_buffer, BUFFER_SIZE);
HAL_UART_Receive_DMA(rs485_uart(), rs485_rx_buffer, BUFFER_SIZE);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == debug_uart())
{
}
else if (huart == rs485_uart())
{
}
}
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
}
The memcpy()'s may not be strictly required, but they do provide a level of isolation between all the buffers. Technically, there probably should be semaphores providing even more protection...
Note that I DO NOT use HAL_UART_Transmit_IT() for the debug UART - If you want to use HAL_UART_Transmit_IT (i.e. interrupt generated on completion of Tx), you will need to write code that handles transmission of characters from a circular buffer
Related
I've been trying to write some code on STM32F411re usign IAR workbench in order to learn more about Cortex. I tried to implement TIMER3 PWM mode (center-aligned) with TIMER 2 being called every (half a second, second doesnt matter as much performing LED blink) and ADC performing continious regular conversion on one channel. I've tried to implement it all using interrupts. TIMER3 interrupt is inteded to be generated on Overflow and underflow and within ISR i would change PWM width with value from ADC (changed with potentiometer).
Problem that i faced while creating project seems to be that, when TIMER3 is activated, program doesnt hit breakpoint ( does not enter) ADC ISR routine nor within any line of program within while(1) loop. When i comment TIMER 3, program normally goes through ADC ISR.
#include "stdio.h"
void Uart6Configuration(void);
void send_data (uint8_t c);
void init_PWM(void);
void init_ADC(void) ;
void init_Interupts(void);
unsigned long vrednost_ADC=0;
float temp=0;
unsigned long counter=0;
int main()
{
RCC->APB1ENR|=(1<<0); //TIMER 2
RCC->AHB1ENR|=(1<<0); //GPIOA
RCC->AHB1ENR|=(1<<2); //GPIOC
GPIOA->MODER|=(1<<10);
RCC->APB2ENR|=(1<<5); // USART6[PC6,PC7]
/* Define TIMER-a 3 */
RCC->APB1ENR|=(1<<1); //TIMER 3
GPIOB->MODER|=(1<<9);
GPIOB->AFR[0]|=(1<<17);
TIM2->PSC=89;
TIM2->ARR=0xFFFF;
TIM2->DIER|= (1<<0);
TIM2->EGR|= (1<<0);
Uart6Configuration();
init_PWM();
init_ADC();
init_Interupts();
TIM2->CR1|=(1<<0);
TIM3->CR1|=(1<<0);
while(!(TIM2->SR & (1<<0)));
ADC1->CR2|=(1<<30); // START ADC
/*GLAVNA PROGRAMSKA PETLJA*/
while(1)
{
counter++;
if(counter>100000)
{
printf("AD konverzija=%f \n\r",temp); //Terminal I/O
counter=0;
}
}
/* ************************/
return 0;
}
void TIM2_IRQHandler(void )
{
if(TIM2->SR & TIM_SR_UIF)
{
TIM2->SR &= ~TIM_SR_UIF;
GPIOA->ODR^=(1<<5);
}
TIM2->SR =0;
}
void Uart6Configuration (void)
{
GPIOC->MODER |= (2<<12); // --> Alternate Function for Pin PA11
GPIOC->MODER |= (2<<14); // --> Alternate Function for Pin PA12
GPIOC->OSPEEDR|=(3<<12)|(3<<14);
GPIOC->AFR[0] |= (8<<24); //AF7 bitovi 8,9,10,11 PC6
GPIOC->AFR[0] |= (8<<28); //AF7 bitovi 15,14,13,12 PC7
USART6->CR1=0;
USART6->CR1|=(1<<13);
USART6->CR1 &= ~(1<<12);
USART6->BRR=(3<<0)|(104<<4);
USART6->CR1|=(1<<2);
USART6->CR1|=(1<<3);
}
void send_data (uint8_t c)
{
while(!(USART6->SR & (1<<6)));
USART6->DR=c;
}
uint8_t UART6_GetChar (void)
{
/*********** STEPS FOLLOWED *************
1. Wait for the RXNE bit to set. It indicates that the data has been received and can be read.
2. Read the data from USART_DR Register. This also clears the RXNE bit
****************************************/
uint8_t temp;
while (!(USART2->SR & (1<<5))); // wait for RXNE bit to set
temp = USART2->DR; // Read the data. This clears the RXNE also
return temp;
}
void init_PWM(void)
{
/*PB_4*/
TIM3->PSC=15;
TIM3->ARR=750;
TIM3->CR1|= (1<<5)|(1<<6) | (1<<2); // PWM CENTAR EDGE MODE
TIM3->CCER|=(1<<0); //Capture/Compare 1 output enable.
TIM3->CCR1=500; //DUTY CYCLE
TIM3->CCMR1|=(1<<5)|(1<<6); // PWM MODE bit 5 i6
TIM3->DIER|=(1<<0);
}
void init_ADC(void)
{
RCC->APB2ENR|=(1<<8); // Clock za adc
GPIOA->MODER|=(1<<2)|(1<<3); // Analog mode PA.1
ADC1->SQR3|=(1<<0); // Choose channel ADC1/1
ADC1->CR1|=(1<<5); //EOCIE interupt generates when ADC finish conversion
ADC1->CR2|=(1<<1)|(1<<0); // Continious mode, ADC ON
}
void ADC_IRQHandler(void)
{
vrednost_ADC=ADC1->DR;
temp=(float)((vrednost_ADC/4095.0)*3.3) ;
}
void TIM3_IRQHandler(void )
{
if((TIM3->CNT & 10)<=0) // DETECTOVATI UNDERFLOW
{
TIM3->CCR1=(vrednost_ADC/4095)*1000;
TIM3->EGR|=(1<<0);
}
}
void init_Interupts(void)
{
NVIC_SetPriority (ADC_IRQn, (13));
NVIC_SetPriority (TIM2_IRQn, 14);
NVIC_SetPriority (TIM3_IRQn, 15);
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_EnableIRQ(TIM3_IRQn);
NVIC_EnableIRQ(ADC_IRQn );
}```
I am making HID for some data acquisition system. There are a lot of sensors who store test data and when I need I get to them and connect via USB and take it. USB host sent 3 bytes and USB device, if bytes are correct, sends its stored data. Sounds simple.
Previously it was implemented on PC, but now I try to implement it on STM32F769 Discovery and have some serious problems.
I am using ARM Keil 5.27, code generated with STM32CubeMX 5.3.0. I tried just to make a plain simple program, later to integrate with the entire touchscreen interface. I tried to implement this code in main:
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
{
Transmission_function();
}
And the function itself:
#define DLE 0x10
#define STX 0x2
uint8_t tx_buf[]={DLE, STX, 120}, RX_FLAG;
uint32_t size_tx=sizeof(tx_buf);
void Transmission_function (void)
{
if (Appli_state == APPLICATION_READY)
{
i=0;
USBH_CDC_Transmit(&hUsbHostHS, tx_buf, size_tx);
HAL_Delay(50);
RX_FLAG=0;
}
}
It should send the message after I press the blue button on the Discovery board. All that I get is Hard Fault. While trying to debug, I tried manually to check after which action I get this error and it was functioning in stm32f7xx_ll_usb.c:
HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src,
uint8_t ch_ep_num, uint16_t len, uint8_t dma)
{
uint32_t USBx_BASE = (uint32_t)USBx;
uint32_t *pSrc = (uint32_t *)src;
uint32_t count32b, i;
if (dma == 0U)
{
count32b = ((uint32_t)len + 3U) / 4U;
for (i = 0U; i < count32b; i++)
{
USBx_DFIFO((uint32_t)ch_ep_num) = *((__packed uint32_t *)pSrc);
pSrc++;
}
}
return HAL_OK;
}
But trying to scroll back in disassembly I notice, that just before Hard Fault program was in this function inside stm32f7xx_hal_hcd.c, in case GRXSTS_PKTSTS_IN:
static void HCD_RXQLVL_IRQHandler(HCD_HandleTypeDef *hhcd)
{
USB_OTG_GlobalTypeDef *USBx = hhcd->Instance;
uint32_t USBx_BASE = (uint32_t)USBx;
uint32_t pktsts;
uint32_t pktcnt;
uint32_t temp;
uint32_t tmpreg;
uint32_t ch_num;
temp = hhcd->Instance->GRXSTSP;
ch_num = temp & USB_OTG_GRXSTSP_EPNUM;
pktsts = (temp & USB_OTG_GRXSTSP_PKTSTS) >> 17;
pktcnt = (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
switch (pktsts)
{
case GRXSTS_PKTSTS_IN:
/* Read the data into the host buffer. */
if ((pktcnt > 0U) && (hhcd->hc[ch_num].xfer_buff != (void *)0))
{
(void)USB_ReadPacket(hhcd->Instance, hhcd->hc[ch_num].xfer_buff, (uint16_t)pktcnt);
/*manage multiple Xfer */
hhcd->hc[ch_num].xfer_buff += pktcnt;
hhcd->hc[ch_num].xfer_count += pktcnt;
if ((USBx_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) > 0U)
{
/* re-activate the channel when more packets are expected */
tmpreg = USBx_HC(ch_num)->HCCHAR;
tmpreg &= ~USB_OTG_HCCHAR_CHDIS;
tmpreg |= USB_OTG_HCCHAR_CHENA;
USBx_HC(ch_num)->HCCHAR = tmpreg;
hhcd->hc[ch_num].toggle_in ^= 1U;
}
}
break;
case GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
break;
case GRXSTS_PKTSTS_IN_XFER_COMP:
case GRXSTS_PKTSTS_CH_HALTED:
default:
break;
}
}
Last few lines from Dissasembly shows this:
0x080018B4 E8BD81F0 POP {r4-r8,pc}
0x080018B8 0000 DCW 0x0000
0x080018BA 1FF8 DCW 0x1FF8
Why it fails? How could I fix it? I do not have much experience with USB protocol.
I will post my walkaround this, but I am not sure why it worked. Solution was to use EXTI0 interrupt instead of just detection if PA0 is high, as I showed I used here:
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
Transmission_function();
I changed it to this:
void EXTI0_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_IRQn 0 */
if(Appli_state == APPLICATION_READY){
USBH_CDC_Transmit(&hUsbHostHS, Buffer, 3);
}
/* USER CODE END EXTI0_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
/* USER CODE BEGIN EXTI0_IRQn 1 */
/* USER CODE END EXTI0_IRQn 1 */
}
I'm trying to program LPC824 microcontroller board ([https://www.switch-science.com/catalog/2265/][1]) with LPCOpen.
I'm using it with LPCLink 2 debugger board.
My goal is to get some information from the "pressure sensor" with an ADC.
My code stops with a HardFault when executing a NVIC_EnableIRQ function(on line: 92).
If I don't use "NVIC interrupt controller" then my code works and I can get value from sensor with ADC.
What I am doing wrong?
Here is my adc.c code:
#include "board.h"
static volatile int ticks;
static bool sequenceComplete = false;
static bool thresholdCrossed = false;
#define TICKRATE_HZ (100) /* 100 ticks per second */
#define BOARD_ADC_CH 2
/**
* #brief Handle interrupt from ADC sequencer A
* #return Nothing
*/
void ADC_SEQA_IRQHandler(void) {
uint32_t pending;
/* Get pending interrupts */
pending = Chip_ADC_GetFlags(LPC_ADC);
/* Sequence A completion interrupt */
if (pending & ADC_FLAGS_SEQA_INT_MASK) {
sequenceComplete = true;
}
/* Threshold crossing interrupt on ADC input channel */
if (pending & ADC_FLAGS_THCMP_MASK(BOARD_ADC_CH)) {
thresholdCrossed = true;
}
/* Clear any pending interrupts */
Chip_ADC_ClearFlags(LPC_ADC, pending);
}
/**
* #brief Handle interrupt from SysTick timer
* #return Nothing
*/
void SysTick_Handler(void) {
static uint32_t count;
/* Every 1/2 second */
if (count++ == TICKRATE_HZ / 2) {
count = 0;
Chip_ADC_StartSequencer(LPC_ADC, ADC_SEQA_IDX);
}
}
/**
* #brief main routine for ADC example
* #return Function should not exit
*/
int main(void) {
uint32_t rawSample;
int j;
SystemCoreClockUpdate();
Board_Init();
/* Setup ADC for 12-bit mode and normal power */
Chip_ADC_Init(LPC_ADC, 0);
Chip_ADC_Init(LPC_ADC, ADC_CR_MODE10BIT);
/* Need to do a calibration after initialization and trim */
Chip_ADC_StartCalibration(LPC_ADC);
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC))) {
}
/* Setup for maximum ADC clock rate using sycnchronous clocking */
Chip_ADC_SetClockRate(LPC_ADC, ADC_MAX_SAMPLE_RATE);
Chip_ADC_SetupSequencer(LPC_ADC, ADC_SEQA_IDX,
(ADC_SEQ_CTRL_CHANSEL(BOARD_ADC_CH) | ADC_SEQ_CTRL_MODE_EOS));
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SWM);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC2);
Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_SWM);
/* Setup threshold 0 low and high values to about 25% and 75% of max */
Chip_ADC_SetThrLowValue(LPC_ADC, 0, ((1 * 0xFFF) / 4));
Chip_ADC_SetThrHighValue(LPC_ADC, 0, ((3 * 0xFFF) / 4));
Chip_ADC_ClearFlags(LPC_ADC, Chip_ADC_GetFlags(LPC_ADC));
Chip_ADC_EnableInt(LPC_ADC,
(ADC_INTEN_SEQA_ENABLE | ADC_INTEN_OVRRUN_ENABLE));
Chip_ADC_SelectTH0Channels(LPC_ADC, ADC_THRSEL_CHAN_SEL_THR1(BOARD_ADC_CH));
Chip_ADC_SetThresholdInt(LPC_ADC, BOARD_ADC_CH, ADC_INTEN_THCMP_CROSSING);
/* Enable ADC NVIC interrupt */
NVIC_EnableIRQ(ADC_SEQA_IRQn);
Chip_ADC_EnableSequencer(LPC_ADC, ADC_SEQA_IDX);
SysTick_Config(SystemCoreClock / TICKRATE_HZ);
/* Endless loop */
while (1) {
/* Sleep until something happens */
__WFI();
if (thresholdCrossed) {
thresholdCrossed = false;
printf("********ADC threshold event********\r\n");
}
/* Is a conversion sequence complete? */
if (sequenceComplete) {
sequenceComplete = false;
/* Get raw sample data for channels 0-11 */
for (j = 0; j < 12; j++) {
rawSample = Chip_ADC_GetDataReg(LPC_ADC, j);
/* Show some ADC data */
if (rawSample & (ADC_DR_OVERRUN | ADC_SEQ_GDAT_DATAVALID)) {
printf("Chan: %d Val: %d\r\n", j, ADC_DR_RESULT(rawSample));
printf("Threshold range: 0x%x ",
ADC_DR_THCMPRANGE(rawSample));
printf("Threshold cross: 0x%x\r\n",
ADC_DR_THCMPCROSS(rawSample));
printf("Overrun: %s ",
(rawSample & ADC_DR_OVERRUN) ? "true" : "false");
printf("Data Valid: %s\r\n\r\n",
(rawSample & ADC_SEQ_GDAT_DATAVALID) ?
"true" : "false");
}
}
}
}
}
Hard fault usually means that you try to execute code outside allowed addresses. If you have not registered the interrupt in the vector table but enabled it, the MCU will jump to whatever address that's written there instead, after which the program crashes.
How to fix that depends on tool chain. Assuming LPCXpresso, you have several options to set up libraries (I don't know about LPCOpen specifically), so where to find the vector table is different from case to case. However, this works quite similar on most MCUs, ARM or not. Somewhere in a "crt start-up" file you should have something along the lines of this:
void (* const g_pfnVectors[])(void) = ...
This is an array of function pointers which will be the vector table allocated in memory at address 0 on Cortex M. You have to place your function at the relevant interrupt vector. For example it may say something like
PIN_INT0_IRQHandler, // PIO INT0
If that's the interrupt you should implement, then you replace that line:
#include "my_irq_stuff.h"
...
void (* const g_pfnVectors[])(void) =
...
my_INT0, // PIO INT0
Assuming my_irq_stuff.h contains the function prototype my_INT0 for the interrupt service routine. The actual routine should be implemented in the corresponding .c file.
I'm writing a small embedded program, where I send some commands over uart to the atmega328p chip. The commands start with the character $ and end with the character # (so I know when to perform the parsing). Upon receiving the command I parse it and turn the device on (COMMAND:TURN_ON_I1) or off (COMMAND:TURN_OFF_I1). The application currently looks like this:
// ------- Defines -------- //
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#include <stdio.h>
#include <string.h>
#include "pinDefines.h"
#include "USART.h"
#define RECEIVE_BUFFER_SIZE 100
// Control output value
#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
// Set pin mode (input or output)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)
// The DDRD port contains only two pins:
#define REL_BTN_SIM_2 PD6 // PD6 = REL_BTN_SIM_2
void initUSART(void) { /* requires BAUD */
UBRR0H = UBRRH_VALUE; /* defined in setbaud.h */
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
/* Enable USART transmitter/receiver */
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); /* 8 data bits, 1 stop bit */
}
void printString(const char myString[]) {
uint8_t i = 0;
while (myString[i]) {
transmitByte(myString[i]);
i++;
}
}
uint8_t receiveByte(void) {
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait for incoming data */
return UDR0; /* return register value */
}
void transmitByte(uint8_t data) {
/* Wait for empty transmit buffer */
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = data; /* send data */
}
int main(void) {
//$COMMAND:TURN_ON_I1#
//$COMMAND:TURN_OFF_I1#
char s[RECEIVE_BUFFER_SIZE];
char readSerialCharacter;
// -------- Inits --------- //
DDRB = 0b00000111;
DDRC = 0b00001000;
DDRD = 0b11000000;
initUSART();
// ------ Event loop ------ //
while (1) {
printString("Waiting for the start of string (char $).\r\n");
do { } while ( receiveByte() != '$'); // Wait for start of string.
// Fill the array until the end of transmission is received
int i=0;
do {
// If nearing end of buffer, don't fill the buffer and exit the loop
if(i<RECEIVE_BUFFER_SIZE-1){
readSerialCharacter = receiveByte();
s[i++] = readSerialCharacter;
}else
break;
} while (readSerialCharacter != '#'); // Wait for end of string.
s[i] ='\0'; // Terminate the string
printString("The whole received command:\r\n");
printString(s);
printString("\r\n");
// Other commands (temperature, relay control)
// REL_BTN_SIM_2
else if(strstr(s, "COMMAND:TURN_ON_I1") != NULL)
{
printString("Will set I1 on!");
output_high(PORTD, REL_BTN_SIM_2);
}
else if(strstr(s, "COMMAND:TURN_OFF_I1") != NULL)
{
printString("Will set I1 off!");
output_low(PORTD, REL_BTN_SIM_2);
}
else
printString("Unknown command.\r\n");
// Clear the buffer
memset(s,'\0', sizeof(s));
}
/* End event loop */
return (0);
}
I noticed that after I send a command around seven or eight times (or more), the serial communication is interrupted or that the command is executed with a delay. I can also see, that the debug strings "Will set I1 off!", "Will set I1 on!" are printed, but the state of the outputs are not changed (or are changed with a delay of a couple of seconds).
I was wondering if someone would know, what I'm doing wrong?
Thanks.
You have a nice definition of set_output(), but you are not using it. So I suspect that you never enabled the output driver. By setting the port register, you just enable the weak pull-up. Maybe that is not strong enough to switch on your relay driver fast. Do you have a capacitor in that driver circuit?
(also asked on SE: Electrical Engineering)
In my application, I've set up a STM32F4, SD-Card and USB-CDC (all with CubeMX).
Using a PC, I send commands to the STM32, which then does things on the SD-Card.
The commands are handled using a "communicationBuffer" (implemented by me) which waits for commands over USB, UART, ... and sets a flag, when a \n character was received. The main loop polls for this flag and if it is set, a parser handles the command. So far, so good.
When I send commands via UART, it works fine, and I can get a list of the files on the SD-Card or perform other access via FatFs without a problem.
The problem occurs, when I receive a command via USB-CDC. The parser works as expected, but FatFs claims FR_NO_FILESYSTEM (13) in f_opendir.
Also other FatFs commands fail with this error-code.
After one failed USB-command, commands via UART will also fail. It seems, as if the USB somehow crashes the initialized SD-Card-driver.
Any idea how I can resolve this behaviour? Or a starting point for debugging?
My USB-Implementation:
I'm using CubeMX, and therefore use the prescribed way to initialize the USB-CDC interface:
main() calls MX_USB_DEVICE_Init(void).
In usbd_conf.c I've got:
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = OTG_FS_DM_Pin|OTG_FS_DP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 7, 1);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
and the receive-process is implemented in usbd_cdc_if.c as follows:
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
mRootObject->mUsbBuffer->fillBuffer(Buf, *Len);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
fillBuffer is implemented as follows (I use the same implementation for UART and USB transfer - with separate instances for the respective interfaces. mBuf is an instance-variable of type std::vector<char>):
void commBuf::fillBuffer(uint8_t *buf, size_t len)
{
// Check if last fill has timed out
if(SystemTime::getMS() - lastActionTime > timeout) {
mBuf.clear();
}
lastActionTime = SystemTime::getMS();
// Fill new content
mBuf.insert(mBuf.end(), buf, buf + len);
uint32_t done = 0;
while(!done) {
for(auto i = mBuf.end() - len, ee = mBuf.end(); i != ee; ++i) {
if(*i == '\n') {
newCommand = true;
myCommand = std::string((char*) &mBuf[0],i - mBuf.begin() + 1);
mBuf.erase(mBuf.begin(), mBuf.begin() + (i - mBuf.begin() + 1));
break;
}
}
done = 1;
}
}
I resolved the problem:
In usb_cdc_if.c the #define APP_RX_DATA_SIZE was set to 4 (for some unknown reason). As this is lower than the packet size, incoming packets of a larger size than 4 bytes were overwriting my memory.
It happened, that the following portion of my memory was the FATFS* FatFs[] pointer-list to the initialized FATFS-Filesystem structs.
So subsequently the address to this struct was overwritten, when a command of 5 or more bytes arrived.
Phew, that was a tough one.