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.
Related
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.
I am trying to query a temperature sensor (TMP007) over an I2C bus using the L011K4's DMA controller. I've managed to get TMP007 to respond with good data, but that data isn't appearing in the buffer array that I pass my I2C DMA receive function.
What I expect to happen is that the I2C hardware is set up by the software in the Init and Read function, then the start condition is generated, the slave's address and register address are sent, then a second start/a restart is sent out followed by the slave's address again, and finally ending with a transmission of two bytes from TMP007 and a stop condition.
The data acquired from the temperature sensor should be appearing in a buffer that is passed into Start_Read (as MemoryBaseAddress), but that data does not appear when viewing the processor in debug mode after the read function has been called.
The Channel 3 transfer complete interrupt is being called, so I know the DMA has activated and managed the RX transfer, but the data doesn't appear in the desired buffer.
Here's an image of the communication being observed by a logic analyzer: https://i.imgur.com/SzySsXl.png
DIO2 is connected to the onboard LED, which tells us that the DMA transfer has completed (it's the only ISR with the LED toggle).
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
void I2C1_DMA_Start_Read(int SlaveAddress, int RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
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 |= (0b11 << 12)|(0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) &(I2C1->RXDR);
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_NACKIE; // Enable no acknowledge interrupt for I2C1
I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address
I2C1->CR2 &= ~I2C_CR2_RD_WRN; // Master requests a write transfer in order to send the register
I2C1->CR1 |= I2C_CR1_PE|I2C_CR1_RXDMAEN; // Enable I2C1 and RX DMA for I2C1
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x01 << I2C_CR2_NBYTES_Pos); // Transfer one byte (the slave's register address)
while(!(I2C1->ISR & I2C_ISR_TXE)); // Wait until TX data register is empty
I2C1->TXDR = RegisterAddress; // Set the transmit data to the desired register address
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
while(!(I2C1->ISR & I2C_ISR_TC)); // Wait until hardware clears the transmit complete bit
I2C1->CR2 |= I2C_CR2_RD_WRN; // Master requests a read transfer after sending over the register address
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x02 << I2C_CR2_NBYTES_Pos); // Master looks for two bytes from the slave
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
return;
}
void DMA1_Channel2_3_IRQHandler() {
if(DMA1->ISR & DMA_ISR_TCIF3) {
GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTCIF3; // Clear transfer complete interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
} else if(DMA1->ISR & DMA_ISR_TEIF3) {
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition (if I2C comm is already stopped on error, delete this and following line)
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTEIF3; // Clear transfer error interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
}
return;
}
void I2C1_IRQHandler() {
if(I2C1->ISR & I2C_ISR_NACKF) {
//GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->ICR |= I2C_ICR_NACKCF;
}
return;
}
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.
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.