本帖最后由 yangjiaxu 于 2022-3-31 14:10 编辑
首先感谢21ic和小管家举办的这个[活动] 【第1期】你选开发板,二姨家买单;很荣幸因为脸熟(其实是混子)得到了这次活动的中奖名额,说实话这板子到手挺长时间了,这么晚才发属实是我个人原因,还有就是疫情肆虐,真的让我板子与人相隔两地。哈哈哈,好了闲话少说,还是开启这次主题任务吧。本次豆包选择的开发板是沁恒的开发板“CH32V103R8”为什么选择这款开发板呢,其实原因有三。第一,因为没怎么接触过沁恒RISC-V内核的开发板,工作中一直都在使用Arm内核的单片机在玩耍,可能接触和体验新鲜事物是做工程师的一种本能吧,所以让我选择了沁恒。第二,因为朋友推荐我玩沁恒的MCU是因为沁恒的MCU较容易好上手,而且这个32位单片机在国产32位的MCU中是比较能打的(主要是有性价比)。第三,主要是我缺了个link调试器,我是一个对调试工具比较有感觉的一个野生工程师,手里的调试器也算是多种多样,所以一看沁恒这个买开发板送调试器的活动,我岂有不buy的道理。因此,众多理由让我选择了沁恒这款开发板。到手之后,果然没让我失望。接下来就来赏析这款开发板的样子吧。 开发板包装
开发板和调试器
其实这次购买还赠送了2片MCU,在这个芯片紧缺,在朋友圈盛行的一句 “芯片和白菜一个价的时代”,送我两片芯片无疑是地主行为。
接下来就是搭环境了,其实Risc-V的环境怎么搭我是真不是很清楚,但是沁恒这个CH32V103这个搭建环境是真的很容易,首先下载个开发环境,下载MounRiver_Studio_Setup_V170.zip即可, 下载链接。接下来就是安装,然后找沁恒关于CH32V103的资料例程即可, 资料链接。通过官网也可以看到CH32V103的开发资源和外设情况。
产品特点 - 青稞V3A处理器,最高80MHz系统主频;
- 支持单周期乘法和硬件除法;
- 20KB SRAM,64KB CodeFlash;
- 供电范围:2.7V ~ 5.5V,GPIO同步供电电压;
- 多种低功耗模式:睡眠/停止/待机;
- 上电/断电复位(POR/PDR);
- 可编程电压监测器(PVD);
- 7通道DMA控制器;
- 16路TouchKey通道监测;
- 16路12位ADC转换通道;
- 7个定时器;
- 1个USB2.0主机/设备接口(全速和低速);
- 2个IIC接口(支持SMBus/PMBus);
- 3个USART接口;
- 2个SPI接口(支持Master和Slave模式);
- 51个I/O口,所有的I/O口都可以映射到16个外部中断;
- CRC计算单元,96位芯片唯一ID;
- 串行单线调试(SWD)接口;
- 封装形式:LQFP64M、LQFP48、QFN48。
介绍完硬件,其实就可以写代码来玩玩这款得意的MCU了。 打开一个官方例程,我选择的是GPIO的例程,然后改了个名字,因为我想用这个MCU驱动一下FM17522射频阅读器,所以索性改了工程名字。先看看代码风格之类的,emmm,真香,这不是很熟悉的库函数形式么。
接下来添加FM17522的驱动代码,然后就是移植程序了。 以前换一款MCU来说,移植程序是非常费劲儿的,不过使用沁恒这款MCU的话就不用担心这个问题,基本上一看就会,一用就OK,所以我移植这个工程代码也是非常容易的。因为还需要修改一些变量和参数之类的。所以,大概用了20分钟吧。 分享一下基础代码: - /**********************************以下为用户程序************************************/
- void FM17522_IOinit(void)
- {
- u8 loop = 0;
- GPIO_InitTypeDef GPIO_InitStructure;
- // GPIO_InitTypeDef GPIO_InitStructure = {0};
- //不要忘记开启AFIO时钟(JTAG复用时RCC_APB2Periph_AFIO)
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
- // GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //输入
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- FM17522_Reset();
- no_fm_flag = 1;
- while(loop++ < 6)
- {
- if(FM17522_TEST()) // 测试读卡芯片FM17522是否在位
- {
- no_fm_flag = 0;
- break;
- }
- }
- }
- /*********************************************************************************************************
- ** 函数名称:FM17522_TEST
- ** 函数描述:MCU与读卡芯片通信测试函数
- ** 输入参数:无
- ** 返回值 :TRUE---测试通过 FALSE----测试失败
- ** 说明:不同的读卡芯片的选取用于测试的寄存器不一样,需要更具具体芯片而确定目标寄存器
- *********************************************************************************************************/
- u8 FM17522_TEST(void)
- {
- volatile u8 ucRegVal;
- FM17522_Reset(); // 硬件复位
- ucRegVal = FM17522_Read_Reg(ControlReg);
- FM17522_Write_Reg(ControlReg, 0x10); // 启动读写器模式
- ucRegVal = FM17522_Read_Reg(ControlReg);
- FM17522_Write_Reg(GsNReg, 0xF0 | 0x04); // CWGsN = 0xF; ModGsN = 0x4
- ucRegVal = FM17522_Read_Reg(GsNReg);
- if(ucRegVal != 0xF4) // 验证接口正确
- return FALSE;
- return TRUE;
- }
- void FM17522_Reset(void)
- {
- SPI_SCK_SET;
- Delay_Us(300); // 延时300us
- SPI_RST_CLR;
- Delay_Us(500); // 延时500us
- SPI_RST_SET;
- Delay_Us(1000); // 延时1000us
- }
- /********************************************
- 函数名: Clear_BitMask
- 功能: 清除位寄存器操作
- 输入参数: reg_add,寄存器地址;mask,寄存器清除位
- ********************************************/
- void Clear_BitMask(u8 reg_add, u8 mask)
- {
- FM17522_Write_Reg(reg_add, FM17522_Read_Reg(reg_add) & ~mask); // clear bit mask
- }
- /*************************************************************/
- /*函数名: Set_BitMask */
- /*功能: 置位寄存器操作 */
- /*输入参数: reg_add,寄存器地址;mask,寄存器置位 */
- /*返回值: TRUE */
- /* FALSE */
- /*************************************************************/
- void Set_BitMask(u8 reg_add, u8 mask)
- {
- FM17522_Write_Reg(reg_add, FM17522_Read_Reg(reg_add) | mask); // set bit mask
- }
- /**********************************************/
- /** 函数名: Set_RF **/
- /** 功能: 设置射频输出 **/
- /** 输入参数: mode,射频输出模式 **/
- /** 0,关闭输出 **/
- /** 1,仅打开TX1输出 **/
- /** 2,仅打开TX2输出 **/
- /** 3,TX1,TX2打开输出,TX2为反向输出 **/
- /**********************************************/
- void Set_Rf(u8 mode)
- {
- if((FM17522_Read_Reg(TxControlReg) & 0x03) == mode)
- return;
- if (mode == 0)
- {
- Clear_BitMask(TxControlReg, 0x03); //关闭TX1,TX2输出
- }
- else if (mode == 1)
- {
- Clear_BitMask(TxControlReg, 0x01); //仅打开TX1输出
- }
- else if (mode == 2)
- {
- Clear_BitMask(TxControlReg, 0x02); //仅打开TX2输出
- }
- else if (mode == 3)
- {
- Set_BitMask(TxControlReg, 0x03); //打开TX1,TX2输出
- }
- Delay_Ms(100);
- }
- void Fm17522_ConfigISOType(u8 type)
- {
- if (type == 0) //ISO14443_A
- {
- Set_BitMask(ControlReg, 0x10); //ControlReg 0x0C 设置reader模式
- Set_BitMask(TxAutoReg, 0x40); //TxASKReg 0x15 设置100%ASK有效
- FM17522_Write_Reg(TxModeReg, 0x00); //TxModeReg 0x12 设置TX CRC无效,TX FRAMING =TYPE A
- FM17522_Write_Reg(RxModeReg, 0x00); //RxModeReg 0x13 设置RX CRC无效,RX FRAMING =TYPE A
- FM17522_Write_Reg(RFCfgReg, 0x48); //增益调节至48DB 2021.01.16
- }
- else if (type == 1) //ISO14443_B
- {
- FM17522_Write_Reg(ControlReg, 0x10);
- FM17522_Write_Reg(TxModeReg, 0x83); //BIT1~0 = 2'b11:ISO/IEC 14443B
- FM17522_Write_Reg(RxModeReg, 0x83); //BIT1~0 = 2'b11:ISO/IEC 14443B
- FM17522_Write_Reg(TxAutoReg, 0x00);
- FM17522_Write_Reg(RxThresholdReg, 0x55);
- FM17522_Write_Reg(RFCfgReg, 0x48); //?????????????
- FM17522_Write_Reg(TxBitPhaseReg, 0x87); //默认值
- FM17522_Write_Reg(GsNReg, 0x83);
- FM17522_Write_Reg(CWGsPReg, 0x10);
- FM17522_Write_Reg(GsNOffReg, 0x38);
- FM17522_Write_Reg(ModGsPReg, 0x10);
- }
- Delay_Us(1000);
- }
- /********************************************
- 函数名: Fm17522_SetTimer
- 功能: 设置接收延时
- 输入参数: delaytime,延时时间(单位为毫秒)
- 返回值: TRUE
- ********************************************/
- u8 Fm17522_SetTimer(unsigned long delaytime)//设定超时时间(ms)
- {
- unsigned long TimeReload;
- unsigned int Prescaler;
- Prescaler = 0;
- TimeReload = 0;
- while(Prescaler < 0xfff)
- {
- TimeReload = ((delaytime * (long)13560) - 1) / (Prescaler * 2 + 1);
- if( TimeReload < 0xffff)
- break;
- Prescaler++;
- }
- TimeReload = TimeReload & 0xFFFF;
- Set_BitMask(TModeReg, Prescaler >> 8);
- FM17522_Write_Reg(TPrescalerReg, Prescaler & 0xFF);
- FM17522_Write_Reg(TReloadMSBReg, TimeReload >> 8);
- FM17522_Write_Reg(TReloadLSBReg, TimeReload & 0xFF);
- return TRUE;
- }
- /*************************************************************
- 函数名: Clear_FIFO
- 功能: 清空FIFO
- 输入参数: 无
- 输出参数:
- 返回值: TRUE
- FALSE
- ************************************************************/
- u8 Clear_FIFO(void)
- {
- Set_BitMask(FIFOLevelReg, 0x80); //清除FIFO缓冲
- if (FM17522_Read_Reg(FIFOLevelReg) == 0)
- return TRUE;
- else
- return FALSE;
- }
- //ATQA是只有两个字节,UID却是4个字节加一个BCC校验值所以针对7字节UID防碰撞时就需要最多15字节的UID来存放
- //SAK是一个字节的SAK+CRC_A(2字节)
- /****************************************************************************************/
- /*名称:TypeA_Request */
- /*功能:TypeA_Request卡片寻卡 */
- /*输入: */
- /* */
- /* */
- /*输出: */
- /* pTagType[0],pTagType[1] =ATQA */
- /* TRUE: 应答正确 */
- /* FALSE: 应答错误 */
- /****************************************************************************************/
- u8 Fm17522_TypeA_Request(void)
- {
- unsigned char result, send_buff[1], rece_buff[2];
- unsigned int rece_bitlen;
-
- Clear_BitMask(TxModeReg, 0x80); //关闭TX CRC
- Clear_BitMask(RxModeReg, 0x80); //关闭RX CRC
- Set_BitMask(RxModeReg, 0x08);//关闭位接收
- Clear_BitMask(Status2Reg, 0x08);
- FM17522_Write_Reg(BitFramingReg, 0x07);
- send_buff[0] = 0x26;
- Fm17522_SetTimer(1);
- Clear_FIFO();
- result = Fm17522_Comm(Transceive, send_buff, 1, rece_buff, &rece_bitlen);
- if ((result == TRUE) && (rece_bitlen == 2 * 8))
- {
- memcpy(atqa, rece_buff, 2);
- }
- return result;
- }
- /*********************************************
- 函数名: Fm17522_Comm (不利用IRQ管脚的情况)
- 功能: 读卡器通信
- 输入参数: Command,通信操作命令;
- pInData,发送数据数组;
- InLenByte,发送数据数组字节长度;
- pOutData,接收数据数组;
- pOutLenBit,接收数据的位长度
- 返回值: TRUE
- FALSE
- ********************************************/
- u8 Fm17522_Comm(u8 Command, u8 *pInData, u8 InLenByte, u8 *pOutData, uint *pOutLenBit)
- {
- uint8_t status = FALSE;
- uint8_t irqEn = 0x00;
- uint8_t waitFor = 0x00;
- uint8_t lastBits;
- uint8_t n;
- uint32_t i;
- FM17522_Write_Reg(ComIrqReg, 0x7F); //清楚IRQ标记
- FM17522_Write_Reg(TModeReg, 0x80); //设置TIMER自动启动
- switch (Command)
- {
- case MFAuthent: /* Mifare认证 */
- irqEn = 0x12;
- waitFor = 0x10;
- break;
- case Transceive: /* 发送FIFO中的数据到天线,传输后激活接收电路*/
- irqEn = 0x77;
- waitFor = 0x30;
- break;
- default:
- break;
- }
- FM17522_Write_Reg(ComIEnReg, irqEn | 0x80);
- FM17522_Write_Reg(CommandReg, Idle);
- Set_BitMask(FIFOLevelReg, 0x80);
- for (i = 0; i < InLenByte; i++)
- {
- FM17522_Write_Reg(FIFODataReg, pInData[i]);
- }
- FM17522_Write_Reg(CommandReg, Command);
- if (Command == Transceive)
- {
- Set_BitMask(BitFramingReg, 0x80);
- }
- i = 20000; /* 根据时钟频率调整,操作M1卡最大等待时间25ms*/
- do
- {
- n = FM17522_Read_Reg(ComIrqReg);
- i--;
- }
- while ((i != 0) && !(n & 0x01) && !(n & waitFor)); //i==0表示延时到了,n&0x01!=1表示PCDsettimer时间未到
- Clear_BitMask(BitFramingReg, 0x80);
- if (i != 0)
- {
- if(!(FM17522_Read_Reg(ErrorReg) & 0x1B))
- {
- status = TRUE;
- if (n & irqEn & 0x01)
- {
- status = 0xEE;
- }
- if (Command == Transceive)
- {
- n = FM17522_Read_Reg(FIFOLevelReg);
- lastBits = FM17522_Read_Reg(ControlReg) & 0x07;
- if (lastBits)
- {
- *pOutLenBit = (n - 1) * 8 + lastBits;
- }
- else
- {
- *pOutLenBit = n * 8;
- }
- if (n == 0)
- {
- n = 1;
- }
- if (n > 64)
- {
- n = 64;
- }
- for (i = 0; i < n; i++)
- {
- pOutData[i] = FM17522_Read_Reg(FIFODataReg);
- }
- }
- }
- else
- {
- status = FALSE;
- }
- }
- Clear_BitMask(BitFramingReg, 0x80); //关闭发送
- return status;
- }
- /****************************************************************************************/
- /*名称:Fm17522_TypeA_Anticollision */
- /*功能:卡片防冲突 */
- /*输入:selcode =0x93,0x95,0x97 */
- /* */
- /* */
- /*输出: */
- /* TRUE: 应答正确 */
- /* FALSE: 应答错误 */
- /****************************************************************************************/
- u8 Fm17522_TypeA_Anticollision(unsigned char selcode)
- {
- unsigned char result, i, send_buff[2], rece_buff[5];
- unsigned int rece_bitlen;
- Clear_BitMask(TxModeReg, 0x80);
- Clear_BitMask(RxModeReg, 0x80);
- Clear_BitMask(Status2Reg, 0x08);
- FM17522_Write_Reg(BitFramingReg, 0x00);
- Clear_BitMask(CollReg, 0x80);
- send_buff[0] = selcode;
- send_buff[1] = 0x20; //NVB
- Fm17522_SetTimer(1);
- Clear_FIFO();
- result = Fm17522_Comm(Transceive, send_buff, 2, rece_buff, &rece_bitlen);
- i = ((selcode - 0x93) / 2) * 5;
- if (result == TRUE)
- {
- // memcpy(uid[i], rece_buff, 5);
- for (i = 0; i < 5; ++i) {
- uid[i] = rece_buff[i];
- }
- if (uid[4 + i] != (uid[i]^uid[1 + i]^uid[2 + i]^uid[3 + i])) //UID0、UID1、UID2、UID3、BCC(校验字节)
- result = FALSE;
- }
- return result;
- }
- /****************************************************************************************/
- /*名称:TypeA_Select */
- /*功能:TypeA_Select卡片选卡 */
- /*输入:selcode =0x93,0x95,0x97 */
- /* pSnr[0],pSnr[1],pSnr[2],pSnr[3]pSnr[4] =UID */
- /* */
- /*输出: */
- /* pSak[0],pSak[1],pSak[2] =SAK */
- /* TRUE: 应答正确 */
- /* FALSE: 应答错误 */
- /****************************************************************************************/
- u8 Fm17522_TypeA_Select(unsigned char selcode)
- {
- unsigned char result, i, send_buff[7], rece_buff[5];
- unsigned int rece_bitlen;
- FM17522_Write_Reg(BitFramingReg, 0x00);
- Set_BitMask(TxModeReg, 0x80); //打开TX CRC
- Set_BitMask(RxModeReg, 0x80); //打开接收RX 的CRC校验
- Clear_BitMask(Status2Reg, 0x08);
- send_buff[0] = selcode; //SEL
- send_buff[1] = 0x70; //NVB
- i = ((selcode - 0x93) / 2) * 5;
- // memcpy(&send_buff[2],uid[i], 5);
- for (i = 0; i < 5; ++i) {
- send_buff[i + 2] = uid[i];
- }
- Fm17522_SetTimer(1);
- Clear_FIFO();
- result = Fm17522_Comm(Transceive, send_buff, 7, rece_buff, &rece_bitlen);
- i = (selcode - 0x93) / 2;
- if (result == TRUE)
- {
- sak[i] = rece_buff[0];
- }
- return result;
- }
- /****************************************************************************************/
- /*名称:Fm17522_TypeA_CardActive */
- /*功能:TypeA_CardActive卡片激活 */
- /*输入: */
- /*输出: pTagType[0],pTagType[1] =ATQA */
- /* pSnr[0],pSnr[1],pSnr[2],pSnr[3]pSnr[4] =UID */
- /* pSak[0],pSak[1],pSak[2] =SAK */
- /****************************************************************************************/
- u8 Fm17522_TypeA_CardActive(void)
- {
- u8 result, i;
- Set_Rf(3); //turn on radio
- Fm17522_ConfigISOType(0);
- for(i = 0; i < 2; i++)
- {
- result = Fm17522_TypeA_Request(); //寻卡 Standard send request command Standard mode
- if(result == 1) break;
- }
- if(i == 2)
- return FALSE;
- if((atqa[0] & 0xC0) == 0x00) //M1卡,ID号只有4位
- {
- result = Fm17522_TypeA_Anticollision(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- }
- else if((atqa[0] & 0xC0) == 0x40) //ID号有7位
- {
- result = Fm17522_TypeA_Anticollision(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Anticollision(0x95);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x95);
- if (result == FALSE)
- {
- return FALSE;
- }
- }
- else if((atqa[0] & 0xC0) == 0x80) //ID号有10位
- {
- result = Fm17522_TypeA_Anticollision(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x93);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Anticollision(0x95);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x95);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Anticollision(0x97);
- if (result == FALSE)
- {
- return FALSE;
- }
- result = Fm17522_TypeA_Select(0x97);
- if (result == FALSE)
- {
- return FALSE;
- }
- }
- return result;
- }
- /*****************************************************************************************/
- /*名称:Fm17522_Mifare_Auth */
- /*功能:Mifare_Auth卡片认证 */
- /*输入:mode,认证模式(0:key A认证,1:key B认证);sector,认证的扇区号(0~15) */
- /* *mifare_key,6字节认证密钥数组;*card_uid,4字节卡片UID数组 */
- /*输出: */
- /* TRUE :认证成功 */
- /* FALSE :认证失败 */
- /*****************************************************************************************/
- u8 Fm17522_Mifare_Auth(u8 mode, u8 sector, u8 *mifare_key, u8 *card_uid)
- {
- unsigned char send_buff[12], rece_buff[1], result;
- unsigned int rece_bitlen;
- if(mode == 0x0)
- send_buff[0] = 0x60; //keyA认证
- if(mode == 0x1)
- send_buff[0] = 0x61; //keyB认证
- send_buff[1] = sector * 4 + 3;
- send_buff[2] = mifare_key[0];
- send_buff[3] = mifare_key[1];
- send_buff[4] = mifare_key[2];
- send_buff[5] = mifare_key[3];
- send_buff[6] = mifare_key[4];
- send_buff[7] = mifare_key[5];
- send_buff[8] = card_uid[0];
- send_buff[9] = card_uid[1];
- send_buff[10] = card_uid[2];
- send_buff[11] = card_uid[3];
- Fm17522_SetTimer(1);
- Clear_FIFO();
- result = Fm17522_Comm(MFAuthent, send_buff, 12, rece_buff, &rece_bitlen); //Authent认证
- if (result == TRUE)
- {
- if(FM17522_Read_Reg(Status2Reg) & 0x08) //判断加密标志位,确认认证结果
- return TRUE;
- else
- return FALSE;
- }
- return FALSE;
- }
其实用这个软件也就大概用这几个地方,编译,调试和下载,调试的话用官方例程的话几乎不需要修改,所以非常容易就可以上手。 在写代码的时候用memcpy会出现问题,有一些警告,这个可以通过添加#include <string.h>来解决。还有个问题就是我用这个语句赋值的时候,会有警告。memcpy(uid,rece_buff, 5);,警告内容为:warning: passing argument 1 of'memcpy' makes pointer from integer without a cast [-Wint-conversion]。也不知道为什么,最后换了一种方式,采用for循环的方式赋值解决了。
最后分享一下效果吧,小密说如果LED是全亮或者全灭这样效果更加明显,可是我懒了,哈哈哈,就没改。 最后感谢小管家这个给力的活动,还让我学会了新技能,奈斯。 |