I'm developing code for the NXP LPC1788 microcontroller and lately I've been trying to improve the way that the USB works. My current issue with the USB is that it's configured in slave mode and, due to the high volume of messages that have to be sent and received in normal operation, the CPU spends most of its time handling USB which creates a bottleneck.
I've been trying to resolve this issue by switching from slave-mode configuration to DMA-mode. I've been using example projects to help and I think most of the code I need is in place.
USB initialisation works fine, as before. The problem is that as soon as I try to send a USB message to an endpoint that I configure in DMA-mode (by enabling the appropriate bit in the EpDMAEn register), I get a USB System Error Interrupt for that endpoint. The only information I can get about this is that:
If a system error (AHB bus error) occurs when transferring the data or when fetching or
updating the DD the corresponding bit is set in this register. SysErrIntSt is a read-only
register.
At this point in the program, I haven't touched the UDCA or setup any DMA descriptors or anything like that past initialisation. This is an error that occurs as soon as the first message is received on the USB bus before I need to do any of that, I believe.
I'm using endpoints 2 IN and OUT, which are double-buffered bulk endpoints with a maximum packet size of 64 bytes.
I've confirmed that the USB works fine if I don't use the DMA.
I've confirmed that the USB works fine if I go through the process of initialising the DMA engine but configure the endpoint in slave-mode instead of DMA-mode.
I've confirmed that the USB Mass Storage example under Example Projects -> NXP -> LP17xx -> 177x_8x CMSIS works fine if I use its default configuration:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000000
...
but also breaks in the same way if I change this to:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000030 /* Endpoint 2 IN and OUT */
...
At the beginning of the USB hardware source file, I put the following:
#ifdef USB_DMA
// Stores information received using DMA on OUT endpoints.
//uint8_t dataBufferOUT[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferOUT = (uint8_t*)DMA_BUF_ADR;
// Stores information transferred using DMA on IN endpoints.
//uint8_t dataBufferIN[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferIN = (uint8_t*)(DMA_BUF_ADR+DD_BUFFER_SIZE*
USB_MAX_PACKET_SIZE);
// Current dataBufferOUT index;
uint16_t dataOUT;
// Current dataBufferIN index.
uint16_t dataIN;
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
I initialise the USB peripheral with this code:
void USBInit()
{
// Configure USB pins.
PINSEL_ConfigPin(0, 29, 1); // USB_D+1
PINSEL_ConfigPin(0, 30, 1); // USB_D-1
PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
PINSEL_ConfigPin(2, 9, 1); // USB_CONNECT1
PINSEL_ConfigPin(1, 30, 2); // USB_VBUS
// Turn on power and clock
CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);
//PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);
// Set DEV_CLK_EN and AHB_CLK_EN.
LPC_USB->USBClkCtrl |= 0x12;
// Wait until change is reflected in clock status register.
while((LPC_USB->USBClkSt & 0x12) != 0x12);
// Enable NVIC USB interrupts.
NVIC_EnableIRQ(USB_IRQn);
// Reset the USB.
USBReset();
// Set device address to 0x0 and enable device & connection.
USBSetAddress(0);
// TEMP.
sendMessageFlag = 0;
#ifdef USB_DMA
dataIN = 0;
dataOUT = 0;
#endif
}
My USB reset code:
void USBReset()
{
LPC_USB->EpInd = 0;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
LPC_USB->EpInd = 1;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);
LPC_USB->EpIntClr = 0xFFFFFFFF;
#ifdef USB_DMA
LPC_USB->EpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;
#else
LPC_USB->EpIntEn = 0xFFFFFFFF;
#endif
LPC_USB->DevIntClr = 0xFFFFFFFF;
LPC_USB->DevIntEn = DEV_STAT_INT | EP_SLOW_INT /*| EP_FAST_INT*/ ;
#ifdef USB_DMA
uint32_t n;
LPC_USB->UDCAH = USB_RAM_ADR;
LPC_USB->DMARClr = 0xFFFFFFFF;
LPC_USB->EpDMADis = 0xFFFFFFFF;
LPC_USB->EpDMAEn = USB_DMA_EP;
LPC_USB->EoTIntClr = 0xFFFFFFFF;
LPC_USB->NDDRIntClr = 0xFFFFFFFF;
LPC_USB->SysErrIntClr = 0xFFFFFFFF;
LPC_USB->DMAIntEn = 0x00000007;
DDMemMap[0] = 0x00000000;
DDMemMap[1] = 0x00000000;
for (n = 0; n < USB_EP_NUM; n++) {
udca[n] = 0;
UDCA[n] = 0;
}
#endif
}
When ready, this is used to run the USB:
void USBRun()
{
USBSetConnection(TRUE);
}
Finally, my USB interrupt routine:
void USB_IRQHandler(void)
{
OS_EnterInterrupt();
uint32_t data, val, pIndex, lIndex, currEpisr;
uint32_t interruptData = LPC_USB->DevIntSt;
#ifdef USB_DMA
uint32_t dmaInterruptData = LPC_USB->DMAIntSt;
#endif
//printf("InterruptData: 0x%x\n", interruptData);
if (interruptData & ERR_INT)
{
writeSIECommand(CMD_RD_ERR_STAT);
data = readSIECommandData(DAT_RD_ERR_STAT);
// printf("Error data: 0x%x\n", data);
//getchar();
}
// Handle device status interrupt (reset, connection change, suspend/resume).
if(interruptData & DEV_STAT_INT)
{
LPC_USB->DevIntClr = DEV_STAT_INT;
writeSIECommand(CMD_GET_DEV_STAT);
data = readSIECommandData(DAT_GET_DEV_STAT);
//printf("Data: 0x%x\n", data);
// Device reset.
if(data & DEV_RST)
{
USBReset();
USBResetCore();
//printf("USB Reset\n");
}
// Connection change.
if(data & DEV_CON_CH)
{
//printf("Connection change\n");
/* Pass */
}
// Suspend/resume.
if(data & DEV_SUS_CH)
{
if(data & DEV_SUS)
{
//printf("USB Suspend\n");
USBSuspend();
}
else
{
//printf("USB Resume\n");
USBResume();
}
}
OS_LeaveInterrupt();
return;
}
// Handle endpoint interrupt.
if(interruptData & EP_SLOW_INT)
{
//printf("Endpoint slow\n");
data = LPC_USB->EpIntSt;
//printf("EP interrupt: 0x%x\n", data);
currEpisr = 0;
for(pIndex=0; pIndex < USB_EP_NUM; pIndex++)
{
lIndex = pIndex >> 1;
if(data == currEpisr) break;
if(data & (1 << pIndex))
{
currEpisr |= (1 << pIndex);
LPC_USB->EpIntClr = 1 << pIndex;
while((LPC_USB->DevIntSt & CDFULL_INT) == 0);
val = LPC_USB->CmdData;
// OUT endpoint.
if((pIndex & 1) == 0)
{
// Control OUT endpoint.
if(pIndex == 0)
{
// Setup Packet.
if(val & EP_SEL_STP)
{
if(USB_P_EP[0])
{
USB_P_EP[0](USB_EVT_SETUP);
continue;
}
}
}
if(USB_P_EP[lIndex])
{
USB_P_EP[lIndex](USB_EVT_OUT);
}
}
// IN endpoint.
else
{
if(USB_P_EP[lIndex])
{
if(lIndex > 0) clearSendMessageFlag(lIndex);
USB_P_EP[lIndex](USB_EVT_IN);
}
}
}
}
LPC_USB->DevIntClr = EP_SLOW_INT;
}
#ifdef USB_DMA
if (dmaInterruptData & 0x00000001) { /* End of Transfer Interrupt */
data = LPC_USB->EoTIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_EOT);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_EOT);
}
}
}
}
LPC_USB->EoTIntClr = data;
}
if (dmaInterruptData & 0x00000002) { /* New DD Request Interrupt */
data = LPC_USB->NDDRIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_NDR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_NDR);
}
}
}
}
LPC_USB->NDDRIntClr = data;
}
if (dmaInterruptData & 0x00000004) { /* System Error Interrupt */
data = LPC_USB->SysErrIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_ERR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_ERR);
}
}
}
}
LPC_USB->SysErrIntClr = data;
}
#endif /* USB_DMA */
OS_LeaveInterrupt();
}
What I'm ideally looking for is a solution if you can spot an error in any of my code or a working example program that can be run on an LPC1788 that demonstrates USB message transmission and reception using the DMA engine.
I'd also appreciate any information on what can cause an AHB bus error.
EDIT
In response to Turbo J's answer below:
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.
In my USB hardware header file, I have:
/* USB RAM Definitions */
#define USB_RAM_ADR LPC_PERI_RAM_BASE /* USB RAM Start Address */
#define USB_RAM_SZ 0x00004000 /* USB RAM Size (4kB) */
LPC_PERI_RAM_BASE has the value 0x20000000UL.
In my source file, I have:
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
Where USB_EP_NUM is 32.
Therefore, UDCA should be a 128-byte array that begins at the start of the RAM memory block, I believe.
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.
Related
I'm trying to write a stm32 code in stm8. The problem is I can't find a SPI_Transmit function only SPI_SendData. I need this function to transmit an array through SPI.
The stm32 SPI_transmit function looks like this:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart;
HAL_StatusTypeDef errorcode = HAL_OK;
uint16_t initial_TxXferCount;
/* Check Direction parameter */
assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
/* Process Locked */
__HAL_LOCK(hspi);
/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();
initial_TxXferCount = Size;
if (hspi->State != HAL_SPI_STATE_READY)
{
errorcode = HAL_BUSY;
goto error;
}
if ((pData == NULL) || (Size == 0U))
{
errorcode = HAL_ERROR;
goto error;
}
/* Set the transaction information */
hspi->State = HAL_SPI_STATE_BUSY_TX;
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->pTxBuffPtr = (uint8_t *)pData;
hspi->TxXferSize = Size;
hspi->TxXferCount = Size;
/*Init field not used in handle to zero */
hspi->pRxBuffPtr = (uint8_t *)NULL;
hspi->RxXferSize = 0U;
hspi->RxXferCount = 0U;
hspi->TxISR = NULL;
hspi->RxISR = NULL;
/* Configure communication direction : 1Line */
if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
{
/* Disable SPI Peripheral before set 1Line direction (BIDIOE bit) */
__HAL_SPI_DISABLE(hspi);
SPI_1LINE_TX(hspi);
}
#if (USE_SPI_CRC != 0U)
/* Reset CRC Calculation */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SPI_RESET_CRC(hspi);
}
#endif /* USE_SPI_CRC */
/* Check if the SPI is already enabled */
if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(hspi);
}
/* Transmit data in 16 Bit mode */
if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
/* Transmit data in 8 Bit mode */
else
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint8_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
#if (USE_SPI_CRC != 0U)
/* Enable CRC Transmission */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
}
#endif /* USE_SPI_CRC */
/* Check the end of the transaction */
if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK)
{
hspi->ErrorCode = HAL_SPI_ERROR_FLAG;
}
/* Clear overrun flag in 2 Lines communication mode because received is not read */
if (hspi->Init.Direction == SPI_DIRECTION_2LINES)
{
__HAL_SPI_CLEAR_OVRFLAG(hspi);
}
if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
{
errorcode = HAL_ERROR;
}
error:
hspi->State = HAL_SPI_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hspi);
return errorcode;
}
This is what the SPI_SendData function looks like:
void SPI_SendData(SPI_TypeDef* SPIx, uint8_t Data)
{
SPIx->DR = Data; /* Write in the DR register the data to be sent*/
}
Stm8 doesn't have HAL functions. I think I have to define a completely new function for this to work in stm8, but I'm not sure. Is there another way?
I think I have to define a completely new function for this to work in stm8, but I'm not sure.
Yes very likely a completely new function/driver. STM8 is an entirely different architecture compared to STM32. If you are lucky some STM8 hardware peripherals were re-used in STM32, but I doubt it since STM8 is a way older legacy architecture. For example I would guess that it only supports 8 bit mode (which is the common implementation).
The way to actually learn microcontroller programming proper, is to learn how to write drivers yourself instead of relying on various bloatware libs such as the horrible, so-called "HAL" by ST.
SPI is an excellent peripheral to use when practicing this, since it is relatively simple hardware. Some tricky things to look out for:
SPI does not transmit or receive, it transceives in full duplex.
Flag clearing in SPI peripherals is often done automatically, for example by reading a status register followed by reading a data register. This can sometimes mess up debuggers that display registers or a raw memory map, since the debugger might get the idea to read registers when you single step and thereby destroy the flags.
/SS chip select might sometimes be handled by the SPI hardware, sometimes you have to handle it manually with GPIO. Also some devices might have peculiar timing requirements forcing you to handle things manually.
You can tailor the driver for a specific application or make it more generic, at your own preference.
I am developing uart communication on STMicroelectronics NUCLEO-G474RE Board(stm32G474RETX MCU, 64pins) using MDK-v5, but run into a perplexing problem these several days, make no progress on it and have to reach out to seek some help. If anyone could give me a right way to go, it would be appreciated greatly.
Code is not complicated, I use cubeMX to initialize the code base with usart2 configured, and use two threads based on cmsis v2 api for receiving occassinally and transmiting periodically(in 2 seconds).
The auto-generated usart2 init code
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_RTS;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
Interrupt call back code
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
printf("Interrupt error occured(0x%x)!!!\r\n", huart->ErrorCode);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart)
{
UNUSED(huart);
if(huart == &huart2 || huart == &huart3){
uint8_t aEnterLine[] = "\r\n";
//HAL_UART_Transmit(huart, aEnterLine, sizeof(aEnterLine), 0xFFFF);
//HAL_UART_Transmit(huart, gRxBuffer, 11, 0xFFFF);
//HAL_UART_Transmit(huart, aEnterLine, sizeof(aEnterLine), 0xFFFF);
//printf("%s\r\n", gRxBuffer);
if(g_cmdExecuting == 0)
osEventFlagsSet(evt_id, 0x00000001U);
}else if(huart == &huart1){
}else{
}
HAL_UART_Receive_IT(huart, (unsigned char*)gRxBuffer, 11);
}
int8_t app_usart_transmit(uint8_t* buf, uint8_t len)
{
return 0;
}
int8_t app_usart_init(UART_HandleTypeDef* huart)
{
if(huart == NULL)
return -1;
if(huart == &huart2 || huart == &huart3){
uint8_t aTxStartMessage[] = "\r\n****UART-Hyperterminal commmunication based on IT****"
"\r\nEnter 10 characters using keyboard: \r\n";
HAL_UART_Transmit(huart, aTxStartMessage, sizeof(aTxStartMessage), 0xFFFF);
}
clear_rx_buffer();
HAL_UART_Receive_IT(huart, (uint8_t*)gRxBuffer, 11);
return 0;
}
rx & tx worker code
extern osMutexId_t myMutex01Handle;
extern osEventFlagsId_t evt_id;
extern osEventFlagsId_t evt_id1;
osTimerId_t timer_id;
static osThreadId gTask1Handle;
static const osThreadAttr_t gTask1Attrbutes = {
.name = "Task1",
.priority = (osPriority_t) osPriorityAboveNormal,
.stack_size = 512
};
static osThreadId gTask2Handle;
static const osThreadAttr_t gTask2Attrbutes = {
.name = "Task2",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 512
};
uint8_t g_cmdExecuting = 0;
uint8_t g_SemExec = 0;
uint32_t timer_cnt;
void update_stat_timer(void* argument)
{
timer_cnt++;
//osMutexAcquire(myMutex01Handle, osWaitForever);
printf("update_stat_timer executes %u times, ====++++====++++\r\n", timer_cnt);
//osMutexRelease(myMutex01Handle);
}
void update_stat_task_run(void* argument)
{
uint32_t count;
for(;;){
osDelay(2000);
count++;
osMutexAcquire(myMutex01Handle, osWaitForever);
printf("update_stat_task_run executes %d times, ====++++====++++\r\n", count);
osMutexRelease(myMutex01Handle);
}
}
void exec_cmd_task_run(void* argument)
{
uint32_t flags;
for(;;){
flags = osEventFlagsWait(evt_id, 0x00000001U, osFlagsWaitAny, osWaitForever);
g_cmdExecuting = 1;
osMutexAcquire(myMutex01Handle, osWaitForever);
osDelay(1000);
printf("exec_cmd_task Print\r\n");
osMutexRelease(myMutex01Handle);
g_cmdExecuting = 0;
}
}
int8_t app_task_init()
{
osStatus_t status;
gTask1Handle = osThreadNew(exec_cmd_task_run, NULL, &gTask1Attrbutes);
gTask2Handle = osThreadNew(update_stat_task_run, NULL, &gTask2Attrbutes);
//uint32_t exec = 1;
//timer_id = osTimerNew(update_stat_timer, osTimerPeriodic, &exec, NULL);
//if(status != osOK){
//;
//}
//osTimerStart(timer_id, 2000U);
return 0;
}
My Question:
It could receive and transmit msg at very begining, but after receiving three or four hundred times, it couldn't receive the msg anymore while the transmitting is ok, and I also use the timer api to replace the transmitting thread, the result is the same, it couldn't receive any msg after hundreds times.
the testing result pic
You can perform the HAL status and error checks using uint32_t HAL_UART_GetError(UART_HandleTypeDef * huart) and HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart) (http://www.disca.upv.es/aperles/arm_cortex_m3/llibre/st/STM32F439xx_User_Manual/group__uart__exported__functions__group4.html)
The source code for HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) and HAL_UART_RxCplt_Callback() is in https://github.com/ARMmbed/mbed-hal-st-stm32cubef4/blob/master/source/stm32f4xx_hal_uart.c
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t tmp = 0;
tmp = huart->State;
if((tmp == HAL_UART_STATE_READY) || (tmp == HAL_UART_STATE_BUSY_TX))
{
if((pData == HAL_NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX)
{
huart->State = HAL_UART_STATE_BUSY_TX_RX;
}
else
{
huart->State = HAL_UART_STATE_BUSY_RX;
}
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
Note that the HAL mutually uses locking mechanisms (__HAL_LOCK, code line 107 in https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_def.h)
To get the UART status use i.e.
HAL_UART_StateTypeDef UARTStatus;
UARTStatus = HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
The definition of HAL_UART_StateTypeDef is
typedef enum
{
HAL_UART_STATE_RESET = 0x00, /*!< Peripheral is not yet Initialized */
HAL_UART_STATE_READY = 0x01, /*!< Peripheral Initialized and ready for use */
HAL_UART_STATE_BUSY = 0x02, /*!< an internal process is ongoing */
HAL_UART_STATE_BUSY_TX = 0x12, /*!< Data Transmission process is ongoing */
HAL_UART_STATE_BUSY_RX = 0x22, /*!< Data Reception process is ongoing */
HAL_UART_STATE_BUSY_TX_RX = 0x32, /*!< Data Transmission and Reception process is ongoing */
HAL_UART_STATE_TIMEOUT = 0x03, /*!< Timeout state */
HAL_UART_STATE_ERROR = 0x04 /*!< Error */
}HAL_UART_StateTypeDef;
(https://github.com/synthetos/Motate/blob/master/MotateProject/motate/cmsis/TARGET_STM/TARGET_DISCO_F407VG/stm32f4xx_hal_uart.h code line 97)
The definition of HAL_StatusTypeDef (general HAL status) is
typedef enum
{
HAL_OK = 0x00,
HAL_ERROR = 0x01,
HAL_BUSY = 0x02,
HAL_TIMEOUT = 0x03
} HAL_StatusTypeDef;
You can print the HAL status (i.e. with UART transmit or SWO ITM_SendChar((*ptr++));, see https://ralimtek.com/stm32_debugging/ )
The HAL has its own reference manual, the HAL status is described in the reference manual https://www.st.com/content/ccc/resource/technical/document/user_manual/2f/71/ba/b8/75/54/47/cf/DM00105879.pdf/files/DM00105879.pdf/jcr:content/translations/en.DM00105879.pdf on page 54 in chapter 2.9 HAL common resources.
Debugging techniques are described in https://www.st.com/content/ccc/resource/technical/document/application_note/group0/3d/a5/0e/30/76/51/45/58/DM00354244/files/DM00354244.pdf/jcr:content/translations/en.DM00354244.pdf, also for your programming adapter, that you can directly integrate in CubeMX (i use Atollic with integrated CubeMX because Atollic is based on Eclipse IDE and is almost identical to use)
Another debugging tutorial is https://ardupilot.org/dev/docs/debugging-with-gdb-on-stm32.html (if you are on linux).
This could also be a race condition either of the HAL or the OS. The problem that i have is that you do not post your full code, especially not concerning your OS and this is possibly critical here
So maybe first check the HAL status and then focus on the OS part (race conditions, buffer, stack or heap overflow, ... https://www.st.com/content/ccc/resource/technical/document/user_manual/2d/60/ff/15/8c/c9/43/77/DM00105262.pdf/files/DM00105262.pdf/jcr:content/translations/en.DM00105262.pdf)
Stack overflows are always critical on embedded systems so here is links https://barrgroup.com/embedded-systems/how-to/prevent-detect-stack-overflow , https://www.iar.com/support/resources/articles/detecting-and-avoiding-stack-overflow-in-embedded-systems/
Also see Uart dma receive interrupt stops receiving data after several minutes and UART receive interrupt stops triggering after several hours of successful receive
Many developers avoid using the HAL because it is a bit buggy and not very transparent, i personally would also not use it when i want full control over the system behavior
I am trying to create a file with FatFs on USB flash, but my f_open call trying to read boot sector for first time file system mount hangs on this function.
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
BYTE status = USBH_MSC_OK;
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if(HCD_IsDeviceConnected(&USB_OTG_Core))
{
do
{
status = USBH_MSC_Read10(&USB_OTG_Core, buff,sector,512 * count);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
return RES_ERROR;
}
}
while(status == USBH_MSC_BUSY ); // Loop which create hanging state
}
if(status == USBH_MSC_OK)
return RES_OK;
return RES_ERROR;
}
The main problem is the loop which creates hanging state
while(status == USBH_MSC_BUSY );
So I do not know what to do to avoid this. Using debugger I discover that state is caused by parameter CmdStateMachine of structure USBH_MSC_BOTXferParam, type USBH_BOTXfer_TypeDef is equal CMD_UNINITIALIZED_STATE which actually cause miss up of switch statement of USBH_MSC_Read10 function.
/**
* #brief USBH_MSC_Read10
* Issue the read command to the device. Once the response received,
* it updates the status to upper layer
* #param dataBuffer : DataBuffer will contain the data to be read
* #param address : Address from which the data will be read
* #param nbOfbytes : NbOfbytes to be read
* #retval Status
*/
uint8_t USBH_MSC_Read10(USB_OTG_CORE_HANDLE *pdev,
uint8_t *dataBuffer,
uint32_t address,
uint32_t nbOfbytes)
{
uint8_t index;
static USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
uint16_t nbOfPages;
status = USBH_MSC_BUSY;
if(HCD_IsDeviceConnected(pdev))
{
switch(USBH_MSC_BOTXferParam.CmdStateMachine)
{
case CMD_SEND_STATE:
/*Prepare the CBW and relevant field*/
USBH_MSC_CBWData.field.CBWTransferLength = nbOfbytes;
USBH_MSC_CBWData.field.CBWFlags = USB_EP_DIR_IN;
USBH_MSC_CBWData.field.CBWLength = CBW_LENGTH;
USBH_MSC_BOTXferParam.pRxTxBuff = dataBuffer;
for(index = CBW_CB_LENGTH; index != 0; index--)
{
USBH_MSC_CBWData.field.CBWCB[index] = 0x00;
}
USBH_MSC_CBWData.field.CBWCB[0] = OPCODE_READ10;
/*logical block address*/
USBH_MSC_CBWData.field.CBWCB[2] = (((uint8_t*)&address)[3]);
USBH_MSC_CBWData.field.CBWCB[3] = (((uint8_t*)&address)[2]);
USBH_MSC_CBWData.field.CBWCB[4] = (((uint8_t*)&address)[1]);
USBH_MSC_CBWData.field.CBWCB[5] = (((uint8_t*)&address)[0]);
/*USBH_MSC_PAGE_LENGTH = 512*/
nbOfPages = nbOfbytes/ USBH_MSC_PAGE_LENGTH;
/*Tranfer length */
USBH_MSC_CBWData.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]) ;
USBH_MSC_CBWData.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]) ;
USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SEND_CBW;
/* Start the transfer, then let the state machine
manage the other transactions */
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_USB_TRANSFERS;
USBH_MSC_BOTXferParam.BOTXferStatus = USBH_MSC_BUSY;
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_WAIT_STATUS;
status = USBH_MSC_BUSY;
break;
case CMD_WAIT_STATUS:
if((USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_OK) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Commands successfully sent and Response Received */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_OK;
}
else if (( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_FAIL ) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
}
else if ( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_PHASE_ERROR )
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_PHASE_ERROR;
}
else
{
/* Wait for the Commands to get Completed */
/* NO Change in state Machine */
}
break;
default:
break;
}
}
return status;
}
Here is USBH_BOTXfer_TypeDef type declaration;
typedef struct _BOTXfer
{
uint8_t MSCState;
uint8_t MSCStateBkp;
uint8_t MSCStateCurrent;
uint8_t CmdStateMachine;
uint8_t BOTState;
uint8_t BOTStateBkp;
uint8_t* pRxTxBuff;
uint16_t DataLength;
uint8_t BOTXferErrorCount;
uint8_t BOTXferStatus;
} USBH_BOTXfer_TypeDef;
During the debug I discover that all fields of it is 0x00.
Here are my FatFs calls
int main(void)
{
FATFS Fat;
FIL file;
FRESULT fr;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
/* Enable SWO output */
DBGMCU->CR = 0x00000020;
GPIOD->MODER=0x55000000;
GPIOD->OTYPER = 0x00000000;
GPIOD->OSPEEDR = 0x00000001;
while(1)
{
if (!USB_MSC_IsInitialized())
{
USB_MSC_Initialize();
}
if (USB_MSC_IsConnected())
{
GPIOD->ODR = (1 << 15);
disk_initialize(0);
fr = f_mount(0, &Fat);
if(fr == FR_OK)
{
fr = f_open(&file,"0:DP_lab8.pdf",(FA_CREATE_ALWAYS | FA_WRITE));
if (fr == FR_OK)
{
f_close(&file);
}
f_mount(0, NULL);
}
}
else
{
GPIOD->ODR = (1 << 14);
}
USB_MSC_Main();
}
}
USB_MSC_IsConnected function is:
int USB_MSC_IsConnected(void)
{
if (g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED)
{
USB_MSC_Uninitialize();
}
return !(g_USB_MSC_HostStatus == USB_DEV_DETACHED ||
g_USB_MSC_HostStatus == USB_HOST_NO_INIT ||
g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED);
}
And device states are:
typedef enum
{
USB_HOST_NO_INIT = 0, /* USB interface not initialized */
USB_DEV_DETACHED, /* no device connected */
USB_SPEED_ERROR, /* unsupported USB speed */
USB_DEV_NOT_SUPPORTED, /* unsupported device */
USB_DEV_WRITE_PROTECT, /* device is write protected */
USB_OVER_CURRENT, /* overcurrent detected */
USB_DEV_CONNECTED /* device connected and ready */
} USB_HostStatus;
The value of g_USB_MSC_HostStatus is received by standard USB HOST user callbacks.
I think it is a bug in ST host library. I've hunted it down, as my usb host was unable to pass enumeration stage. After a fix the stack is Ok.
There is union _USB_Setup in usbh_def.h file in "STM32Cube/Repository/STM32Cube_FW_F7_V1.13.0/Middlewares/ST/STM32_USB_Host_Library/Core/Inc" (any chip, not only F7, any version, not only V1.13.0). It has uint16_t bmRequestType and uint16_t bRequest. These two fileds must be uint8_t as of USB specs. Fixing this issue made usb host go as needed. Enumeration stage passes ok, and all other stages as well.
I have experienced a very strange problem in developing my application for the ST Microelectronics iNemo. My applications consists in:
Gyroscope reading with SPI
Accelerometer and Magnetometer (in the same device) reading with I2C
Attitude estimation algorithm
PD functions
Data receiving with USART, with interrupt without DMA
Sending of logging packet, with USART
The loop is triggered by a timer at 100Hz.
The program works well (I have tested it with some USART debug prints) until I start sending data with the USART: my initial guess was that, since this fact enables the receiving of interrupts, it causes problem with the I2C bus arbitrage mechanism. My guess is derived that when I have succeeded in debugging the problem (that is time dependent), with USART prints, I have detected that the last print is always before the accelerometer of magnetometer prints (the first that I call in my code).
Additionally, If I enable the verbose debug prints via USART that I have mentioned the problem occurs in fewer occasions, while if I disable it and I send only logging packet the problem occurs always and immediately. Anyone can give me an idea of what can be the cause of this problem? Thanks
EDIT: I attach my I2C code:
#define DMA_BUFFER_SIZE 196
#define FORCE_CRITICAL_SEC
/**
* #brief DMA initialization structure variable definition.
*/
DMA_InitTypeDef I2CDMA_InitStructure;
/**
* #brief Volatile variable definition for I2C direction.
*/
__IO uint32_t I2CDirection = I2C_DIRECTION_TX;
void iNemoI2CInit(I2C_TypeDef* I2Cx, uint32_t I2CxSpeed)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIO clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);
/* Configure I2C pins: SCL and SDA */
if(I2Cx==I2C2)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
}
else
{
GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
}
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2CxSpeed;
/* Apply I2C configuration after enabling it */
I2C_Init(I2Cx, &I2C_InitStructure);
/* I2C Peripheral Enable */
I2C_Cmd(I2Cx, ENABLE);
/* Enable DMA if required */
#if (defined(I2C1_USE_DMA_TX) || defined(I2C1_USE_DMA_RX))
if (I2Cx==I2C1)
iNemoI2CDMAInit(I2C1);
#endif
#if (defined(I2C2_USE_DMA_TX) || defined(I2C2_USE_DMA_RX))
if (I2Cx==I2C2)
iNemoI2CDMAInit(I2C2);
#endif
}
void iNemoI2CDMAInit(I2C_TypeDef* I2Cx)
{
/* Enable the DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* I2C TX DMA Channel configuration */
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF; /* This parameter will be configured durig communication */
I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
if(I2Cx==I2C2)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
#ifdef I2C2_USE_DMA_TX
DMA_DeInit(I2C2_DMA_CHANNEL_TX);
DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
#endif
#ifdef I2C2_USE_DMA_RX
/* I2C2 RX DMA Channel configuration */
DMA_DeInit(I2C2_DMA_CHANNEL_RX);
DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
#endif
}
if(I2Cx==I2C1)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
#ifdef I2C1_USE_DMA_TX
DMA_DeInit(I2C1_DMA_CHANNEL_TX);
DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
#endif
#ifdef I2C1_USE_DMA_RX
/* I2C1 RX DMA Channel configuration */
DMA_DeInit(I2C1_DMA_CHANNEL_RX);
DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
#endif
}
void iNemoI2CDMAConfig(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t lBufferSize, uint32_t lDirection)
{
/* Initialize the DMA with the new parameters */
if (lDirection == I2C_DIRECTION_TX)
{
/* Configure the DMA Tx Channel with the buffer address and the buffer size */
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize;
if(I2Cx==I2C2)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
DMA_Cmd(I2C2_DMA_CHANNEL_TX, ENABLE);
}
else
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
DMA_Cmd(I2C1_DMA_CHANNEL_TX, ENABLE);
}
}
else /* Reception */
{
/* Configure the DMA Rx Channel with the buffer address and the buffer size */
I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize;
if(I2Cx==I2C2)
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);
DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
DMA_Cmd(I2C2_DMA_CHANNEL_RX, ENABLE);
}
else
{
I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);
DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
DMA_Cmd(I2C1_DMA_CHANNEL_RX, ENABLE);
}
}
}
void iNemoI2CBufferReadDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t* pcBuffer, uint8_t cReadAddr, uint8_t cNumByteToRead)
{
__IO uint32_t temp = 0;
__IO uint32_t Timeout = 0;
/* Enable I2C errors interrupts */
I2Cx->CR2 |= I2C_IT_ERR;
/* Set the MSb of the register address in case of multiple readings */
if(cNumByteToRead>1)
cReadAddr |= 0x80;
#ifdef FORCE_CRITICAL_SEC
__disable_irq();
#endif
#ifdef USART_DEBUG2
USART1_Printf("FLAG BUSY\r\n");
#endif
Timeout = 0xFFFF;
/* While the bus is busy */
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
if (Timeout-- == 0)
return;
}
/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);
#ifdef USART_DEBUG2
USART1_Printf("MASTER MODE\r\n");
#endif
Timeout = 0xFFFF;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){
if (Timeout-- == 0)
return;
}
/* Send LSM303DLH address for read */
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter);
Timeout = 0xFFFF;
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
if (Timeout-- == 0)
return;
}
/* Clear EV6 by setting again the PE bit */
I2C_Cmd(I2Cx, ENABLE);
/* Send the LSM303DLH_Magn's internal address to write to */
I2C_SendData(I2Cx, cReadAddr);
#ifdef USART_DEBUG2
USART1_Printf("BYTE TRANSMITTED\r\n");
#endif
Timeout = 0xFFFF;
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
if (Timeout-- == 0)
return;
}
/* Configure I2Cx DMA channel */
iNemoI2CDMAConfig(I2Cx, pcBuffer, cNumByteToRead, I2C_DIRECTION_RX);
/* Set Last bit to have a NACK on the last received byte */
I2Cx->CR2 |= 0x1000;
/* Enable I2C DMA requests */
I2C_DMACmd(I2Cx, ENABLE);
Timeout = 0xFFFF;
/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);
Timeout = 0xFFFF;
/* Wait until SB flag is set: EV5 */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if (Timeout-- == 0)
return;
}
Timeout = 0xFFFF;
/* Send LSM303DLH address for read */
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Receiver);
Timeout = 0xFFFF;
/* Wait until ADDR is set: EV6 */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if (Timeout-- == 0)
return;
}
/* Clear ADDR flag by reading SR2 register */
temp = I2Cx->SR2;
if(I2Cx == I2C2)
{
Timeout = 0xFFFF;
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC5)){
if (Timeout-- == 0)
return;
}
/* Disable DMA Channel */
DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);
/* Clear the DMA Transfer Complete flag */
DMA_ClearFlag(DMA1_FLAG_TC5);
}
else
{
/* Wait until DMA end of transfer */
#ifdef USART_DEBUG2
USART1_Printf("END TRANSFER\r\n");
#endif
Timeout = 0xFFFF;
while (!DMA_GetFlagStatus(DMA1_FLAG_TC7)){
if (Timeout-- == 0)
return;
}
/* Disable DMA Channel */
DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);
/* Clear the DMA Transfer Complete flag */
DMA_ClearFlag(DMA1_FLAG_TC7);
}
/* Disable Ack for the last byte */
I2C_AcknowledgeConfig(I2Cx, DISABLE);
/* Send STOP Condition */
I2C_GenerateSTOP(I2Cx, ENABLE);
#ifdef USART_DEBUG2
USART1_Printf("STOP BIT\r\n");
#endif
Timeout = 0xFFFF;
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1 & 0x0200) == 0x0200){
if (Timeout-- == 0)
return;
}
/* Enable Acknowledgement to be ready for another reception */
I2C_AcknowledgeConfig(I2Cx, ENABLE);
#ifdef FORCE_CRITICAL_SEC
__enable_irq();
#endif
}
void iNemoI2CBufferWriteDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t* pcBuffer, uint8_t cWriteAddr, uint8_t cNumByteToWrite)
{
__IO uint32_t temp = 0;
__IO uint32_t Timeout = 0;
static uint8_t pcDmaBuffer[DMA_BUFFER_SIZE+1];
/* Set to 1 the MSb of the register address in case of multiple byte writing */
if(cNumByteToWrite>1)
cWriteAddr |= 0x80;
pcDmaBuffer[0]=cWriteAddr;
memcpy(&pcDmaBuffer[1],pcBuffer,cNumByteToWrite);
/* Enable Error IT */
I2Cx->CR2 |= I2C_IT_ERR;
Timeout = 0xFFFF;
/* Configure the DMA channel for I2Cx transmission */
iNemoI2CDMAConfig(I2Cx, pcDmaBuffer, cNumByteToWrite+1, I2C_DIRECTION_TX);
/* Enable DMA for I2C */
I2C_DMACmd(I2Cx, ENABLE);
/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);
/* Wait until SB flag is set: EV5 */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if (Timeout-- == 0)
return;
}
Timeout = 0xFFFF;
/* Send LSM303DLH address for write */
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter);
/* Wait until ADDR is set: EV6 */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if (Timeout-- == 0)
return;
}
/* Clear ADDR flag by reading SR2 register */
temp = I2Cx->SR2;
/* Disable the DMA1 channel */
if(I2Cx == I2C2)
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));
/* Disable DMA Channel */
DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC4);
}
else
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC6));
/* Disable DMA Channel */
DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC6);
}
/* EV8_2: Wait until BTF is set before programming the STOP */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send STOP Condition */
I2C_GenerateSTOP(I2Cx, ENABLE);
/* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
while ((I2Cx->CR1 & 0x0200) == 0x0200);
}
I see that for some while loops you have a timeout, but for some you don't:
while ((I2Cx->CR1 & 0x0200) == 0x0200);
Make all your loops timeout, and also make a note where the error condition occurs (it needs to be investigated - if you don't know the reason, it will come back to haunt you later).
Hardware can be a bit buggy at times, so it completely possible you're doing everything correctly, but it still doesn't work. Check errata (for STM32 I2C and your I2C slaves) for documented bugs.
A few years ago I came across an issue where I2C lines would stay low, and I had to reconfigure pins as GPIO, bit-bang some bits out, and then it I could switch back to I2C operation.
I have been playing around with ATtiny's and arduino for a while but am a complete newbee in 'real' AVR programming.
I am building a laser gun and target for my son and want to have the target play a random silly animal sound whenever it is hit.
The other functions of the target is maintained by Attiny85s and i therefore stumbled upon Elm-Chans code for interfacing a microSD card with Attiny85 and play .wav files by PWM.
However the code is written so that it plays files on the SD card root folder (or other dir) in order and skips to next in response to a pushbutton on one of the pins.
What i want it to do is to pick and play a random file from the root when button is pushed (i.e. target is hit).
It should play the sound only once and preferably interrupt an already playing file and play another if the target is hit again before the sound has ended.
I have been trying really hard to figure out what to change in the code but come to realize that i need to start from the beginning with learning AVR programming from scratch...however that will take some time and before i get there i humbly hope that any of you could grant me a quick fix and tell me what to do, so i can finish the toy.
Kind regards
Mads
/*----------------------------------------------------------------------------/
/ 8-pin SD audio player R0.05d (C)ChaN, 2011 /
/-----------------------------------------------------------------------------/
/ This project, program codes and circuit diagrams, is opened under license
/ policy of following trems.
/
/ Copyright (C) 2010, ChaN, all right reserved.
/
/ * This project is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/ personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/----------------------------------------------------------------------------*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <string.h>
#include "pff.h"
#include "xitoa.h"
#ifndef MODE
#error Wrong make file.
#endif
#if MODE == 0 /* Single output */
FUSES = {0xE1, 0xDD, 0xFF}; /* Fuse bytes for mono: Low, High and Extended */
#else /* Dual output */
FUSES = {0xE1, 0x7D, 0xFF}; /* Fuse bytes for stereo and mono-HR: Low, High and Extended (*HVS mode only*) */
#endif
/* This is the fuse settings of this project. The fuse data will be included
in the output hex file with program code. However some old flash programmers
cannot load the fuse bits from hex file. If it is the case, remove this line
and use these values to program the fuse bits. */
#define FCC(c1,c2,c3,c4) (((DWORD)c4<<24)+((DWORD)c3<<16)+((WORD)c2<<8)+(BYTE)c1) /* FourCC */
void delay_us (WORD); /* Defined in asmfunc.S */
/*---------------------------------------------------------*/
/* Work Area */
/*---------------------------------------------------------*/
volatile BYTE FifoRi, FifoWi, FifoCt; /* FIFO controls */
BYTE Buff[256]; /* Wave output FIFO */
FATFS Fs; /* File system object */
DIR Dir; /* Directory object */
FILINFO Fno; /* File information */
WORD rb; /* Return value. Put this here to avoid avr-gcc's bug */
/*---------------------------------------------------------*/
static
DWORD load_header (void) /* 0:Invalid format, 1:I/O error, >=1024:Number of samples */
{
DWORD sz, f;
BYTE b, al = 0;
if (pf_read(Buff, 12, &rb)) return 1; /* Load file header (12 bytes) */
if (rb != 12 || LD_DWORD(Buff+8) != FCC('W','A','V','E')) return 0;
for (;;) {
wdt_reset();
pf_read(Buff, 8, &rb); /* Get Chunk ID and size */
if (rb != 8) return 0;
sz = LD_DWORD(&Buff[4]); /* Chunk size */
switch (LD_DWORD(&Buff[0])) { /* Switch by chunk ID */
case FCC('f','m','t',' ') : /* 'fmt ' chunk */
if (sz & 1) sz++; /* Align chunk size */
if (sz > 100 || sz < 16) return 0; /* Check chunk size */
pf_read(Buff, sz, &rb); /* Get content */
if (rb != sz) return 0;
if (Buff[0] != 1) return 0; /* Check coding type (LPCM) */
b = Buff[2];
if (b != 1 && b != 2) return 0; /* Check channels (1/2) */
GPIOR0 = al = b; /* Save channel flag */
b = Buff[14];
if (b != 8 && b != 16) return 0; /* Check resolution (8/16 bit) */
GPIOR0 |= b; /* Save resolution flag */
if (b & 16) al <<= 1;
f = LD_DWORD(&Buff[4]); /* Check sampling freqency (8k-48k) */
if (f < 8000 || f > 48000) return 4;
OCR0A = (BYTE)(F_CPU / 8 / f) - 1; /* Set sampling interval */
break;
case FCC('d','a','t','a') : /* 'data' chunk */
if (!al) return 0; /* Check if format is valid */
if (sz < 1024 || (sz & (al - 1))) return 0; /* Check size */
if (Fs.fptr & (al - 1)) return 0; /* Check word alignment */
return sz; /* Start to play */
case FCC('D','I','S','P') : /* 'DISP' chunk */
case FCC('L','I','S','T') : /* 'LIST' chunk */
case FCC('f','a','c','t') : /* 'fact' chunk */
if (sz & 1) sz++; /* Align chunk size */
pf_lseek(Fs.fptr + sz); /* Skip this chunk */
break;
default : /* Unknown chunk */
return 0;
}
}
return 0;
}
static
void ramp (
int dir /* 0:Ramp-down, 1:Ramp-up */
)
{
#if MODE != 3
BYTE v, d, n;
if (dir) {
v = 0; d = 1;
} else {
v = 128; d = (BYTE)-1;
}
n = 128;
do {
v += d;
OCR1A = v; OCR1B = v;
delay_us(100);
} while (--n);
#else
dir = dir ? 128 : 0;
OCR1A = (BYTE)dir; OCR1B = (BYTE)dir;
#endif
}
static
FRESULT play (
const char *dir, /* Directory */
const char *fn /* File */
)
{
DWORD sz;
FRESULT res;
BYTE sw;
WORD btr;
wdt_reset();
xsprintf((char*)Buff, PSTR("%s/%s"), dir, fn);
res = pf_open((char*)Buff); /* Open sound file */
if (res == FR_OK) {
sz = load_header(); /* Check file format and ready to play */
if (sz < 1024) return 255; /* Cannot play this file */
FifoCt = 0; FifoRi = 0; FifoWi = 0; /* Reset audio FIFO */
if (!TCCR1) { /* Enable audio out if not enabled */
PLLCSR = 0b00000110; /* Select PLL clock for TC1.ck */
GTCCR = 0b01100000; /* Enable OC1B as PWM */
TCCR1 = MODE ? 0b01100001 : 0b00000001; /* Start TC1 and enable OC1A as PWM if needed */
TCCR0A = 0b00000010; /* Statr TC0 as interval timer at 2MHz */
TCCR0B = 0b00000010;
TIMSK = _BV(OCIE0A);
ramp(1);
}
pf_read(0, 512 - (Fs.fptr % 512), &rb); /* Snip sector unaligned part */
sz -= rb;
sw = 1; /* Button status flag */
do { /* Data transfer loop */
wdt_reset();
btr = (sz > 1024) ? 1024 : (WORD)sz;/* A chunk of audio data */
res = pf_read(0, btr, &rb); /* Forward the data into audio FIFO */
if (rb != 1024) break; /* Break on error or end of data */
sz -= rb; /* Decrease data counter */
sw <<= 1; /* Break on button down */
} while ((PINB & 1) || ++sw != 1);
}
while (FifoCt) ; /* Wait for audio FIFO empty */
OCR1A = 128; OCR1B = 128; /* Return output to center level */
return res;
}
static
void delay500 (void)
{
wdt_reset();
TCCR0B = 0; TCCR0A = 0; /* Stop TC0 */
if (TCCR1) { /* Stop TC1 if enabled */
ramp(0);
TCCR1 = 0; GTCCR = 0;
}
WDTCR = _BV(WDE) | _BV(WDIE) | 0b101; /* Set WDT to interrupt mode in timeout of 0.5s */
set_sleep_mode(SLEEP_MODE_PWR_DOWN); /* Enter power down mode */
sleep_mode();
wdt_reset();
WDTCR = _BV(WDE) | 0b110; /* Set WDT to reset mode in timeout of 1s */
}
EMPTY_INTERRUPT(WDT_vect);
/*-----------------------------------------------------------------------*/
/* Main */
int main (void)
{
FRESULT res;
char *dir;
BYTE org_osc = OSCCAL;
MCUSR = 0;
WDTCR = _BV(WDE) | 0b110; /* Enable WDT reset in timeout of 1s */
PORTB = 0b101001; /* Initialize port: - - H L H L L P */
DDRB = 0b111110;
sei();
for (;;) {
if (pf_mount(&Fs) == FR_OK) { /* Initialize FS */
wdt_reset();
Buff[0] = 0;
if (!pf_open("osccal")) pf_read(Buff, 1, &rb); /* Adjust frequency */
OSCCAL = org_osc + Buff[0];
res = pf_opendir(&Dir, dir = "wav"); /* Open sound file directory */
if (res == FR_NO_PATH)
res = pf_opendir(&Dir, dir = ""); /* Open root directory */
while (res == FR_OK) { /* Repeat in the dir */
res = pf_readdir(&Dir, 0); /* Rewind dir */
while (res == FR_OK) { /* Play all wav files in the dir */
wdt_reset();
res = pf_readdir(&Dir, &Fno); /* Get a dir entry */
if (res || !Fno.fname[0]) break; /* Break on error or end of dir */
if (!(Fno.fattrib & (AM_DIR|AM_HID)) && strstr(Fno.fname, ".WAV"))
res = play(dir, Fno.fname); /* Play file */
}
}
}
delay500(); /* Delay 500ms in low power sleep mode */
}
}
I reckon your case requires more C-coding knowledge than AVR-hacking one.
Here:
if (!(Fno.fattrib & (AM_DIR|AM_HID)) && strstr(Fno.fname, ".WAV"))
res = play(dir, Fno.fname); /* Play file */
Every time matching file is found, play function is called. What you want instead is to add this file descriptor to some form of container (e.g. list) of available files. After all files are listed, you may want to wait for external input (hit by laser), and then play the file and move current file index to the next.
To enable pausing and resetting playback, I recommend using external interrupt for handling "on hit" events.
PS Building toys for your kid using AVR? +1 from me!