Старт ARM. Работаем с UART-ом на SAM3N4C.

Всем привет!) Продолжаем изучать армы от атмел и сегодня поговорим про uart. UART (Universal Asynchronous Receiver-Transmitter – Универсальный асинхронный приёмопередатчик) работает аналогично RS-232, только на TTL уровне.

Как организовать работа с этим интерфейсом на sam3n4c, для начала необходимо:
1. Разрешить PMC для clock pmc_enable_periph_clk(ID_UART0) или PMC->PMC_PCER0 = 1 << ID_UART0, UART0 имеет следующий адрес (0x400E0600U), информацию по регистрам uart-а можно найти в заголовочном файле сomponet_uart.h
2. Настроим PIO регистры (пины соответствия GPIO – UART0) PIO_IDR, PIO_PUDR, PIO_PDR, PIO_ABCDSR.
3. Необходимо проинициализировать UART0 115200 8n1.

После того как сформирована инициализация UART, можно приступать к работе и к выводу сообщения.


Карта регистров в Atmel Studio 6


Наш UART на product mapping-е


В таблице приведены пины микроконтроллера необходимые для организации канала передачи данных


Блок схема функционирования работы UART


Расчет скорости передачи данных ведется относительно MCK, берётся системная частота APB, она рассчитывается относительно ФАПЧ умноженный на частоту кварца.


Карта регистров UART

US_CR – регистр команд UART,
US_MR – регистр режима UART,
US_IER – регистр разрешения прерываний UART,
US_IER – регистр запрещения прерываний UART,
US_IMR – регистр маски прерываний UART,
US_SR – регистр состояния канала UART,
US_RHR – регистр временного хранения информации приема UART,
US_THR – регистр временного хранения информации передачи UART,
US_ВRGR – регистр генератор скорости обмена через UART.

static void my_uart_init(void){	
	//Отключить защиту от записи
	PMC->PMC_WPMR = 0x504D4300;
	//Включить PMC (контроллер питания), разрешение периферийных тактовых частот	
	PMC->PMC_PCER0 = ((1UL << ID_PIOA) |   (1UL << ID_UART0)  );   
	//Настраиваем пины UARTа 
	PIOA->PIO_IDR    =
	PIOA->PIO_PUDR   =
	PIOA->PIO_PDR    =  (PIO_PA9 | PIO_PA10);
	PIOA->PIO_ABCDSR[0]  &= ~(PIO_PA9 | PIO_PA10);
	PIOA->PIO_ABCDSR[1]  &= ~(PIO_PA9 | PIO_PA10);
	//Настраиваем UART	
	UART0->UART_CR = (UART_CR_RSTRX | UART_CR_RSTTX) | (UART_CR_RXDIS | UART_CR_TXDIS);
	  UART0->UART_IDR   = 0xFFFFFFFF;
	  UART0->UART_MR    = (0x4 <<  9); //(UART) No Parity  
	  UART0->UART_BRGR  = 0x0000001A;	//115200
	  UART0->UART_PTCR  = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
	  UART0->UART_CR    = UART_CR_RXEN     | UART_CR_TXEN;
	//Включить защиту от записи  
	PMC->PMC_WPMR = 0x504D4301;	
}	

и в главной функции добавляем следующие строки

my_uart_init();
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = 'm';
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = 'c';
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = 'u';
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = '.';
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = 'b';
	while (!(UART0->UART_SR  & UART_SR_TXRDY));
	UART0->UART_THR = 'y';


Hello world! в стиле mcu.by, относительно низкого уровня работы с UART-ом пока хватит. Сейчас перейдем к высоко уровневым функция работы с UART-ом, для этого необходимо создать ASF проект. В ASF проекте выбрать wizard и подключить ниже следующие библиотеки.


Настраиваем UART с использованием ASF

Вот небольшой пример с иполизованием UART0:

#include "asf.h"
#include "stdio_serial.h"
#include "conf_board.h"
#include "conf_clock.h"
/** IRQ приоритет для PIO
#define IRQ_PRIOR_PIO    0
#define BLINK_PERIOD     1000
#define STRING_EOL    "\r"
#define STRING_HEADER "-- Getting Started Example --\r\n" \
		"-- "BOARD_NAME" --\r\n" \
		"-- Compiled: "__DATE__" "__TIME__" --"STRING_EOL
// LED0 управление миганием
volatile bool g_b_led0_active = true;
// LED1 управление миганием
volatile bool g_b_led1_active = true;
volatile uint32_t g_ul_ms_ticks = 0;
//Кнопки, упрааление миганием
static void ProcessButtonEvt(uint8_t uc_button)
{
	if (uc_button == 0) {
		g_b_led0_active = !g_b_led0_active;
		if (!g_b_led0_active)
			gpio_set_pin_high(LED0_GPIO);
	} else {
		g_b_led1_active = !g_b_led1_active;
		if (g_b_led1_active) {
			gpio_set_pin_low(LED1_GPIO);
			tc_start(TC0, 0);
		}
		else {
			gpio_set_pin_high(LED1_GPIO);
			tc_stop(TC0, 0);
		}
	}
}
//Прерывание по системным тикам
void SysTick_Handler(void)
{
	g_ul_ms_ticks++;
}
static void Button1_Handler(uint32_t id, uint32_t mask)
{
	if (PIN_PUSHBUTTON_1_ID == id && PIN_PUSHBUTTON_1_MASK == mask)
		ProcessButtonEvt(0);
}
static void Button2_Handler(uint32_t id, uint32_t mask)
{
	if (PIN_PUSHBUTTON_2_ID == id && PIN_PUSHBUTTON_2_MASK == mask)
		ProcessButtonEvt(1);
}
//Кнопки
static void configure_buttons(void)
{
	//Настраиваем первую кнопку
	pmc_enable_periph_clk(PIN_PUSHBUTTON_1_ID);
	pio_set_debounce_filter(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_MASK, 10);
	pio_handler_set(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_ID, PIN_PUSHBUTTON_1_MASK, PIN_PUSHBUTTON_1_ATTR, Button1_Handler);	/* Interrupt on rising edge  */
	NVIC_EnableIRQ((IRQn_Type) PIN_PUSHBUTTON_1_ID);
	pio_handler_set_priority(PIN_PUSHBUTTON_1_PIO, (IRQn_Type) PIN_PUSHBUTTON_1_ID, IRQ_PRIOR_PIO);
	pio_enable_interrupt(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_MASK);
	//Настраиваем вторую кнопку
	pmc_enable_periph_clk(PIN_PUSHBUTTON_2_ID);
	pio_set_debounce_filter(PIN_PUSHBUTTON_2_PIO, PIN_PUSHBUTTON_2_MASK, 10);
	pio_handler_set(PIN_PUSHBUTTON_2_PIO, PIN_PUSHBUTTON_2_ID, PIN_PUSHBUTTON_2_MASK, PIN_PUSHBUTTON_2_ATTR, Button2_Handler);	/* Interrupt on falling edge */
	NVIC_EnableIRQ((IRQn_Type) PIN_PUSHBUTTON_2_ID);
	pio_handler_set_priority(PIN_PUSHBUTTON_2_PIO, (IRQn_Type) PIN_PUSHBUTTON_2_ID, IRQ_PRIOR_PIO);
	pio_enable_interrupt(PIN_PUSHBUTTON_2_PIO, PIN_PUSHBUTTON_2_MASK);
}

//Прерывание для TCO
void TC0_Handler(void)
{
	volatile uint32_t ul_dummy;
	ul_dummy = tc_get_status(TC0, 0);
	UNUSED(ul_dummy);
	gpio_toggle_pin(LED1_GPIO);
	printf("2 ");
}
static void configure_tc(void)
{
	uint32_t ul_div;
	uint32_t ul_tcclks;
	uint32_t ul_sysclk = sysclk_get_cpu_hz();
	pmc_enable_periph_clk(ID_TC0);
	//Настраиваем таймер/счетчик на 4 MHz
	tc_find_mck_divisor(4, ul_sysclk, &ul_div, &ul_tcclks, ul_sysclk);
	tc_init(TC0, 0, ul_tcclks | TC_CMR_CPCTRG);
	tc_write_rc(TC0, 0, (ul_sysclk / ul_div) / 4);
	NVIC_EnableIRQ((IRQn_Type) ID_TC0);
	tc_enable_interrupt(TC0, 0, TC_IER_CPCS);
	if (g_b_led1_active) {
		tc_start(TC0, 0);
	}
}
//UART
static void configure_console(void)
{
	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
		.paritytype = CONF_UART_PARITY
	};
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	stdio_serial_init(CONF_UART, &uart_serial_options);
}
//Задежка
static void mdelay(uint32_t ul_dly_ticks)
{
	uint32_t ul_cur_ticks;
	ul_cur_ticks = g_ul_ms_ticks;
	while ((g_ul_ms_ticks - ul_cur_ticks) < ul_dly_ticks);
}
int main(void)
{
	//Инициализируем борд (системное время, EFC, PMC и другие вещи)
	sysclk_init();
	board_init();
	//Инициализируем UART0
	сonfigure_console();
	//отправдем базовы отчет
	puts(STRING_HEADER);
	//Настраиваем systick на 1 мс
	puts("Configure system tick to get 1ms tick period.\r");
	if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
		puts("-F- Systick configuration error\r");
		while (1);
	}
	puts("Configure TC.\r");
	configure_tc();
	puts("Configure buttons with debouncing.\r");
	configure_buttons();
	puts("Press USRBP1 to Start/Stop the blue LED D2 blinking.\r");
	puts("Press USRBP2 to Start/Stop the green LED D3 blinking.\r");
	while (1) {
		while (!g_b_led0_active);
				if (g_b_led0_active) {
			gpio_toggle_pin(LED0_GPIO);
			printf("1 ");
		}
		//Задержка на 500 мс
		mdelay(500);
	}
}

Думаю, на сегодня все, всем пока!)