I have a STM32F401xB/C board.
I am trying to create a PWM signal for my DC motors. I have followed this tutorial and seem to understand the code.
https://ruturajn.hashnode.dev/generating-pwm-pulses-on-the-stm32f407-for-servo-motor-control-using-bare-metal-programming
But after I change the pin I want the PWM output from I get no signal. The tutorial refrences the PA5 pin, which works, but PA15 does not work even though it is connected to the same timer TIM2 and channel.
Any idea?
This is my code:
//initialises the GPIO pins
void GPIO_Init(){
//give and clock to the GPIOB and GPIOA device
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
//THESE PINS ARE THE PWM DRIVERS
//PA15
//set alternative mode
GPIOA->MODER &= ~(GPIO_MODER_MODER15_1);
GPIOA->MODER |= GPIO_MODER_MODER15_1;
//low part of the alternate function register
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL15_0;
}
//initialise the TIM2 timer device
void TIM2_Init(){
//give pwr and clk to TIM2 device
RCC->AHB1ENR |= RCC_APB1ENR_TIM2EN;
//set prescaler to 1Mhz = 1 microSeconds
TIM2->PSC = 16-1;
//total period of the timer = 20ms
TIM2->ARR = 20000;
//set counter to 0
TIM2->CNT = 0;
//set capture/compare mode register 1 to PWM Mode 1
TIM2->CCMR1 = 0x0060;
//set capture/compare enable register to output on chanel 1
TIM2->CCER |= 1;
//set >50% power
TIM2->CCR1 = 10000;
}
void setup(){
//set the timer to 16 mhz
RCC->CFGR |= 0 << 10;
GPIO_Init();
TIM2_Init();
//start TIM2 timer
TIM2->CR1 |= 1;
}
This only clears a single bit
//set alternative mode
GPIOA->MODER &= ~(GPIO_MODER_MODER15_1);
...
Should be
GPIOA->MODER &= ~(GPIO_MODER_MODE15_Msk);
...
PA15 is shared with JTDI and could have external interference.
By default it has pull-up enabled, that should be cleared if this pin is used as an output.
The problem was that I was not setting the clock and power properly for the clock. The register I should have checked is RCC->APB1ENR instead of RCC->AHB1ENR. The fact that I got power through PA5 was a coincidence.
Related
I am learning to use the STM32F302R8 board and I have this problem.
TIM2 is configured with toggle output CH1 on PA0. (This works fine).
TIM15 is configured as input capture CH1 on PA2.
I have a jumper between PA0 and PA2.
The aim is that when TIM2 reaches the value of CCR1 it triggers the PA0 pin, which happens, and having the jumper with the PA2 pin should trigger the TIM15 input but this does not happen.
The while that checks the CC1IF flag never ends, it doesn't detect anything.
What can it be?
while (1)
{
// wait until input edge is captured
while ( !(TIM15->SR & TIM_SR_CC1IF)) {}
timestamp = TIM15->CCR1; // read captured counter value
}
void mi_GPIO_Init ( void ) {
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
GPIOA->MODER &= ~GPIO_MODER_MODER0; // clear PA0 mode
GPIOA->MODER |= GPIO_MODER_MODER0_1; // set pin to alt function
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL0; // clear pin AF bits
GPIOA->AFR[0] |= 0x0000001; // set pin to AF1 for TIM2_CH1
GPIOA->MODER &= ~GPIO_MODER_MODER2; // clear PA2 mode
GPIOA->MODER |= GPIO_MODER_MODER2_1; // set pin to alt function
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL2; // clear pin AF bits
GPIOA->AFR[0] |= 0x0000900; // set pin to AF9 for TIM15_CH1
// Configure TIM2 to wrap around at 1 Hz and toggle CH1 output when the counter value is 0
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
TIM2->PSC = 800 - 1; // divided by 800
TIM2->ARR = 10000 -1; // divided by 10000
TIM2->CCMR1 = TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1; // set output to toggle on match
// The rest of the bits are set to 0.
TIM2->CCR1 = 0; // set match value
TIM2->CCER |= TIM_CCER_CC1E; // enable CH1 compare mode
TIM2->CNT = 0; // clear timer counter
TIM2->CR1 |= TIM_CR1_CEN; // enable TIM2
// Configure TIM15 to do input capture
RCC->APB2ENR |= RCC_APB2ENR_TIM15EN; // enable TIM15 clock
TIM15->PSC = 8000 - 1; // divided by 8000
TIM15->ARR = 0xFFFF; // count until ARR
TIM15->CCMR1 &= ~TIM_CCMR1_CC1S; // set CH1 to capture at every edge
TIM15->CCMR1 |= TIM_CCMR1_CC1S_0; // CC1 as input, IC1 is mapped on TI1
TIM15->CCER |= TIM_CCER_CC1E; // enable CH1 capture rising edge
TIM15->CR1 |= TIM_CR1_CEN; // enable TIM15
}
I moved the clock trigger lines to the beginning of the code and instead of PA2 I use PB14 and now it works fine.
At the moment I use TIM2 on the stm32f4 - Discovery Board to count pulses (rising edges). How can I adjust the thresholds for the rising edges ? I want to count 1 V pulses. At the moment it is just possible to count 2V pulses. I'm not able to find something about this in the documentation.
Here is the code of my timer function.
void timer_2_pulse_counter_gpioa1_Init(){
RCC->AHB1ENR |= 0x01; // 1: IO port A clock enabled
//RCC->AHB1ENR |= 0x10; // 1: IO port E clock enabled
// APB1 peripheral reset register
RCC->APB1ENR |= 0x01; // 1: enable TIM2
// GPIO port mode register (GPIOx_MODER)
GPIOA->MODER |= 0x00000008; // 10: Alternate function mode PA1 => AF mode
GPIOA->AFR[0] |= 0x00000010; // 1000: Must refer to AF1 (alternate function for TIM1/ TIm2)
GPIOA->PUPDR |= 0x00000008; // Sets pull down resistor for PA1
// CCMR!: capture/compare mode register 1
TIM2->CCMR1 |= 0x0100; // CC2 channel is configured as input, IC2 is mapped on TI2
// SMCR: Slave Mode control register
TIM2->SMCR |= 0x0007; // Bits[2:0] 111: External Clock Mode 1 - Rising edges of the selected trigger clock the counter.
TIM2->SMCR |= 0x0060; // Bits[6:4] 110: selected Trigger: Filtered Timer Input 2 (TI2FP2)
TIM2->ARR = 0xFFFF; // Set the timer reset on the highest possible value
TIM2->CR1 |= 0x0001; //0001 Enable Timer
}
Many thanks in advance for your support!
Digital input cannot "trigger" at particular voltage set by the programmers level. But you can use it in the analog mode using ADC "analogue watchdog" mode.
If your micro has a built in comparator (many STM32Fxxxxs have one) you can use it to set the "trigger" voltage.
I use Stm32f103c8t6 processor and I want to make 1 second counter. Normaly I dont use tımer update ınterrupt and my counter counts correctly but when I add timer update ınterrupt in code my counter doesnt count correctly. So fast increment. And ı cant remove update interrupt flag (UIF) in TIM1's SR register. If I remove this flag my code is entering infinty loop. I cant any solution for this problem. Thanks for help
This part is bring in startup_stm32f10x_md.s
/**
* #brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* #param None
* #retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop -----------------------------> my code is stuck here
.size Default_Handler, .-Default_Handler
And this is a my code.
#include <stddef.h>
#include "stm32f10x.h"
void CLK_Config(){
RCC-> CR = 0x00000000; //CR Reset
RCC-> CR |= 0x00010000; //HSE enable
while(!(RCC-> CR & 0x00020000)); //HSE FLAG control
RCC-> CR |= 0x00080000; //CSS enable
RCC-> CR |= 0x01000000; //PLL On
RCC-> CFGR |= 0x00010000; //HSE Select PLL input
RCC-> CFGR |= 0x001C0000; //PLL Multi With 9 = 72 Mhz
RCC-> CFGR |= 0x00000002; //PLL Select as SYSCLK
RCC-> CFGR |= 0x00000400; //APB1 Clock divided by 2
RCC-> APB1ENR |= 0x18000000; //APB1 BKP Clock Enable
PWR-> CR |= 0x0100; //PWR BKP Access Enable
RCC-> APB2ENR |= 0x00000001; //APB2 AFIO Clock Enable
}
void TIM1_Config(){
RCC-> APB2ENR |= 0x00000800; //TIM1 CLK Enable
TIM1-> CR1 |= 0x0085; //Update Request Source, Counter Enable
TIM1-> DIER = 0x0001; //Update Interrupt Enable
TIM1-> ARR = 0x1F40; //8000 is set as Auto Reload Value
TIM1-> PSC = 0x2327; //9000 is set as Prescaler Value for 1 sn Formula: 1sn=1Hz=(PCLK/(PSC*ARR))
}
void USART1_Config(){
RCC-> APB2ENR |= 0x00000004; //GPIOA CLK Enable
GPIOA-> CRH |= 0x00000AA0; //GPIOA 10,9 Push-Pull Alternate Function 2Mhz
RCC-> APB2ENR |= 0x00004000; //USART1 CLK Enable
USART1-> BRR |= 0x00001D4C; //USART1 Baund Rate 9600
USART1-> CR1 |= 0x000020C8; //USART, TXE Interrupt, TC Interrupt, Transmitter Enable
}
void Interrupt_Config(){
NVIC-> ISER[0] |= 0x02000000; //NVIC TIM1 UP
NVIC-> ISER[1] |= 0x00000020; //NVIC USART1 Global Interrupt
NVIC-> IP[25] = 0x10; //TIM1 UP Interrupt Priority 2. 25th Interrupt
NVIC-> IP[37] = 0x40; //USART1 Global Interrupt Priority 5. 37th Interrupt
}
uint8_t count1sec; // Global variable
int main(void)
{
CLK_Config();
Interrupt_Config();
TIM1_Config();
USART1_Config();
while (1)
{
if(count1sec != 0){
USART1-> DR = count1sec;
}
}
}
void TIM1_UP_IRQHandler(){
TIM1-> SR = 0x00000000;
NVIC-> ICPR[0] = 0x02000000;
count1sec += 1;
}
I'm not able to detect the exact cause of your problem, but I can provide some suggestions and make some guesses.
1) Do not use magic numbers! Use predefined bit names. Here is an example:
USART2->BRR = 0x1a0; // 115200 bps # 24 MHz (OVER8=1, ONEBIT=1)
USART2->CR1 |= USART_CR1_OVER8 // Oversampling is reduced for higher baud rates
| USART_CR1_IDLEIE // Idle line detection interrupt is enabled
| USART_CR1_TE // Transmitter is enabled
| USART_CR1_RE; // Receiver is enabled
USART2->CR3 |= USART_CR3_ONEBIT // One bit mode for increased clock deviation tolerance
| USART_CR3_DMAT // DMA for TX
| USART_CR3_DMAR; // DMA for RX
USART2->CR1 |= USART_CR1_UE; // Enable USART2
2) Use CMSIS functions to access NVIC functionality. Here is an example:
NVIC_SetPriority(TIM6_DAC_IRQn, 2);
NVIC_EnableIRQ(TIM6_DAC_IRQn);
3) You have Auto-reload preload enable bit set in TIM1->CR1 register. This normally delays the update of the ARR register until the next update event. I'm not sure how it works during the initial run of the timer, but I suggest to avoid using it until you're sure everything works fine.
4) You normally don't need to clear pending bits in NVIC. I suggest removing that code from the ISR. Clearing the flags in the peripheral registers is all you need and you're already doing it with TIM1->SR = 0 line.
5) The reason you stuck in Infinite_Loop is probably the USART TX interrupt. It gets fired but as you didn't supply a ISR for it, it falls into the Default_Handler. You load DR manually in the main loop, so in this case you don't even need USART TX interrupts.
I have same problem today.
I'm using STM32H743zi.
In my case, after clearing SR, at least 13 'nop' required.
I don't know why.
My final solution is:
void TIM16_IRQHandler(void)
{
TIM16->SR = 0 ;
volatile uint32_t sr = TIM16->SR ;
UNUSED(sr) ;
}
Reading back make it work, but I don't know why.
I try to disable MPU and/or DCACHE, but it have no difference.
I'm programming a board and want a PWM signal to appear on a pin to drive a LED. I'm using a STM32 NUCLEO-F207ZG board, and only low-level register programming. It is not working.
I've looked into the manual, datasheet and application note. Also some Google searches have been done.
//Enable timer 1 clock:
RCC->APB2ENR |= BIT0;
//Output mode on PWM
TIM1->CCMR1 |= BIT5 | BIT6;
//Period:
TIM1->ARR = 0x0000FFFF;
//Duty cycle:
TIM1->CCR1 = 0x00007FFF;
//Enable preload
TIM1->CCMR1 |= BIT3;
TIM1->CR1 |= BIT7;
//Enable CC1 output
TIM1->CCER |= BIT0;
//Enable timer
TIM1->CR1 |= BIT0;
//Enable GPIOE clock
RCC->AHB1ENR |= BIT4;
//Alternate function mode voor pin PE_9
GPIOE->MODER |= BIT19;
GPIOE->AFR[1] |= BIT4;
I expect a PWM signal on pin D6 (PE_9), to drive a LED. But the LED doesn't seem to do anything.
I didn't check your code bit by bit, but it seems OK in general. But I suspect a possible cause of the problem: Normally you should wait a few clock cycles before accessing any peripheral just after enabling its clock. I may be wrong, but it's possible that these 2 lines of code are ignored by the peripherals, as they are executed just after enabling the clocks:
TIM1->CCMR1 |= BIT5 | BIT6;
GPIOE->MODER |= BIT19;
I suggest using a debugger to check if all the peripheral registers are loaded with the correct values.
I also suggest trying other PWM channels. There may be some conflicts with the pin you're using because of the board hardware configuration.
I'm using the STM32F4 trying to read over SPI. Debug environments is Visual Studio with the VisualGDB plugin.
The spi config is as follows. 8-bit frames, MSB first, using GPIOA/B for the SPI signals
void spiInit() {
RCC->AHB1ENR |= 3; //enable gpioA/B clock
RCC->APB2ENR |= 0x1000; //enable SPI1 clock (bit 12)
// configure A5 for SCLK
GPIOA->MODER &= ~0x00000C00; //clear pin mode for pin 5
GPIOA->MODER |= 0x00000800; //alternate function mode for pin 5
GPIOA->AFR[0] &= ~0x00F00000; //clear alternate mode
GPIOA->AFR[0] |= 0x00500000; //setup pins 5 for AF5
// configure B4 for MISO
GPIOB->MODER &= ~0x00000300; //clear pin mode for pin 4
GPIOB->MODER |= 0x00000200; //alternate function mode for pin 5
GPIOB->AFR[0] &= ~0x000F0000; //clear alternate mode
GPIOB->AFR[0] |= 0x00050000; //setup pins 4 for AF5
// configure B5 for MOSI
GPIOB->MODER &= ~0x00000C00; //clear pin mode for pin 5
GPIOB->MODER |= 0x00000800; //alternate function mode for pin 5
GPIOB->AFR[0] &= ~0x00F00000; //clear alternate mode
GPIOB->AFR[0] |= 0x00500000; //setup pins 5 for AF5
// configure A15 for output SS
GPIOA->MODER &= ~0xC0000000; //clear PA15 function bits
GPIOA->MODER |= 0x40000000; //set PA15 (SS) as output
SPI1->CR1 = 0x31E; //8-bit frames
SPI1->CR2 = 0; //mostly read/status/interrupt config
SPI1->CR1 |= 0x40; //enable SPI1
GPIOA->BSRR = 0x00008000; //deassert SS
}
Writes are fine, data is loaded correctly to slave and it responds as expected, reads from the slave however are not working. I can see that MISO looks good on the bus when i look at the waveforms, but it's not picked up by the Master.
After a few attempts at watching the SPI status-register flags, (getting the results described above) I'm using a utility function that i saw on another SO question (here). The data always reads 0 and i am confused, the code waits for the receive buffer to fill, but there seems to nothing there when it is read.
uint8_t SPI_ReadWrite8(SPI_TypeDef *spi, uint8_t data)
{
while(!(spi -> SR & SPI_SR_TXE)); //Wait for tx buffer to empty
*(volatile uint8_t *)&spi->DR = data; //Send data
while (!(spi -> SR & SPI_SR_RXNE)); //Wait for rx buffer to fill
return *(volatile uint8_t *)&spi->DR;
}
Any ideas what i'm missing?