端口驱动函数:
/**===================================================================== 驱动功能函数 SCLK: ──┐ SCLK: ┌── └─ ─┘ DIN: ─┐ //开始 DIN: ┌─ //结束 └── ──┘ 7 6 5 4 3 2 1 0 传输: ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─ ──┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ ┌───┐───┌───┐───┌───┐───┌───┐───┐ ┌ ─┘───└───┘───└───┘───└───┘───└───┘─┘ 7 6 5 4 3 2 1 0=====================================================================**///开始-----------------------------void start(void){ //首次开始前(初始化期间)先触发1次结束, 以使SCLK=1, DIN=1 DisDIN = 0; //DIN由H→L, 开始 DisCLK = 0; //置SCLK为L, 为发送DIN位作准备}//发送数据-------------------------void Send_DisDat(u8 dx){ u8 i; for(i=0; i<8; i++){ //开始时已置SCLK=0 DisDIN = (bit)(dx & 0x01); //发送位, 低位先传 DisCLK = 1; //位发送结束-DIN信号保持 dx >>= 1; DisCLK = 0; //置SCLK=0, 为发送下一位/结束准备 } //DisDIN = 0; //该条语句与结束函数中的首条重复, 故可省掉}//结束-----------------------------void stop(void){ DisDIN = 0; DisCLK = 1; //置SCLK为H-结束准备 DisDIN = 1; //DIN由L→H, 结束, 为下一次开始准备}注:以上三段函数省掉了不必要的语句,但须在初始化阶段完成端口电平的就绪状态(也即是结束数据传输后的状态)。
实际运行的数据信号波形(黄色为SCLK,青色为DIN):
提示:根据TM1640的规格书说明——CLK为高电平时,DIN高电平->低电平即为开始;CLK为高电平时,DIN低电平->高电平即为结束。换句话说就是,TM1640在下降沿捕捉开始信号,上升沿捕捉结束信号,且开始/结束信号的捕捉区间为CLK处于高电平时。 CLK处于低电平时为 数据信号的捕捉区间,且捕捉发生在CLK的上升沿。
为何要分为三段函数,而不是将它们合为一个函数?
因为在发送位数据的过程中有单字节和多字节数据 的区分,当然可以使用2层循环及使用指针传送单字节数据或数组地址来解决,比如下面的代码。但是,使用2层循环会涉及循环变量及必要的逻辑判断,且会消耗过多的执行时间,单单循环内末语句结束到退出循环——循环结束,就得10来个机器周期的时间,还不如使用函数调用(2~4个机器周期即完成)。
提示:对于底层操作代码,每一个多余的指令都会对直接影响上层操作代码的执行效率——若底层代码包含1条冗余代码,上层代码调用时可能会执行n多次该冗余代码。 简单来说,“一个很小的基数乘以一个很大的倍数”的问题对于底层 代码来说应需重视。
//数据紧凑发送---------------------void sendDisDat(u8 *p){ u8 i, j, dat, len; len = sizeof(p); //---------------------------开始 DisDIN = 0; //DIN由H→L, 开始 DisCLK = 0; //置SCLK为L, 为发送DIN位作准备 //-----------------------发送数据 for(i=0; i<len; i++){ //开始时已置SCLK=0 if(len==1){dat = p;}else{dat = p[i];} //单字节时指针值即为数据值,多字节时指针值为数组地址 for(j=0; j<8; j++){ DisDIN = (bit)(dat & 0x01); //发送位 DisCLK = 1; //位发送结束-DIN信号保持 dat >>= 1; DisCLK = 0; //置SCLK=0, 为发送下一位/结束准备 } } //---------------------------结束 DisDIN = 0; DisCLK = 1; //置SCLK为H-结束准备 DisDIN = 1; //DIN由H→L, 结束, 为下一次开始准备}
数据据操作函数:
/**===================================================================== 函数名称: Update_DisDat 输 入: addr-显存地址, dx-8位地址数据 功能描述: 更新dx指定的地址的数据 说 明: =====================================================================**/void Update_DisDat(u8 addr, u8 dx){ start(); Send_DisDat(DATA_MODE_FIXED); stop(); //固定地址 start(); Send_DisDat(addr); //指定地址 Send_DisDat(DisBuf[dx]); //数据传输-1字符 stop();}/**===================================================================== 函数名称: UpdateAllDisDat 输 入: 无 功能描述: 更新所有显示数据 说 明: =====================================================================**/void UpdateAllDisDat(void){ u8 i; //start(); Send_DisDat(DIS_DIS); stop(); //显示关 start(); Send_DisDat(DATA_MODE_AUTO); stop(); //自动地址 start(); Send_DisDat(FIRST_ADDR); //起始地址 for(i=0; i<15; i++){ //数据传输开始-15字符 Send_DisDat(DisBuf[i]); } stop(); //数据传输结束 //start(); Send_DisDat(DIS_EN); stop(); //显示开}/**===================================================================== 函数名称: DisBufFresh 输 入: dat[]-计数器 功能描述: 错误时对应的编号闪烁 说 明: =====================================================================**/void DisBufFresh(u8 *dat){ //显示缓冲更新代码}初始化函数:
/**===================================================================== 函数名称: Dis_Init() 输 入: 无 功能描述: 显示缓冲及驱动初始化 说 明: =====================================================================**/void DisBufInit(void){ u8 i; stop(); //触发1次结束, 以使SCLK=1, DIN=1 start(); Send_DisDat(DIS_EN); stop(); //显示开 for(i=0; i<16; i++){ DisBuf[i] = SegCode[24]; //全部熄灭 } UpdateAllDisDat();}