I have to wire a code for STM32F4 discovery and pcf8574 with I2C.
I can't use any library function. I try something I didn't do it. I did write after init code.
My init code
RCC->APB1ENR|=RCC_APB1ENR_I2C1EN ; // enable APB1 peripheral clock for I2C1
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOBEN; // enable clock for SCL and SDA pins
//SCL on PB6 and SDA on PB7
GPIOB->MODER|=GPIO_MODER_MODER6; // set pin to alternate function
GPIOB->MODER|=GPIO_MODER_MODER7; // set pin to alternate function
GPIOB->OSPEEDR |=GPIO_OSPEEDER_OSPEEDR6; //set GPIO speed
GPIOB->OSPEEDR |=GPIO_OSPEEDER_OSPEEDR7; //set GPIO speed
GPIOB-> OTYPER |= GPIO_OTYPER_OT_6; // set output to open drain --> the line has to be only pulled low, not driven high
GPIOB-> OTYPER |= GPIO_OTYPER_OT_7; // set output to open drain --> the line has to be only pulled low, not driven high
GPIOB-> PUPDR |=GPIO_PUPDR_PUPDR6_0; // enable pull up resistors
GPIOB-> PUPDR |=GPIO_PUPDR_PUPDR7_0; // enable pull up resistors
GPIOB-> AFR[1] = 0x44000000; // Connect I2C1 pins to AF (af4)
// configure I2C1
I2C1-> CR2 = 8; // set peripheral clock to 8mhz
I2C1-> CR2 = 40; // 100khz i2c clock
I2C1-> CR2 |= ~(I2C_CR1_SMBUS); // I2C mode
I2C1-> OAR2 = 0x00; // address not important
I2C1-> CR2 |= 1; // i2c enable;
I2C1-> CR2 |= ~(I2C_CR1_SMBUS); // I2C mode
This line does something different that you think. If the idea was to clear that bit it shuold be
I2C1-> CR2 &= ~(I2C_CR1_SMBUS); // I2C mode
Otherwise you set all the bits in the CR2 register except I2C_CR1_SMBUS which is left unchanged.
Another problem is that you try to set CR2 using CR1 bit definitions.
Same is with the enable bit - wrong register.
I2C should be reset before first use on many STM32 micros.
Hi I am having the same problem with setting an STM32F4 DISCO board to go
I noticed that you are setting
I2C1-> CR2 = 8; // set peripheral clock to 8mhz
I2C1-> CR2 = 40; // 100khz i2c clock
you are writing twice to the same register.
For the I2C clock you need to set I2C1->CCR to the calculated value
I hope this helps
Related
I have this problem when trying to read the ADC value (using adc1_in6) on the nucleo-l476rg micro controlle: I get in an infinite loop when checking if the EOC bit of the status register (ISR) "adc_read function".
Here's the adc.c file content:
#include "stm32l476xx.h"
#include "adc.h"
#define ADC1_SEQL1 0X00
void pa1_adc1_init(void)
{
/**************configure adc gpio pin ******************/
//enable clock access to GPIOA
RCC->AHB2ENR |= (1U<<0);
//set the mode of pa1 to analog mode
GPIOA->MODER |=(1U<<2);
GPIOA->MODER |=(1U<<3);
/****************configure adc periph ******************/
// enable clock access to adc module
RCC->AHB2ENR |=(1U<<13);
/*******configure adc parameters****************/
//conversion sequence start
ADC1->SQR1 |=(1U<<7);
ADC1->SQR1 |=(1U<<8);
//conversion sequence length
ADC1->SQR1 &= 0xFFFFFFF0UL;
//Enable adc module
// Check voltage regulator status
ADC1->CR &= 0x00000000;
ADC1->CR |= ADC_CR_ADVREGEN;
while (!(ADC1->CR & ADC_CR_ADVREGEN)) {
// Wait for voltage regulator startup time to pass
}
// Set ADVREGEN bit to 1
ADC1->CR |= ADC_CR_ADVREGEN;
// Wait for voltage regulator startup time to pass
// Set ADEN bit to 1
ADC1->CR |= ADC_CR_ADEN;
}
void start_conversion(void)
{
/*start adc conversion */
ADC1->CR |=ADC_CR_ADSTART;
}
uint32_t adc_read(void)
{
// wait for conversion to be complete
while (!(ADC1->ISR & ADC_ISR_EOC)){}
//read converted result from ADC1 data register
return (ADC1->DR);
}
I reviewed the reference manual to check if there are any specific requirements about the ISR register but it doesn't show anything special to do.
You need to have delay (or readback as access is strongly ordered) when enable the clock as the next register access is very likely to fail.
ADC has more than one clock domain and enabling digital part is not enough
You should calibrate the ADC before use.
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.
Right Now I am working with the STM32F103C8 blue pill board, I am working with UART protocol here I am able to send data to putty it's working but send data from putty to microcontroller (unable to receive data from the putty). what is the problem I can't understand everything is okay but I can't receive data?
Code:
#include "stm32f10x.h" // Device header
volatile char data;
int main(void){
//configure HSI SYSTEM clock .
RCC->CR = RCC_CR_HSION;
RCC->CFGR = RCC_CFGR_SW_HSI;
//Enable Alternate function clock, PORTA and USART1.
RCC->APB2ENR = RCC_APB2ENR_AFIOEN|RCC_APB2ENR_USART1EN|RCC_APB2ENR_IOPAEN;
//Alternate function mode PA9
GPIOA->CRH = GPIO_CRH_MODE9_1|GPIO_CRH_CNF9_1;
//ALternate function mode PA10.
GPIOA->CRH |= GPIO_CRH_MODE10_1|GPIO_CRH_CNF10_1;
//USART1 Enable.
USART1->CR1 = USART_CR1_UE;
//8-bit word length.
USART1->CR1 |= ~(USART_CR1_M);
//baudrate 8MHz,9600 baudrate.
USART1->BRR = 0x341;
//Enable TE,RE bits
USART1->CR1 |= USART_CR1_RE|USART_CR1_TE;
while(1){
//wait for receive data
while((USART1->SR &USART_SR_RXNE )==0){}
data = USART1->DR;
while((USART1->SR & USART_SR_TXE)== 0){}
USART1->DR = data;
while((USART1->SR &USART_SR_TC) == 0){}
}
this code receives data from the PC and sends data to the PC.
I got the solution for the above problem.
don't make GPIO pins as alternate function mode when you receiving data.
//ALternate function mode PA10.
GPIOA->CRH |= GPIO_CRH_MODE10_1|GPIO_CRH_CNF10_1;
here RX pin as output push-pull mode in alternate function --- it is wrong.
make this pin as input floating mode or input push-pull/pull-down mode -- it is correct if you receiving data from the PC.(according to user manual).
3.dont use the HSI clock for this, if you use the HSI clock on this board you will get the wrong data.HSI clock, not a good idea to use.
Make sure to enable the HSE clock as a system clock you will get proper output when you receive data from the PC.
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.
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.