手头上有个驱动为HX8347的ardunio接口的LCD屏,今天我们就要ATF407来驱动它。屏幕用SPI方式驱动,同时SD卡也跟屏幕挂在同一路SPI上。
我们通过CS选择,分时使用这2个外设。并移植FATFS,让其显示SD卡里面的BMP图片。
废话不多说,我们先来配置SPI,这里使用SPI1,其中SPI的时钟速度不能太快,要考虑到SD卡的兼容性:
static void SPI1_GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
RCC_APB2PeriphClockCmd(SPI1_GPIO_CLK|RCC_APB2PERIPH_SPI1|RCC_APB2PERIPH_AFIO, ENABLE);
/* Configure SPI_FLASH pins*/
GPIO_InitStructure.GPIO_Pins = SPI1_PIN_MOSI;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pins = SPI1_PIN_MISO;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pins = SPI1_PIN_SCK;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SPI1_GPIO, &GPIO_InitStructure);
//CS
GPIO_InitStructure.GPIO_Pins = LCD_CS_PIN;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
GPIO_Init(LCD_CS_GPIO, &GPIO_InitStructure);
}
//SPI1 初始化
void SPI1_Init(void)
{
SPI_InitType SPI_InitStructure;
/* Enable SPIy clock and GPIO clock for SPIy and SPIz */
RCC_APB2PeriphClockCmd(SPI1_CLK , ENABLE);
/* 1st phase: SPI1 Master */
/* GPIO configuration ------------------------------------------------------*/
SPI1_GPIO_Configuration();
//CS 置高
__LCD_CS_SET();
/* SPI1 Config -------------------------------------------------------------*/
SPI_DefaultInitParaConfig(&SPI_InitStructure);
SPI_InitStructure.SPI_TransMode = SPI_TRANSMODE_FULLDUPLEX;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2EDGE;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_HIGH;
SPI_InitStructure.SPI_CPOLY = 0;
SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
SPI_InitStructure.SPI_FrameSize = SPI_FRAMESIZE_8BIT;
SPI_InitStructure.SPI_MCLKP = SPI_MCLKP_8;
//SPI_InitStructure.SPI_MCLKP = SPI_MCLKP_32;
SPI_InitStructure.SPI_NSSSEL = SPI_NSSSEL_SOFT;
SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
SPI_Init(SPI1, &SPI_InitStructure);
/* Enable SPI1 */
SPI_Enable(SPI1, ENABLE);
}
配置好了后,我们单独写一个SPI读写一个字节的函数:
unsigned int SPI1ReadWriteByte(unsigned char tx_data)
{
/* Wait for SPIy Tx buffer empty */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TE) == RESET);
/* Send SPIy data */
SPI_I2S_TxData(SPI1,tx_data);
/* Wait for SPIy data reception */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RNE) == RESET);
/* Read SPIy received data */
return SPI_I2S_RxData(SPI1);
}
我们定义LCD的读写,直接调用SPI1的读写函数:
#define __LCD_WRITE_BYTE(__DATA) SPI1ReadWriteByte(__DATA)
其中LCD的其他几个控制引脚的配置,与JTAG的引脚有冲突,我们remap为SWD调试模式,释放PA15 PB3 PB4 3个引脚作为普通引脚:
void lcd_port_init(void)
{
GPIO_InitType GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA| RCC_APB2PERIPH_GPIOC||RCC_APB2PERIPH_AFIO , ENABLE);
//调试仅SWD
GPIO_PinsRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//释放PA15 PB3 PB4
GPIO_StructInit(&GPIO_InitStructure);
//LCD-CS PA15
GPIO_InitStructure.GPIO_Pins = LCD_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(LCD_CS_GPIO, &GPIO_InitStructure);
//LCD-DC PA8
GPIO_InitStructure.GPIO_Pins = LCD_DC_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(LCD_DC_GPIO, &GPIO_InitStructure);
//LCD-BKL PC7
GPIO_InitStructure.GPIO_Pins = LCD_BKL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(LCD_BKL_GPIO, &GPIO_InitStructure);
}
LCD其他操作函数:
void lcd_write_byte(uint8_t chByte, uint8_t chCmd)
{
if (chCmd) {
__LCD_DC_SET();
} else {
__LCD_DC_CLR();
}
__LCD_CS_CLR();
__LCD_WRITE_BYTE(chByte);
__LCD_CS_SET();
}
void lcd_write_word(uint16_t hwData)
{
__LCD_DC_SET();
__LCD_CS_CLR();
__LCD_WRITE_BYTE(hwData >> 8);
__LCD_WRITE_BYTE(hwData & 0xFF);
__LCD_CS_SET();
}
void lcd_write_register(uint8_t chRegister, uint8_t chValue)
{
lcd_write_byte(chRegister, LCD_CMD);
lcd_write_byte(chValue, LCD_DATA);
}
//set the specified position of cursor on lcd.
//hwXpos specify x position
//hwYpos specify y position
void lcd_set_cursor(uint16_t hwXpos, uint16_t hwYpos)
{
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
lcd_write_register(0x02, hwXpos >> 8);
lcd_write_register(0x03, hwXpos & 0xFF); //Column Start
lcd_write_register(0x06, hwYpos >> 8);
lcd_write_register(0x07, hwYpos & 0xFF); //Row Start
}
//clear the lcd with the specified color.
void lcd_clear_screen(uint16_t hwColor)
{
uint32_t i, wCount = LCD_WIDTH;
wCount *= LCD_HEIGHT;
lcd_set_cursor(0, 0);
lcd_write_byte(0x22, LCD_CMD);
__LCD_DC_SET();
__LCD_CS_CLR();
for (i = 0; i < wCount; i ++) {
__LCD_WRITE_BYTE(hwColor >> 8);
__LCD_WRITE_BYTE(hwColor & 0xFF);
}
__LCD_CS_SET();
}
//draw a point on the lcd with the specified color.
//hwXpos specify x position.
//hwYpos specify y position.
//hwColor color of the point.
void lcd_draw_point(uint16_t hwXpos, uint16_t hwYpos, uint16_t hwColor)
{
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
lcd_set_cursor(hwXpos, hwYpos);
lcd_write_byte(0x22, LCD_CMD);
lcd_write_word(hwColor);
}
//display a char at the specified position on lcd.
void lcd_display_char(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint8_t chChr, //a char is display.
uint8_t chSize, //specify the size of the char
uint16_t hwColor) //specify the color of the char
{
uint8_t i, j, chTemp;
uint16_t hwYpos0 = hwYpos, hwColorVal = 0;
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
for (i = 0; i < chSize; i ++) {
if (FONT_1206 == chSize) {
chTemp = c_chFont1206[chChr - 0x20][i];
} else if (FONT_1608 == chSize) {
chTemp = c_chFont1608[chChr - 0x20][i];
}
for (j = 0; j < 8; j ++) {
if (chTemp & 0x80) {
hwColorVal = hwColor;
lcd_draw_point(hwXpos, hwYpos, hwColorVal);
}
chTemp <<= 1;
hwYpos ++;
if ((hwYpos - hwYpos0) == chSize) {
hwYpos = hwYpos0;
hwXpos ++;
break;
}
}
}
}
//_pow
uint32_t _pow(uint8_t m, uint8_t n)
{
uint32_t result = 1;
while(n --) result *= m;
return result;
}
//display a number at the specified position on lcd.
void lcd_display_num(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint32_t chNum, //a number is display.
uint8_t chLen, //length ot the number
uint8_t chSize, //specify the size of the number
uint16_t hwColor) //specify the color of the number
{
uint8_t i;
uint8_t chTemp, chShow = 0;
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
for(i = 0; i < chLen; i ++) {
chTemp = (chNum / _pow(10, chLen - i - 1)) % 10;
if(chShow == 0 && i < (chLen - 1)) {
if(chTemp == 0) {
lcd_display_char(hwXpos + (chSize / 2) * i, hwYpos, ' ', chSize, hwColor);
continue;
} else {
chShow = 1;
}
}
lcd_display_char(hwXpos + (chSize / 2) * i, hwYpos, chTemp + '0', chSize, hwColor);
}
}
//display a string at the specified position on lcd.
void lcd_display_string(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
const uint8_t *pchString, //a pointer to string
uint8_t chSize, // the size of the string
uint16_t hwColor) // specify the color of the string
{
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
while (*pchString != '\0') {
if (hwXpos > (LCD_WIDTH - chSize / 2)) {
hwXpos = 0;
hwYpos += chSize;
if (hwYpos > (LCD_HEIGHT - chSize)) {
hwYpos = hwXpos = 0;
lcd_clear_screen(0x00);
}
}
lcd_display_char(hwXpos, hwYpos, (uint8_t)*pchString, chSize, hwColor);
hwXpos += chSize / 2;
pchString ++;
}
}
//draw a line at the specified position on lcd.
void lcd_draw_line(uint16_t hwXpos0, //specify x0 position.
uint16_t hwYpos0, //specify y0 position.
uint16_t hwXpos1, //specify x1 position.
uint16_t hwYpos1, //specify y1 position.
uint16_t hwColor) //specify the color of the line
{
int x = hwXpos1 - hwXpos0;
int y = hwYpos1 - hwYpos0;
int dx = abs(x), sx = hwXpos0 < hwXpos1 ? 1 : -1;
int dy = -abs(y), sy = hwYpos0 < hwYpos1 ? 1 : -1;
int err = dx + dy, e2;
if (hwXpos0 >= LCD_WIDTH || hwYpos0 >= LCD_HEIGHT || hwXpos1 >= LCD_WIDTH || hwYpos1 >= LCD_HEIGHT) {
return;
}
for (;;){
lcd_draw_point(hwXpos0, hwYpos0 , hwColor);
e2 = 2 * err;
if (e2 >= dy) {
if (hwXpos0 == hwXpos1) break;
err += dy; hwXpos0 += sx;
}
if (e2 <= dx) {
if (hwYpos0 == hwYpos1) break;
err += dx; hwYpos0 += sy;
}
}
}
//draw a circle at the specified position on lcd.
void lcd_draw_circle(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint16_t hwRadius, //specify the radius of the circle.
uint16_t hwColor) //specify the color of the circle.
{
int x = -hwRadius, y = 0, err = 2 - 2 * hwRadius, e2;
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
do {
lcd_draw_point(hwXpos - x, hwYpos + y, hwColor);
lcd_draw_point(hwXpos + x, hwYpos + y, hwColor);
lcd_draw_point(hwXpos + x, hwYpos - y, hwColor);
lcd_draw_point(hwXpos - x, hwYpos - y, hwColor);
e2 = err;
if (e2 <= y) {
err += ++ y * 2 + 1;
if(-x == y && e2 <= x) e2 = 0;
}
if(e2 > x) err += ++ x * 2 + 1;
} while(x <= 0);
}
//fill a rectangle out at the specified position on lcd.
void lcd_fill_rect(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint16_t hwWidth, //specify the width of the rectangle.
uint16_t hwHeight, //specify the height of the rectangle.
uint16_t hwColor) //specify the color of rectangle.
{
uint16_t i, j;
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
for(i = 0; i < hwHeight; i ++){
for(j = 0; j < hwWidth; j ++){
lcd_draw_point(hwXpos + j, hwYpos + i, hwColor);
}
}
}
//draw a vertical line at the specified position on lcd.
void lcd_draw_v_line(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint16_t hwHeight, //specify the height of the vertical line.
uint16_t hwColor) //specify the color of the vertical line.
{
uint16_t i, y1 = MIN(hwYpos + hwHeight, LCD_HEIGHT - 1);
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
for (i = hwYpos; i < y1; i ++) {
lcd_draw_point(hwXpos, i, hwColor);
}
}
//draw a horizonal line at the specified position on lcd.
void lcd_draw_h_line(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint16_t hwWidth, //specify the width of the horizonal line.
uint16_t hwColor) //specify the color of the horizonal line.
{
uint16_t i, x1 = MIN(hwXpos + hwWidth, LCD_WIDTH - 1);
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
for (i = hwXpos; i < x1; i ++) {
lcd_draw_point(i, hwYpos, hwColor);
}
}
void lcd_draw_rect(uint16_t hwXpos, //specify x position.
uint16_t hwYpos, //specify y position.
uint16_t hwWidth, //specify the width of the rectangle.
uint16_t hwHeight, //specify the height of the rectangle.
uint16_t hwColor) //specify the color of rectangle.
{
if (hwXpos >= LCD_WIDTH || hwYpos >= LCD_HEIGHT) {
return;
}
lcd_draw_h_line(hwXpos, hwYpos, hwWidth, hwColor);
lcd_draw_h_line(hwXpos, hwYpos + hwHeight, hwWidth, hwColor);
lcd_draw_v_line(hwXpos, hwYpos, hwHeight, hwColor);
lcd_draw_v_line(hwXpos + hwWidth, hwYpos, hwHeight + 1, hwColor);
}
//initialize the lcd.
//phwDevId pointer to device ID of lcd
void lcd_init(void)
{
// __LCD_RST_CLR();
// delay_ms(100);
// __LCD_RST_SET();
__LCD_CS_SET();
__LCD_BKL_SET();
//Driving ability Setting
lcd_write_register(0xEA,0x00); //PTBA[15:8]
lcd_write_register(0xEB,0x20); //PTBA[7:0]
lcd_write_register(0xEC,0x0C); //STBA[15:8]
lcd_write_register(0xED,0xC4); //STBA[7:0]
lcd_write_register(0xE8,0x38); //OPON[7:0]
lcd_write_register(0xE9,0x10); //OPON1[7:0]
lcd_write_register(0xF1,0x01); //OTPS1B
lcd_write_register(0xF2,0x10); //GEN
//Gamma 2.2 Setting
lcd_write_register(0x40,0x01); //
lcd_write_register(0x41,0x00); //
lcd_write_register(0x42,0x00); //
lcd_write_register(0x43,0x10); //
lcd_write_register(0x44,0x0E); //
lcd_write_register(0x45,0x24); //
lcd_write_register(0x46,0x04); //
lcd_write_register(0x47,0x50); //
lcd_write_register(0x48,0x02); //
lcd_write_register(0x49,0x13); //
lcd_write_register(0x4A,0x19); //
lcd_write_register(0x4B,0x19); //
lcd_write_register(0x4C,0x16); //
lcd_write_register(0x50,0x1B); //
lcd_write_register(0x51,0x31); //
lcd_write_register(0x52,0x2F); //
lcd_write_register(0x53,0x3F); //
lcd_write_register(0x54,0x3F); //
lcd_write_register(0x55,0x3E); //
lcd_write_register(0x56,0x2F); //
lcd_write_register(0x57,0x7B); //
lcd_write_register(0x58,0x09); //
lcd_write_register(0x59,0x06); //
lcd_write_register(0x5A,0x06); //
lcd_write_register(0x5B,0x0C); //
lcd_write_register(0x5C,0x1D); //
lcd_write_register(0x5D,0xCC); //
//Power Voltage Setting
lcd_write_register(0x1B,0x1B); //VRH=4.65V
lcd_write_register(0x1A,0x01); //BT (VGH~15V,VGL~-10V,DDVDH~5V)
lcd_write_register(0x24,0x2F); //VMH(VCOM High voltage ~3.2V)
lcd_write_register(0x25,0x57); //VML(VCOM Low voltage -1.2V)
//****VCOM offset**///
lcd_write_register(0x23,0x88); //for Flicker adjust //can reload from OTP
//Power on Setting
lcd_write_register(0x18,0x34); //I/P_RADJ,N/P_RADJ, Normal mode 60Hz
lcd_write_register(0x19,0x01); //OSC_EN='1', start Osc
lcd_write_register(0x01,0x00); //DP_STB='0', out deep sleep
lcd_write_register(0x1F,0x88);// GAS=1, VOMG=00, PON=0, DK=1, XDK=0, DVDH_TRI=0, STB=0
delay_ms(5);
lcd_write_register(0x1F,0x80);// GAS=1, VOMG=00, PON=0, DK=0, XDK=0, DVDH_TRI=0, STB=0
delay_ms(5);
lcd_write_register(0x1F,0x90);// GAS=1, VOMG=00, PON=1, DK=0, XDK=0, DVDH_TRI=0, STB=0
delay_ms(5);
lcd_write_register(0x1F,0xD0);// GAS=1, VOMG=10, PON=1, DK=0, XDK=0, DDVDH_TRI=0, STB=0
delay_ms(5);
//262k/65k color selection
lcd_write_register(0x17,0x05); //default 0x06 262k color // 0x05 65k color
//SET PANEL
lcd_write_register(0x36,0x00); //SS_P, GS_P,REV_P,BGR_P
//Display ON Setting
lcd_write_register(0x28,0x38); //GON=1, DTE=1, D=1000
delay_ms(40);
lcd_write_register(0x28,0x3F); //GON=1, DTE=1, D=1100
lcd_write_register(0x16,0x18);
//Set GRAM Area
lcd_write_register(0x02,0x00);
lcd_write_register(0x03,0x00); //Column Start
lcd_write_register(0x04,0x00);
lcd_write_register(0x05,0xEF); //Column End
lcd_write_register(0x06,0x00);
lcd_write_register(0x07,0x00); //Row Start
lcd_write_register(0x08,0x01);
lcd_write_register(0x09,0x3F); //Row End
lcd_clear_screen(WHITE);
//lcd_clear_screen(RED);
}
接下来,我们配置SD卡的驱动程序:
SD卡读写一样调用的SPI1的读写函数:
#define __SD_WRITE_BYTE(__DATA) SPI1ReadWriteByte(__DATA)
SD开的CS端配置,其中触摸芯片XPT2046也挂在这个SPI上面,本例我们先不要,但是定义它的CS引脚,让它处于不选通状态:
void SD_port_init(void)
{
GPIO_InitType GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOB, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
//PB4 PB5
GPIO_InitStructure.GPIO_Pins = SD_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pins = XPT2046_CS_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
__SD_CS_SET();
__XPT2046_CS_SET();
}
SD其他操作函数:
//data: data to be written to sd card.
//return: data read from sd card.
uint8_t SD_SPI_ReadWriteByte(uint8_t data)
{
return __SD_WRITE_BYTE(data);
}
//set spi in low speed mode.
void SD_SPI_SpeedLow(void)
{
SPI1->CTRL1&=0XFFC7;
SPI1->CTRL1|=SPI_MCLKP_256;
SPI_Enable(SPI1, ENABLE);
}
//set spi in high speed mode.
void SD_SPI_SpeedHigh(void)
{
SPI1->CTRL1&=0XFFC7;
SPI1->CTRL1|=SPI_MCLKP_32;
SPI_Enable(SPI1, ENABLE);
}
//released spi bus
void SD_DisSelect(void)
{
__SD_CS_SET();
SD_SPI_ReadWriteByte(0xff);//providing extra 8 clocks
}
//pick sd card and waiting until until it's ready
//return: 0: succed 1: failure
uint8_t SD_Select(void)
{
__SD_CS_CLR();
if(SD_WaitReady()==0)return 0;
SD_DisSelect();
return 1;
}
//waiting for sd card until it's ready
uint8_t SD_WaitReady(void)
{
uint32_t t=0;
do
{
if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;
t++;
}while(t<0XFFFFFF);
return 1;
}
//waiting for response from sd card.
//Response: expect from sd card.
//return: succeed for 0, fail for other else
//return: 0 for success, other for failure.
uint8_t SD_GetResponse(uint8_t Response)
{
uint16_t Count=0xFFFF;
while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;
if (Count==0)return MSD_RESPONSE_FAILURE;
else return MSD_RESPONSE_NO_ERROR;
}
//read a buffer from sd card.
//*buf: pointer to a buffer.
//len: length of the buffer.
//return: 0 for success, other for failure.
uint8_t SD_RecvData(uint8_t*buf,uint16_t len)
{
if(SD_GetResponse(0xFE))return 1;//waiting for start command send back from sd card.
while(len--)//receiving data...
{
*buf=__SD_WRITE_BYTE(0xFF);
buf++;
}
//send 2 dummy write (dummy CRC)
SD_SPI_ReadWriteByte(0xFF);
SD_SPI_ReadWriteByte(0xFF);
return 0;
}
//write a buffer containing 512 bytes to sd card.
//buf: data buffer
//cmd: command
//return: 0 for success, other for failure.
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
uint16_t t;
if(SD_WaitReady())return 1;
SD_SPI_ReadWriteByte(cmd);
if(cmd!=0XFD)
{
for(t=0;t<512;t++)__SD_WRITE_BYTE(buf[t]);
SD_SPI_ReadWriteByte(0xFF);//ignoring CRC
SD_SPI_ReadWriteByte(0xFF);
t=SD_SPI_ReadWriteByte(0xFF);
if((t&0x1F)!=0x05)return 2;
}
return 0;
}
//send a command to sd card
//cmd:command
//arg: parameter
//crc: crc
//return: response sent back from sd card.
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t Retry=0;
SD_DisSelect();
if(SD_Select())return 0XFF;
SD_SPI_ReadWriteByte(cmd | 0x40);
SD_SPI_ReadWriteByte(arg >> 24);
SD_SPI_ReadWriteByte(arg >> 16);
SD_SPI_ReadWriteByte(arg >> 8);
SD_SPI_ReadWriteByte(arg);
SD_SPI_ReadWriteByte(crc);
if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff); //Skip a stuff byte when stop reading
Retry=0X1F;
do
{
r1=SD_SPI_ReadWriteByte(0xFF);
}while((r1&0X80) && Retry--);
return r1;
}
//obtain CID including manufacturer informationfrom sd card
//*cid_dat: pointer to the buffer storing CID, at least 16 bytes.
//return: 0 no error 1 error
uint8_t SD_GetCID(uint8_t *cid_data)
{
uint8_t r1;
r1=SD_SendCmd(CMD10,0,0x01);
if(r1==0x00)
{
r1=SD_RecvData(cid_data,16);
}
SD_DisSelect();
if(r1)return 1;
else return 0;
}
//obtain CSD including storage and speed.
//*csd_data : pointer to the buffer storing CSD, at least 16 bytes.
//return: 0 no error 1 error
uint8_t SD_GetCSD(uint8_t *csd_data)
{
uint8_t r1;
r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD send CMD9 in order to get CSD
if(r1==0)
{
r1=SD_RecvData(csd_data, 16);
}
SD_DisSelect();
if(r1)return 1;
else return 0;
}
//obtian the totals of sectors of sd card.
//return: 0 error, other else for storage of sd card.
//numbers of bytes of each sector must be 512, otherwise fail to initialization.
uint32_t SD_GetSectorCount(void)
{
uint8_t csd[16];
uint32_t Capacity;
uint8_t n;
uint16_t csize;
if(SD_GetCSD(csd)!=0) return 0;
//calculation for SDHC below
if((csd[0]&0xC0)==0x40) //V2.00
{
csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
Capacity = (uint32_t)csize << 10; //totals of sectors
}else//V1.XX
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
Capacity= (uint32_t)csize << (n - 9);
}
return Capacity;
}
//initialize sd card
uint8_t SD_Initialize(void)
{
uint8_t r1;
uint16_t retry;
uint8_t buf[4];
uint16_t i;
__SD_CS_SET();
SD_SPI_SpeedLow();
for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);
retry=20;
do
{
r1=SD_SendCmd(CMD0,0,0x95);//enter to idle state
}while((r1!=0X01) && retry--);
SD_Type=0;
if(r1==0X01)
{
if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
{
for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF); //Get trailing return value of R7 resp
if(buf[2]==0X01&&buf[3]==0XAA)//is it support of 2.7~3.6V
{
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0x40000000,0X01);
}while(r1&&retry--);
if(retry&&SD_SendCmd(CMD58,0,0X01)==0) //start to identify the SD2.0 version of sd card.
{
for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//get OCR
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC; //check CCS
else SD_Type=SD_TYPE_V2;
}
}
}else//SD V1.x/ MMC V3
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do //exit idle state
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
}while(r1&&retry--);
}else
{
SD_Type=SD_TYPE_MMC;//MMC V3
retry=0XFFFE;
do
{
r1=SD_SendCmd(CMD1,0,0X01);
}while(r1&&retry--);
}
if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;
}
}
SD_DisSelect();
SD_SPI_SpeedHigh();
if(SD_Type)return 0;
else if(r1)return r1;
return 0xaa;
}
//read SD card
//buf: data buffer
//sector: sector
//cnt: totals of sectors]
//return: 0 ok, other for failure
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;
if(cnt==1)
{
r1=SD_SendCmd(CMD17,sector,0X01);
if(r1==0)
{
r1=SD_RecvData(buf,512);
}
}else
{
r1=SD_SendCmd(CMD18,sector,0X01);
do
{
r1=SD_RecvData(buf,512);
buf+=512;
}while(--cnt && r1==0);
SD_SendCmd(CMD12,0,0X01);
}
SD_DisSelect();
return r1;//
}
//write sd card
//buf: data buffer
//sector: start sector
//cnt: totals of sectors]
//return: 0 ok, other for failure
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(SD_Type!=SD_TYPE_V2HC)sector *= 512;
if(cnt==1)
{
r1=SD_SendCmd(CMD24,sector,0X01);
if(r1==0)
{
r1=SD_SendBlock(buf,0xFE);
}
}else
{
if(SD_Type!=SD_TYPE_MMC)
{
SD_SendCmd(CMD55,0,0X01);
SD_SendCmd(CMD23,cnt,0X01);
}
r1=SD_SendCmd(CMD25,sector,0X01);
if(r1==0)
{
do
{
r1=SD_SendBlock(buf,0xFC);
buf+=512;
}while(--cnt && r1==0);
r1=SD_SendBlock(0,0xFD);
}
}
SD_DisSelect();
return r1;
}
下面我们开始移植FATFS,这个主要就是diskio和ff的文件,这里也加载了FATFS常见操作的fatfs_storage文件。
主要修改的就是diskio文件,让我们本例程中的SD具体实现函数来替代FATFS里面的底层操作函数,当然也可在ff_config
头文件里面精简FATFS系统:
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
#define SD_CARD 0 //SD卡,卷标为0
#define FLASH_SECTOR_SIZE 512
//初始化磁盘
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0..) */
)
{
uint8_t res=0;
switch(drv)
{
case SD_CARD://SD卡
res = SD_Initialize();//SD_Initialize()
if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
{
SD_SPI_SpeedLow();
SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
SD_SPI_SpeedHigh();
}
break;
default:
res=1;
}
if(res)return STA_NOINIT;
else return 0; //初始化成功
}
//获得磁盘状态
DSTATUS disk_status (
BYTE drv /* Physical drive nmuber (0..) */
)
{
return 0;
}
//读扇区
//drv:磁盘编号0~9
//*buff:数据接收缓冲首地址
//sector:扇区地址
//count:需要读取的扇区数
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
uint8_t res=0;
if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
switch(drv)
{
case SD_CARD://SD卡
res=SD_ReadDisk(buff,sector,count);
if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
{
SD_SPI_SpeedLow();
SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
SD_SPI_SpeedHigh();
}
break;
default:
res=1;
}
//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
if(res==0x00)return RES_OK;
else return RES_ERROR;
}
//写扇区
//drv:磁盘编号0~9
//*buff:发送数据首地址
//sector:扇区地址
//count:需要写入的扇区数
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
uint8_t res=0;
if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
switch(drv)
{
case SD_CARD://SD卡
res=SD_WriteDisk((uint8_t*)buff,sector,count);
break;
default:
res=1;
}
//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
if(res == 0x00)return RES_OK;
else return RES_ERROR;
}
#endif /* _READONLY */
//其他表参数的获得
//drv:磁盘编号0~9
//ctrl:控制代码
//*buff:发送/接收缓冲区指针
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
if(drv==SD_CARD)//SD卡
{
switch(ctrl)
{
case CTRL_SYNC:
__SD_CS_CLR();
if(SD_WaitReady()==0)res = RES_OK;
else res = RES_ERROR;
__SD_CS_SET();
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 8;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = SD_GetSectorCount();
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
}else res=RES_ERROR;//其他的不支持
return res;
}
/*-----------------------------------------------------------------------*/
/* User defined function to give a current time to fatfs module */
/* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
/* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
DWORD get_fattime (void)
{
return 0;
}
其他不予多说,在main文件里面添加2个函数,一个是SD挂载FATFS,一个是轮流显示SD卡根目录里面的BMP:
char* pDirectoryFiles[MAX_BMP_FILES];
FATFS microSDFatFs;
uint8_t str[20];
void SDCard_Config(void)
{
uint32_t counter = 0;
/* Check the mounted device */
if(f_mount(µSDFatFs, (TCHAR const*)"/", 0) != FR_OK)
{
lcd_display_string(0, 16, "FATFS_NOT_MOUNTED", 16, RED);
}
else
{
/* Initialize the Directory Files pointers (heap) */
for (counter = 0; counter < MAX_BMP_FILES; counter++)
{
pDirectoryFiles[counter] = malloc(11);
}
}
}
static void Display_Images(void)
{
uint32_t bmplen = 0x00;
uint32_t checkstatus = 0x00;
uint32_t filesnumbers = 0x00;
uint32_t bmpcounter = 0x00;
DIR directory;
FRESULT res;
/* Open directory */
res= f_opendir(&directory, "/");
if((res != FR_OK))
{
if(res == FR_NO_FILESYSTEM)
{
/* Display message: SD card not FAT formated */
lcd_display_string(0, 32, "SD_CARD_NOT_FORMATTED", 16, RED);
}
else
{
/* Display message: Fail to open directory */
lcd_display_string(0, 48, "SD_CARD_OPEN_FAIL", 16, RED);
}
}
/* Get number of bitmap files */
filesnumbers = Storage_GetDirectoryBitmapFiles ("/", pDirectoryFiles);
/* Set bitmap counter to display first image */
bmpcounter = 1;
while (1)
{
sprintf((char*)str, "%-11.11s", pDirectoryFiles[bmpcounter -1]);
checkstatus = Storage_CheckBitmapFile((const char*)str, &bmplen);
if(checkstatus == 0)
{
/* Format the string */
Storage_OpenReadFile(0, 0, (const char*)str);
}
else if (checkstatus == 1)
{
/* Display message: SD card does not exist */
lcd_display_string(0, 64, "SD_CARD_NOT_FOUND", 16, RED);
}
else
{
/* Display message: File not supported */
lcd_display_string(0, 80, "SD_CARD_FILE_NOT_SUPPORTED", 16, RED);
}
bmpcounter ++;
if(bmpcounter > filesnumbers)
{
bmpcounter = 1;
}
delay_ms(1000);
// delay_ms(1000);
// delay_ms(1000);
}
}
最终main里面添加:
int main(void)
{
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_at32f403_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_at32f4xx.c file
*/
/* Config LED */
AT32_Board_Init();
/* SPIy Config -------------------------------------------------------------*/
SPI1_Init();
lcd_port_init();
lcd_init();
SD_port_init();
SDCard_Config();
lcd_draw_rect(30, 40, 150, 100, RED);
lcd_draw_circle(120, 160, 50, BLUE);
lcd_draw_line(30, 40, 180, 140, RED);
lcd_draw_line(30, 220, 210, 240, RED);
lcd_draw_line(30, 220, 120, 280, RED);
lcd_draw_line(120, 280, 210, 240, RED);
lcd_clear_screen(RED);
Delay_ms(300);
lcd_clear_screen(BLUE);
Delay_ms(300);
lcd_clear_screen(YELLOW);
Delay_ms(300);
lcd_clear_screen(BLACK);
Delay_ms(300);
lcd_clear_screen(GRAY);
Delay_ms(300);
lcd_clear_screen(GREEN);
Delay_ms(300);
lcd_clear_screen(WHITE);
Delay_ms(300);
lcd_display_string(60, 120, (const uint8_t *)"Hello, world !", FONT_1608, BLUE);
lcd_display_string(30, 152, (const uint8_t *)"2.8' TFT Touch Shield", FONT_1608, RED);
lcd_display_string(30, 152+32, (const uint8_t *)"T want fly high !", FONT_1608, GREEN);
while (1)
{
// AT32_LEDn_Toggle(LED2);
// AT32_LEDn_Toggle(LED3);
// Delay_ms(300);
Display_Images();
}
}
编译,下载,查看:
代码(有需要的自取):
SPI_LCD _FATFS_BMP.rar
(5.74 MB)
|
|
分享的图片无法显示,可否重新上传一下呢?