STM32F103 PWM output not working - stm32

I'm trying to set up one of the LEDs on the STM3210E-EVAL board as a PWM output so that I can vary the brightness.
I am targeting the red LED, which is on port F, pin 8. I have set up timer 13 which should be tied to that pin for PWM output, but I feel like like I am missing a step somewhere. Here is the current function to initialize the pin, setup the timer, and set up the PWM:
void led_init(void)
{
TIM_OC_InitTypeDef sConfigOC;
TIM_HandleTypeDef htim13;
/* Configure GPIO pins : PF8 */
__HAL_AFIO_REMAP_TIM13_ENABLE();
__GPIOF_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
htim13.Instance = TIM13;
htim13.Init.Prescaler = (uint32_t)(72000000 / 2000000) - 1;
htim13.Init.CounterMode = TIM_COUNTERMODE_UP;
htim13.Init.Period = 700;
htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim13);
HAL_TIM_PWM_Init(&htim13);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 350;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&htim13, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim13, TIM_CHANNEL_1);
}

It seems you aren't enabling the timer's clock:
__HAL_RCC_TIM13_CLK_ENABLE()
Did you start your project from an example or STM32cubeMX? Usually some init code like this part goes to the stm32f1_hal_msp.c file! It's kind of elegant to also put your PWM pin (PF8) init there!

I think you should specify which alternate function you use on the GPIO. In that case it is PWM. There must be a function like GPIO_PinAFConfig.

Related

STM32 Multi-Channel ADC. Unexpected behaviour when unpopulated

I have added ADC functionality to my Nucleo-F446RE development board. 4 channels, DMA enabled, scan and continuous conversion mode enabled, DMA continuous requests enabled, varying sample time per channel. I'll post code at the bottom of this post (all HAL, all done in STM32CubeMX).
I have found some strange behaviour when the channels are unpopulated (e.g., analog channel pin left open). All four channels will hover at around 0.9V with no channels connected. If I add a 3.3V source to channel 0, it'll show 3.3V, but CH1 will show 2.5V, CH2 will show 1.9V, CH3 1.6V. A waterfall effect. That waterfall effect is the same if I move the 3.3V source to CH1 and leave the rest unpopulated, and the waterfall effect loops back around to CH0.
If I give each channel their own source, they'll all show them correctly, but when unpopulated the channels are influenced by the populated channel. Why is this? I have found some sources saying that this is because of the sample+hold capacitor, and the solution is to correct the sampling times, but I have played a lot with the times going from very fast to as slow as possible sampling (I am only interested in sampling the data at 1kHz, but the ADC conversion seems to be, at a minimum, a magnitude above this), but it doesn't make a change. I wondered if changing the analog channel pin configuration to pull-down would help, but again no change.
I am hoping that this isn't something to be too concerned about, as the channels appear correct when populated, but perhaps there is some background influence that I am not seeing even when populated that I want to avoid. I am certain I haven't optimised my circuit, so any advice on that would also be great. There are lots of tutorials and examples online for STM32 ADC DMA with a single channel, but not so many with multi-channel. I also don't find the STM32 provided examples to be too helpful and often seem very inefficient.
ADC definitions
(main clock 180MHz, APB2 prescaler 2 = 90MHz, although I have also dropped it to a prescaler of 16 (11.25MHz) which didn't help)
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 3;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = 4;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
DMA definition
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
*/
GPIO_InitStruct.Pin = analog1_Pin|analog2_Pin|analog3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = analog4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(analog4_GPIO_Port, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
Analog read code
(analog_scale is called once per channel every 1kHz)
#include "dma.h"
#include "adc.h"
#include "analog.h"
volatile uint32_t analogBuffer[4];
void analog_init()
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
}
uint16_t analog_scale(char ch)
{
return (uint16_t)(((analogBuffer[ch] * 3.3) / 4096.0) * 1000.0);
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
HAL_GPIO_TogglePin(test4_GPIO_Port, test4_Pin);
}
That's not a Software issue, it's normal hardware behavior.
If ADC pins are floating, they "gather" stray voltages, e.g. from adjacent Sample and Hold Capacitors, from the Voltage Reference or any voltage that is induced in the traces on the PCB or attached cables.
The "Waterfall" effect you see, is simply your input voltage on Channel 0 or 1 coupling through the sample and hold capacitors and resistors from one channel to the next, transferred by the multiplexers parasitic capacitances: a small amount of charge is transferred from one voltage path to the next while switching through the channels, and this charge has no path to flow when the connections are open, except through the ADC, resulting in a pseudo-voltage reading.
To prevent this, tie all unused channels to ground, using appropriate pull down resistors (10 kOhm should be OK …), or if you want a software solution: multiply all unused channels with 0.

STM32F4 I2C with DMA not working

I'm using one STM32F4 and I want to communicate with my LSM303 accelerometer. For that I'm using I2C, and just using I2C works fine but when I try to use DMA with it, it stops working.
When I use HAL_I2C_Master_Transmit_DMA it works and I got the IRQHandler and . But when after that I want to use HAL_I2C_Master_Receive_DMA it says that the State of the I2C is not ready...
I read that the I2C was kind of messed up with the STM32FX but I don't understand why it's working fine without DMA.
Also when it hits the callback I2C_DMAXferCplt for the Master_Transmit_DMA it says that the CurrentState of the I2C_HandleTypeDef is still equal to HAL_I2C_STATE_BUSY_TX and therefor it does not put the state back to READY. That why it does not receive anything when I call the Master_Receive_DMA.
Here's my I2C init :
void MX_I2C2_Init(void)
{
I2C_ST_INS.Instance = I2C2;
I2C_ST_INS.Init.ClockSpeed = 400000;
I2C_ST_INS.Init.DutyCycle = I2C_DUTYCYCLE_2;
I2C_ST_INS.Init.OwnAddress1 = 0;
I2C_ST_INS.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
I2C_ST_INS.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
I2C_ST_INS.Init.OwnAddress2 = 0;
I2C_ST_INS.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
I2C_ST_INS.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
HAL_I2C_Init(&I2C_ST_INS);
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(i2cHandle->Instance==I2C1)
{
//Not useful for this post
}
else if(i2cHandle->Instance==I2C2)
{
GPIO_InitStruct.Pin = MASTER_IMUB_I2C_SDA_Pin|MASTER_IMUB_I2C_SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
__HAL_RCC_I2C2_CLK_ENABLE();
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_i2c2_rx.Instance = DMA1_Stream2;
hdma_i2c2_rx.Init.Channel = DMA_CHANNEL_7;
hdma_i2c2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2c2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c2_rx.Init.Mode = DMA_NORMAL;
hdma_i2c2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_i2c2_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_i2c2_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_i2c2_rx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_i2c2_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_i2c2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c2_rx);
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
hdma_i2c2_tx.Instance = DMA1_Stream7;
hdma_i2c2_tx.Init.Channel = DMA_CHANNEL_7;
hdma_i2c2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c2_tx.Init.Mode = DMA_NORMAL;
hdma_i2c2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_i2c2_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_i2c2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_i2c2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_i2c2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_i2c2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c2_tx);
HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
}
}
Do you have any ideas why it does not work when I'm using DMA with I2C ?
Thanks,
Victor
It worked for me when I enabled the I2C_event interrupt on top of the DMA interrupt, see generated code and CubeMX config below
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
CubeMX does not automatically check the I2C1 event global interrupt when selecting DMA, I think it should (STmicro please fix this) as I dont see how it can work without it.
I've had the same issue. I've solved it by lowering the frequency.
ST Errata document says you have to step down the I2C frequency to 88kHz to fix some other problem.
I know it doesn't explain why this error doesn't occur in blocking mode but happens with DMA, but I hope it helps.
I had been struggling with the same problem on STM32F407 and I2C1.
After searching for potential bugs in the program flow, I found out that the function HAL_I2C_Master_Transmit_DMA leads to following line:
dmaxferstatus = HAL_DMA_Start_IT(hi2c->hdmatx, (uint32_t)hi2c->pBuffPtr, (uint32_t)&hi2c->Instance->DR, hi2c->XferSize);
After the first transfer, this won't return HAL_OK, which is necessary for the transmission to continue.
So my solution was simply abort the previous DMA interrupt in the callback function which is called after the transmission has completed. The same can be implied with HAL_I2C_Master_Receive_DMA. To resolve the problem, I added the following callback functions in main.c:
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance==hi2c1.Instance)
{
HAL_DMA_Abort_IT(hi2c->hdmatx);
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance==hi2c1.Instance)
{
HAL_DMA_Abort_IT(hi2c->hdmarx);
}
}
Please consider this is only a workaround. If someone finds out, I would like to know more about the underlying reason for this bug.

STM32F4 PLL Precision

Im trying to configure clocks on STM32F4 Discovery for precise time measurement. I have this configuration:
int main(void)
{
NVIC_InitTypeDef nvici;
GPIO_InitTypeDef gpioi;
TIM_TimeBaseInitTypeDef timtbi;
SystemInit();
RCC_HSEConfig(RCC_HSE_ON);
RCC_PLLConfig(RCC_PLLCFGR_PLLSRC_HSE, 8, 320, 8, 8);
RCC_PLLCmd(ENABLE);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCK);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
nvici.NVIC_IRQChannel = TIM2_IRQn;
nvici.NVIC_IRQChannelPreemptionPriority = 0;
nvici.NVIC_IRQChannelSubPriority = 1;
nvici.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvici);
gpioi.GPIO_Pin = GPIO_Pin_15;
gpioi.GPIO_Mode = GPIO_Mode_OUT;
gpioi.GPIO_OType = GPIO_OType_PP;
gpioi.GPIO_Speed = GPIO_Speed_100MHz;
gpioi.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD,&gpioi);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
timtbi.TIM_Period = 20000000;
timtbi.TIM_Prescaler = 0;
timtbi.TIM_ClockDivision = 0;
timtbi.TIM_CounterMode = TIM_CounterMode_Up;
timtbi.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timtbi);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
GPIO_SetBits(GPIOD,GPIO_Pin_15);
while(1)
{
}
}
void TIM2_IRQHandler()
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOD,GPIO_Pin_15);
}
with this i should have TIM2 sourced with 20MHz clock, but it appears to have diffrent frequency (about 10-30% diffrent). This problem appears for all other PLL configurations i tried, but when i use HSE as SYSCLK directly it works just fine. Am i doing something wrong, or is it PLL that isn't reliable?
Can't say with 100% certainty whether that's the problem, but after enabling the HSE using RCC_HSEConfig(), you should call RCC_WaitForHSEStartUp() since it takes a while for the HSE to start oscillating, and check the return code to make sure the call was successful and the HSE actually initialized.
Also, if you're using the system_stm32f4xx.c file that comes with the Standard Peripheral Library, you can scrap your PLL initialization code and just use the code that's called by SystemInit(). There are a few #defines that control the PLL configurations, near the beginning of the file (#define PLL_M, #define PLL_N and so on; their purpose should be self-evident). I always initialize my clocks using the code there, and they're always precise to within the crystal's accuracy. Note that this code assumes a 25 MHz oscillator by using PLL_M equal to 25, so you should set it to 8 for use with the STM32F4DISCOVERY board -- exactly as you've already done in your code. I'm not suggesting this because I have any prejudices against your code, but the code there has been tested far and wide, and in my experience it can be trusted.

stm32 DMA cannot send data to SPI1 DR (Cannot use DMA to send data to SPI)

I am trying to use DMA to send data to SPI1. SPI1 will then control DAC for voltage update.
The chip used is STM32F407. Therefore, the corresponding channel/stream is: channel3/stream5, as is shown in reference manual. However, when the DMA is enabled, no data is shown in SPI1->DR and there are no results shown in the oscilloscope.
The SPI works fine when SPI1->DR is written by software. Could anyone help to check out what has happened? Here comes the code:
uint16_t DACData[1];
void InitDMA(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_Cmd(DMA2_Stream2, DISABLE);
while (DMA2_Stream2->CR & DMA_SxCR_EN);
//SPI1 Tx DMA
DMA_DeInit(DMA2_Stream5);
DMA_StructInit(&DMA_InitStructure);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //Init DMA clock
DMA_InitStructure.DMA_Channel = DMA_Channel_3;//SPI1 Tx
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR); //Set the SPI1 Tx
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&DACData; //Set the memory location
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //
DMA_InitStructure.DMA_BufferSize = 1; //Define the number of bytes to send
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Normal mode (not circular)
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //Operate in 'direct mode'
DMA_InitStructure.DMA_MemoryBurst =DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst =DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_InitStructure); //DMA2, Channel 3, stream 5
//Enable the transfer complete interrupt for DMA2 Stream5
DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, ENABLE);
}
Here is how SPI1 initialized
void InitSPI1(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); //SCK
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); //MISO
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); //MOSI
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx; // one line transmission
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_16b; // one packet of data is 16 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;; // set the NSS management to hardware
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // SPI frequency is APB2 frequency / 4 = 21MHz
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE); // enable SPI1
}
I try to use these commands to start DMA:
DACData[0]=0xff00;
DMA_Cmd(DMA2_Stream5, ENABLE);
In case it may help, here comes the register values for DMA2, Stream 5
Such a line has been missed:
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
It enables SPI1 to use the DMA.
Just to add:
If you want to restart DMA, it seems you'll have to call not just:
DMA_Cmd(DMA2_Stream5, ENABLE);
but
DMA_ClearFlag(DMA1_Stream5, DMA_FLAG_TCIF5);
as well. Even though I didn't enable interrupts it seems this is still required.
Based on a quick look, the first thing that stands out is that you try to use parts of the DMA peripheral before enabling it. Try moving the
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
call before
DMA_Cmd(DMA2_Stream2, DISABLE);
while (DMA2_Stream2->CR & DMA_SxCR_EN);
as it could be eg. getting stuck in the loop. (I'm not sure which way the bits get read if the peripheral is not enabled when reading its registers)
If that doesn't help, leave a comment and I can try looking at it a bit more closely later.
I had a problem but nothing on the internet could help me, my solution was to change this;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
Other than that, my code is almost simliar, I don't get why yours would work.

How to configure the STM32103 for CAN and remapping the pins to PB8 and PB9

I am trying to, at this stage, simply send a transmission using CAN on the STM32F103 V8T6. The chip is implemented on a board that was customized by my company. Looking at the schematic I see that the CAN Tx and Rx pins were remapped to PB9 (Tx) and PB8 (Rx). All that being said, I have been able to use LoopBack mode successfully (using the latest example from STM "V3.5.0") but have have NOT been able to get Normal mode working. Can someone please let me know if they see an obvious flaw in my initial configuration?! I have included only the code related to configuration and left out the transmission function call.
int main (void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure CAN pin: RX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure CAN pin: TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Remap2 is for PB8 and PB9
GPIO_PinRemapConfig(GPIO_Remap2_CAN1 , ENABLE);
}
Here is my transmit function
void CAN_TransmitMyMsg(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
uint32_t i = 0;
uint8_t TransmitMailbox = 0;
uint8_t status = 0;
/* CAN register init */
CAN_DeInit(CANx);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE;
CAN_InitStructure.CAN_ABOM=DISABLE;
CAN_InitStructure.CAN_AWUM=DISABLE;
CAN_InitStructure.CAN_NART=DISABLE;
CAN_InitStructure.CAN_RFLM=DISABLE;
CAN_InitStructure.CAN_TXFP=DISABLE;
CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;
/* Baudrate = 125kbps*/
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1=CAN_BS1_2tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;
CAN_InitStructure.CAN_Prescaler=48;
CAN_Init(CANx, &CAN_InitStructure);
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=0;
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* transmit */
TxMessage.StdId=0x11;
TxMessage.RTR=CAN_RTR_DATA;
TxMessage.IDE=CAN_ID_STD;
TxMessage.DLC=2;
TxMessage.Data[0]=0xCA;
TxMessage.Data[1]=0xFE;
TransmitMailbox=CAN_Transmit(CANx, &TxMessage);
//wait until CAN transmission is OK
i = 0;
while((status != CANTXOK) && (i != 0xFFFF))
{
status = CAN_TransmitStatus(CANx, TransmitMailbox);
i++;
}
}
This resource has been helpful, but ultimately insufficient.
STM32F103 microcontroller CAN messages
Thanks!
Daniel
I had similar problems. My problem was in bad selection of comm parameters (timequantums and prescaler)
I debug it in this way:
I checked, that RSS pin of CAN transceiver (I used MCP 2551 I/P)
I switched it to loopback and with osciloscope check output signal on TX channel and on CANH to CANL (if the output is configured properly)
I looked to RCC configuration - the prescaler of APB1 clock has been changed to 2 (I excepted 4)
For a successful transmission, the CAN controller expects an 'acknowledge' bit which is part of the CAN protocol. If your CAN monitor (by NI) is in listen mode, there will not be an acknowledge-bit(!), and thus you don't get an indication of a successfull transmission at the sending end.
GPIO_Remap2_CAN1 remaps the CAN1 peripheral to PD0 and PD1, not PB8 and PB9. You want GPIO_Remap1_CAN1.
STD peripheral library is deprecated, but since I couldn't find any better examples, I used your not working code as a base.
Initially, I had the same behavior that it worked only in loopback.
After removing remapping to pins PB8 and PB9 and setting high speed GPIO for TX pin it started working. Here are my GPIO settings for PA11 and PA12
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure CAN pin: RX */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure CAN pin: TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = **GPIO_Speed_50MHz**;
GPIO_Init(GPIOA, &GPIO_InitStructure);
The rest I kept the same. (Except changing CANx to CAN1).