| 
              #include <stdio.h>#include <stm32f10x.h>
 
 #define _BV(n) (1u << (n))
 
 uint16_t rca;
 
 // 延时n毫秒
 void delay(uint16_t n)
 {
 TIM6->ARR = 10 * n - 1; // nms
 TIM6->PSC = 7199; // 72MHz/7200=10kHz
 TIM6->CR1 = TIM_CR1_URS | TIM_CR1_OPM; // UG不置位UIF, 非循环模式
 TIM6->EGR = TIM_EGR_UG; // 保存设置
 TIM6->CR1 |= TIM_CR1_CEN; // 开始计时
 while ((TIM6->SR & TIM_SR_UIF) == 0); // 等待计时完毕
 TIM6->SR &= ~TIM_SR_UIF; // 清除溢出标志
 }
 
 int fputc(int ch, FILE *fp)
 {
 if (fp == stdout)
 {
 if (ch == '\n')
 {
 while ((USART1->SR & USART_SR_TXE) == 0);
 USART1->DR = '\r';
 }
 while ((USART1->SR & USART_SR_TXE) == 0);
 USART1->DR = ch;
 }
 return ch;
 }
 
 // 检查命令是否收到了回应, 若没收到则重发命令
 void Card_CheckCommand(void)
 {
 while (SDIO->STA & SDIO_STA_CTIMEOUT)
 {
 SDIO->ICR = SDIO_ICR_CTIMEOUTC; // 清除标志
 SDIO->CMD = SDIO->CMD; // 重发
 printf("Timeout! Resend CMD%d\n", SDIO->CMD & SDIO_CMD_CMDINDEX);
 while (SDIO->STA & SDIO_STA_CMDACT);
 }
 }
 
 void Card_SendCMD52(uint8_t is_write, uint8_t read_after_write, uint8_t function_number, uint32_t register_address, uint8_t data_to_write)
 {
 SDIO->ARG = (is_write << 31) | (function_number << 28) | (read_after_write << 27) | (register_address << 9) | data_to_write;
 SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 52;
 while (SDIO->STA & SDIO_STA_CMDACT);
 }
 
 void Card_ShowShortResponse(void)
 {
 printf("Command response received: CMD%d, RESP_%08x\n", SDIO->RESPCMD, SDIO->RESP1);
 }
 
 // 读寄存器
 uint8_t Card_Read(uint8_t func_num, uint32_t addr)
 {
 Card_SendCMD52(0, 0, func_num, addr, 0);
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 return SDIO->RESP1 & 0xff;
 }
 else
 {
 printf("Card_Read failed, SDIO->STA=0x%08x!\n", SDIO->STA);
 return 0;
 }
 }
 
 // 写寄存器, 返回写入后寄存器的实际内容
 uint8_t Card_Write(uint8_t func_num, uint32_t addr, uint8_t value)
 {
 Card_SendCMD52(1, 1, func_num, addr, value);
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 return SDIO->RESP1 & 0xff;
 }
 else
 {
 printf("Card_Write failed, SDIO->STA=0x%08x!\n", SDIO->STA);
 return 0;
 }
 }
 
 // SDIO Simplified Specification Version 3.00
 // 3. SDIO Card Initialization
 void Card_Init(void)
 {
 printf("Initialization begins...\n");
 SDIO->POWER = SDIO_POWER_PWRCTRL;
 SDIO->CLKCR = SDIO_CLKCR_CLKEN | 178; // 初始化时最高允许的频率: 72MHz/(178+2)=400kHz
 delay(5); // 延时可防止CMD5重发
 
 // 不需要发送CMD0, 因为SD I/O card的初始化命令是CMD52
 // An I/O only card or the I/O portion of a combo card is NOT reset by CMD0. (See 4.4 Reset for SDIO)
 
 /* 发送CMD5: IO_SEND_OP_COND */
 SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 5;
 while (SDIO->STA & SDIO_STA_CMDACT);
 Card_CheckCommand(); // 为了保险起见还是要检查一下是否要重发命令
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 Card_ShowShortResponse();
 }
 
 /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
 SDIO->ARG = 0x300000;
 SDIO->CMD = SDIO->CMD;
 while (SDIO->STA & SDIO_STA_CMDACT);
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 Card_ShowShortResponse();
 if (SDIO->RESP1 & _BV(31))
 {
 // Card is ready to operate after initialization
 printf("Number of I/O Functions: %d\n", (SDIO->RESP1 >> 28) & 7);
 printf("Memory Present: %d\n", (SDIO->RESP1 & _BV(27)) != 0);
 }
 }
 
 /* 获取Wi-Fi模块地址 (CMD3: SEND_RELATIVE_ADDR, Ask the card to publish a new relative address (RCA)) */
 SDIO->ARG = 0;
 SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 3;
 while (SDIO->STA & SDIO_STA_CMDACT);
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 rca = SDIO->RESP1 >> 16;
 printf("Relative card address: 0x%04x\n", rca);
 }
 
 /* 选中Wi-Fi模块 (CMD7: SELECT/DESELECT_CARD) */
 SDIO->ARG = rca << 16;
 SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 7;
 while (SDIO->STA & SDIO_STA_CMDACT);
 if (SDIO->STA & SDIO_STA_CMDREND)
 {
 SDIO->ICR = SDIO_ICR_CMDRENDC;
 printf("Card selected! status=0x%08x\n", SDIO->RESP1);
 }
 
 // 提高时钟频率
 SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 70; // 72MHz/(70+2)=1MHz
 SDIO->DTIMER = 1000000; // 当频率为1MHz时, 超时时间为1秒
 //SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 1; // 72MHz/(1+2)=24MHz
 
 /* 选择总线宽度 (Wide Bus Selection) */
 // For an SDIO card a write to the CCCR using CMD52 is used to select bus width. (See 4.5 Bus Width)
 // CMD52: IO_RW_DIRECT, CCCR: Card Common Control Registers
 Card_Write(0, 0x07, Card_Read(0, 0x07) | 0x02); // Bus Width: 4-bit bus
 SDIO->CLKCR |= SDIO_CLKCR_WIDBUS_0;
 }
 
 int main(void)
 {
 RCC->AHBENR = RCC_AHBENR_SDIOEN;
 RCC->APB1ENR = RCC_APB1ENR_TIM6EN;
 RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN| RCC_APB2ENR_USART1EN;
 
 GPIOA->CRH = 0x000004b0;
 GPIOB->CRH = 0x00030000; // PB12为WiFi模块电源开关, PB12=0时打开WiFi模块
 GPIOC->CRH = 0x000bbbbb;
 GPIOD->CRL = 0x00000b00;
 
 USART1->BRR = 0x271;
 USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
 
 Card_Init();
 
 while (1);
 }
 
 |