When I write the following code. Putty prints the text but I can't type anything below ..
For example :
First he does a printf --> who are you ?
Then I do a fgets where I normally most type something , that doensn't work.
At last he does another printf where he say ; good to meet you ...
int main(void)
{
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
char name[10];
printf("Who are you? \n");
fgets(name,10,stdin);
printf("Good to meet you, %s.\n",name);
return(0);
}
can anyone help me ?
thanks !
You have overwritten the implemnetation of fputs in your source code. So your linker doesn't need to lookup the library implementation of that function. With your implementation of fputc you have connected the stdio-output-path to your UART transmitter.
You need to define the corresponding input-path. This could be done by implementing a fgetc function. In that can the library function fgets can call your function, polling the UART receiver.
The default implementation of stdio in embedded environment is highly vendor dependent. It can be connected to
the JTAG debugger
the microcontroller specific debug/console ports
nothing.
Therefore you are responsible to make the right implemantation of that connection. This can be more complecated than the fputc implementation because you have to check, if there is data received at the UART at the time of call.
Related
I want to receive UART interrupts, but shouldn't the basic structure of the interrupts be made like that?
Sometimes it doesn't work and sometimes it prints out printf ("test\r\n") but sometimes it doesn't print out the received data, but I don't know what the cause is Can you tell me the solution?
I tried to pull the HAL_UART_Receive_IT door out while, but it didn't work
I don't think it's a connection problem since I'm sending a message
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 */
/* USER CODE END USART1_IRQn 1 */
}
When I entered this code, there was an error
Error[Li006]: duplicate definitions for "USART1_IRQHandler";
The code below is the code that is currently experiencing a problem
uint8_t received_msg[10];
char check_msg[10];
//UART read
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart -> Instance == USART1){
printf("test\r\n");
HAL_UART_Receive_IT(&huart1, (uint8_t*)received_msg, sizeof(received_msg));
}
}
/* USER CODE END 0 */
/**
* #brief The application entry point.
* #retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_TIM6_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
calibrate(0.01, 4);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
rcvStat = HAL_UART_Receive_IT(&huart1, received_msg, sizeof(received_msg));
if(rcvStat != HAL_OK){
printf("error\r\n");
}
sprintf(check_msg, "%s", (char*)received_msg);
printf("%s\r\n", check_msg);
printf("%d, %d\r\n", timer, flag);
HAL_Delay(timer);
printf("---------------------\r\n");
memset(received_msg, 0, strlen(received_msg));
memset(check_msg, 0, strlen(check_msg));
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Call the RX interrupt once before the while loop and then after you have recieved the data in the ISR. Remove it from the inside of the while.
It seems like in the update loop you are re-issuing the RX_IT call, you should call this once and wait for data to arrive (ISR fires) and then re-issue at that point inside of ISR. Otherwise with a message size greater than one byte, you could re-issue the IT function after it recieved a few bytes and miss some bytes since it requests 10 bytes before the interrupt fires after being issued once.
Your ISR should primarily only be there for moving the RX'd data from one spot, to another for processing.
Printf is a pig and will most likely cause you to miss messages coming in if they are coming in real-time and not a one-off message.
A suggestion, would be to implement some type of circular FIFO queue in the ISR which stuffs the data in a FIFO buffer and that FIFO is accessed in your control loop for parsing, or action. That way, you are reciving each symbol and quickly exiting the interrupt and all data should be there for processing. I have had great success with this method in the past.
Here's what I have: PCA9555 chip that has inputs, if a signal state on the input changes, the interrupt signal is sent. Then I can read the chip via I2C to check the inputs.
What I need - when a pin changes state, I need to read the chip, check which pin changed state and notify my app about it.
So I have an interrupt and the interrupt handler MUST NOT block the MCU.
My obvious choice is using HAL_I2C_Mem_Read_IT(), right?
I made the whole code, tested it. It seemed like it worked... For a while.
Until I added reading the chip like every 100ms.
The code still works, but I see the blinking things stutter, stop blinking for more than a second or even 2. So - it became obvious that HAL_I2C_Mem_Read_IT() BLOCKS my interrupt that causes the MCU to freeze.
I checked the HAL sources and found this:
static HAL_StatusTypeDef I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout,
uint32_t Tickstart)
{
I2C_TransferConfig(hi2c, DevAddress, (uint8_t)MemAddSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* If Memory address size is 8Bit */
if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
{
/* Send Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* If Memory address size is 16Bit */
else
{
/* Send MSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(MemAddress);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Send LSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* Wait until TC flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
As the name I2C_WaitOnTXISFlagUntilTimeout() suggests - it WAITS. Yes, it's a while loop that blocks the executing thread until a flag is set:
static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status,
uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
}
}
return HAL_OK;
}
There are 3 of those lagging functions.
For my application this is a show stopper. It just doesn't work, since it depends on handling the events in real time. Also it has a GUI that freezes when the interrupt handler blocks.
Is there a quick workaround for this? Is it a bug in HAL driver?
Do I have to implement my own non-blocking function? It seems like many, many hours of coding, since the function is non trivial and tightly coupled with the rest of the module.
My idea is to rewrite it and replace while loops with my non-blocking delay function that uses a timer interrupt to continue work after some time passes. To make it more non-trivial, each callback would have to receive the necessary state data to continue. Then the state machine to figure out where we are with my I2C_RequestMemoryRead_ process. At the end I just call the registered callback and done. It should work truly non-blocking...
But I have deadlines. Can it be done faster? How is it even possible the HAL "_IT" function BLOCKS the thread with some while loops? It's just wrong! It defeats the entire purpose of an "interrupt mode function". If it blocks, there already IS a blocking version that is simpler.
I solved the problem with hacking the original HAL driver.
https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623
After adding the files to the project the original HAL driver needs to be modified as described in the stm32h7xx_hal_i2c_nb.h comment.
For use with STM32CubeIDE it's best to move the modified driver file to a different location to prevent the IDE from overwriting it.
I left other *IT functions unchanged, but it's very easy to add similar modifications to all remaining functions.
The hacked driver was tested in real world application on my STM32H747I-DISCO board with PCA9555 16-bit I/O expander.
For the test I spammed the input with random noise signals that just crashed the original driver. Here it works, doesn't block other threads, the GUI works with full speed.
I am extremely disconcerted.
I'm making a remote controlled machine using a pi pico to drive the motors and read some sensors, and a raspberry pi 4 to send commands to the pi pico via serial and host the web interface.
The following code seems to work... but... If I remove the if with uart_is_writable coding and its content it doesn't work. Does anyone have any idea why?
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
//DEFINES
#define UART_ID uart0
#define BAUD_RATE 19200
#define DATA_BITS 8
#define STOP_BITS 1
#define PARITY UART_PARITY_NONE
#define UART_TX_PIN 0
#define UART_RX_PIN 1
#define LED_PIN PICO_DEFAULT_LED_PIN
static int chars_rxed = 0;
volatile char uCommand[32] = {0, 0};
void on_uart_rx(void) {
char tmp_string[] = {0, 0};
new_command = true;
while (uart_is_readable(UART_ID)) {
uint8_t ch = uart_getc(UART_ID);
tmp_string[0] = ch;
strcat(uCommand, tmp_string);
if(uart_is_writable(UART_ID)){
uart_putc(UART_ID, '-');
uart_puts(UART_ID, uCommand);
uart_putc(UART_ID, '-');
}
chars_rxed++;
}
}
int main(){
uart_init(UART_ID, BAUD_RATE);
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
uart_set_hw_flow(UART_ID, false, false);
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
uart_set_fifo_enabled(UART_ID, false);
int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
irq_set_enabled(UART_IRQ, true);
uart_set_irq_enables(UART_ID, true, false);
uart_puts(UART_ID, "\nOK\n");
while (1){
tight_loop_contents();
if(uCommand[0] != 0){
uart_putc(UART_ID, '/');
uart_puts(UART_ID, uCommand);
memset(uCommand, 0, sizeof(uCommand));
}
}
}
The example code you linked to is for a simple tty/echo implementation.
You'll need to tweak it for your use case.
Because Tx interrupts are disabled, all output to the transmitter has to be polled I/O. Also, the FIFOs in the uart are disabled, so only single char I/O is used.
Hence, the uart_is_writable to check whether a char can be transmitted.
The linked code ...
echos back the received char in the Rx ISR. So, it needs to call the above function. Note that if Tx is not ready (i.e. full), the char is not echoed and is dropped.
I don't know whether uart_putc and uart_puts check for ready-to-transmit in this manner.
So, I'll assume that they do not.
This means that if you call uart_putc/uart_puts and the Tx is full, the current/pending char in the uart may get trashed/corrupted.
So, uart_is_writable should be called for any/each char to be sent.
Or ... uart_putc/uart_puts do check and will block until space is available in the uart Tx fifo. For you use case, such blocking is not desirable.
What you want ...
Side note: I have done similar [product/commercial grade] programming on an RPi for motor control via a uart, so some of this is from my own experience.
For your use case, you do not want to echo the received char. You want to append it to a receive buffer.
To implement this, you probably want to use ring queues: one for received chars and one for chars to be transmitted.
I assume you have [or will have] devised some sort of simple packet protocol to send/receive commands. The Rpi sends commands that are (e.g.):
Set motor speed
Get current sensor data
The other side should respond to these commands and execute the desired action or return the desired data.
Both processors probably need to have similar service loops and ISRs.
The Rx ISR just checks for space available in the Rx ring queue. If space is available, it gets a char from the uart and enqueues it. If no Rx char is available in the uart, the ISR may exit.
The base level code service loop should:
Check if the uart Tx can accept another character (via uart_is_writable) and, if so, it can dequeue a char from the Tx ring queue [if available] and send it (via uart_putc). It can loop on this to keep the uart transmitter busy.
Check to see if enough chars are received to form a packet/message from the other side. If there is such a packet, it can service the "request" contained in it [dequeueing the chars to make more space in the Rx ring queue].
If the base level needs to send a message, it should enqueue it to the Tx ring queue. It will be sent [eventually] in the prior step.
Some additional thoughts ...
The linked code only enables Rx interrupts and runs the Tx in polled mode. This may be enough. But, for maximum throughput and lowest latency, you may want to make the Tx interrupt driven as well.
You may also wish the enable the FIFOs in the uart so you can queue up multiple characters. This can reduce the number of calls to the ISR and provide better throughput/latency because the service loop doesn't have to poll so often.
I have a code generated by STMCubeMX where I use the portal PA0 like ADC Input. I'm trying to read this input using HAL Library of STM in C and transmit the value to USB port using CDC. See the main, where i try to get the data and show this:
#include "main.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_ADC1_Init(void);
int main(void)
{
uint8_t buffer[8];
HAL_Init();
SystemClock_Config();
MX_USB_DEVICE_Init();
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
while (1)
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14);
HAL_ADC_PollForConversion(&hadc1, 1000);
sprintf((char*)buffer,"%d\n",(int)HAL_ADC_GetValue(&hadc1));
//HAL_Delay(500);
CDC_Transmit_FS(buffer,8);
}
}
Debugging the code I saw that function "HAL_ADC_PollForConversion(&hadc1, 1000)" never returns "HAL_OK".
In the terminal, a single value appears.
UPDATE
Well, for the code works I have to put the "Start" of ADC inside of infinit loop and a call of "Stop" on the final of loop.
Ps: The Adc is running on continuous conversion mode.
int main(void)
{
uint8_t buffer[8];
HAL_Init();
SystemClock_Config();
MX_USB_DEVICE_Init();
MX_ADC1_Init();
while (1)
{
HAL_ADC_Start(&hadc1);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_14);
HAL_ADC_PollForConversion(&hadc1, 1000);
sprintf((char*)buffer,"%d\n",(int)HAL_ADC_GetValue(&hadc1));
//HAL_Delay(500);
CDC_Transmit_FS(buffer,8);
HAL_ADC_Stop(&hadc1);
}
}
As an addition to the answer in the comments:
The call to HAL_ADC_PollForConversion(&hadc1, 1000); explicitly stops the conversion, even if continuous conversion is activated.
If you you want/must wait for each conversion to get finished, you must restart the ADC with HAL_ADC_Start(&hadc1); after waiting (and reading) for the result.
I just started working with STM32F407VGT Discovery kit and I for the start I wanted to turn on/off LED's on-board. I am using CubeMX to generate initialization code and SystemWorkBench Eclipse to compile and also ST-LINK STM32 for programming my board. In CubeMX I initialized PD12-PD15 ports as output. I used function HAL_GPIO_WritePin to set my LED's ON. After compilation and programming to device, nothing happens... Please give me some advice what I did wrong.
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Problem solved! It seems that ST-LINK is not uploading hex correctly, I used AC6 to upload and everythig seem to work fine