#include "system/includes.h" #include "media/includes.h" //#include "device/iic.h" #include "asm/iic_hw.h" #include "asm/iic_soft.h" #if 0 /* [[[ README ]]] 1. iic从机需要赋值hw_iic_cfg.role = IIC_SLAVE,若role没有赋值或赋值成 IIC_MASTER,则默认是主机模式。 2. iic从机的demo使用中断-任务的交互方式,避免while(!iic_pnd)的阻塞方式造成的 CPU浪费及响应不及时。由于IIC没有DMA,每传输1 byte就会触发1次中断,所以IIC主机 必须每传输完成1字节delay一段时间,以至少等待IIC从机响应中断。iic stop产生的 end中断被用于计算接收字节数及复位状态机,不要去掉end中断的使能及处理。IIC可能 会收到意外的end中断,所以end中断的处理必须用start-end包住。 3. demo的接收/发送使用double buffer,避免处理数据过程中被下次接收/发送的数据 覆盖。 4. IIC从机收到IIC_S_RADDR后需要尽快准备需要发送的数据,因为主机会很短时间内发 送下一个IIC字节时钟以请求数据。 5. IIC主机的TX的STOP到下个RX的START需要delay一段时间,否则会收多收一个不正确 的字节。 6. 未尽事项代码中的注释。 */ #define IIC_S_RADDR 0x61 #define IIC_S_WADDR 0x60 #define IIC_S_DEV 0 #define IIC_S_TXBUF_SIZE 128 #define IIC_S_RXBUF_SIZE 128 enum { IIC_S_MSG_TX, IIC_S_MSG_RX, }; struct iic_s_tx_statemachine { u8 *buf[2]; u32 b_size; bool toggle; u32 cur_cnt; u32 tx_cnt; }; struct iic_s_rx_statemachine { u8 *buf[2]; u32 b_size; bool toggle; u32 cur_cnt; u32 rx_cnt; }; struct iic_slave { enum {IIC_S_TX, IIC_S_RX} dir; struct iic_s_tx_statemachine tx; struct iic_s_rx_statemachine rx; u8 bus_occupy; }; static struct iic_slave iic_s; static u8 iic_s_txbuf[2][IIC_S_TXBUF_SIZE]; static u8 iic_s_rxbuf[2][IIC_S_RXBUF_SIZE]; SET_INTERRUPT static void iic_slave_isr() { u8 is_addr = 0; u8 byte; if (hw_iic_get_pnd(IIC_S_DEV)) { hw_iic_clr_pnd(IIC_S_DEV); putchar('a'); if (iic_s.dir == IIC_S_RX) { byte = hw_iic_slave_rx_byte(IIC_S_DEV, &is_addr); //判断是否为地址 if (is_addr) { iic_s.rx.cur_cnt = 0; iic_s.rx.rx_cnt = 0; iic_s.tx.cur_cnt = 0; iic_s.tx.tx_cnt = 0; if (byte == IIC_S_WADDR) { putchar('b'); hw_iic_slave_rx_prepare(IIC_S_DEV, 1); //触发下1 byte接收,并使能recv ack iic_s.bus_occupy = 1; } else if (byte == IIC_S_RADDR) { putchar('c'); iic_s.dir = IIC_S_TX; os_taskq_post_msg("iic_slave", 1, IIC_S_MSG_TX); iic_s.bus_occupy = 1; //包住start-stop,避免处理意外的stop处理 } } else { putchar('d'); if (iic_s.rx.cur_cnt < iic_s.rx.b_size) { iic_s.rx.buf[iic_s.rx.toggle][iic_s.rx.cur_cnt++] = byte; } hw_iic_slave_rx_prepare(IIC_S_DEV, 1); //触发下1 byte接收,并使能recv ack } } else { putchar('e'); //如果主机接收ACK或者是最后1 byte主机NACK,发送下1 byte,否则重发当前byte if (hw_iic_slave_tx_check_ack(IIC_S_DEV) || (iic_s.tx.tx_cnt - iic_s.tx.cur_cnt == 1)) { iic_s.tx.cur_cnt++; } if (iic_s.tx.cur_cnt < iic_s.tx.tx_cnt) { hw_iic_slave_tx_byte(IIC_S_DEV, iic_s.tx.buf[iic_s.tx.toggle][iic_s.tx.cur_cnt]); } else { //如果主机请求字节数比实际IIC发送字节数多,则发送0xff,防止主机while(!iic_pnd)阻塞卡死 hw_iic_slave_tx_byte(IIC_S_DEV, 0xff); } } } if (hw_iic_get_end_pnd(IIC_S_DEV)) { hw_iic_clr_end_pnd(IIC_S_DEV); putchar('f'); //start-stop包住,收到stop时的处理 if (iic_s.bus_occupy) { iic_s.bus_occupy = 0; if (iic_s.dir == IIC_S_RX) { putchar('g'); iic_s.rx.toggle = !iic_s.rx.toggle; iic_s.rx.rx_cnt = iic_s.rx.cur_cnt; os_taskq_post_msg("iic_slave", 2, IIC_S_MSG_RX, iic_s.rx.rx_cnt); } else { putchar('h'); iic_s.tx.toggle = !iic_s.tx.toggle; } iic_s.dir = IIC_S_RX; iic_s.rx.cur_cnt = 0; iic_s.tx.cur_cnt = 0; iic_s.tx.tx_cnt = 0; hw_iic_slave_rx_prepare(IIC_S_DEV, 0); //触发下1 byte接收,并NACK } } } static void iic_slave_task(void *arg) { int res; int msg[8]; u32 rxlen = 0; u32 txlen = 0; u8 *addr; printf("iic_slave_task run\n"); memset(&iic_s, 0, sizeof(struct iic_slave)); iic_s.dir = IIC_S_RX; iic_s.rx.buf[0] = iic_s_rxbuf[0]; iic_s.rx.buf[1] = iic_s_rxbuf[1]; iic_s.rx.b_size = IIC_S_RXBUF_SIZE; iic_s.tx.buf[0] = iic_s_txbuf[0]; iic_s.tx.buf[1] = iic_s_txbuf[1]; iic_s.tx.b_size = IIC_S_TXBUF_SIZE; hw_iic_init(IIC_S_DEV); //设置IIC从机地址,并且使能地址包自动ACK hw_iic_slave_set_addr(IIC_S_DEV, IIC_S_WADDR, 1); //注册中断isr request_irq(IRQ_IIC_IDX, 3, iic_slave_isr, 0); //使能byte传输中断 hw_iic_set_ie(IIC_S_DEV, 1); //使能stop中断 hw_iic_set_end_ie(IIC_S_DEV, 1); //请求接收,禁止ACK,避免与地址包自动ACK冲突。bit ACK需要在接收数据前设置,关 //闭ACK并打开地址包自动ACK是为了挂多个IIC从机时,本IIC从机不会ACK其他IIC设备 //地址,造成多从机失效。地址包自动ACK只有当从机收到设置的地址才会ACK,否则 //NACK hw_iic_slave_rx_prepare(IIC_S_DEV, 0); __asm__ volatile("%0 = icfg" : "=r"(res)); printf("icfg = %08x\n", res); while (1) { res = os_taskq_pend("taskq", msg, 8); switch (res) { case OS_TASKQ: switch (msg[0]) { case Q_MSG: switch (msg[1]) { case IIC_S_MSG_RX: puts(">>>>>> iic_s rx msg\n"); rxlen = msg[2]; addr = iic_s.rx.buf[!iic_s.rx.toggle]; printf("rx len: %d\n", rxlen); //put_buf(addr, rxlen); for (int i = 0; i < rxlen; i++) { putchar(*(addr + i)); putchar(0x20); if (i % 16 == 15) { putchar('\n'); } } break; case IIC_S_MSG_TX: puts(">>>>>> iic_s tx msg\n"); txlen = rxlen; memcpy(iic_s.tx.buf[!iic_s.tx.toggle], iic_s.rx.buf[!iic_s.rx.toggle], txlen); iic_s.tx.tx_cnt = txlen; hw_iic_slave_tx_byte(IIC_S_WADDR, iic_s.tx.buf[!iic_s.tx.toggle][0]); iic_s.tx.toggle = !iic_s.tx.toggle; printf("tx req len: %d\n", txlen); break; } break; } break; } } } void iic_demo_slave_main() { printf("%s() %d\n", __func__, __LINE__); os_task_create(iic_slave_task, NULL, 30, 1024, 64, "iic_slave"); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~ iic host below ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #define IIC_H_DEV 0 #define IIC_H_TXBUF_SIZE 128 #define IIC_H_RXBUF_SIZE 128 #define IIC_H_DELAY 500 #define iic_h_check_ack(ack, pCnt) \ if (!(ack)) { \ printf("nack %d\n", __LINE__); \ if (*(pCnt) > 0 && --(*(pCnt))) { \ continue; \ } else { \ break; \ } \ } static u8 iic_h_txbuf[IIC_H_TXBUF_SIZE]; static u8 iic_h_rxbuf[IIC_H_RXBUF_SIZE]; void iic_demo_host_main() { int i; int ret; u32 retry; u8 byte; printf("%s() %d\n", __func__, __LINE__); hw_iic_init(IIC_H_DEV); for (i = 0; i < IIC_H_TXBUF_SIZE; i++) { iic_h_txbuf[i] = 'A' + i % 26; } for (u8 times = 0; times < 3; times++) { //测试次数 retry = 10; do { hw_iic_start(IIC_H_DEV); putchar('a'); ret = hw_iic_tx_byte(IIC_H_DEV, IIC_S_WADDR); putchar('b'); iic_h_check_ack(ret, &retry); delay(IIC_H_DELAY); i = 0; while (i < IIC_H_TXBUF_SIZE) { ret = hw_iic_tx_byte(IIC_H_DEV, iic_h_txbuf[i]); putchar('c'); iic_h_check_ack(ret, &retry); delay(IIC_H_DELAY); i++; } break; } while (1); hw_iic_stop(IIC_H_DEV); delay(IIC_H_DELAY); //stop后需要delay一段时间后再start retry = 10; do { hw_iic_start(IIC_H_DEV); putchar('d'); ret = hw_iic_tx_byte(IIC_H_DEV, IIC_S_RADDR); putchar('e'); iic_h_check_ack(ret, &retry); delay(IIC_H_DELAY); i = 0; while (i < IIC_H_RXBUF_SIZE - 1) { iic_h_rxbuf[i] = hw_iic_rx_byte(IIC_H_DEV, 1); putchar('f'); delay(IIC_H_DELAY); i++; } iic_h_rxbuf[i] = hw_iic_rx_byte(IIC_H_DEV, 0); //IIC主机接收最后1 byte NACK putchar('g'); delay(IIC_H_DELAY); break; } while (1); hw_iic_stop(IIC_H_DEV); //stop后需要delay一段时间后再start delay(IIC_H_DELAY); putchar('\n'); ret = 0; for (i = 0; i < IIC_H_RXBUF_SIZE; i++) { putchar(iic_h_rxbuf[i]); putchar(0x20); if (i % 16 == 15) { putchar('\n'); } if (iic_h_txbuf[i] != iic_h_rxbuf[i]) { ret = 1; } } if (!ret) { puts("iic slave test pass\n"); } else { puts("iic slave test fail\n"); } } } #endif