I am working with STM32 F3 discovery kit and started messing with SPI peripheral. I started with a simple loop-back system: I check the TXFIFOLVL status and if it is not full I send my data to DR register, which then should loop back to my RxBuffer (I read data from DR while RXFIFOLVL is not empty), but I've hit a problem - I don't get anything back on my receiving buffer and I can't seem to see why. I don't use HAL or Standard Peripheral Library, so I configure the SPI and use it via the register values like this:
Header file for SPI code:
#define GPIOA_ENABLE 0b1<<17 // Enable GPIO port A clock in AHBENR register
#define SPI1_CLOCK_ENABLE 0b1<<12 // Enable SPI1 clock in APB2ENR register
#define SPI1_PIN_ALT_FNC 0b1010<<4 // Sets PA5,PA6 & PA7 to Alternative function
#define SPI1_OUTPUT_TYPE ~(0b111<<5) // Sets PA5, PA6 & PA7 to push-pull
#define SPI1_PIN_SPEED 0b1111<<4 // Sets pins from 4 to 7 to work on 50 MHz output speed
#define SPI1_PIN_ALT_FNC_LOW 0b0101<<4 // Sets the Alternative function to AF5 in alternative function low register
#define SPI1_PIN_ALT_FNC_HIGH 0b0101<<4 // Sets the Alternative function to AF5 in alternative function high register
#define SPI1_BAUDRATE_PRESCALER_2 0b000<<3 // F_PCLK/2
#define SPI1_BAUDRATE_PRESCALER_128 0b110<<3 // F_PCLK/128
#define SPI1_MASTER_MODE 0b1<<2 // Sets the SPI1 to master mode
#define SPI1_PERI_ENABLE 0b1<<6 // Enable the SPI peripheral
#define SPI1_SSM_ENABLE 0b1<<9 // Enable SPI software slave management
#define SPI1_SSI_ENABLE 0b1<<8 // SPI1 internal slave select
#define SPI1_NSSP_ENABLE 0b1<<3 // Enable NSS pulse management
#define SPI1_FRXTH_8BIT 0b1<<12 //Set the FIFO reception threshold to 8 bits
#define SPI1_DATA_SIZE 0b0111<<8 // SPI1 DATA size
#define SPI1_TXFIFO_FULL_FLAG 0b11<<11 // SPI1 Tx FIFO transmission flag
#define SPI1_RXFIFO_EMPTY_FLAG 0b00<<9 // SPI1 Rx FIFO reception flag
#include "main.h"
#include "stm32f3xx_hal.h"
void spi_init();
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize);
Code file for SPI code:
#include "SPI_toSD.h"
/* SPI1 configuration
* PA5 - SCK
* PA6 - MISO
* PA7 - MOSI
*/
void spi_init(){
// Start the GPIO and peripheral clocks in Reset and Clock Control register
RCC->AHBENR |= GPIOA_ENABLE;
RCC->APB2ENR |= SPI1_CLOCK_ENABLE;
// Configure the GPIOs for SPI communication
GPIOA->MODER |= SPI1_PIN_ALT_FNC;
GPIOA->OTYPER &= SPI1_OUTPUT_TYPE;
GPIOA->OSPEEDR |= SPI1_PIN_SPEED;
GPIOA->AFR[0] |= SPI1_PIN_ALT_FNC_LOW;
GPIOA->AFR[1] |= SPI1_PIN_ALT_FNC_HIGH;
// Configure the SPI peripheral
SPI1->CR1 |= SPI1_BAUDRATE_PRESCALER_2;
SPI1->CR1 |= SPI1_SSM_ENABLE;
SPI1->CR1 |= SPI1_MASTER_MODE;
SPI1->CR1 |= SPI1_SSI_ENABLE;
SPI1->CR2 |= SPI1_DATA_SIZE;
SPI1->CR2 |= SPI1_FRXTH_8BIT;
SPI1->CR2 |= SPI1_NSSP_ENABLE;
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
}
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize){
int i;
while((SPI1->SR & 0b11<<11)==SPI1_TXFIFO_FULL_FLAG);
for(i=0;i<bufferSize;i++){
SPI1->DR |= *txBuffer; // send *txBuffer++
txBuffer++;
while((SPI1->SR & 0b11<<9)!=SPI1_RXFIFO_EMPTY_FLAG){
*rxBuffer = SPI1->DR;
rxBuffer++;
}
}
}
In main I simply define my buffers and initialize them like this:
uint8_t rx_buff[SIZE] = {0,0,0,0,0,0,0,0,0,0};
uint8_t tx_buff[SIZE] = {1,2,3,4,5,6,7,8,9,10};
So naturally after my spi_WriteRead() function is called I expect these buffers to have the same values.
I call my spi_init() function and in my while loop I call spi_WriteRead() function:
spi_WriteRead(rx_buff,tx_buff,SIZE);
SIZE is defined in my main.c as:
#define SIZE 10
I use SW4STM32 environment to code and debug so in my debugger I can see all of the register values. My SPI is initialized just as I defined and my data is being sent to TXFIFO buffer, but nothing comes to RXFIFO buffer. If I check SPI SR register I can see that my TXFIFO fills up, but RXFIFO flags say that it is empty.
Does anyone have any clue what I might be doing wrong? Am I grossly misunderstanding something simple about SPI? Thanks for your input!
EDIT:
Take a good look here:
#define SPI1_SSI_ENABLE 0b1<<8
...
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
Now you'll probably know why #define macros are generally considered a bad idea. You wouldn't have this problem if you'd use #define values from stm32f3xxx.h header, as all values with operations have parentheses there. You don't have them. That's why your code looks like this for the compiler:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~0b1<<8;
Which is equivalent to:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= (~0b1)<<8;
And going further:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= 0xffffff00;
Probably not what you wanted.
You should also know, that if your device is a master, then SSI and SSM bits should both be set. https://stackoverflow.com/a/42169600/157344
ORIGINAL:
Do note, that in these devices when you access SPI1->DR directly you send/receive TWO bytes at once. That's because this register is defined as uint16_t and SPI supports so called "Data packing" (search for it in the Reference Manual). If you want to send/receive one byte at a time, then you need to cast the register for write and read like that:
readByte = (volatile uint8_t*)SPI1->DR;
(volatile uint8_t*)SPI1->DR = writeByte;
BTW - why don't you use #defines provided by the CMSIS headers? You wouldn't have to define things like SPI1_MASTER_MODE...
Related
I'm trying to make a custom library for nRF24l01 for a stm32f103 target device, and I am writing code for a primary TX device.
And here I'm trying to read the register contents of nRF by sending the R_REGISTER command along with the address I am looking for, but I'm not able to figure out how to read the data after the R_REGISTER command is transmitted.
And i'm using the standard stm32f10x.h header file which comes along with startup files on Kiel uVision5.
Here are the configurations,
clock setup
RCC->CR |= RCC_CR_HSION; //HSI on
while( !(RCC_CR_HSIRDY & (RCC->CR)) ); //wait till its ready
//clocks for peripherals
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable clock forport A
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //enable clock for alternate functions
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //enable clock for SPI1
GPIO setup
these are my custom-defined functions, they just work fine
//GPIO pin setup as alternate function
pin_mode(IOPA, GPIOA, 7, op_50MHz, op_afpp); //MOSI pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 6, ip, ip_pupd); //MISO pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 5, op_50MHz, op_afpp); //SCK pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 4, op_50MHz, op_gppp); //CS pin as GPIO general_puspose_pin can run upto 50MHz
SPI setup
SPI1->CR1 |= SPI_CR1_MSTR; //master mode
SPI1->CR1 |= SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2; //at 571Kbps, max 31Mbps
SPI1->CR1 |= SPI_CR1_SSI; //Software slave management enabled
SPI1->CR2 |= SPI_CR2_SSOE; //SS o/p enable
SPI1->CR1 |= SPI_CR1_SPE; //turn on the SPI
Im stuck here
uint8_t SPI_read_uint8_t(uint8_t addr){
uint8_t reg_val;
//sending the read command first along with address where we are reading from
delay_us(50);
digital_writepin(GPIOA, 4, LOW);
SPI1->DR = (R_REGISTER | addr); //sending the R_REGISTER command along with address
while( (SPI1->SR) & (SPI_SR_BSY) );
//please help here, how do I read the Register data from MISO pin
uint8_t spi_read_write(uint8_t data)
{
while(SPI1 -> SR & SPI_SR_RXNE) (void)*(volatile uint8_t *)&SPI1 -> DR; //clean the FIFO
*(volatile uint8_t *)&SPI1 -> DR = data;
while(!(SPI1 -> SR & SPI_SR_RXNE));
return *(volatile uint8_t *)&SPI1 -> DR;
}
uint8_t youroperation(uint8_t command, uint16_t *data)
{
uint8_t status;
setCSLine();
status = spi_read_write(command);
*data = spi_read_write(0);
*data |= ((uint16_t)spi_read_write(0)) << 8;
clearCSLine();
return status
}
youroperation will return Sn status register
command parameter means Cn command bits
data will contain the Dx data bits
To read data from SPI slave you need to send dummy bytes because master provides the clock for the slave.
I'm attempting to use the STM32L011K4's DMA controller to communicate with slave devices over I2C. Currently, I have no slave devices and am just trying to get the microcontroller to send the start condition out onto the I2C bus, but that is not happening.
When I run this code in debugging mode through the STM32CubeIDE, I notice that the start bit is set, but it never clears even though the reference manual says it should be cleared by hardware once the start condition occurs (page 656 for I2C_CR2).
Monitoring the SDA and SCL lines on my oscilloscope also show that they are a logical 1. Note: I'm using the NUCLEO-L011K4 on a breadboard, so the IO pins are tied to Vref through 1k resistors. All configuration registers appear to contain the desired value when the code is stuck sending the start condition, so I don't believe they are getting clobbered by a random line of code.
I'm not sure what's preventing the start condition from being sent, so any help would be greatly appreciated.
STM32L011K4 Datasheet:
https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf
STM32L011K4 Reference Manual: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
Initialization code:
void Init_I2C1_DMA() {
/* Basic I2C Initialization for 100 kHz I2C, 24 MHz SYSCLK, /1 APB1 scaler */
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Enable peripheral clock for I2C1
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
I2C1->CR1 = 0; // Reset CR1
I2C1->TIMINGR = 0; // Reset timer settings
/* APB1 clock (I2C1 clock) is set in RCC_CFGR reg -- keep at divide by 1 -- 24 MHz SYSCLK
* Refer to table 103 for timing value source. t_presc was found to be 250 ns for 100 kHz I2C, so PRESC was set to match that for SYSCLK = 24 MHz
* All subsequent settings are copied from table 103 from STM32L011K4 reference manual
*/
I2C1->TIMINGR |= (0x5 << 28)|(0x4 << 20)|(0x2 << 16)|(0x0F << 8)|(0x13 << 0);
/* Desired settings:
* RXDMAEN enable, ANF enable.
*/
I2C1->CR1 |= (0x8 << I2C_CR1_DNF_Pos)|I2C_CR1_ERRIE;
I2C1->CR2 = 0; // Reset contents (ACKs are enabled by default)
NVIC_EnableIRQ(I2C1_IRQn);
NVIC_SetPriority(I2C1_IRQn, 0);
/* DMA initialization */
/* Since this is peripheral to memory, we use I2C1_RX, which is available on DMA channels 3,7. We used channel 3, but 7 would work the same. */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable peripheral clock for DMA1
DMA1_Channel3->CCR &= ~(0x00000001); // Disable Channel 3 DMA
// Configure DMA channel mapping
DMA1_CSELR->CSELR &= ~0x00000F00; // Channel 3 re-mapping mask
DMA1_CSELR->CSELR |= 0x00000600; // Channel 3 re-mapped to I2C1_RX
/* Configure NVIC for DMA */
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
return;
}
void I2C1_DMA_Start_Read(uint8_t SlaveAddress, uint8_t RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
// We need to put the device address out on the serial line before we can hand it over to the DMA
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
DMA1_Channel3->CCR &= ~(0x00007FFF); // Channel 3 DMA mask
// Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
DMA1_Channel3->CCR |= (0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) RegisterAddress;
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
I2C1->CR1 &= (~I2C_CR1_TXDMAEN); // Disable TX DMA for I2C1
I2C1->CR1 |= I2C_CR1_RXDMAEN; // Enable RX DMA for I2C1
// I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
while(!(I2C1->ISR & I2C_ISR_TXE));
I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
I2C1->CR2 |= I2C_CR2_RD_WRN;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C1
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
// ???
return;
}
According with reference manual(p. 604)
you need uncomment I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); and comment I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); for set slave address, and you need set the number of bytes to be transferred.
I can't see your initialisation of GPIO. Check is GPIO settings right (Alternative function and open-drain mode).
Also in reference manual written this
PE must be kept low during at least 3 APB clock cycles in order to perform the software
reset. This is ensured by writing the following software sequence: - Write PE=0 - Check
PE=0 - Write PE=1.
I think you should try to do so.
Also I advise using 4.7k resistor for pulling to VDD.
I'm trying to write my own driver for USART_TX on an STM32L476RG Nucleo Board.
Here the datasheet and the reference manual.
I'm using Keil uVision 5 and I set in the Manage dialog:
CMSIS > Core
Device > Startup
Xtal=16MHz
I want to create a single character transmitter. According to the manual instructions in Sec. 40 p 1332 I wrote this code:
// APB1 connects USART2
// The USART2 EN bit on APB1ENR1 is the 17th
// See alternate functions pins and label for USART2_TX! PA2 is the pin and AF7 (AFRL register) is the function to be set
#include "stm32l4xx.h" // Device header
#define MASK(x) ((uint32_t) (1<<(x)));
void USART2_Init(void);
void USART2_Wr(int ch);
void delayMs(int delay);
int main(void){
USART2_Init();
while(1){
USART2_Wr('A');
delayMs(100);
}
}
void USART2_Init(void){
RCC->APB1ENR1 |= MASK(17); // Enable USART2 on APB1
// we know that the pin that permits the USART2_TX is the PA2, so...
RCC->AHB2ENR |= MASK(0); // enable GPIOA
// Now, in GPIOA 2 put the AF7, which can be set by placing AF7=0111 in AFSEL2 (pin2 selected)
// AFR[0] refers to GPIOA_AFRL register
// Remember: each pin asks for 4 bits to define the alternate functions. see pg. 87
// of the datasheet
GPIOA->AFR[0] |= 0x700;
GPIOA->MODER &= ~MASK(4);// now ... we set the PA2 directly with moder as alternate function "10"
// USART Features -----------
//USART2->CR1 |=MASK(15); //OVER8=1
USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?
//USART2->BRR = 0x1A1; //This one works!!!
USART2->CR1 |=MASK(0); //UE
USART2->CR1 |=MASK(3); //TE
}
void USART2_Wr(int ch){
//wait when TX buffer is empty
while(!(USART2->ISR & 0x80)) {} //when data is transfered in the register the ISR goes 0x80.
//then we lock the procedure in a while loop until it happens
USART2->TDR =(ch & 0xFF);
}
void delayMs(int delay){
int i;
for (; delay>0; delay--){
for (i=0; i<3195; i++);
}
}
Now, the problem:
The system works, but not properly. I mean: if I use RealTerm at 9600 baud-rate, as configured by 0x683 in USART_BRR reg, it shows me wrong char but if I set 2400 as baud rate on real term it works!
To extract the 0x683 in USART_BRR reg i referred to Sec. 40.5.4 USART baud rate generation and it says that if OVER8=0 the USARTDIV=BRR. In my case, USARTDIV=16MHz/9600=1667d=683h.
I think that the problem lies in this code row:
USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?
because if I replace it as
USART2->BRR = 0x1A1; //USARTDIV=16Mhz/9600?
THe system works at 9600 baud rate.
What's wrong in my code or in the USARTDIV computation understanding?
Thank you in advance for your support.
Sincerely,
GM
The default clock source for the USART is PCLK1 (figure 15) PCLK1 is SYSCLK / AHB_PRESC / AHB1_PRESC. If 0x1A1 results in a baud rate of 9600, that suggests PCLK1 = 4MHz.
4MHz happens to be the default frequency of your processor (and PCLK1) at start-up when running from the internal MSI RC oscillator. So the most likely explanation is that you have not configured the clock tree, and are not running from the 16MHz HSE as you believe.
Either configure your clock tree to use the 16MHz source, or perform your calculations on the MSI frequency. The MSI precision is just about good enough over normal temperature range to maintain a sufficiently accurate baud rate, but it is not ideal.
I am trying to create a C program which receives a char via UART, "prints" the correspondent binary by turning on 8 leds in my breadboard and send the char back to the transmitter.
Here is the code I am using:
//CPU clock
#define F_CPU 1000000UL
//Baud
#define BAUD 9600
//Baud rate
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
#include <avr/io.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdint.h>
//Communication Parameters:
//8 bits of data
//1 bit stop
//No parity
void uart_init(void){
//Bit 7 - RXCIEn: RX complete interrupt enable
//Bit 6 - TXCIEn: TX complete interrupt enable
//Bit 5 - UDRIE: USART data register empty interrupt enable
//Bit 4 - RXENn: Receiver enable
//Bit 3 - TXENn: Transmitter enable
UCSR0B = 0b10011000;
//Bit 7 - RXCn: USART receive complete.
//Bit 6 - TXCn: USART transmit complete
//Bit 5 - UDREn: USART data register empty
UCSR0A = 0b00000000;
//Bit 11:0 – UBRR11:0: USART baud rate register
//Whereas H are the higher bits and L the lower bits
//It comes from the setbaud.h
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Bit 7:6 - UMSELn1:0: USART mode select
//00 Asynchronous USART
//01 Synchronous USART
//11 Master SPI
//Bit 5:3 - Reserved bits in MSPI mode
//Bit 2 - UDORDn: Data order
//Bit 1 - UCPHAn: Clock phase
//Bit 0 - UCPOLn: Clock polarity
UCSR0C = 0b10000110;
}
// function to send data
void uart_transmit (uint8_t data)
{
while (!( UCSR0A & (1<<UDRE0))); // wait while register is free
UDR0 = data; // load data in the register
}
int main (void)
{
//Starts UART
uart_init();
//All led GPIOs as output
DDRB = 0xFF;
DDRC = 0x01;
//Enabling interrupts
sei();
while(1)
{
;
}
return 0;
}
ISR(USART_RX_vect)
{
//Variable to hold the incoming char
uint8_t received_bit = UDR0;
PORTC ^= 0x01;
PORTB = 0x00;
PORTB = received_bit;
uart_transmit(received_bit);
}
When I flash it to the chip and start using it, I get a weird behaviour.
I am sending a "U" which is a nice binary 01010101 to compare with.
However I am getting weird answers back from my chip:
My questions regarding UART under an ATMEGA168a are the following:
When setting the F_CPU am I supposed to stay with the 1MHZ used by the ATMEGA168a or do I have to use the one of my transmitter (Intel i7)? Could it be the problem?
When does the UDR0 gets "updated"? Whenever I hit the enter to send the character to chip via Terminal?
What could be generating this issue?
In the function uart_init() you set bits 7:6 to 10 which is a reserved state according to the ATMega 168A manual. To get the desired asynchronous UART functionality, set them to 00:
UCSR0C = 0b00000110;
The other reason why your example was not working was the baudrate settings, as explained in my comment below.
You already included the <util/setbaud.h> header file, which contains macros to make UART setup easier. Look here for the documentation. These macros take the input provided by you in F_CPU and BAUDRATE and calculate the settings for the UART configuration registers (UBRRH_VALUE and UBRRL_VALUE).
You used it almost correctly, however to take advantage of the UART baudrate doubling feature of the ATmega, add the following code after setting the UBRR0H/L value:
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
This sets or clears the U2X0 bit dependent on the calculations of the setbaud macros.
Also, I believe you can remove the line
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
because that's exactly what setbaud.h does.
I'm trying to get PWM functioning on two pins of my STM32030R8T6, it's on a Nucleo development board and I'm using Keil. For learning, I've mostly been following the material on this website, but with adaptations as that site uses a different MCU. There really isn't much to setting up the PWM so I'm not quite sure what I've done wrong, I know the timer is working because the on-board LED blinks 1.5 times per second, but when I monitor the Ch1 and Ch2 output pins with my scope I get nothing. I'm pretty sure the pins are correctly set in Alternate Function Push-Pull because they're set the same as the MCO pin which is functioning and showing 24 MHz (Though my cheap scope has some problems determining that...). I've attached all of my relevant and even remotely possibly relevant code. And for your convenience:
UM0360 Reference Manual (STM32F030...)
I'd post links to the Nucleo User Manual and Device Datasheet as well but I can't post more than two links, since this is my first question and my reputation is less than ten.
Any help on what I might be doing wrong is appreciated, I'm sure it's something stupid.
#include "stm32f0xx.h"
void Initializations(void);
int main(void)
{
Initializations();
while(1)
{
/* Toggle onboard LED whenever timer overflows */
if((TIM3->SR & TIM_SR_UIF))
{
TIM3->SR &= ~TIM_SR_UIF;
GPIOA->ODR ^= GPIO_ODR_5;
}
}
}
void Initializations(void)
{
/* CLK CONFIG */
RCC->CFGR |= RCC_CFGR_HPRE_DIV2 |
RCC_CFGR_PPRE_DIV16 |
RCC_CFGR_MCO_SYSCLK |
RCC_CFGR_PLLMUL6;
/* Activate PLL, wait */
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* Enable IO CLKs */
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
/* Enable peripheral CLKs */
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* PIN INITIALIZATIONS */
GPIOA->MODER |= GPIO_MODER_MODER5_0 | // Onboard LED (General output)
GPIO_MODER_MODER2_1 | // USART2 TX (Alternate function)
GPIO_MODER_MODER3_1 | // USART2 RX (Alternate function)
GPIO_MODER_MODER6_1 | // TIM3 CH1 (Alternate function)
GPIO_MODER_MODER7_1 | // TIM3 CH2 (Alternate function)
GPIO_MODER_MODER8_1 | // MCO (Alternate function)
GPIO_MODER_MODER9_1 | // USART1 TX (Alternate function)
GPIO_MODER_MODER10_1; // USART1 RX (Alternate function)
/* TIMER INITS */
TIM3->PSC = 7;
TIM3->ARR = 59999;
/* CCM1 */
TIM3->CCMR1 |= TIM_CCMR1_OC1M_0 |
TIM_CCMR1_OC1M_1;
TIM3->CCR1 |= 4499;
TIM3->CCER |= TIM_CCER_CC1E; // Enable Ch1
/* CCM2 */
TIM3->CCMR1 |= TIM_CCMR1_OC2M_0 |
TIM_CCMR1_OC2M_1;
TIM3->CCR2 |= 29999;
TIM3->CCER |= TIM_CCER_CC2E; // Enable Ch2
TIM3->CR1 |= TIM_CR1_CEN; // Enable TIM3
/* USART INITS */
RCC->CFGR3 |= RCC_CFGR3_USART1SW_0; // Clock USART1 from SYSCLK
}
In addition to setting the pin to use an alternate function, you must also set which alternate function to use.
This is described in section 8.3.2 (pdf page 128) of the document you linked.
These are the AFRL (for pins 0-7) and AFRH (for pins 8-15) registers on the port.
For example, based on your code, and if TIM3 uses alternate function 2 and is on pins 6 and 7, (and assuming the alternate code was currently 0) you'd do
GPIOA->AFRL |= (2 << (6 * 4)) | (2 << (7 * 4));
If it isn't 0 or you want to be sure, mask off the bits first (each pin gets 4 bits).
(Note, your header may name registers differently than mine, and your alternate functions may also be different; I usually work with STM32F407 or STM32F334. To find the table of alternate functions to see which one you need, you'll have to look that up in the datasheet for the particular chip you are using, as opposed to the family reference manual which you linked above)
The more general form is
mode << (pin * 4)
for AFRL and
mode << ((pin - 8) * 4)
for AFRH.