本程序所用的单片机型号为:STM32F103RE
PB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。
PB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。
注意:WM-G-MR-09模块的芯片组(Chip Set)就是Marvell 88W8686。
SD内存卡和WiFi模块是完全连接在一起的,接到了STM32的SDIO接口上。
只能接1个WiFi模块和1张SD内存卡。WiFi模块必须接上,SD卡可插可不插。
- #include <stdio.h>
- #include <stm32f10x.h>
- #define _BV(n) (1u << (n))
- uint16_t rca_sd, rca_wifi;
- // 延时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;
- }
- }
- // 选中指定RCA地址的卡
- void Card_Select(uint16_t rca)
- {
- 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 0x%04x selected! status=0x%08x\n", rca, SDIO->RESP1);
- }
- else if (SDIO->STA & SDIO_STA_CTIMEOUT)
- {
- SDIO->ICR = SDIO_ICR_CTIMEOUTC;
- printf("Card not selected! CMD7 timeout!\n");
- }
- }
- // SDIO Simplified Specification Version 3.00
- // 3. SDIO Card Initialization
- void Card_Init(void)
- {
- uint8_t hcs;
- printf("Initialization begins...\n");
- SDIO->POWER = SDIO_POWER_PWRCTRL;
- SDIO->CLKCR = SDIO_CLKCR_CLKEN | 178; // 初始化时最高允许的频率: 72MHz/(178+2)=400kHz
- delay(5); // 延时可防止CMD5重发
- /* 发送CMD0: GO_IDLE_STATE, 将SD内存卡复位 */
- SDIO->ARG = 0;
- SDIO->CMD = SDIO_CMD_CPSMEN;
- while (SDIO->STA & SDIO_STA_CMDACT);
- SDIO->ICR = SDIO_ICR_CMDSENTC;
- /* 发送CMD8: SEND_IF_COND */
- SDIO->ARG = 0x138; // 2.7-3.6V, check pattern: 0x38
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 8;
- while (SDIO->STA & SDIO_STA_CMDACT);
- if (SDIO->STA & SDIO_STA_CMDREND)
- {
- SDIO->ICR = SDIO_ICR_CMDRENDC;
- hcs = 1;
- }
- else
- {
- SDIO->ICR = SDIO_ICR_CCRCFAILC | SDIO_ICR_CTIMEOUTC;
- hcs = 0;
- }
- printf("HCS=%d\n", hcs); // 根据CMD8有无回应确定hcs的值, 待会儿会传入ACMD41
- /* 发送CMD5: IO_SEND_OP_COND */
- SDIO->ARG = 0;
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 5;
- while (SDIO->STA & SDIO_STA_CMDACT);
- Card_CheckCommand(); // 为了保险起见还是要检查一下是否要重发命令 (若没有插入WiFi模块, 则会一直重发该命令, 这里规定WiFi模块必须插入)
- 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_wifi = SDIO->RESP1 >> 16;
- printf("WiFi RCA: 0x%04x\n", rca_wifi);
- }
- /* 选中Wi-Fi模块 (CMD7: SELECT/DESELECT_CARD), 禁用CMD3 */
- Card_Select(rca_wifi);
- /* 继续初始化SD内存卡, 发送ACMD41 */
- rca_sd = 1;
- do
- {
- SDIO->ARG = 0;
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 55;
- while (SDIO->STA & SDIO_STA_CMDACT);
- if (SDIO->STA & SDIO_STA_CTIMEOUT)
- {
- // 若前导命令CMD55未被响应, 则说明没有插入SD卡, 退出
- SDIO->ICR = SDIO_ICR_CTIMEOUTC;
- rca_sd = 0;
- printf("No SD memory card inserted!\n");
- break;
- }
- else if (SDIO->STA & SDIO_STA_CMDREND)
- {
- SDIO->ICR = SDIO_ICR_CMDRENDC;
- SDIO->ARG = (hcs << 30) | 0xff8000; // HCS + OCR
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 41;
- while (SDIO->STA & SDIO_STA_CMDACT);
- SDIO->ICR = SDIO_ICR_CMDRENDC | SDIO_ICR_CCRCFAILC; // ACMD41的回应CRC一定为0x7f, 所以CRC校验一定出错
- }
- } while ((SDIO->RESP1 & _BV(31)) == 0);
- if (rca_sd) // 若插入了SD卡, 则继续初始化
- {
- if (SDIO->RESP1 & _BV(30))
- printf("SDHC/SDXC Card!\n");
- else
- printf("SDSC Card!\n");
- SDIO->ARG = 0;
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP | 2; // CMD2是长回应
- while (SDIO->STA & SDIO_STA_CMDACT);
- if (SDIO->STA & SDIO_STA_CMDREND)
- {
- SDIO->ICR = SDIO_ICR_CMDRENDC;
- printf("Memory CID: 0x%08x 0x%08x 0x%08x 0x%08x\n", SDIO->RESP1, SDIO->RESP2, SDIO->RESP3, SDIO->RESP4);
- }
- /* 获取SD内存卡的RCA相对地址 */
- // WiFi模块的CMD3已被禁用, 所以不会和这里的CMD3碰撞
- do
- {
- 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_sd = SDIO->RESP1 >> 16;
- }
- } while (rca_sd == rca_wifi); // 若SD卡的RCA地址与WiFi模块的重复, 则重发CMD3更换一个新的
- printf("Memory RCA: 0x%04x\n", rca_sd);
- }
- // 若没有插入SD内存卡, 则rca_sd=0
- // 提高时钟频率
- 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
- /* 发送CMD52, 设置WiFi模块的总线宽度 */
- Card_Write(0, 0x07, Card_Read(0, 0x07) | 0x02); // Bus Width: 4-bit bus
- /* 发送ACMD6, 设置SD内存卡的总线宽度 */
- if (rca_sd)
- {
- Card_Select(rca_sd); // 使用CMD7命令选中SD内存卡
- SDIO->ARG = rca_sd << 16; // 这回CMD55是发给SD内存卡的
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 55;
- while (SDIO->STA & SDIO_STA_CMDACT);
- SDIO->ICR = SDIO_ICR_CMDRENDC;
- SDIO->ARG = 2; // ACMD6的参数: 4-bit bus
- SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 6; // SET_BUS_WIDTH
- while (SDIO->STA & SDIO_STA_CMDACT);
- if (SDIO->STA & SDIO_STA_CMDREND)
- {
- SDIO->ICR = SDIO_ICR_CMDRENDC;
- printf("Memory bus width is changed! status=0x%08x\n", SDIO->RESP1);
- }
- }
- SDIO->CLKCR |= SDIO_CLKCR_WIDBUS_0; // 将STM32上的SDIO外设设为4位数据宽度
- Card_Select(rca_wifi); // 选中WiFi模块
- }
- int main(void)
- {
- uint32_t sta;
- 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)
- {
- sta = SDIO->STA;
- printf("SDIO->STA=0x%08x\n", sta);
- while (sta == SDIO->STA);
- }
- }
【SD内存卡插上时程序的运行结果】
- Initialization begins...
- HCS=1
- Command response received: CMD63, RESP_90ff8000
- Command response received: CMD63, RESP_90300000
- Number of I/O Functions: 1
- Memory Present: 0
- WiFi RCA: 0x0001
- Card 0x0001 selected! status=0x00001e00
- SDHC/SDXC Card!
- Memory CID: 0x12345653 0x44000000 0x00000000 0x180109ee
- Memory RCA: 0x0002
- Card 0x0002 selected! status=0x00800700
- Memory bus width is changed! status=0x00000900
- Card 0x0001 selected! status=0x00001e00
- SDIO->STA=0x00000000
【不插内存卡时程序的运行结果】
- Initialization begins...
- HCS=0
- Command response received: CMD63, RESP_90ff8000
- Command response received: CMD63, RESP_90300000
- Number of I/O Functions: 1
- Memory Present: 0
- WiFi RCA: 0x0001
- Card 0x0001 selected! status=0x00001e00
- No SD memory card inserted!
- Card 0x0001 selected! status=0x00001e00
- SDIO->STA=0x00000000