USART и частота

Забажил USART в режиме RS485. Непонятненько так забажил — то прерывания выдает, то не выдает, но стабильно забивает выделенный под прием буфер единичками.
Хорошо, что в буфере было сделано циклическое заполнение — до конца дошел, загоняйся с начала, а то программа падала бы без объяснения причин…


Копания в даташитах особо так ничего не дали, начали рыться в коде…

Исходное положение: в тестовой прошивке все работает нормально.
После переключения проца на PLL и разгона его этим с 24 до 48 МГц начинаются баги.

Упражнение раз — отключаем PLL, возвращаем все к начальным значениям. Имеем следующий код на инициализацию:

#define FOSC0           12000000                              //!< Osc0 frequency: Hz.
#define OSC0_STARTUP    AVR32_PM_OSCCTRL0_STARTUP_2048_RCOSC  //!< Osc0 startup time: RCOsc periods.

#define APPLI_CPU_SPEED   24000000
#define APPLI_PBA_SPEED   24000000

//
// Hardware initialization
//
pm_freq_param_t   pm_freq_param=
{
	.cpu_f  =       APPLI_CPU_SPEED,
	.pba_f    =     APPLI_PBA_SPEED,
	.osc0_f     =   FOSC0,
	.osc0_startup = OSC0_STARTUP
};

usart_options_t USART_OPTIONS =
{
    .baudrate     = 115200,
    .charlength   = 8,
    .paritytype   = USART_NO_PARITY,
    .stopbits     = USART_1_STOPBIT,
    .channelmode  = USART_NORMAL_CHMODE
};

void init(void)
{
	volatile avr32_pm_t* pm = &AVR32_PM;

	// CLOCK initialization

	pm_configure_clocks(&pm_freq_param);
//	init_dbg_rs232(pm_freq_param.pba_f);
	set_cpu_hz(pm_freq_param.cpu_f);
//	pm_configure_usb_clock();

   ...
	// RS485 initialization

	gpio_enable_module(USART_GPIO_MAP, sizeof(USART_GPIO_MAP) / sizeof(USART_GPIO_MAP[0]));
	usart_init_rs485(&AVR32_USART1, &USART_OPTIONS, APPLI_CPU_SPEED);

}

Все работает...

Упражнение два - анализируем функцию перевода мэйнклок на PLL:

/* Start PLL0, enable a generic clock with PLL0 output then switch main clock to PLL0 output.
   All calculations in this function suppose that the Osc0 frequency is 12MHz. */
void local_start_pll0(volatile avr32_pm_t* pm)
{
  pm_switch_to_osc0(pm, FOSC0, OSC0_STARTUP);  // Switch main clock to Osc0.

  /* Setup PLL0 on Osc0, mul=3 ,no divisor, lockcount=16, ie. 12Mhzx8 = 96MHz output */
  /*void pm_pll_setup(volatile avr32_pm_t* pm,
                 unsigned int pll,
                 unsigned int mul,
                 unsigned int div,
                 unsigned int osc,
                 unsigned int lockcount) {
  */
  pm_pll_setup(pm,
               0,   // use PLL0
               7,   // MUL=7 in the formula
               1,   // DIV=1 in the formula
               0,   // Sel Osc0/PLL0 or Osc1/PLL1
               16); // lockcount in main clock for the PLL wait lock

  /*
  This function will set a PLL option.
  *pm Base address of the Power Manager (i.e. &AVR32_PM)
  pll PLL number 0
  pll_freq Set to 1 for VCO frequency range 80-180MHz, set to 0 for VCO frequency range 160-240Mhz.
  pll_div2 Divide the PLL output frequency by 2 (this settings does not change the FVCO value)
  pll_wbwdisable 1 Disable the Wide-Bandith Mode (Wide-Bandwith mode allow a faster startup time and out-of-lock time). 0 to enable the Wide-Bandith Mode.
  */
  /* PLL output VCO frequency is 96MHz. We divide it by 2 with the pll_div2=1. This enable to get later main clock to 48MHz */
  pm_pll_set_option(pm, 0, 1, 1, 0);

  /* Enable PLL0 */
  /*
    void pm_pll_enable(volatile avr32_pm_t* pm,
                   unsigned int pll) {
   */
   pm_pll_enable(pm,0);

   /* Wait for PLL0 locked */
   pm_wait_for_pll0_locked(pm) ;

   /* Setup generic clock on PLL0, with Osc0/PLL0, no divisor */
   /*
   void pm_gc_setup(volatile avr32_pm_t* pm,
                   unsigned int gc,
                   unsigned int osc_or_pll, // Use Osc (=0) or PLL (=1)
                   unsigned int pll_osc, // Sel Osc0/PLL0 or Osc1/PLL1
                   unsigned int diven,
                   unsigned int div) {
   */
   pm_gc_setup(pm,
               EXAMPLE_GCLK_ID,
               1,  // Use Osc (=0) or PLL (=1), here PLL
               0,  // Sel Osc0/PLL0 or Osc1/PLL1
               0,  // disable divisor
               0); // no divisor

   /* Enable Generic clock */
   pm_gc_enable(pm, EXAMPLE_GCLK_ID);

   /* Set the GCLOCK function to the GPIO pin */
   gpio_enable_module_pin(EXAMPLE_GCLK_PIN, EXAMPLE_GCLK_FUNCTION);

   /* Divide PBA clock by 2 from main clock (PBA clock = 48MHz/2 = 24MHz).
      Pheripheral Bus A clock divisor enable = 1
      Pheripheral Bus A select = 0
      Pheripheral Bus B clock divisor enable = 0
      Pheripheral Bus B select = 0
      High Speed Bus clock divisor enable = 0
      High Speed Bus select = 0
   */
   pm_cksel(pm, 1, 0, 0, 0, 0, 0);

   // Set one wait-state (WS) for flash controller. 0 WS access is up to 30MHz for HSB/CPU clock.
   // As we want to have 48MHz on HSB/CPU clock, we need to set 1 WS on flash controller.
   flashc_set_wait_state(1);

   pm_switch_to_clock(pm, AVR32_PM_MCSEL_PLL0); /* Switch main clock to 48MHz */
 }

И обращаем внимание на один момент. Несмотря на то, что PM переводится на 48 МГц, PBA тактируется с включенным делителем, т.е. на частоте 24 МГц.

Правим дефайны в соответствии с...

#define APPLI_CPU_SPEED   48000000
#define APPLI_PBA_SPEED   24000000

И все равно баг не уходит. Тогда смотрим на инициализацию USART:

	usart_init_rs485(&AVR32_USART1, &USART_OPTIONS, APPLI_CPU_SPEED);

И видим - USART тупо инициируется не с той частотой, вместо частоты шины А, ему передается частота проца.
Когда PM был запитан от 12МГц кристала и частота шины и проца была одинаковой, все прекрасно работало. Как только частоты стали разными - пошли глюки.
Для нахождения баги из одной(!) переменной потребовалось перешерстить весь код инициализации не по одному разу, а исправление ее заняло секунду:

	usart_init_rs485(&AVR32_USART1, &USART_OPTIONS, APPLI_PBA_SPEED);

Мораль дня: внимательнее относитесь к частотам, с которыми инициализируете периферию. Особенно если их (частот и периферии) у вас много...

Похожий бред:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Protected by WP Anti Spam