CMSIS UART driver for Tiva launchpad - cortex-m3

I'm trying to make the CMSIS UART driver example work on my Tiva launchpad: https://www.keil.com/pack/doc/CMSIS/Driver/html/group__usart__interface__gr.html
My code is the following:
#include "Driver_USART.h"
#include <cmsis_os.h> /* ARM::CMSIS:RTOS:Keil RTX */
#include <stdio.h>
#include <string.h>
/* USART Driver */
extern ARM_DRIVER_USART Driver_UART0;
void myUART_Thread(void const *argument);
osThreadId tid_myUART_Thread;
void myUSART_callback(uint32_t event)
{
switch (event)
{
case ARM_USART_EVENT_RECEIVE_COMPLETE:
case ARM_USART_EVENT_TRANSFER_COMPLETE:
case ARM_USART_EVENT_SEND_COMPLETE:
case ARM_USART_EVENT_TX_COMPLETE:
/* Success: Wakeup Thread */
osSignalSet(tid_myUART_Thread, 0x01);
break;
case ARM_USART_EVENT_RX_TIMEOUT:
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
case ARM_USART_EVENT_RX_OVERFLOW:
case ARM_USART_EVENT_TX_UNDERFLOW:
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
}
}
/* CMSIS-RTOS Thread - UART command thread */
void myUART_Thread(const void* args)
{ // static ARM_DRIVER_USART * USARTdrv = &Driver_UART0;
ARM_DRIVER_VERSION version;
ARM_USART_CAPABILITIES drv_capabilities;
char cmd;
/*Initialize the USART driver */
Driver_UART0.Initialize(myUSART_callback);
/*Power up the USART peripheral */
Driver_UART0.PowerControl(ARM_POWER_FULL);
/*Configure the USART to 4800 Bits/sec */
Driver_UART0.Control(ARM_USART_MODE_ASYNCHRONOUS |
ARM_USART_DATA_BITS_8 |
ARM_USART_PARITY_NONE |
ARM_USART_STOP_BITS_1 |
ARM_USART_FLOW_CONTROL_NONE, 4800);
/* Enable Receiver and Transmitter lines */
Driver_UART0.Control (ARM_USART_CONTROL_TX, 1);
Driver_UART0.Control (ARM_USART_CONTROL_RX, 1);
Driver_UART0.Send("\nPress Enter to receive a message", 34);
osSignalWait(0x01, osWaitForever);
while (1)
{
Driver_UART0.Receive(&cmd, 1); /* Get byte from UART */
osSignalWait(0x01, osWaitForever);
if (cmd == 13) /* CR, send greeting */
{
Driver_UART0.Send("\nHello World!", 12);
osSignalWait(0x01, osWaitForever);
}
}
}
int main(void){
osKernelInitialize (); // initialize CMSIS-RTOS
osKernelStart (); // start thread execution
}
Now I don't expect it to work right away, however my builder starts complaining at the linkig phase that Driver_UART0 is undefined:
.\Objects\bl.axf: Error: L6218E: Undefined symbol Driver_UART0 (referred from blinky.o).
Any idea what I could do to fix this?
Thank you,
Botond

Unfortunately TI doesn't support the CMSIS Driver API at this time. I've have been working on creating this API on my own and have a working example in my GitHub repository:
This is the example:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/blob/master/CMSIS-Driver-Examples/UART-CMSIS-Driver/main.c
You'll have to set your include path in project options to this folder:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/tree/master/inc
Also add the files in this folder to your project:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/tree/master/src
and also this file
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/blob/master/startup_LM4F.s

Related

What could be the cause of a constant voltage offset on the STM32L041G6 adc

I have been testing the ADC on my STM32L0 with 12-bit resolution and for some reason, every measurement I take has a 0.048 V offset. I've tried a different supply voltage (from 1.8 V to 3.3 V), but the offset stays there.
This offset starts at the first count, when I apply 0 V to the ADC input and read 130 counts (with 1.8 V Vdda) it stays for the whole range.
My voltage source is very stable and I've verified my ADC input voltage and the STM32 supply voltage with 3 different accurate pieces of measuring equipment.
The voltage is applied to the circuit through a bnc connector connected to a signal generator, in my case I use the Dc output option with high impedance on a normal signal generator. As for the code, I used stm32cubemx to generate some of the code but I didn't use any calibration function, so unless it is enabled by default, I dont do any calibration. As for the adc pin, I just used cubemx to set-up IN0 as an adc input and didn't touch anything else. Then to read the adc, I used 2 hal function "HAL_ADC_START()" and "HAL_ADC_PollForConversion()". Then I send the result to be read by uart.
The board I'm using is a custom PCB, but I only placed the STM32 and some passive components to get it to function (coupling capacitors and reset pull-up).
I also tested multiple boards with different configurations and I always get this offset voltage. I outputed Vrefint and measured it, and I got 1.225 V (specified is 1.224 V) so the ADC reference seems fine too.
I was wondering if someone has a suggestion on the possible cause of this offset error? I've been looking for a solution but I'm having no luck so far, so I would really appreciate the input of more experienced developers.
Here is a simplified version of the code I use :
/* USER CODE BEGIN Header */
/**
******************************************************************************
* #file adc.c
* #brief This file provides code for the configuration
* of the ADC instances.
******************************************************************************
* #attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
ADC_HandleTypeDef hadc;
/* ADC init function */
void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = ENABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC GPIO Configuration
PA0-CK_IN ------> ADC_IN0
*/
GPIO_InitStruct.Pin = SENSOR_ANALOG_IN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(SENSOR_ANALOG_IN_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC GPIO Configuration
PA0-CK_IN ------> ADC_IN0
*/
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
And here is the main file where I call the function to read the adc and send it to uart to be read :
#include "main.h"
#include "gpio.h"
#include "adc.h"
#include "usart.h"
#include <stdlib.h>
extern "C"
void execute(){
HAL_Delay(10);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
const auto value = HAL_ADC_GetValue(&hadc);
char buffer [5];
itoa(value, buffer, 10);
HAL_UART_Transmit(&huart2, reinterpret_cast<uint8_t*>(buffer), 5, HAL_MAX_DELAY);
}
Here is the schematic, as I said, the test circuit only had the mcu and some passive components : https://im.ge/i/FbQPIp

How does a receiving process actually receive a process from a sender process via message queues?

I'm working on a project with a sender and receiver process using shared memory and a message queue. For the life of me, I cannot get the receiver process to receive the file, but i can't figure out what's actually going wrong. It seems like it's actually not getting the file or it's just stuck in an infinite loop.
So I understand that using a message queue, you need to attach to the message queue, using the message queue id. Then when you send a message, it goes to the message queue, and the receiver can receive it from that message queue. However that's what i'm doing (i think) in my code, but as mentioned before the receiver process doesn't seem to get the file. So am I wrong in how a process receives a message and thus implementing the code wrong? Any help would be greatly appreciated, as my professor just kinda threw us into this with no explanation on how to do this, so i've been trying to teach myself. This is what i have for my code:
message.h:
#include <stdio.h>
/* The information type */
#define SENDER_DATA_TYPE 1
/* The done message */
#define RECV_DONE_TYPE 2
/**
* The message structure
*/
struct message
{
/* The message type */
long mtype;
/* How many bytes in the message */
int size;
/**
* Prints the structure
* #param fp - the file stream to print to
*/
void print(FILE *fp)
{
fprintf(fp, "%ld %d", mtype, size);
}
};
Sender.cpp:
#include <iostream>
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h" /* For the message struct */
/* The size of the shared memory chunk */
#define SHARED_MEMORY_CHUNK_SIZE 1000
/* The ids for the shared memory segment and the message queue */
int shmid, msqid;
/* The pointer to the shared memory */
void* sharedMemPtr;
/**
* Sets up the shared memory segment and message queue
* #param shmid - the id of the allocated shared memory
* #param msqid - the id of the shared memory
*/
void init(int& shmid, int& msqid, void*& sharedMemPtr)
{
std::cout<<"Creating key"<<std::endl;
key_t key = ftok("keyfile.txt", 'a');
std::cout<<"Key created"<<std::endl;
shmid =shmget(key,SHARED_MEMORY_CHUNK_SIZE, 0644|IPC_CREAT);
std::cout<<"allocated shared memory"<<std::endl;
sharedMemPtr = shmat(shmid,NULL,0);
std::cout<<"Attached to shared memory"<<std::endl;
msqid = msgget(key,0644|IPC_CREAT);
std::cout<<"Attahed to message queue"<<std::endl;
}
void cleanUp(const int& shmid, const int& msqid, void* sharedMemPtr)
{
/* TODO: Detach from shared memory */
shmdt(sharedMemPtr);
}
/**
* The main send function
* #param fileName - the name of the file
*/
void send(const char* fileName)
{
/* Open the file for reading */
FILE* fp = fopen(fileName, "r");
/* A buffer to store message we will send to the receiver. */
message sndMsg;
/* A buffer to store message received from the receiver. */
message rcvMsg;
/* Was the file open? */
if(!fp)
{
perror("fopen");
exit(-1);
}
/* Read the whole file */
while(!feof(fp))
{
if((sndMsg.size = fread(sharedMemPtr, sizeof(char), SHARED_MEMORY_CHUNK_SIZE, fp)) < 0)
{
perror("fread");
exit(-1);
}
/* TODO: Send a message to the receiver telling him that the data is ready
* (message of type SENDER_DATA_TYPE)
*/
sndMsg.mtype = SENDER_DATA_TYPE;
sndMsg.size = 0;
msgsnd(msqid,&sndMsg,sizeof(sndMsg),0);
std::cout<<"Sent data ready message"<<std::endl;
/* TODO: Wait until the receiver sends us a message of type RECV_DONE_TYPE telling us
* that he finished saving the memory chunk.
*/
std::cout<<"Waiting for reciever message"<<std::endl;
msgrcv(msqid,&rcvMsg,0,RECV_DONE_TYPE,0);
std::cout<<"Message received"<<std::endl;
}
/** TODO: once we are out of the above loop, we have finished sending the file.
* Lets tell the receiver that we have nothing more to send. We will do this by
* sending a message of type SENDER_DATA_TYPE with size field set to 0.
*/
sndMsg.size =0;
sndMsg.mtype = SENDER_DATA_TYPE;
std::cout<<"Sending empty message"<<std::endl;
msgsnd(msqid,&sndMsg,sizeof(sndMsg),0);
std::cout<<"Empty message sent"<<std::endl;
/* Close the file */
fclose(fp);
}
int main(int argc, char** argv)
{
/* Check the command line arguments */
if(argc < 2)
{
fprintf(stderr, "USAGE: %s <FILE NAME>\n", argv[0]);
exit(-1);
}
/* Connect to shared memory and the message queue */
init(shmid, msqid, sharedMemPtr);
/* Send the file */
send(argv[1]);
/* Cleanup */
cleanUp(shmid, msqid, sharedMemPtr);
return 0;
}
Receiver.cpp:
#include <iostream>
#include <sys/shm.h>
#include <sys/msg.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h" /* For the message struct */
/* The size of the shared memory chunk */
#define SHARED_MEMORY_CHUNK_SIZE 1000
/* The ids for the shared memory segment and the message queue */
int shmid, msqid;
/* The pointer to the shared memory */
void *sharedMemPtr;
/* The name of the received file */
const char recvFileName[] = "recvfile";
/**
* Sets up the shared memory segment and message queue
* #param shmid - the id of the allocated shared memory
* #param msqid - the id of the shared memory
* #param sharedMemPtr - the pointer to the shared memory
*/
void init(int& shmid, int& msqid, void*& sharedMemPtr)
{
std::cout<<"Creating key"<<std::endl;
key_t key = ftok("keyfile.txt", 'a');
shmid =shmget(key,SHARED_MEMORY_CHUNK_SIZE, 0644 | IPC_CREAT);
std::cout<<"allocated shared memory"<<std::endl;
sharedMemPtr = shmat(shmid,NULL,0);
std::cout<<"Attached to shared memory"<<std::endl;
msqid = msgget(key,0644 | IPC_CREAT);
std::cout<<"Attahed to message queue"<<std::endl;
}
void mainLoop()
{
/* The size of the mesage */
int msgSize =0;
/* Open the file for writing */
FILE* fp = fopen(recvFileName, "w");
/* Error checks */
if(!fp)
{
perror("fopen");
exit(-1);
}
std::cout<<"Waiting for message"<<std::endl;
/* TODO: Receive the message and get the message size. The message will
* contain regular information. The message will be of SENDER_DATA_TYPE
* (the macro SENDER_DATA_TYPE is defined in msg.h). If the size field
* of the message is not 0, then we copy that many bytes from the shared
* memory region to the file. Otherwise, if 0, then we close the file and
* exit.
* Keep receiving until the sender set the size to 0, indicating that
* there is no more data to send
*/
while(msgSize != 0){
message recvdMsg;
msgrcv(msqid,&recvdMsg,sizeof(recvdMsg),SENDER_DATA_TYPE,0);
msgSize = recvdMsg.size;
std::cout<<"Entering main loop"<<std::endl;
/* If the sender is not telling us that we are done, then get to work */
if(msgSize != 0)
{
/* Save the shared memory to file */
if(fwrite(sharedMemPtr, sizeof(char), msgSize, fp) < 0)
{
perror("fwrite");
}
/* TODO: Tell the sender that we are ready for the next file chunk.
* I.e. send a message of type RECV_DONE_TYPE (the value of size field
* does not matter in this case).
*/
message sentMsg;
sentMsg.mtype = RECV_DONE_TYPE;
std::cout<<"Ready for next file chunk"<<std::endl;
msgsnd(msqid,&sentMsg,0,0);
std::cout<<"Ready message sent"<<std::endl;
}
/* We are done */
else
{
/* Close the file */
fclose(fp);
}
}
}
void cleanUp(const int& shmid, const int& msqid, void* sharedMemPtr)
{
printf("Detaching from shared memory\n");
shmdt(sharedMemPtr);
printf("Deallocating shared memory chunk\n");
shmctl(shmid,IPC_RMID,NULL);
printf("deallocating message queue\n");
msgctl(msqid,IPC_RMID,NULL);
}
void ctrlCSignal(int signal)
{
/* Free system V resources */
cleanUp(shmid, msqid, sharedMemPtr);
}
int main(int argc, char** argv)
{
/* TODO: Install a signal handler
* In a case user presses Ctrl-c your program should delete message
* queues and shared memory before exiting. You may add the cleaning functionality
* in ctrlCSignal().
*/
signal(SIGINT, ctrlCSignal);
/* Initialize */
init(shmid, msqid, sharedMemPtr);
/* Go to the main loop */
mainLoop();
/** TODO: Detach from shared memory segment, and deallocate shared memory and message queue (i.e. call cleanup) **/
std::cout<<"Cleaning up"<<std::endl;
cleanUp(shmid, msqid, sharedMemPtr);
std::cout<<"Finished"<<std::endl;
return 0;
}

USART with STM32F373VCt6

I'm new here. I've just started learning STM32F373 with STM32F373VCT6. I use CMSIS to configure UART1. that seems there are no any error with my code. But when I use PL2303 to convert serial-usb for connecting with PC. I don't receive anything.
Here is my code. Can anyone help me to find your mistake?
/* Includes ------------------------------------------------------------------*/
#include "stm32f37x.h"
#include"main.h"
uint8_t ledVal = 0;
static __IO uint32_t TimingDelay;
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define BSRR_VAL 0x0003
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
/* Private function prototypes -----------------------------------------------*/
uint8_t SendChar (uint8_t ch);
void GPIO_Config(void);
void USART1_Config(void);
uint8_t GetChar (void);
int main(void)
{
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f37x.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f37x.c file
*/
//SystemInit();
/* GPIOC Periph clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
/* Configure PC0 and PC1 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Config();
USART1_Config();
if (SysTick_Config(SystemCoreClock / 1000))
while (1);
while (1)
{
/* Set PC0 and PC1 */
GPIO_WriteBit(GPIOC , GPIO_Pin_13 , (ledVal) ? Bit_SET : Bit_RESET);
ledVal = 1 - ledVal;
SendChar('f');
Delay(250);
}
}
#ifdef USE_FULL_ASSERT
/**
* #brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* #param file: pointer to the source file name
* #param line: assert_param error line source number
* #retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**
* #}
*/
/**
* #}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
void GPIO_Config(void) {
// PC4 configuration (TX)
RCC->AHBENR |= 1 << 19; // enable GPIOC clock
GPIOC->MODER |= 2 << (4*2); // GPIO_Mode_AF
GPIOC->OTYPER |= 1 << (4*1); // GPIO_OType_OD
GPIOC->OSPEEDR |= 3 << (4*2); // GPIO_Speed_50MHz
GPIOC->PUPDR &= ~(3 << (4*2)); // GPIO_PuPd_NOPULL
GPIOC->AFR[0] |= 7 << (4*4); // AF7
// PC5 configuration (RX)
GPIOC->MODER |= 2 << (5*2); // GPIO_Mode_AF
GPIOC->AFR[0] |= 7 << (5*4); // AF7
}
/* Configuring USART1 */
void USART1_Config(void)
{
RCC->APB2ENR |= RCC_APB2ENR_USART1EN|RCC_APB2ENR_SYSCFGEN; // Enable USART1 clock
USART1->BRR = 72000000/115200;
USART1->CR1 &= ~USART_CR1_OVER8; // Oversampling mode = 16
USART1->CR1 &= ~USART_CR1_M; // Word length = 8 bits
USART1->CR2 &= ~(USART_CR2_STOP_1 | USART_CR2_STOP_0); // one stop bit
USART1->CR1 &= ~USART_CR1_PCE; // No parity
USART1->CR1 |= USART_CR1_UE; // USART enable
USART1->CR1 |= USART_CR1_RE; // Receiver enable
USART1->CR1 |= USART_CR1_TE; // Transmitter enable
}
uint8_t SendChar (uint8_t ch)
{
while (!(USART1->ISR & USART_ISR_TXE));
USART1->TDR = (ch & 0xFF);
return (ch);
}
uint8_t GetChar (void)
{
while (!(USART1->ISR & USART_ISR_RXNE));
return ((uint8_t)(USART1->RDR & 0xFF));
}
/* Delay fuction */
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
/* Decrement */
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
TimingDelay --;
}
Delays after RCC operations are missing
The errata for STM32F373 series says,
2.1.2 Delay after an RCC peripheral clock enabling
Description
A delay between an RCC peripheral clock enable and the effective peripheral enabling
should be taken into account in order to manage the peripheral read/write from/to registers.
This delay depends on the peripheral mapping.
If peripheral is mapped on AHB: the delay is 2 AHB clock cycles after the clock enable bit is
set on the hardware register.
If peripheral is mapped on APB: the delay is 2 APB clock cycles after the clock enable bit is set on the hardware register.
Workarounds
Enable the peripheral clock sometimes before the peripheral read/write register is required.
For AHB peripheral, insert two dummy read to the peripheral register.
For APB peripheral, insert a dummy read to the peripheral register.
Without this delay, a read (or two) immediately following the RCC enable operation could return 0 regardless of the register value, or the first two writes could be ignored by the peripheral.
The library function RCC_AHBPeriphClockCmd() may already include this delay, but check the Stdperiph sources to be sure.
I usually group RCC accesses at the start if possible, starting with the peripheral I'm going to use first.
RCC->AHBENR |= 1 << 19; // enable GPIOC clock // (1)
RCC->APB2ENR |= RCC_APB2ENR_USART1EN|RCC_APB2ENR_SYSCFGEN; // (2)
GPIOC->MODER = whatever; // (3)
// ...
USART1->BRR = divisor;
This way, the read-modify-write operation in (2) would introduce a sufficient delay between enabling GPIOC (1) and using it (3).

STM32 HAL USART receive by interrupt

I have some trouble to receive data over the USART. What I actually want to achieve ist, that I can receive a command over USART with no specific length (only a maximum possible length). So I use the interrupt routine to check each character received, but I somehow still cannot achieve what I want. The routine is called each time I receive a new character, but somehow HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx) does not upgrade in realtime, then I don't see the received character when I check rx_data[pointer], but a few time later it is in the rx_data buffer.
What I have so far:
int pointer =0;
...
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if ( USART1->ISR & UART_IT_TXE) {
}
if ( USART1->ISR & UART_IT_RXNE) {
HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx);
if(rx_data[pointer]=='\0') {
pointer=0;
readCommand(rx_data);
clearBuffer(rx_data,buff_size_rx);
} else {
pointer++;
if(pointer>=buff_size_rx) {
pointer=0;
}
}
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
HAL_UART_Receive_IT() is not meant to be called from an interrupt handler that way, but to initiate receiving a fixed number of bytes via interrupt.
A possible workaround is to check your input buffer after HAL_UART_IRQHandler() completes, i.e. in the /* USER CODE BEGIN USART1_IRQn 1 */ section. When a command is processed, you can reset pRxBuffPtr and RxXferCount in the handle structure to their original values to start from the start of the buffer again.
Another horrible possible workaround would be to call HAL_UART_Receive_IT() with a buffer size of 1, and set up a HAL_UART_RxCpltCallback() handler that checks the received byte each time, and calls HAL_UART_Receive_IT() again when necessary.
Of course you could do it without HAL, as PeterJ and others (always) suggest.
You've already implemented pin and interrupt setup, leave them unchanged at first.
Calculate the UART->BRR value according to the reference manual, or copy the relevant code from hal.
set UART->CR1=USART_CR1_RE|USART_CR1_TE|USART_CR1_UE|USART_CR1_RXNEIE; Now, you are getting interrupts.
In the interrupt function, read UART->SR into a temporary variable, and examine it.
Read UART->DR when there is a received byte waiting, do the error handling otherwise (later).
Get rid of the rest of the HAL calls when the above is working.
Interrupt response and processing time is often critical in embedded applications, and the HAL just wastes a lot of that.
The normal HAL library is not useful for continuous reception or commands with different length.
If you have the complete HAL package installed, you could look at the examples for the LowLevel interface.
Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous
The main thing is to set you usart to continuous reception:
void Configure_USART(void) {
/* (1) Enable GPIO clock and configures the USART pins *********************/
/* Enable the peripheral clock of GPIO Port */
USARTx_GPIO_CLK_ENABLE();
/* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_TX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);
/* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_RX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);
/* (2) NVIC Configuration for USART interrupts */
/* - Set priority for USARTx_IRQn */
/* - Enable USARTx_IRQn */
NVIC_SetPriority(USARTx_IRQn, 0);
NVIC_EnableIRQ(USARTx_IRQn);
/* (3) Enable USART peripheral clock and clock source ***********************/
USARTx_CLK_ENABLE();
/* (4) Configure USART functional parameters ********************************/
/* TX/RX direction */
LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);
/* 8 data bit, 1 start bit, 1 stop bit, no parity */
LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
/* No Hardware Flow control */
/* Reset value is LL_USART_HWCONTROL_NONE */
// LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);
/* Oversampling by 16 */
/* Reset value is LL_USART_OVERSAMPLING_16 */
// LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);
/* Set Baudrate to 115200 using APB frequency set to 100000000/APB_Div Hz */
/* Frequency available for USART peripheral can also be calculated through LL RCC macro */
/* Ex :
Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or
LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance
In this example, Peripheral Clock is expected to be equal to
100000000/APB_Div Hz => equal to SystemCoreClock/APB_Div
*/
LL_USART_SetBaudRate(USARTx_INSTANCE, SystemCoreClock/APB_Div, LL_USART_OVERSAMPLING_16, 115200);
/* (5) Enable USART *********************************************************/
LL_USART_Enable(USARTx_INSTANCE);
}
The USART IT Handler should look like
void USARTx_IRQHandler(void)
{
/* Check RXNE flag value in SR register */
if(LL_USART_IsActiveFlag_RXNE(USARTx_INSTANCE) && LL_USART_IsEnabledIT_RXNE(USARTx_INSTANCE))
{
/* RXNE flag will be cleared by reading of DR register (done in call) */
/* Call function in charge of handling Character reception */
USART_CharReception_Callback();
}
else
{
/* Call Error function */
Error_Callback();
}
}
The last thing to set up is the Callback
void USART_CharReception_Callback(void);
Where you could put the bytes into an buffer and handle it in the main loop or where you want.
Since I stumbled over the problem today and could not find a good solution to it, I like to present a very simple one, using most of the HAL but avoiding the problems described...
Short verison of my approach is:
In the last user code section (for the appropriate USART instance, if using more than one) of void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle), enable the IRQ with:
__HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);
Then put the desired code in your interrupt function:
void USART3_IRQHandler(void) {
/* USER CODE BEGIN USART3_IRQn 0 */
CallMyCodeHere();
return; // To avoid calling the default HAL handler at all
// (in case you want to save the time)
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3); // This is the CubeMX generated HAL handler
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
Don't use HAL_UART_Receive_IT anywhere, it will disable the IRQ and you need to re-enable it, if you want to get called with every reception.
The long version could be found in my post here...
Here is the full example of receiving data and idle line detection by interrupts:
Enable the receive interrupts and idle line detection in main.c:
/* USER CODE BEGIN USART2_Init 2 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); // enable receive intterupts
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // enable idle line detection
/* USER CODE END USART2_Init 2 */
Sort out the idle line event from within USARTx_IRQHandler in stm32f4xx_it.c:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // taken from https://electronics.stackexchange.com/questions/471272/setting-up-stm32-timer-for-uart-idle-detection#comment1353999_480556
uart2_idleHandler();
} else {
uart2_handler();
}
return;
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
Testing:
Create the following handlers:
char my_uart_buffer[256];
int my_uart_buffer_index = 0;
void uart2_handler(void){
char buff;
HAL_UART_Receive (&huart2, (uint8_t *)&buff, 1, 400);
my_uart_buffer[my_uart_buffer_index++] = buff;
}
void uart2_idleHandler(){
my_uart_buffer_index = 0;
}
Open a serial port client in your PC, setup as 115200 baud, 8N1.
Set a breakpoint to uart2_idleHandler().
Send "Hello world".
You can examine your buffer by p/c *my_uart_buffer#20 when breakpoint hits.
Example Project
Here is the full example that runs on STM32F407 Discovery board.
You can make it work using HAL! It may not be as elegant as other implementations but it is doable.
You have to create an error handler function and then the function that calls the HAL_UART_RCV_IT must flush the UART RX whenever there is an overrun error.
In addition, I work with two buffers. While the Interrupt is filling one buffer the main loop is emptying the other.
Here it how it is working well for me:
typedef enum
{
UARTREADY = 0,
UARTBUSY = 1,
UARTDATA = 2,
UARTERROR = 3
} enumUartStatus;
while(1){
if(UARTREADY == isUsart3RxReady()){
Usart3RxBuffer((char *)&SOMRxBytesBuffer[use_buffer_index], RCV_BUFFER_BANK_SIZE); // receive bytes in the raw buffer
if(use_buffer_index == RCV_BUFFER_BANK1_INDEX){
use_buffer_index = RCV_BUFFER_BANK2_INDEX;
rxb1_stats++;
}else{
use_buffer_index = RCV_BUFFER_BANK1_INDEX;
rxb2_stats++;
}
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(huart == NULL){
return;
}
if(huart->Instance == USART3){
if(HAL_UART_GetError(huart) == HAL_UART_ERROR_FE){
Usart3Ready = UARTREADY;
}else{
Usart3Ready = UARTERROR;
}
}
}
void Usart3RxBuffer(char *buffer, unsigned short rxbuffersize){
/* Reset transmission flag */
if(Usart3Ready != UARTREADY)
{
return;
}
if(HAL_UART_GetState(&huart3) == HAL_UART_STATE_READY){
/*##-3- Put UART peripheral in reception process ###########################*/
if (HAL_UART_Receive_IT(&huart3, (uint8_t *)buffer, rxbuffersize) != HAL_OK)
{
// TODO: Error_Handler();
DEBUG_TRACE(DEBUG_MSK_MAIN, "UART3 error starting receiver!\r\n");
}else{
// An interrupt HAL_UART_ErrorCallback hit right here !!!!
// There is an overrun error happening here so we have to retry
// this is because we are using the Receive_IT in a continous communication and there is no handshake or flow control
if(Usart3Ready != UARTERROR){
/* Busy waiting to receive bytes */
Usart3Ready = UARTBUSY;
}
}
}
if(Usart3Ready == UARTERROR){
HAL_UART_AbortReceive_IT(&huart3);
Usart3Ready = UARTREADY;
}
}

Cannot mount SD using FatFS on STM32

I'm trying to write to a MicroSD-Card using STM32F405 chip.
The pins are connected correctly and each pin on the MicroSD-Card slot can be written to by using HAL_GPIO_WritePin. (Messured with ossciloscope) I'm using CubeMX to generate the init code for TrueStudio, so hopefully everything is ok there as well. But when I run the following code, f_mount returns FR_DISK_ERR. The MicroSD-Card can be written to and read from. If I use a different Device number, i.e. "1:", I get FR_INVALID_DRIVE
So my question is: what could cause FR_DISK_ERR except a faulty MicroSD-Card?
Here is my code so far:
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();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDIO_SD_Init();
MX_FATFS_Init();
/* USER CODE BEGIN WHILE */
FATFS fileSystem;
FIL testFile;
uint8_t testBuffer[16] = "SD write success";
UINT testBytes;
FRESULT res;
while((res = f_mount(&fileSystem, SD_MOUNT_PATH, 1)) != FR_OK){
printf("%d", res); //used to debug res, only for TrueStudio Debugger
}
uint8_t path[13] = "testfile.txt";
path[12] = '\0';
res = f_open(&testFile, (char*)path, FA_WRITE | FA_CREATE_ALWAYS);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
res = f_write(&testFile, testBuffer, 16, &testBytes);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
res = f_close(&testFile);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}
in MX_FATFS_Init() FATFS_LinkDriver(&SD_Driver, SD_Path) is called and returns 0.
Without the SD_MOUNT_PATH macro, does the f_mount call look like this:
f_mount(&fileSystem, "0:", 1) ?'
Are you saying that f_open, f_write return FR_OK even if f_mount fails?!
FR_DISK_ERR usually means disk_read() or disk_write() failed. Try giving 100ms or 1 sec delay before using f_mount() and between between fatfs function calls.