本帖最后由 黑皮男 于 2016-12-3 15:45 编辑
不知不觉已经试用了好几个星期了,这周实现了用DM9000网络模块和电脑进行了简单的通信。DM9000的模块是在网上买的几十块钱,也就是用这个模块第一次用STM32和电脑进行了简单的通信,还是挺好的。不过这里我采用的是模拟IO口,用了模拟IO口虽然慢了些,但是对并行总线的了解更加清晰,对后续的开发是有一定的帮助的,同样用IO模拟6800总线驱动过12864液晶屏,原理是一样的。首先声明一下,相关的内容在STMCU发过一篇帖子,不是抄袭,只不过这里在F412上实现了一下。
首先创建并行总线接口,这个接口是在bus.c总线中实现的。一些IO相关的宏是在bus.h中定义的,这里仅仅是定义了数据端口,对于控制端口的定义是在DM9000的驱动程序中定义的。总线定义要跟DM9000的16位数据总线对应如下
- #define BUS_BITS 16//定义了总线位宽
- #define BUS_IO15_PIN GPIO_PIN_12
- #define BUS_IO15_PORT GPIOE
- #define BUS_IO14_PIN GPIO_PIN_6
- #define BUS_IO14_PORT GPIOE
- #define BUS_IO13_PIN GPIO_PIN_10
- #define BUS_IO13_PORT GPIOE
- #define BUS_IO12_PIN GPIO_PIN_5
- #define BUS_IO12_PORT GPIOE
- #define BUS_IO11_PIN GPIO_PIN_7
- #define BUS_IO11_PORT GPIOE
- #define BUS_IO10_PIN GPIO_PIN_4
- #define BUS_IO10_PORT GPIOE
- #define BUS_IO9_PIN GPIO_PIN_8
- #define BUS_IO9_PORT GPIOE
- #define BUS_IO8_PIN GPIO_PIN_2
- #define BUS_IO8_PORT GPIOE
- #define BUS_IO7_PIN GPIO_PIN_11
- #define BUS_IO7_PORT GPIOB
- #define BUS_IO6_PIN GPIO_PIN_9
- #define BUS_IO6_PORT GPIOF
- #define BUS_IO5_PIN GPIO_PIN_10
- #define BUS_IO5_PORT GPIOB
- #define BUS_IO4_PIN GPIO_PIN_7
- #define BUS_IO4_PORT GPIOF
- #define BUS_IO3_PIN GPIO_PIN_15
- #define BUS_IO3_PORT GPIOE
- #define BUS_IO2_PIN GPIO_PIN_8
- #define BUS_IO2_PORT GPIOF
- #define BUS_IO1_PIN GPIO_PIN_14
- #define BUS_IO1_PORT GPIOE
- #define BUS_IO0_PIN GPIO_PIN_3
- #define BUS_IO0_PORT GPIOE
复制代码
参考了ST的相关IO的控制代码,使用数据来进行管理。
- GPIO_TypeDef * BUS_PORT[BUS_BITS]={BUS_IO0_PORT, BUS_IO1_PORT, BUS_IO2_PORT, BUS_IO3_PORT, BUS_IO4_PORT, BUS_IO5_PORT, BUS_IO6_PORT, BUS_IO7_PORT,
- BUS_IO8_PORT, BUS_IO9_PORT, BUS_IO10_PORT, BUS_IO11_PORT, BUS_IO12_PORT, BUS_IO13_PORT, BUS_IO14_PORT, BUS_IO15_PORT};
- const UINT16_T BUS_PIN[BUS_BITS]={BUS_IO0_PIN, BUS_IO1_PIN, BUS_IO2_PIN, BUS_IO3_PIN, BUS_IO4_PIN, BUS_IO5_PIN, BUS_IO6_PIN, BUS_IO7_PIN,
- BUS_IO8_PIN, BUS_IO9_PIN, BUS_IO10_PIN, BUS_IO11_PIN, BUS_IO12_PIN, BUS_IO13_PIN, BUS_IO14_PIN, BUS_IO15_PIN};
复制代码
总线初始化以及总线数据方向的相关代码在bus_init,bus_inputmode,bus_outputmode。其中bus_init对总线进行了初始化,bus_inputmode设置总线为输入模式,bus_outputmode设置总线为输出模式,在总线读写时切换总线模式。代码如下:
- void bus_init(void){
- GPIO_InitTypeDef GPIO_InitStructure;
- __GPIOB_CLK_ENABLE();
- __GPIOE_CLK_ENABLE();
- __GPIOF_CLK_ENABLE();
- GPIO_InitStructure.Pin = BUS_IO5_PIN|BUS_IO7_PIN;
- GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStructure.Pull = GPIO_PULLUP;
- GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO0_PIN|BUS_IO1_PIN|BUS_IO3_PIN|BUS_IO8_PIN|BUS_IO9_PIN|BUS_IO10_PIN|BUS_IO11_PIN|BUS_IO12_PIN|BUS_IO13_PIN|BUS_IO14_PIN|BUS_IO15_PIN;
- HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO2_PIN|BUS_IO4_PIN|BUS_IO6_PIN;
- HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);
- }
- void bus_inputmode(void){
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.Pin = BUS_IO5_PIN|BUS_IO7_PIN;
- GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
- GPIO_InitStructure.Pull = GPIO_PULLUP;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO0_PIN|BUS_IO1_PIN|BUS_IO3_PIN|BUS_IO8_PIN|BUS_IO9_PIN|BUS_IO10_PIN|BUS_IO11_PIN|BUS_IO12_PIN|BUS_IO13_PIN|BUS_IO14_PIN|BUS_IO15_PIN;
- HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO2_PIN|BUS_IO4_PIN|BUS_IO6_PIN;
- HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);
- bus_write16bits(0x0000);
- }
- void bus_outputmode(void){
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.Pin = BUS_IO5_PIN|BUS_IO7_PIN;
- GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStructure.Pull = GPIO_PULLUP;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO0_PIN|BUS_IO1_PIN|BUS_IO3_PIN|BUS_IO8_PIN|BUS_IO9_PIN|BUS_IO10_PIN|BUS_IO11_PIN|BUS_IO12_PIN|BUS_IO13_PIN|BUS_IO14_PIN|BUS_IO15_PIN;
- HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
- GPIO_InitStructure.Pin = BUS_IO2_PIN|BUS_IO4_PIN|BUS_IO6_PIN;
- HAL_GPIO_Init(GPIOF, &GPIO_InitStructure);
- bus_write16bits(0xffff);
- }
复制代码
使用模拟总线进行读写速度要慢,只要能通,就能达到实现的目的。总线的读写函数如下:
- void bus_write16bits(UINT16_T val){
- UINT8_T i;
- for(i=16;i>0;i--){
- if(val&0x8000)
- HAL_GPIO_WritePin(BUS_PORT[i-1], BUS_PIN[i-1], GPIO_PIN_SET);
- //SET_BITS(BUS_PORT[i-1]->ODR, BUS_PIN[i-1]);
- else
- HAL_GPIO_WritePin(BUS_PORT[i-1], BUS_PIN[i-1], GPIO_PIN_RESET);
- //CLEAR_BITS(BUS_PORT[i-1]->ODR, BUS_PIN[i-1]);
-
- val=val<<1;
- }
- }
- UINT16_T bus_read16bits(void){
- UINT8_T i;
- UINT16_T res=0x00;
- for(i=0;i<16;i++){
- if(READ_BITS(BUS_PORT[i]->IDR, BUS_PIN[i])){
- res =res|(1<
- }
- }
- return res;
- }
复制代码
DM9000部分的驱动,主要也是调用模拟总线来进行数据读写。总线控制IO的映射关系如下:
- #define DM9000_RST_PIN GPIO_PIN_3
- #define DM9000_RST_PORT GPIOA
- #define DM9000_CS_PIN GPIO_PIN_0
- #define DM9000_CS_PORT GPIOA
- #define DM9000_CMD_PIN GPIO_PIN_9
- #define DM9000_CMD_PORT GPIOE
- #define DM9000_WR_PIN GPIO_PIN_0
- #define DM9000_WR_PORT GPIOC
- #define DM9000_RD_PIN GPIO_PIN_0
- #define DM9000_RD_PORT GPIOG
- #define DM9000_IRQ_PIN GPIO_PIN_3
- #define DM9000_IRQ_PORT GPIOC
- #define DM9000_RST_HIGH HAL_GPIO_WritePin(DM9000_RST_PORT, DM9000_RST_PIN, GPIO_PIN_SET)
- #define DM9000_RST_LOW HAL_GPIO_WritePin(DM9000_RST_PORT, DM9000_RST_PIN, GPIO_PIN_RESET)
- #define DM9000_CS_HIGH HAL_GPIO_WritePin(DM9000_CS_PORT, DM9000_CS_PIN, GPIO_PIN_SET)
- #define DM9000_CS_LOW HAL_GPIO_WritePin(DM9000_CS_PORT, DM9000_CS_PIN, GPIO_PIN_RESET)
- #define DM9000_CMD_HIGH HAL_GPIO_WritePin(DM9000_CMD_PORT, DM9000_CMD_PIN, GPIO_PIN_SET)
- #define DM9000_CMD_LOW HAL_GPIO_WritePin(DM9000_CMD_PORT, DM9000_CMD_PIN, GPIO_PIN_RESET)
- #define DM9000_WR_HIGH HAL_GPIO_WritePin(DM9000_WR_PORT, DM9000_WR_PIN, GPIO_PIN_SET)
- #define DM9000_WR_LOW HAL_GPIO_WritePin(DM9000_WR_PORT, DM9000_WR_PIN, GPIO_PIN_RESET)
- #define DM9000_RD_HIGH HAL_GPIO_WritePin(DM9000_RD_PORT, DM9000_RD_PIN, GPIO_PIN_SET)
- #define DM9000_RD_LOW HAL_GPIO_WritePin(DM9000_RD_PORT, DM9000_RD_PIN, GPIO_PIN_RESET)
复制代码
至于DM9000的驱动程序,大家可以参看附件,也可以参考正点原子的代码,我这里是融合了正点原子和购买模块时卖家赠送的驱动代码。这里重点说一下数据读写部分。需要注意时序问题,时序很重要。读数据是在读控制时钟变高之前。
- UINT16_T DM_ReadReg(UINT16_T reg)
- {
- UINT16_T res;
- DM_WriteCMD(reg);
- DM9000_INPUT_MODE();
- //DM_DelayUs(20);
- DM9000_CMD_HIGH;
- DM9000_CS_LOW;
- DM9000_RD_LOW;
- res=DM_READ_DATA();//注意数据读应该在读控制时钟变高之前,而卖家附赠的则是在读控制时钟之后
- DM9000_RD_HIGH;
- //res=DM_READ_DATA();
- DM9000_CS_HIGH;
- DM9000_OUTPUT_MODE();
- return res;
- }
- UINT16_T DM_ReadData(void)
- {
- UINT16_T res;
- DM9000_CMD_HIGH;
- DM9000_CS_LOW;
- DM9000_RD_LOW;
- res = DM_READ_DATA();
- DM9000_RD_HIGH;
- //res = DM_READ_DATA();
- DM9000_CS_HIGH;
- return res;
- }
复制代码
DM9000数据的接收可以使用查询模式,也可以使用中断模式。这里先实现arp数包的收发,获取一下主机的相关信息。
- void arp_request(void)
- {
- arpbuf=(ARP_HDR *)Buffer;
- memcpy(arpbuf->ethhdr.d_mac, net_tcb.host_mac, 6);
- memcpy(arpbuf->ethhdr.s_mac, net_tcb.mac, 6);
- arpbuf->ethhdr.type = HON(0x0806); //arp request/response type
- arpbuf->hwtype = HON(1); //ethernet type
- arpbuf->protocol = HON(0x0800);
- arpbuf->hwlen = 6;
- arpbuf->protolen = 4;
- //arpbuf->opcode = HON(1);
- arpbuf->opcode = HON(1); // 1:arp request 2:: arp response 3:rarp request 4: rarp response
- memcpy(arpbuf->s_mac, net_tcb.mac, 6);
- memcpy(arpbuf->s_ipaddr, net_tcb.ip_addr, 4);
- memcpy(arpbuf->d_ipaddr, net_tcb.host_ip_addr, 4);
- packet_len = 42;
- memcpy(TxBuffer, Buffer, 42);
- DM_SendPacket(TxBuffer, packet_len);
- }
- UINT8_T arp_process(void)
- {
- if(packet_len<28)
- {
- return 0;
- }
- switch(HON(arpbuf->opcode)) //arp request
- {
- case 1: //host request
- if(arpbuf->d_ipaddr[0]==net_tcb.ip_addr[0]&&arpbuf->d_ipaddr[1]==net_tcb.ip_addr[1]&&arpbuf->d_ipaddr[2]==net_tcb.ip_addr[2]&&arpbuf->d_ipaddr[3]==net_tcb.ip_addr[3])
- {
- arpbuf->opcode = HON(2);
- memcpy(arpbuf->d_mac, arpbuf->s_mac, 6);
- memcpy(arpbuf->ethhdr.d_mac, arpbuf->s_mac, 6);
- memcpy(arpbuf->s_mac, net_tcb.mac, 6);
- memcpy(arpbuf->d_ipaddr, arpbuf->s_ipaddr, 4);
- memcpy(arpbuf->s_ipaddr, net_tcb.ip_addr, 4);
- arpbuf->ethhdr.type = HON(0x0806);
- packet_len = 42;
- DM_SendPacket(Buffer, packet_len);
- return 1;
- }
- else
- {
- return 0;
- }
- break;
- case 2: //host response
- if(arpbuf->d_ipaddr[0]==net_tcb.ip_addr[0]&&arpbuf->d_ipaddr[1]==net_tcb.ip_addr[1]&&arpbuf->d_ipaddr[2]==net_tcb.ip_addr[2]&&arpbuf->d_ipaddr[3]==net_tcb.ip_addr[3])
- {
- memcpy(net_tcb.host_mac, arpbuf->s_mac, 6);
- memcpy(net_tcb.host_ip_addr, arpbuf->s_ipaddr, 4);
- return 1;
- }
- else
- {
- return 0;
- }
- break;
- default:
- return 0;
- }
- }
- void arp_print(void)
- {
- uint8_t i;
-
- printf("*************Arp***************n");
- //print arp request packet
- printf("Arp Request:n");
- for(i=0;i<42;i++)
- {
- printf("%02x ", TxBuffer[i]);
- }
- //print arp responsr packet
- printf("nnArp Response:n");
- for(i=0;i<42;i++)
- {
- printf("%02x ",Buffer[i]);
- }
- //print host mac address
- printf("nnHost MAC address is ");
- for(i=0;i<5;i++)
- {
- printf("%02x : ", net_tcb.host_mac[i]);
- }
- printf("%02xn", net_tcb.host_mac[5]);
- //print host ip address
- printf("Host IP address is ");
- for(i=0;i<3;i++)
- {
- printf("%d : ", net_tcb.host_ip_addr[i]);
- }
- printf("%d", net_tcb.host_ip_addr[3]);
- printf("n*************Arp END*************n");
- }
- void arp_handle(void)//这个函数在数据接收中断中调用,已接收处理的数据
- {
- uint8_t isr_chk = DM_ReadReg(DM9000_ISR)&0x01;
- if(isr_chk)
- {
- DM_ReceivePacket(Buffer);
- arp_process();
- arp_print();
- isr_chk = 0;
- }
-
- }
复制代码
APP部分
- UINT16_T arp_tmr = 25;
- void net_task(void){
- if(arp_tmr++>=25)
- {
- arp_tmr=0;
- arp_request();
- }
- }
- //在main函数下调用sch_add把任务添加到调度器中。
- sch_add(net_task, 0, 10);
复制代码
下面是接收到的数据,可以使用ipconfig/all查看主机部分MAC地址
下面是实际的接线图,看着有点凌乱,呵呵,凑合着看。
|