📄 atmel-flash.c
字号:
// This file is part of MANTIS OS, Operating System// See http://mantis.cs.colorado.edu///// Copyright (C) 2003,2004,2005 University of Colorado, Boulder//// This program is free software; you can redistribute it and/or// modify it under the terms of the mos license (see file LICENSE)/** @file atmel-flash.c * @brief External 500k flash memory */#include "mos.h"#ifdef ARCH_AVR#include <avr/io.h>#include <avr/interrupt.h>#include <avr/signal.h>#include "clock.h"#include "mutex.h"#include "atmel-flash.h"#include "dev.h"#include "printf.h"mos_mutex_t atmel_flash_mutex;static uint32_t atmel_flash_addr;static uint8_t cur_buff;static uint16_t cur_page;static uint8_t dirty; // This will never be set in unbuffered modestatic uint8_t flash_mode;static void atmel_flash_low();static void atmel_flash_high();static uint8_t atmel_flash_send_byte(uint8_t spiOut);static uint8_t atmel_flash_get_byte();static void atmel_flash_read_memory(uint16_t page, uint16_t offset, void *reqData, uint16_t len);static uint16_t atmel_flash_crc_memory(uint16_t page, uint16_t offset, uint32_t len);static uint8_t atmel_flash_busy();static uint8_t atmel_flash_write_buffer(uint8_t selected, uint16_t offset, void *reqdata, uint16_t len);static uint8_t atmel_flash_flush_buffer(uint8_t selected, uint16_t page);static uint8_t atmel_flash_compare_buffer(uint8_t selected, uint16_t page);static uint8_t atmel_flash_fill_buffer(uint8_t selected, uint16_t page);static uint8_t atmel_flash_erase_page(uint16_t page);uint8_t dev_mode_DEV_ATMEL_FLASH (uint8_t mode){ uint8_t retcode = DEV_OK; if (flash_mode == mode) return retcode; flash_mode = mode; switch(mode) { case ATMEL_FLASH_MODE_UNBUFFERED: if (dirty) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; while (atmel_flash_busy ()) ; } break; case ATMEL_FLASH_MODE_BUFFERED: cur_buff = ATMEL_FLASH_BUFFER_1; // The first comparison in write will always fail cur_page = ATMEL_FLASH_MAX_PAGES; dirty = 0; break; default: retcode = DEV_BAD_MODE; break; } return retcode;}uint8_t dev_ioctl_DEV_ATMEL_FLASH (int8_t request, ...){ uint32_t arg; va_list ap; va_start (ap, request); switch (request) { case DEV_SEEK: arg = va_arg (ap, uint32_t); atmel_flash_addr = arg; break; case DEV_FLUSH: if (dirty) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; while (atmel_flash_busy ()) ; } break; case DEV_LOCK: break; case DEV_UNLOCK: break; default: return DEV_BAD_IOCTL; } va_end (ap); return DEV_OK;}/** @brief Read from the current flash address into p, for count bytes */uint16_t dev_read_DEV_ATMEL_FLASH (void *p, uint16_t count){ uint16_t page, offset; uint8_t *buf = (uint8_t *)p; page = atmel_flash_addr / ATMEL_FLASH_PAGE_SIZE; offset = atmel_flash_addr % ATMEL_FLASH_PAGE_SIZE; if (dirty && page <= cur_page && page + (offset + count) / ATMEL_FLASH_PAGE_SIZE >= cur_page) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; } // Wait for any previous actions to complete while (atmel_flash_busy ()) ; atmel_flash_read_memory (page, offset, buf, count); return count;}/** @brief Write p into the current flash address, for count bytes */uint16_t dev_write_DEV_ATMEL_FLASH (const void *p, uint16_t count){ uint16_t page, offset, num_bytes; uint16_t index = 0; uint8_t *buf = (uint8_t *)p; page = atmel_flash_addr / ATMEL_FLASH_PAGE_SIZE; offset = atmel_flash_addr % ATMEL_FLASH_PAGE_SIZE; while (atmel_flash_busy ()) ; while (count > 0) { if (count + offset > ATMEL_FLASH_PAGE_SIZE) num_bytes = ATMEL_FLASH_PAGE_SIZE - offset; else num_bytes = count; if (flash_mode == ATMEL_FLASH_MODE_BUFFERED) { if (page != cur_page) { if (dirty) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; } cur_buff = (cur_buff==ATMEL_FLASH_BUFFER_1? ATMEL_FLASH_BUFFER_2 : ATMEL_FLASH_BUFFER_1); cur_page = page; if (num_bytes < ATMEL_FLASH_PAGE_SIZE) { while (atmel_flash_busy ()) ; atmel_flash_fill_buffer(cur_buff, page); } // Erase next page right now while (atmel_flash_busy ()) ; atmel_flash_erase_page (page); } atmel_flash_write_buffer(cur_buff, offset, &buf[index], num_bytes); dirty = 1; } else { atmel_flash_fill_buffer (ATMEL_FLASH_DEFAULT_BUFFER, page); while (atmel_flash_busy ()) ; atmel_flash_erase_page (page); while (atmel_flash_busy ()) ; atmel_flash_write_buffer (ATMEL_FLASH_DEFAULT_BUFFER, offset, &buf[index], num_bytes); atmel_flash_flush_buffer (ATMEL_FLASH_DEFAULT_BUFFER, page); while (atmel_flash_busy ()) ; } index += num_bytes; atmel_flash_addr += num_bytes; count -= num_bytes; page++; offset = 0; } return count;}/* device-specific functions */void atmel_flash_init (void){ mos_mutex_init (&atmel_flash_mutex); uint8_t handle = mos_disable_ints (); // set the flash select pin DDRA |= 1 << ATMEL_FLASH_SELECT_PIN; // set the pin high ATMEL_FLASH_SELECT |= 1 << ATMEL_FLASH_SELECT_PIN; // clear flash clock ATMEL_FLASH_PORT &= ~(1 << ATMEL_FLASH_CLK); // set flash clock output direction ATMEL_FLASH_DIRE |= 1 << ATMEL_FLASH_CLK; // clear flash out pin ATMEL_FLASH_PORT &= ~(1 << ATMEL_FLASH_OUT); // set flash out pin direction ATMEL_FLASH_DIRE &= ~(1 << ATMEL_FLASH_OUT); // clear flash in pin ATMEL_FLASH_PORT |= 1 << ATMEL_FLASH_IN; // set flash in pin direction ATMEL_FLASH_DIRE |= 1 << ATMEL_FLASH_IN; mos_enable_ints (handle); // Unbuffered mode is default for backward compatibility flash_mode = ATMEL_FLASH_MODE_UNBUFFERED; atmel_flash_addr = 0; cur_buff = ATMEL_FLASH_BUFFER_1; cur_page = ATMEL_FLASH_MAX_PAGES; dirty = 0; // must wait 20 ms before device is ready to use mos_udelay (20000);}/** @brief Compare buf to the current flash address, for count bytes */uint8_t atmel_flash_compare (uint8_t *buf, uint16_t count){ uint16_t page, offset, num_bytes; uint16_t index = 0; uint8_t compare = 0; if (dirty) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; } // Wait for any previous actions to complete while (atmel_flash_busy ()) ; while (count > 0) { page = atmel_flash_addr / ATMEL_FLASH_PAGE_SIZE; offset = atmel_flash_addr % ATMEL_FLASH_PAGE_SIZE; if (count + offset > ATMEL_FLASH_PAGE_SIZE) num_bytes = ATMEL_FLASH_PAGE_SIZE - offset; else num_bytes = count; // Are we not comparing a whole page? if (num_bytes < ATMEL_FLASH_PAGE_SIZE) { atmel_flash_fill_buffer (ATMEL_FLASH_DEFAULT_BUFFER, page); while (atmel_flash_busy ()) ; } // Write the data we want to compare to the buffer atmel_flash_write_buffer (ATMEL_FLASH_DEFAULT_BUFFER, offset, &buf[index], num_bytes); // Compare the buffer to main memory if (atmel_flash_compare_buffer (ATMEL_FLASH_DEFAULT_BUFFER, page)) { compare = 1; break; } index += num_bytes; atmel_flash_addr += num_bytes; count -= num_bytes; } // In case we exited the loop early, act like we read the whole range atmel_flash_addr += count; return compare;}/** @brief Compute the crc from the current flash address, for count bytes */uint16_t atmel_flash_crc(uint32_t count){ uint16_t page, offset, crc; page = atmel_flash_addr / ATMEL_FLASH_PAGE_SIZE; offset = atmel_flash_addr % ATMEL_FLASH_PAGE_SIZE; if (dirty && page <= cur_page && page+(offset+count)/ATMEL_FLASH_PAGE_SIZE >= cur_page) { while (atmel_flash_busy ()) ; atmel_flash_flush_buffer(cur_buff, cur_page); dirty = 0; } // Wait for any previous actions to complete while (atmel_flash_busy ()) ; crc = atmel_flash_crc_memory (page, offset, count); return crc;}/** @brief Set the flash in low */static inline void atmel_flash_low (void) { uint8_t handle = mos_disable_ints (); // clear flash clock ATMEL_FLASH_PORT &= ~(1 << ATMEL_FLASH_CLK); // clear select pin ATMEL_FLASH_SELECT &= ~(1 << ATMEL_FLASH_SELECT_PIN); mos_enable_ints (handle);}/** @brief Set the flash pin high */static inline void atmel_flash_high (void){ // set the pin high ATMEL_FLASH_SELECT |= 1 << ATMEL_FLASH_SELECT_PIN;}// 0x11010111, 3 and 5 pin, pull low FLASH_IN and FLASH_CLK /** @brief Init the bit macro. */#define BITINIT uint8_t clrClkAndData = PORTD & ~0x28 // first of all, the data is shifted in in rising edge and out in falling// The I/O address for PORTD is 18, for PIND is 16// first set the clk to low and the input to low, then // check the #n bit in spiOut. // if 0, then skip the step of writing the #n bit in FLASH_IN to high // else pull the flash_in to high// then set clk to rising edge// then check whether the FLASH_OUT in PIND is 0// if yes, then skip (i.e. set the spiIn's bit to 0) // else set the spiIn's bit to 1/** @brief Write one bit of data. */#define WRITEBIT(n) \ PORTD = clrClkAndData; \ asm __volatile__ \ ( "sbrc %2," #n "\n" \ "\tsbi 18,3\n" \ "\tsbi 18,5\n" \ : "=d" (spiIn) : "0" (spiIn), "r" (spiOut))/** @brief Read one bit of data. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -