Последние 2 дня я ломал голову, пытаясь выяснить, что не так с моей очень простой программой UART в System Workbench для STM32 от STMicroelectronics. Почему-то, когда я установил:
USART3->BRR = (16000000/9600); //Sets the baud rate to 9600 with a clock of 16MHz
Выход на последовательной линии - rubbi sh, однако, когда я нажимаю кнопку сброса на плате и запускаю ее, я вижу
Hello World
Как и ожидалось. В процессе копания я обнаружил, что источником системных часов является выход PLL (регистр RCC_CFGR), красная стрелка на изображении ниже:
RCC_CFGR
Биты Соответствующий красным стрелкам говорит мне, что PLL включен. Анализируя регистр RCC_PLLCFGR и вводя параметры PLLM, PLLN и PLLP вместе с соответствующими прескаллерами (AHB и APB1) в CubeMX I, я достиг тактовой частоты шины APB1 48 МГц. Итак, я сделал:
USART3->BRR = (48000000/9600); //Sets the baud rate to 9600 with a clock of 48MHz
И voilà делая шаг за шагом на отладчике, я вижу «HELLO WORLD», поэтому тактовая частота шины составляет 48 МГц. НО , и я нажимаю кнопку сброса и оставляю его включенным, теперь вывод не имеет смысла. Что-то в процессе отладки включает включение PLL и использование его в качестве источника SYSCLK.
Я скопировал и вставил код в Keil uVision, и не было никаких проблем, отладка и запуск, поэтому проблема не в сам код Я взглянул на startup_stm32.s, core_cm7.h и файлы отладки из SW4STM32 и не смог найти подсказки о том, почему это происходит. Глядя в справочное руководство Справочное руководство на стр. 170, значение сброса RCC_CFGR составляет 0x0000.0000, поэтому PLL выключается после сброса, поэтому что-то в отладке активно устанавливает PLL ON.
Я использую Tera Term, чтобы посмотреть на последовательный порт ST-LINK и Digital Discovery от Digilent, чтобы подтвердить, что происходит на шине, и оба дают мне четные данные.
Есть идеи?
Полный код:
#include "stm32f767xx.h"
#include "time.h"
/*
* Function declarations
*/
void uS_Delay(int Time);
void mS_Delay(int Time);
void S_Delay(int Time);
void GPIOAa_Init(void);
void Config_TIM5(void);
void TIM5_IRQHandler(void);
void Enable_TIM5Interrupt(void);
void UART_Config(void);
void UART_Send(int character);
int Delay_End = 0;
int main(void)
{
GPIOD_Init();
Config_TIM5();
Enable_TIM5Interrupt();
UART_Config();
int butao = 0;
while(1)
{
UART_Send('H');
mS_Delay(10);
UART_Send('E');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('O');
mS_Delay(10);
UART_Send(' ');
mS_Delay(10);
UART_Send('W');
mS_Delay(10);
UART_Send('O');
mS_Delay(10);
UART_Send('R');
mS_Delay(10);
UART_Send('L');
mS_Delay(10);
UART_Send('D');
mS_Delay(10);
}
}
/*
* Functions
*/
void uS_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 uS clock is:
*
* Nº Steps = 1uS/62.5nS
* Nº Steps = 16
*
* So for a delay of "Time" microseconds the number of steps is:
*
* Delay = Time*16
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, in this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void mS_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16000);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 mS clock is:
*
* Nº Steps = 1uS/62.5nS
* Nº Steps = 16.000
*
* So for a delay of "Time" microseconds the number of steps is:
*
* Delay = Time*16.000
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, is this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void S_Delay(int Time){
uint32_t Delay = (uint32_t) (unsigned long) (Time*16000000);
/* The APB bus cycle period is by default 62.5 nS, defined by the
* default bus frequency of 16 MHz. The number of steps to produce
* a 1 S clock is:
*
* Nº Steps = 1S/62.5nS
* Nº Steps = 16.000.000
*
* So for a delay of "Time" seconds the number of steps is:
*
* Delay = Time*16.000.000
*/
TIM5->ARR = Delay;
/* TIM-ARR value will be the counter boundary to roll over.
* The default bus frequency is 16MHz.
*/
TIM5->CR1 |= 0x09;
/* TIM5->CR1 is the main basic timer controller, is this situation
* the bits 0 and 3 are been set. Those bits enables the counting
* itself and the one-shot-mode, respectively. One-shot-mode makes
* it so when an update event occurs (overflow, underflow, etc) the
* counter stops counting, in other words the bit 0 of TIM%->CR1
* is cleared.
*/
Delay_End = 0;
while(Delay_End == 0);
Delay_End = 0;
/* Delay_End is a flag telling the uS_Delay() function that the
* specified delay hasn't finished (Delay_End = 0), so keeps the
* PC register stuck on (while(Delay_End == 0)). When the counter
* rolls over it generates an interrupt. The handler (TIM5_IRQHandler)
* sets this flag and now the program can get out of the while loop
* the flag is once again cleared (Delay_End = 0) and the program
* resumes.
*/
}
void GPIOD_Init(void){
int a;
RCC->AHB1ENR |= 0x08; //Enables clock for port D
a = RCC->AHB1ENR; //Small delay
GPIOD->MODER |= 0x00020000;
/*
* Sets output mode for alternate mode for pin PD8
*/
GPIOD->AFR[1] |= 0x00000007;
/*
* Alternate function 7 selected for pin PD8
*/
}
void Config_TIM5(void){
int a;
RCC->APB1ENR |= 0x08; //Enables clock for TIM5
a = RCC->APB1ENR; //Small delay
TIM5->DIER |= 0X01; //Enables update interrupt
}
void TIM5_IRQHandler(void){
Delay_End = 1; //The specified delay time has ended
TIM5->SR &= ~(0x01); //Clears the update event flag
}
void Enable_TIM5Interrupt(void){
NVIC->ISER[1] = 0x40000; //Enables interrupt event from TIM5 peripheral
NVIC->IP[50] = 0x00; //Sets the priority for the TIM5 interrupt
}
void UART_Config(void){
int a;
RCC->APB1ENR |= 0x00040000; //Enable clock for UART4
a = RCC->APB1ENR; //Small delay
USART3->CR1 |= 0x08; //Enable the transmitter
USART3->BRR = (48000000/9600); //Sets the baud rate to 9600
USART3->CR1 |= 0x01; //Enable the USART4
}
void UART_Send(int character){
USART3->TDR = (character);
}
Edit1: я запутался с RCC_CR и RCC_CFGR, но проблема та же.
Edit2: я заметил что prescalers для шин APB1 и APB2 также были изменены.
Edit3: Обходное решение при запуске сеанса отладки нажмите значок (Сброс чипа и перезапуск сеанса отладки ), эта кнопка перезапустит сеанс отладки со сбросом. Таким образом, содержимое регистров соответствует ожиданиям. Я все еще нахожусь в поиске постоянного решения.