​Atmel SAM4S学习笔记(八)——RTC(实时时钟)

作者: liunian__92
上传时间为: 2014-12-01 10:41 PM
2014-12-01
阅读:

       现在单片机RTC时钟已成为标配了。SAM4S系列的Real-time Clock是一个为低功耗设计的,为了达到最优的功能,需要由外部32.768Khz的晶振提供时钟信号。他包括一天的完整的带警报功能的时钟,和200年的Gregorian或者Persian日历,能产生周期性的可编程中断。警报和日历都可以通过32位数据总线访问。时间和日历的数值都是以BCD编码的。时间的格式有24小时和AM/PM格式。

特性:
超低功耗

全异步设计

Greforian日历可是计时到2099年或者Persian日历

可编程周期中断

安全特性:

        有效时间和日期程序检查

        有效传输时间和日期程序检查,

晶体振荡时钟校准

波形发生器

寄存器写保护

下面这幅图是RTC时钟的框架图如图8-1所示

从图上可以看出来,RTC的中断源连接到中断控制器的一个中断源上。中断处理需要在配置RTC之前对中断控制器进行编程。

下图是RTC的寄存器如图8-2所示

RTC_CR:控制寄存器
RTC_MR:模式寄存器

RTC_TIMR:时间寄存器

RTC_CALR:日期寄存器

RTC_TIMALR:时间警报寄存器

RTC_CALALR:日历警报寄存器

RTC_SR:状态寄存器

RTC_SCCR:状态清除指令寄存器

RTC_IER:中断允许寄存器

RTC_IDR:中断禁止寄存器

RTC_IMR:中断屏蔽寄存器

RTC_VER:有效入口寄存器

具体的应用,附上ASF提供的代码供大家参考,详细可以去ASF的框架中找例程。

Atmel SAM4S Xplained pro学习笔记(一)-- 开发套件介绍

Atmel SAM4S 学习笔记(二)-- 开发环境搭建

Atmel SAM4S 学习笔记(三)-- 示例代码分析

Atmel SAM4S学习笔记(四)——ASF详解

Atmel SAM4S学习笔记(五)——GPIO

Atmel SAM4S学习笔记(六)--CHIPID(芯片编号)

Atmel SAM4S学习笔记(七)——实时定时器 (RTT)

#include "asf.h"
#include "stdio_serial.h"
#include "conf_clock.h"
#include "conf_board.h"


/* Main menu is being displayed. */
#define STATE_MENU             0
/* Time is being edited. */
#define STATE_SET_TIME         1
/* Date is being edited. */
#define STATE_SET_DATE         2
/* Time alarm is being edited. */
#define STATE_SET_TIME_ALARM   3
/* Date alarm is being edited. */
#define STATE_SET_DATE_ALARM   4
/* Wave generating is being edited. */
#define STATE_WAVEFORM         5

/* Maximum size of edited string. */
#define MAX_EDIT_SIZE          10

/* Macro for converting char to digit. */
#define char_to_digit(c) ((c) - '0')

#define STRING_EOL    "\r"
#define STRING_HEADER "-- RTC Example --\r\n" \
		"-- "BOARD_NAME" --\r\n" \
		"-- Compiled: "__DATE__" "__TIME__" --"STRING_EOL

/* Current state of application. */
static uint32_t gs_ul_state = STATE_MENU;

/* Edited hour. */
static uint32_t gs_ul_new_hour;
/* Edited minute. */
static uint32_t gs_ul_new_minute;
/* Edited second. */
static uint32_t gs_ul_new_second;

/* Edited year. */
static uint32_t gs_ul_new_year;
/* Edited month. */
static uint32_t gs_ul_new_month;
/* Edited day. */
static uint32_t gs_ul_new_day;
/* Edited day-of-the-week. */
static uint32_t gs_ul_new_week;

/* Indicate if alarm has been triggered and not yet cleared */
static uint32_t gs_ul_alarm_triggered = 0;

/* Time string */
static uint8_t gs_uc_rtc_time[8 + 1] =
		{ '0', '0', ':', '0', '0', ':', '0', '0', '\0' };
/* Date string */
static uint8_t gs_uc_date[10 + 1] =
		{ '0', '0', '/', '0', '0', '/', '0', '0', '0', '0', '\0' };
/* Week string */
static uint8_t gs_uc_day_names[7][4] =
		{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };

/* Flag for refreshing menu */
static uint32_t gs_ul_menu_shown = 0;

/**
 *  Configure UART console.
 */
static void configure_console(void)
{
	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
		.paritytype = CONF_UART_PARITY
	};

	/* Configure console UART. */
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	stdio_serial_init(CONF_UART, &uart_serial_options);
}

/**
 * \brief Get new time. Successful value is put in gs_ul_new_hour,
 * gs_ul_new_minute, gs_ul_new_second.
 */
static uint32_t get_new_time(void)
{
	uint8_t uc_key;
	uint32_t i = 0;

	/* Clear setting variable. */
	gs_ul_new_hour = 0xFFFFFFFF;
	gs_ul_new_minute = 0xFFFFFFFF;
	gs_ul_new_second = 0xFFFFFFFF;

	/* Use gs_uc_rtc_time[] as a format template. */
	while (1) {

		while (uart_read(CONSOLE_UART, &uc_key));

		/* End input */
		if (uc_key == 0x0d || uc_key == 0x0a) {
			puts("\r");
			break;
		}

		/* DEL or BACKSPACE */
		if (uc_key == 0x7f || uc_key == 0x08) {
			if (i > 0) {
				/* End of gs_uc_rtc_time[], then one more back of index */
				if (!gs_uc_rtc_time[i]) {
					--i;
				}

				puts("\b \b");
				--i;

				/* Delimiter ':' for time is uneditable */
				if (!((gs_uc_rtc_time[i]) >= '0' && (gs_uc_rtc_time[i]) <= '9')
						&& i > 0) {
					puts("\b \b");
					--i;
				}
			}
		}

		/*
		 * End of gs_uc_rtc_time[], no more input except the above DEL/BS,
		 * or enter to end.
		 */
		if (!gs_uc_rtc_time[i]) {
			continue;
		}

		while (uart_write(CONSOLE_UART, uc_key));
		gs_uc_rtc_time[i++] = uc_key;

	}

	if (i == 0) {
		return 0;
	}

	if (i != 0 && gs_uc_rtc_time[i] != '\0') {
		/* Failure input */
		return 1;
	}

	gs_ul_new_hour = char_to_digit(gs_uc_rtc_time[0]) * 10 +
			char_to_digit(gs_uc_rtc_time[1]);
	gs_ul_new_minute = char_to_digit(gs_uc_rtc_time[3]) * 10 +
			char_to_digit(gs_uc_rtc_time[4]);
	gs_ul_new_second = char_to_digit(gs_uc_rtc_time[6]) * 10 +
			char_to_digit(gs_uc_rtc_time[7]);

	/* Success input. Verification of data is left to RTC internal Error Checking. */
	return 0;
}

/**
 * \brief Calculate week from year, month, day.
 */
static uint32_t calculate_week(uint32_t ul_year, uint32_t ul_month,
		uint32_t ul_day)
{
	uint32_t ul_week;

	if (ul_month == 1 || ul_month == 2) {
		ul_month += 12;
		--ul_year;
	}

	ul_week = (ul_day + 2 * ul_month + 3 * (ul_month + 1) / 5 + ul_year +
			ul_year / 4 - ul_year / 100 + ul_year / 400) % 7;

	++ul_week;

	return ul_week;
}

/**
 * \brief Get new time. Successful value is put in gs_ul_new_year,
 * gs_ul_new_month, gs_ul_new_day, gs_ul_new_week.
 */
static uint32_t get_new_date(void)
{
	uint8_t uc_key;
	uint32_t i = 0;

	/* Clear setting variable */
	gs_ul_new_year = 0xFFFFFFFF;
	gs_ul_new_month = 0xFFFFFFFF;
	gs_ul_new_day = 0xFFFFFFFF;
	gs_ul_new_week = 0xFFFFFFFF;

	/* Use gs_uc_rtc_time[] as a format template */
	while (1) {

		while (uart_read(CONSOLE_UART, &uc_key));

		/* End input */
		if (uc_key == 0x0d || uc_key == 0x0a) {
			puts("\r");
			break;
		}

		/* DEL or BACKSPACE */
		if (uc_key == 0x7f || uc_key == 0x08) {
			if (i > 0) {
				/* End of date[], then one more back of index */
				if (!gs_uc_date[i]) {
					--i;
				}

				puts("\b \b");
				--i;

				/* Delimiter '/' for date is uneditable */
				if (!((gs_uc_date[i]) >= '0' && (gs_uc_date[i]) <='9')
						&& i > 0) {
					puts("\b \b");
					--i;
				}
			}
		}

		/*
		 * End of gs_uc_rtc_time[], no more input except the above DEL/BS,
		 * or enter to end.
		 */
		if (!gs_uc_date[i]) {
			continue;
		}

		while (uart_write(CONSOLE_UART, uc_key));
		gs_uc_date[i++] = uc_key;

	}

	if (i == 0) {
		return 0;
	}

	if (i != 0 && gs_uc_date[i] != '\0' && i != 6) {
		/* Failure input */
		return 1;
	}

	/* MM-DD-YY */
	gs_ul_new_month = char_to_digit(gs_uc_date[0]) * 10
			+ char_to_digit(gs_uc_date[1]);
	gs_ul_new_day = char_to_digit(gs_uc_date[3]) * 10
			+ char_to_digit(gs_uc_date[4]);
	if (i != 6) {
		/* For 'Set Date' option, get the input new year and new week. */
		gs_ul_new_year = char_to_digit(gs_uc_date[6]) * 1000 +
				char_to_digit(gs_uc_date[7]) * 100 +
				char_to_digit(gs_uc_date[8]) * 10 +
				char_to_digit(gs_uc_date[9]);
		gs_ul_new_week = calculate_week(gs_ul_new_year, gs_ul_new_month,
				gs_ul_new_day);
	}

	/*
	 * Success input. Verification of data is left to RTC internal Error
	 * Checking.
	 */
	return 0;
}


/**
 * \brief Display the user interface on the terminal.
 */
static void refresh_display(void)
{
	uint32_t ul_hour, ul_minute, ul_second;
	uint32_t ul_year, ul_month, ul_day, ul_week;

	if (gs_ul_state != STATE_MENU) {
		/* Not in menu display mode, in set mode. */
	} else {
		/* Retrieve date and time */
		rtc_get_time(RTC, &ul_hour, &ul_minute, &ul_second);
		rtc_get_date(RTC, &ul_year, &ul_month, &ul_day, &ul_week);

		/* Display */
		if (!gs_ul_menu_shown) {
			puts("\n\rMenu:\n\r"
					"  t - Set time\n\r"
					"  d - Set date\n\r"
					"  i - Set time alarm\n\r"
					"  m - Set date alarm\r");
#if ((SAM3S8) || (SAM3SD8) || (SAM4S) || (SAM4C) || (SAM4CP) || (SAM4CM))
			puts("  w - Generate Waveform\r");
#endif
			if (gs_ul_alarm_triggered) {
				puts("  c - Clear alarm notification\r");
			}

			printf("\n\r");

			gs_ul_menu_shown = 1;
		}

		/* Update current date and time */
		puts("\r");
		printf(" [Time/Date: u:u:u, u/u/u %s ][Alarm status:%s]",
			(unsigned int)ul_hour, (unsigned int)ul_minute, (unsigned int)ul_second,
			(unsigned int)ul_month, (unsigned int)ul_day, (unsigned int)ul_year,
			gs_uc_day_names[ul_week-1], gs_ul_alarm_triggered?"Triggered!":"");
	}
}

/**
 * \brief Interrupt handler for the RTC. Refresh the display.
 */
void RTC_Handler(void)
{
	uint32_t ul_status = rtc_get_status(RTC);

	/* Second increment interrupt */
	if ((ul_status & RTC_SR_SEC) == RTC_SR_SEC) {
		/* Disable RTC interrupt */
		rtc_disable_interrupt(RTC, RTC_IDR_SECDIS);

		refresh_display();

		rtc_clear_status(RTC, RTC_SCCR_SECCLR);

		rtc_enable_interrupt(RTC, RTC_IER_SECEN);
	} else {
		/* Time or date alarm */
		if ((ul_status & RTC_SR_ALARM) == RTC_SR_ALARM) {
			/* Disable RTC interrupt */
			rtc_disable_interrupt(RTC, RTC_IDR_ALRDIS);

			gs_ul_alarm_triggered = 1;
			refresh_display();
			/* Show additional menu item for clear notification */
			gs_ul_menu_shown = 0;
			rtc_clear_status(RTC, RTC_SCCR_ALRCLR);
			rtc_enable_interrupt(RTC, RTC_IER_ALREN);
		}
	}
}


/**
 * \brief Application entry point for RTC example.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	uint8_t uc_key;

	/* Initialize the SAM system */
	sysclk_init();
	board_init();

	/* Initialize the console uart */
	configure_console();

	/* Output example information */
	puts(STRING_HEADER);

	/* Default RTC configuration, 24-hour mode */
	rtc_set_hour_mode(RTC, 0);

	/* Configure RTC interrupts */
	NVIC_DisableIRQ(RTC_IRQn);
	NVIC_ClearPendingIRQ(RTC_IRQn);
	NVIC_SetPriority(RTC_IRQn, 0);
	NVIC_EnableIRQ(RTC_IRQn);
	rtc_enable_interrupt(RTC, RTC_IER_SECEN | RTC_IER_ALREN);

	/* Refresh display once */
	refresh_display();

	/* Handle keypresses */
	while (1) {

		while (uart_read(CONSOLE_UART, &uc_key));

		/* Set time */
		if (uc_key == 't') {
			gs_ul_state = STATE_SET_TIME;

			do {
				puts("\n\r\n\r Set time(hh:mm:ss): ");
			} while (get_new_time());

			/* If valid input, none of the variables for time is 0xff. */
			if (gs_ul_new_hour != 0xFFFFFFFF && (gs_uc_rtc_time[2] == ':')
					&& (gs_uc_rtc_time[5] == ':')) {
				if (rtc_set_time(RTC, gs_ul_new_hour, gs_ul_new_minute,
						gs_ul_new_second)) {
					puts("\n\r Time not set, invalid input!\r");
				}
			} else {
				gs_uc_rtc_time[2] = ':';
				gs_uc_rtc_time[5] = ':';
				puts("\n\r Time not set, invalid input!\r");
			}

			gs_ul_state = STATE_MENU;
			gs_ul_menu_shown = 0;
			refresh_display();
		}

		/* Set date */
		if (uc_key == 'd') {
			gs_ul_state = STATE_SET_DATE;

			do {
				puts("\n\r\n\r Set date(mm/dd/yyyy): ");
			} while (get_new_date());

			/* If valid input, none of the variables for date is 0xff(ff). */
			if (gs_ul_new_year != 0xFFFFFFFF && (gs_uc_date[2] == '/')
					&& (gs_uc_date[5] == '/')) {
				if (rtc_set_date(RTC, gs_ul_new_year, gs_ul_new_month,
						gs_ul_new_day, gs_ul_new_week)) {
					puts("\n\r Date not set, invalid input!\r");
				}
			} else {
				gs_uc_date[2] = '/';
				gs_uc_date[5] = '/';
				puts("\n\r Time not set, invalid input!\r");
			}

			/* Only 'mm/dd' is input. */
			if (gs_ul_new_month != 0xFFFFFFFF &&
						gs_ul_new_year == 0xFFFFFFFF) {
				puts("\n\r Not Set for no year field!\r");
			}

			gs_ul_state = STATE_MENU;
			gs_ul_menu_shown = 0;
			refresh_display();
		}

		/* Set time alarm */
		if (uc_key == 'i') {
			gs_ul_state = STATE_SET_TIME_ALARM;

			rtc_clear_date_alarm(RTC);

			do {
				puts("\n\r\n\r Set time alarm(hh:mm:ss): ");
			} while (get_new_time());

			if (gs_ul_new_hour != 0xFFFFFFFF && (gs_uc_rtc_time[2] == ':')
					&& (gs_uc_rtc_time[5] == ':')) {
				if (rtc_set_time_alarm(RTC, 1, gs_ul_new_hour,
						1, gs_ul_new_minute, 1, gs_ul_new_second)) {
					puts("\n\r Time alarm not set, invalid input!\r");
				} else {
					printf("\n\r Time alarm is set at u:u:u!",
						(unsigned int)gs_ul_new_hour, (unsigned int)gs_ul_new_minute,
						(unsigned int)gs_ul_new_second);
				}
			} else {
				gs_uc_rtc_time[2] = ':';
				gs_uc_rtc_time[5] = ':';
				puts("\n\r Time not set, invalid input!\r");
			}
			gs_ul_state = STATE_MENU;
			gs_ul_menu_shown = 0;
			gs_ul_alarm_triggered = 0;
			refresh_display();
		}

		/* Set date alarm */
		if (uc_key == 'm') {
			gs_ul_state = STATE_SET_DATE_ALARM;

			rtc_clear_time_alarm(RTC);

			do {
				puts("\n\r\n\r Set date alarm(mm/dd/yyyy): ");
			} while (get_new_date());

			if (gs_ul_new_year != 0xFFFFFFFF && (gs_uc_date[2] == '/')
					&& (gs_uc_date[5] == '/')) {
				if (rtc_set_date_alarm(RTC, 1, gs_ul_new_month, 1,
						gs_ul_new_day)) {
					puts("\n\r Date alarm not set, invalid input!\r");
				} else {
					printf("\n\r Date alarm is set on u/u/%4u!",
							(unsigned int)gs_ul_new_month, (unsigned int)gs_ul_new_day,
							(unsigned int)gs_ul_new_year);
				}
			} else {
				gs_uc_date[2] = '/';
				gs_uc_date[5] = '/';
				puts("\n\r Date alarm not set, invalid input!\r");
			}
			gs_ul_state = STATE_MENU;
			gs_ul_menu_shown = 0;
			gs_ul_alarm_triggered = 0;
			refresh_display();
		}

#if ((SAM3S8) || (SAM3SD8) || (SAM4S) || (SAM4C) || (SAM4CP) || (SAM4CM))
		/* Generate Waveform */
		if (uc_key == 'w') {
			gs_ul_state = STATE_WAVEFORM;
			puts("\n\rMenu:\n\r"
					"  0 - No Waveform\n\r"
					"  1 - 1 Hz square wave\n\r"
					"  2 - 32 Hz square wave\n\r"
					"  3 - 64 Hz square wave\n\r"
					"  4 - 512 Hz square wave\n\r"
					"  5 - Toggles when alarm flag rise\n\r"
					"  6 - Copy of the alarm flag\n\r"
					"  7 - Duty cycle programmable pulse\n\r"
					"  8 - Quit\r");

			while (1) {
				while (uart_read(CONSOLE_UART, &uc_key));

				if ((uc_key >= '0') && (uc_key <= '7')) {
					rtc_set_waveform(RTC, 0, char_to_digit(uc_key));
				}

				if (uc_key == '8') {
					gs_ul_state = STATE_MENU;
					gs_ul_menu_shown = 0;
					refresh_display();
					break;
				}
			}
		}
#endif
		/* Clear trigger flag */
		if (uc_key == 'c') {
			gs_ul_alarm_triggered = 0;
			gs_ul_menu_shown = 0;
			refresh_display();
		}

	}

}											                   
全部评论 ()
条评论
写评论

创建讨论帖子

登录 后参与评论
系统提示